Start integration of tiny backend.

This commit is contained in:
Christoffer Lerno
2021-11-19 09:52:14 +01:00
parent 0af448ee71
commit 86072ae21f
8 changed files with 1072 additions and 7 deletions

View File

@@ -111,6 +111,7 @@ add_executable(c3c
src/compiler/compiler.h
src/compiler/types.c
src/compiler/module.c
src/compiler/tb_codegen.c
src/compiler/llvm_codegen.c
src/utils/stringutils.c
src/utils/find_msvc.c
@@ -162,6 +163,9 @@ endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tb/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")

View File

@@ -69,6 +69,7 @@ static void usage(void)
OUTPUT(" headers <file1> [<file2> ...] Analyse files and generate C headers for public methods.");
OUTPUT("");
OUTPUT("Options:");
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
OUTPUT(" --lib <dir> - Use this directory as the C3 library path.");
OUTPUT(" --path <dir> - Use this as the base directory for the current command.");
@@ -397,6 +398,15 @@ static void parse_option(BuildOptions *options)
options->compile_option = COMPILE_LEX_PARSE_ONLY;
return;
case '-':
if (match_longopt("tinybackend"))
{
#if TB_BACKEND
options->backend = BACKEND_TB;
return;
#else
error_exit("error: The TinyBackend is not supported on this platform.");
#endif
}
if (match_longopt("version"))
{
print_version();
@@ -527,6 +537,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
.command = COMMAND_MISSING,
.pie = PIE_DEFAULT,
.pic = PIC_DEFAULT,
.backend = BACKEND_LLVM,
.files = NULL
};
for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++)

View File

@@ -11,6 +11,13 @@
#define MAX_FILES 2048
#define MAX_THREADS 0xFFFF
#define TB_BACKEND 0
typedef enum
{
BACKEND_LLVM,
BACKEND_TB
} CompilerBackend;
typedef enum
{
@@ -180,6 +187,7 @@ typedef struct BuildOptions_
const char* target_select;
const char* path;
unsigned version;
CompilerBackend backend;
CompilerCommand command;
CompileOption compile_option;
PieGeneration pie;
@@ -231,6 +239,7 @@ typedef struct
PieGeneration pie;
PicGeneration pic;
ArchOsTarget arch_os_target;
CompilerBackend backend;
uint32_t symtab_size;
struct
{

View File

@@ -45,6 +45,8 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
break;
}
target->backend = options->backend;
// Copy optimization levels.
switch (options->optimization_setting_override)
{

View File

@@ -109,12 +109,18 @@ typedef struct CompileData_
Task task;
} CompileData;
void thread_compile_task(void *compiledata)
void thread_compile_task_llvm(void *compiledata)
{
CompileData *data = compiledata;
data->object_name = llvm_codegen(data->context);
}
void thread_compile_task_tb(void *compiledata)
{
CompileData *data = compiledata;
data->object_name = tinybackend_codegen(data->context);
}
void sema_analyze_stage(Module *module, AnalysisStage stage)
{
while (module->stage < stage)
@@ -279,6 +285,7 @@ static void setup_bool_define(const char *id, bool value)
error_exit("Redefined ident %s", id);
}
}
void compiler_compile(void)
{
setup_int_define("C_SHORT_SIZE", platform_target.width_c_short, type_long);
@@ -349,16 +356,33 @@ void compiler_compile(void)
return;
}
llvm_codegen_setup();
void **gen_contexts = NULL;
void (*task)(void *arg);
for (unsigned i = 0; i < module_count; i++)
switch (active_target.backend)
{
void *result = llvm_gen(modules[i]);
if (result) vec_add(gen_contexts, result);
case BACKEND_LLVM:
llvm_codegen_setup();
for (unsigned i = 0; i < module_count; i++)
{
void *result = llvm_gen(modules[i]);
if (result) vec_add(gen_contexts, result);
}
task = &thread_compile_task_llvm;
break;
case BACKEND_TB:
tinybackend_codegen_setup();
for (unsigned i = 0; i < module_count; i++)
{
void *result = tinybackend_gen(modules[i]);
if (result) vec_add(gen_contexts, result);
}
task = &thread_compile_task_tb;
break;
}
if (debug_stats)
{
printf("-- AST/EXPR INFO -- \n");
@@ -398,10 +422,11 @@ void compiler_compile(void)
TaskQueueRef queue = taskqueue_create(16);
for (unsigned i = 0; i < output_file_count; i++)
{
compile_data[i] = (CompileData) { .context = gen_contexts[i] };
compile_data[i].task = (Task) { &thread_compile_task, &compile_data[i] };
compile_data[i].task = (Task) { task, &compile_data[i] };
taskqueue_add(queue, &compile_data[i].task);
}

View File

@@ -1752,6 +1752,10 @@ const char *llvm_codegen(void *context);
void *llvm_gen(Module *module);
void llvm_codegen_setup();
const char *tinybackend_codegen(void *context);
void *tinybackend_gen(Module *module);
void tinybackend_codegen_setup();
void header_gen(Module *module);
void global_context_add_type(Type *type);

695
src/compiler/tb_codegen.c Normal file
View File

@@ -0,0 +1,695 @@
#include "codegen_internal.h"
#if TB_BACKEND
#include <tb.h>
typedef struct
{
Module *code_module;
const char *object_filename;
TB_Module *module;
TB_FeatureSet features;
Decl *current_func_decl;
TB_Function *function;
} GenContext;
typedef enum
{
BE_VALUE,
BE_ADDRESS,
BE_ADDRESS_FAILABLE,
BE_BOOLEAN,
} BackendValueKind;
typedef struct
{
BackendValueKind kind: 5;
AlignSize alignment;
Type *type; // Should never be a distinct or canonical type.
TB_Register value;
TB_Register failable; // TODO what even is a failable?
} BEValue;
static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr);
// Per instance i think?
void tinybackend_codegen_setup()
{
}
static inline bool tinybackend_value_is_addr(BEValue *value)
{ return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_FAILABLE; }
static inline bool tinybackend_value_is_bool(BEValue *value)
{ return value->kind == BE_BOOLEAN; }
static TB_DataType tinybackend_get_type(Type *type)
{
switch (type->type_kind)
{
case TYPE_U8:
case TYPE_I8:
return TB_TYPE_I8(1);
case TYPE_U16:
case TYPE_I16:
return TB_TYPE_I16(1);
case TYPE_U32:
case TYPE_I32:
return TB_TYPE_I32(1);
case TYPE_U64:
case TYPE_I64:
return TB_TYPE_I64(1);
default:
TODO
}
}
static inline TB_Register decl_ref(Decl *decl)
{
if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_ref(decl->var.alias);
return (TB_Register)((uintptr_t)decl->backend_ref);
}
TB_Register
tinybackend_emit_load_aligned(GenContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment, const char *name)
{
TB_Register value = tb_inst_load(c->function, dt, pointer, alignment);
return value;
}
void tinybackend_store_self_aligned(GenContext *c, TB_Register pointer, TB_Register value, Type *type)
{
tb_inst_store(c->function, tinybackend_get_type(type), pointer, value, type_abi_alignment(type));
}
void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destination, BEValue *value)
{
assert(tinybackend_value_is_addr(destination));
if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type))
{
value->value = tinybackend_emit_load_aligned(c,
tinybackend_get_type(value->type),
value->value,
value->alignment,
"");
value->kind = BE_VALUE;
}
AlignSize alignment = destination->alignment;
switch (value->kind)
{
case BE_BOOLEAN:
value->value = tb_inst_zxt(c->function, value->value, TB_TYPE_I8(1));
value->kind = BE_VALUE;
FALLTHROUGH;
case BE_VALUE:
tb_inst_store(c->function,
tinybackend_get_type(destination->type),
destination->value,
value->value,
alignment ? alignment : type_abi_alignment(value->type));
return;
case BE_ADDRESS:
{
// Here we do an optimized(?) memcopy.
ByteSize size = type_size(value->type);
TB_Register copy_size = tb_inst_iconst(c->function,
size <= UINT32_MAX ? TB_TYPE_I32(1) : TB_TYPE_I64(1),
size);
TB_Register source = value->value;
tb_inst_memcpy(c->function,
destination->value,
value->value, copy_size,
value->alignment ? value->alignment : type_abi_alignment(value->type));
return;
}
default:
UNREACHABLE
}
}
void tinybackend_value_set_address(BEValue *value, TB_Register tb_value, Type *type)
{
value->value = tb_value;
value->alignment = type_abi_alignment(type);
value->kind = BE_ADDRESS;
value->type = type_lowering(type);
}
void tinybackend_value_set_decl_address(BEValue *value, Decl *decl)
{
decl = decl_flatten(decl);
tinybackend_value_set_address(value, decl_ref(decl), decl->type);
value->alignment = decl->alignment;
}
void tinybackend_value_set(BEValue *value, TB_Register reg, Type *type)
{
type = type_lowering(type);
assert(reg || type == type_void);
value->value = reg;
value->alignment = type_abi_alignment(type);
value->kind = BE_VALUE;
value->type = type;
}
static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *expr)
{
Type *type = type_reduced_from_expr(expr)->canonical;
switch (expr->const_expr.const_kind)
{
case CONST_INTEGER:
{
TB_Register reg;
Int128 i = expr->const_expr.ixx.i;
TB_DataType dt = tinybackend_get_type(type);
printf("Const int type: %d\n", dt.type);
switch (expr->const_expr.ixx.type)
{
case TYPE_I128:
case TYPE_U128:
reg = tb_inst_iconst128(c->function, dt, (TB_Int128){ .lo = i.low, .hi = i.high });
break;
default:
reg = tb_inst_iconst(c->function, dt, i.low);
break;
}
tinybackend_value_set(value, reg, type);
return;
}
default:
TODO
}
}
BEValue tinybackend_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, TB_Register failable)
{
assert(ref->kind == BE_ADDRESS || ref->kind == BE_ADDRESS_FAILABLE);
BEValue value;
tinybackend_emit_expr(c, &value, expr);
tinybackend_store_bevalue(c, tinybackend_get_type(ref->type), ref, &value);
return value;
}
static int find_member_index(Decl *parent, Decl *member)
{
VECEACH(parent->strukt.members, i)
{
Decl *maybe_member = parent->strukt.members[i];
if (member == maybe_member)
{
return (int)i;
}
if (!maybe_member->name)
{
if (find_member_index(maybe_member, member) != -1) return (int)i;
}
}
return -1;
}
static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member)
{
assert(member->resolve_status == RESOLVE_DONE);
Decl *found = NULL;
do
{
int index = find_member_index(parent, member);
assert(index > -1);
found = parent->strukt.members[index];
// Unlike LLVM, TinyBackend doesn't handle aggregates for you
// so we need offsetof to perform member accesses
switch (parent->type->canonical->type_kind)
{
case TYPE_UNION:
{
TODO
}
break;
case TYPE_STRUCT:
{
TB_Register ref = tb_inst_member_access(c->function, value->value, found->offset);
tinybackend_value_set_address(value, ref, member->type);
value->alignment = found->alignment;
}
break;
default:
UNREACHABLE
}
parent = found;
} while (found != member);
}
void tinybackend_emit_access_addr(GenContext *c, BEValue *be_value, Expr *expr)
{
Expr *parent = expr->access_expr.parent;
tinybackend_emit_expr(c, be_value, parent);
Decl *member = expr->access_expr.ref;
tinybackend_emit_member_addr(c, be_value, type_lowering(parent->type)->decl, member);
}
static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type)
{
if (active_target.feature.trap_on_wrap)
{
return type_is_unsigned(type) ? TB_UNSIGNED_TRAP_ON_WRAP : TB_SIGNED_TRAP_ON_WRAP;
}
return TB_CAN_WRAP;
}
void tinybackend_value_rvalue(GenContext *c, BEValue *value)
{
if (value->kind != BE_ADDRESS && value->kind != BE_ADDRESS_FAILABLE)
{
if (value->type->type_kind == TYPE_BOOL && value->kind != BE_BOOLEAN)
{
//value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
//value->kind = BE_BOOLEAN;
TODO
}
return;
}
//llvm_value_fold_failable(c, value);
value->value = tinybackend_emit_load_aligned(c,
tinybackend_get_type(value->type),
value->value,
value->alignment ? value->alignment : type_abi_alignment(value->type),
"");
if (value->type->type_kind == TYPE_BOOL)
{
//value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
//value->kind = BE_BOOLEAN;
//return;
TODO
}
value->kind = BE_VALUE;
}
void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op)
{
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
{
//gencontext_emit_logical_and_or(c, be_value, expr, binary_op);
//return;
TODO
}
BEValue lhs;
if (lhs_loaded)
{
lhs = *lhs_loaded;
}
else
{
tinybackend_emit_expr(c, &lhs, expr->binary_expr.left);
}
tinybackend_value_rvalue(c, &lhs);
BEValue rhs;
tinybackend_emit_expr(c, &rhs, expr->binary_expr.right);
tinybackend_value_rvalue(c, &rhs);
/*EMIT_LOC(c, expr);
if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
{
llvm_emit_comparison(c, be_value, &lhs, &rhs, binary_op);
return;
}*/
Type *lhs_type = lhs.type;
Type *rhs_type = rhs.type;
Type *vector_type = lhs_type->type_kind == TYPE_VECTOR ? lhs_type->vector.base : NULL;
bool is_float = type_is_float(lhs_type) || (vector_type && type_is_float(vector_type));
TB_Register val = TB_NULL_REG;
TB_Register lhs_value = lhs.value;
TB_Register rhs_value = rhs.value;
TB_DataType dt = tinybackend_get_type(lhs_type);
switch (binary_op)
{
case BINARYOP_ERROR:
UNREACHABLE
case BINARYOP_MULT:
if (is_float)
{
val = tb_inst_fmul(c->function, dt, lhs_value, rhs_value);
break;
}
// TODO(NeGate): review this later, maybe it shouldn't be NO_WRAP
val = tb_inst_mul(c->function, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
break;
case BINARYOP_SUB:
if (lhs_type->type_kind == TYPE_POINTER)
{
/*bool is_constant = LLVMIsConstant(lhs_value) && LLVMIsConstant(rhs_value);
if (lhs_type == rhs_type)
{
LLVMTypeRef int_type = llvm_get_type(c, type_iptrdiff);
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_type, ""),
LLVMBuildPtrToInt(c->builder, rhs_value, int_type, ""), "");
val = LLVMBuildExactSDiv(c->builder, val, llvm_const_int(c, type_iptrdiff, type_abi_alignment(lhs_type->pointer)), "");
break;
}
rhs_value = LLVMConstNeg(rhs_value);
val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value);*/
TODO
}
if (is_float)
{
val = tb_inst_fsub(c->function, dt, lhs_value, rhs_value);
break;
}
val = tb_inst_mul(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
break;
case BINARYOP_ADD:
if (lhs_type->type_kind == TYPE_POINTER)
{
TODO
}
if (is_float)
{
val = tb_inst_fadd(c->function, dt, lhs_value, rhs_value);
break;
}
val = tb_inst_add(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
break;
case BINARYOP_DIV:
//llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", TOKLOC(expr->span.loc));
if (is_float)
{
val = tb_inst_fdiv(c->function, dt, lhs_value, rhs_value);
break;
}
val = tb_inst_div(c->function, dt, lhs_value, rhs_value, !type_is_unsigned(lhs_type));
break;
case BINARYOP_MOD:
{
TODO
}
case BINARYOP_SHR:
if (type_is_unsigned(lhs_type))
{
val = tb_inst_shr(c->function, dt, lhs_value, rhs_value);
return;
}
val = tb_inst_sar(c->function, dt, lhs_value, rhs_value);
break;
case BINARYOP_SHL:
val = tb_inst_shl(c->function, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
break;
case BINARYOP_BIT_AND:
val = tb_inst_and(c->function, dt, lhs_value, rhs_value);
break;
case BINARYOP_BIT_OR:
val = tb_inst_or(c->function, dt, lhs_value, rhs_value);
break;
case BINARYOP_BIT_XOR:
TODO
break;
case BINARYOP_EQ:
case BINARYOP_NE:
case BINARYOP_GE:
case BINARYOP_GT:
case BINARYOP_LE:
case BINARYOP_LT:
UNREACHABLE
case BINARYOP_AND:
case BINARYOP_OR:
case BINARYOP_ASSIGN:
case BINARYOP_MULT_ASSIGN:
case BINARYOP_ADD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
case BINARYOP_DIV_ASSIGN:
case BINARYOP_MOD_ASSIGN:
case BINARYOP_BIT_AND_ASSIGN:
case BINARYOP_BIT_OR_ASSIGN:
case BINARYOP_BIT_XOR_ASSIGN:
case BINARYOP_SHR_ASSIGN:
case BINARYOP_SHL_ASSIGN:
UNREACHABLE
}
assert(val);
tinybackend_value_set(be_value, val, expr->type);
}
void tinybackend_value_addr(GenContext *c, BEValue *value)
{
if (value->kind == BE_ADDRESS) return;
TB_Register temp = tb_inst_local(c->function, type_size(value->type), type_alloca_alignment(value->type));
tinybackend_store_self_aligned(c, temp, value->value, value->type);
tinybackend_value_set_address(value, temp, value->type);
}
static void tinybackend_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr)
{
BinaryOp binary_op = expr->binary_expr.operator;
/*if (binary_op >= BINARYOP_ASSIGN && expr_is_vector_index(expr->binary_expr.left))
{
tinybackend_emit_vector_assign_expr(c, be_value, expr);
return;
}*/
printf("A\n");
if (binary_op > BINARYOP_ASSIGN)
{
BinaryOp base_op = binaryop_assign_base_op(binary_op);
assert(base_op != BINARYOP_ERROR);
BEValue addr;
tinybackend_emit_expr(c, &addr, expr->binary_expr.left);
tinybackend_value_addr(c, &addr);
tinybackend_emit_binary(c, be_value, expr, &addr, base_op);
tinybackend_store_bevalue(c, tinybackend_get_type(addr.type), &addr, be_value);
return;
}
printf("B\n");
if (binary_op == BINARYOP_ASSIGN)
{
Expr *left = expr->binary_expr.left;
tinybackend_emit_expr(c, be_value, expr->binary_expr.left);
assert(tinybackend_value_is_addr(be_value));
*be_value = tinybackend_emit_assign_expr(c, be_value, expr->binary_expr.right, TB_NULL_REG /* failable_ref */);
return;
}
tinybackend_emit_binary(c, be_value, expr, NULL, binary_op);
}
static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr)
{
printf("expr->expr_kind = %d\n", expr->expr_kind);
switch (expr->expr_kind)
{
case EXPR_CONST:
tinybackend_emit_const_expr(c, value, expr);
return;
case EXPR_ACCESS:
tinybackend_emit_access_addr(c, value, expr);
return;
case EXPR_BINARY:
tinybackend_emit_binary_expr(c, value, expr);
return;
case EXPR_IDENTIFIER:
case EXPR_CONST_IDENTIFIER:
tinybackend_value_set_decl_address(value, expr->identifier_expr.decl);
return;
default:
TODO
}
}
static TB_Register tinybackend_emit_local_decl(GenContext *c, Decl *decl)
{
if (decl->var.is_static)
{
TODO
}
TB_DataType dt = tinybackend_get_type(decl->type);
ByteSize size = type_size(decl->type);
AlignSize align = decl->alignment;
TB_Register reg = tb_inst_local(c->function, size, align);
decl->backend_ref = (void *)((size_t)reg);
printf("Local declare: %s (size: %ld, align: %u)\n", decl->type->name, (long)size, align);
if (decl->var.failable_ref)
{
TODO
}
Expr *init = decl->var.init_expr;
if (init)
{
// If we don't have undef, then make an assign.
if (init->expr_kind != EXPR_UNDEF)
{
BEValue value;
tinybackend_value_set_decl_address(&value, decl);
tinybackend_emit_assign_expr(c,
&value,
decl->var.init_expr,
(TB_Register)((uintptr_t)decl->var.failable_ref));
}
}
else
{
Type *type = type_lowering(decl->type);
// Normal case, zero init.
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
{
tb_inst_store(c->function, dt, reg, tb_inst_iconst(c->function, dt, 0), align);
}
else
{
TODO
}
}
return reg;
}
static void tinybackend_emit_stmt(GenContext *c, Ast *ast)
{
switch (ast->ast_kind)
{
case AST_DECLARE_STMT:
tinybackend_emit_local_decl(c, ast->declare_stmt);
break;
case AST_RETURN_STMT:
//gencontext_emit_return(c, ast);
tb_inst_ret(c->function, TB_TYPE_VOID(), TB_NULL_REG);
break;
default:
break;
}
}
static void tinybackend_emit_function_body(GenContext *c, Decl *decl)
{
printf("Function: %s\n", decl->external_name);
c->current_func_decl = decl;
// TODO(NeGate): Actually interpret the return value
c->function = tb_function_create(c->module, decl->external_name, TB_TYPE_VOID());
VECEACH(decl->func_decl.body->compound_stmt.stmts, i)
{
tinybackend_emit_stmt(c, decl->func_decl.body->compound_stmt.stmts[i]);
}
tb_function_print(c->function);
}
static void tinybackend_gen_context(GenContext *c)
{
scratch_buffer_clear();
StringSlice slice = strtoslice(c->code_module->name->module);
while (true)
{
StringSlice part = strnexttok(&slice, ':');
scratch_buffer_append_len(part.ptr, part.len);
if (!slice.len) break;
slice.ptr++;
slice.len--;
scratch_buffer_append_char('.');
}
const char *result = scratch_buffer_to_string();
// TODO filename should depend on platform.
c->object_filename = strformat("%s.o", result);
}
// Generate context per module (single threaded)
void *tinybackend_gen(Module *module)
{
GenContext *c = calloc(sizeof(GenContext), 1);
c->code_module = module;
// TODO identify target architecture
c->module = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_LINUX, &c->features, TB_OPT_O0, 1);
tinybackend_gen_context(c);
printf("Module: %.*s\n", module->name->len, module->name->module);
// Forward decls
VECEACH(module->contexts, j)
{
Context *context = module->contexts[j];
printf(" External symbols: %d\n", vec_size(context->external_symbol_list));
printf(" Functions: %d\n", vec_size(context->functions));
}
VECEACH(module->contexts, j)
{
Context *context = module->contexts[j];
VECEACH(context->functions, i)
{
Decl *decl = context->functions[i];
if (decl->func_decl.body) tinybackend_emit_function_body(c, decl);
}
}
tb_module_compile(c->module);
return c;
}
// Compile module (multi threaded)
const char *tinybackend_codegen(void *context)
{
GenContext *c = (GenContext *)context;
// Write out the object file
FILE *file = fopen(c->object_filename, "wb");
tb_module_export(c->module, file);
fclose(file);
return c->object_filename;
}
#else
const char *tinybackend_codegen(void *context)
{
FATAL_ERROR("Not enabled");
}
void *tinybackend_gen(Module *module)
{
FATAL_ERROR("Not enabled");
}
void tinybackend_codegen_setup()
{
FATAL_ERROR("TB not enabled");
}
#endif

315
tb/tb.h Normal file
View File

@@ -0,0 +1,315 @@
// _______ _ ____ _ _
// |__ __(_) | _ \ | | | |
// | | _ _ __ _ _| |_) | __ _ ___| | _____ _ __ __| |
// | | | | '_ \| | | | _ < / _` |/ __| |/ / _ \ '_ \ / _` |
// | | | | | | | |_| | |_) | (_| | (__| < __/ | | | (_| |
// |_| |_|_| |_|\__, |____/ \__,_|\___|_|\_\___|_| |_|\__,_|
// __/ |
// |___/
//
// It's a relatively small compiler backend all behind a
// simple C API! To get started: TODO
//
#ifndef _TINYBACKEND_H_
#define _TINYBACKEND_H_
// Windows likes it's secure functions, i kinda do too
// but only sometimes and this isn't one of them
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <inttypes.h>
#ifndef TB_MAX_THREADS
#define TB_MAX_THREADS 16
#endif
// Per-thread
#ifndef TB_TEMPORARY_STORAGE_SIZE
#define TB_TEMPORARY_STORAGE_SIZE (1 << 20)
#endif
#ifndef TB_MAX_FUNCTIONS
#define TB_MAX_FUNCTIONS (1 << 22)
#endif
#define TB_HOST_UNKNOWN 0
#define TB_HOST_X86_64 1
// While generating the IR, it's possible to
// perform some optimizations on the spot such
// as CSE and constant folding, if this define
// is 0, the CSE is disabled.
#define TB_FRONTEND_OPT 0
// If on, the labels aren't marked in the object file
// might save on performance at the cost of some assembly
// readability.
#define TB_STRIP_LABELS 0
#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
#define TB_HOST_ARCH TB_HOST_X86_64
#else
#define TB_HOST_ARCH TB_HOST_UNKNOWN
#endif
#define TB_API extern
#define TB_NULL_REG ((TB_Register)0)
#define TB_REG_MAX ((TB_Register)INT32_MAX)
typedef enum TB_ArithmaticBehavior {
// No overflow will assume the value does not
// overflow and if it does this can be considered
// undefined behavior with unknown consequences.
// no signed wrap
TB_ASSUME_NSW,
// no unsigned wrap
TB_ASSUME_NUW,
// Wrapping will allow the integer to safely wrap.
TB_CAN_WRAP,
// Overflow check will throw an error if the result
// cannot be represented in the resulting type.
TB_SIGNED_TRAP_ON_WRAP,
TB_UNSIGNED_TRAP_ON_WRAP,
// Saturated arithmatic will clamp the results in the
// event of overflow/underflow.
TB_SATURATED_UNSIGNED,
TB_SATURATED_SIGNED
} TB_ArithmaticBehavior;
typedef enum TB_Arch {
TB_ARCH_X86_64,
TB_ARCH_AARCH64
} TB_Arch;
typedef enum TB_System {
TB_SYSTEM_WINDOWS,
TB_SYSTEM_LINUX,
// TODO(NeGate): Actually implement these lol
TB_SYSTEM_MACOS,
TB_SYSTEM_ANDROID
} TB_System;
typedef enum TB_BranchHint {
TB_BRANCH_HINT_NONE,
TB_BRANCH_HINT_LIKELY,
TB_BRANCH_HINT_UNLIKELY
} TB_BranchHint;
typedef enum TB_OptLevel {
TB_OPT_O0,
// DCE
// CSE
// MEM2REG
TB_OPT_O1,
// DCE
// CSE
// MEM2REG
TB_OPT_SIZE,
// DCE
// CSE
// MEM2REG
// LOOP_UNROLL
TB_OPT_SPEED,
} TB_OptLevel;
enum {
TB_VOID,
// Boolean
TB_BOOL,
// Integers
TB_I8, TB_I16, TB_I32, TB_I64, TB_I128,
// IEEE 754 Floating point
TB_F32, TB_F64,
// Pointers
// NOTE(NeGate): consider support for multiple address spaces
TB_PTR,
TB_MAX_TYPES
};
#define TB_IS_INTEGER_TYPE(x) ((x) >= TB_I8 && (x) <= TB_I128)
#define TB_IS_FLOAT_TYPE(x) ((x) >= TB_F32 && (x) <= TB_F64)
#define TB_IS_POINTER_TYPE(x) ((x) == TB_PTR)
typedef struct TB_DataType {
uint8_t type;
uint8_t count; // 0 is illegal, except on VOID, it doesn't matter there
} TB_DataType;
typedef struct TB_Int128 {
uint64_t lo;
uint64_t hi;
} TB_Int128;
typedef struct TB_FeatureConstraints {
int max_vector_width[TB_MAX_TYPES];
} TB_FeatureConstraints;
typedef struct TB_FeatureSet {
struct {
bool sse3 : 1;
bool popcnt : 1;
bool lzcnt : 1;
bool sse41 : 1;
bool sse42 : 1;
bool clmul : 1;
bool f16c : 1;
bool bmi1 : 1;
bool bmi2 : 1;
bool avx : 1;
bool avx2 : 1;
} x64;
struct {
bool bf16 : 1;
} aarch64;
} TB_FeatureSet;
typedef int TB_Label;
typedef struct TB_SwitchEntry {
uint32_t key;
TB_Label value;
} TB_SwitchEntry;
typedef int TB_FileID;
typedef int TB_ExternalID;
typedef struct TB_Module TB_Module;
typedef struct TB_Function TB_Function;
typedef int32_t TB_Register;
typedef struct TB_FunctionOutput TB_FunctionOutput;
// *******************************
// Public macros
// *******************************
#define TB_TYPE_VOID() (TB_DataType){ .type = TB_VOID }
#define TB_TYPE_I8(c) (TB_DataType){ .type = TB_I8, .count = c }
#define TB_TYPE_I16(c) (TB_DataType){ .type = TB_I16, .count = c }
#define TB_TYPE_I32(c) (TB_DataType){ .type = TB_I32, .count = c }
#define TB_TYPE_I64(c) (TB_DataType){ .type = TB_I64, .count = c }
#define TB_TYPE_I128(c) (TB_DataType){ .type = TB_I128, .count = c }
#define TB_TYPE_F32(c) (TB_DataType){ .type = TB_F32, .count = c }
#define TB_TYPE_F64(c) (TB_DataType){ .type = TB_F64, .count = c }
#define TB_TYPE_BOOL(c) (TB_DataType){ .type = TB_BOOL, .count = c }
#define TB_TYPE_PTR() (TB_DataType){ .type = TB_PTR, .count = 1 }
// *******************************
// Public functions
// *******************************
TB_API void tb_get_constraints(TB_Arch target_arch, const TB_FeatureSet* features, TB_FeatureConstraints* constraints);
TB_API TB_Module* tb_module_create(TB_Arch target_arch, TB_System target_system, const TB_FeatureSet* features, int optimization_level, int max_threads);
TB_API bool tb_module_compile_func(TB_Module* m, TB_Function* f);
TB_API size_t tb_DEBUG_module_get_full_node_count(TB_Module* m);
TB_API void tb_module_destroy(TB_Module* m);
TB_API bool tb_module_compile(TB_Module* m);
TB_API bool tb_module_export(TB_Module* m, FILE* f);
TB_API void tb_module_export_jit(TB_Module* m);
TB_API void* tb_module_get_jit_func_by_name(TB_Module* m, const char* name);
TB_API void* tb_module_get_jit_func_by_id(TB_Module* m, size_t i);
TB_API void* tb_module_get_jit_func(TB_Module* m, TB_Function* f);
TB_API TB_Function* tb_function_create(TB_Module* m, const char* name, TB_DataType return_dt);
TB_API TB_ExternalID tb_module_extern(TB_Module* m, const char* name);
TB_API TB_FileID tb_register_file(TB_Module* m, const char* path);
TB_API TB_Label tb_get_current_label(TB_Function* f);
TB_API void tb_inst_loc(TB_Function* f, TB_FileID file, int line);
TB_API TB_Register tb_inst_param(TB_Function* f, TB_DataType dt);
TB_API TB_Register tb_inst_param_addr(TB_Function* f, TB_Register param);
TB_API TB_Register tb_inst_sxt(TB_Function* f, TB_Register src, TB_DataType dt);
TB_API TB_Register tb_inst_zxt(TB_Function* f, TB_Register src, TB_DataType dt);
TB_API TB_Register tb_inst_local(TB_Function* f, uint32_t size, uint32_t alignment);
TB_API TB_Register tb_inst_load(TB_Function* f, TB_DataType dt, TB_Register addr, uint32_t alignment);
TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Register addr, TB_Register val, uint32_t alignment);
TB_API TB_Register tb_inst_iconst(TB_Function* f, TB_DataType dt, uint64_t imm);
TB_API TB_Register tb_inst_iconst128(TB_Function* f, TB_DataType dt, TB_Int128 imm);
TB_API TB_Register tb_inst_fconst(TB_Function* f, TB_DataType dt, double imm);
TB_API TB_Register tb_inst_array_access(TB_Function* f, TB_Register base, TB_Register index, uint32_t stride);
TB_API TB_Register tb_inst_member_access(TB_Function* f, TB_Register base, int32_t offset);
TB_API TB_Register tb_inst_call(TB_Function* f, TB_DataType dt, const TB_Function* target, size_t param_count, const TB_Register* params);
TB_API TB_Register tb_inst_ecall(TB_Function* f, TB_DataType dt, const TB_ExternalID target, size_t param_count, const TB_Register* params);
TB_API void tb_inst_memset(TB_Function* f, TB_Register dst, TB_Register val, TB_Register size, int align);
TB_API void tb_inst_memcpy(TB_Function* f, TB_Register dst, TB_Register src, TB_Register size, int align);
TB_API TB_Register tb_inst_not(TB_Function* f, TB_DataType dt, TB_Register n);
TB_API TB_Register tb_inst_neg(TB_Function* f, TB_DataType dt, TB_Register n);
TB_API TB_Register tb_inst_and(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_or(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_xor(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_add(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, TB_ArithmaticBehavior arith_behavior);
TB_API TB_Register tb_inst_sub(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, TB_ArithmaticBehavior arith_behavior);
TB_API TB_Register tb_inst_mul(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, TB_ArithmaticBehavior arith_behavior);
TB_API TB_Register tb_inst_div(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, bool signedness);
TB_API TB_Register tb_inst_shl(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, TB_ArithmaticBehavior arith_behavior);
TB_API TB_Register tb_inst_sar(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_shr(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_fadd(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_fsub(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_fmul(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_fdiv(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_eq(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_ne(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_slt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_sle(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_sgt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_sge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_ult(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_ule(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_ugt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_uge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_flt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_fle(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_fgt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_cmp_fge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b);
TB_API TB_Register tb_inst_phi2(TB_Function* f, TB_DataType dt, TB_Label a_label, TB_Register a, TB_Label b_label, TB_Register b);
TB_API TB_Register tb_inst_label(TB_Function* f, TB_Label id);
TB_API void tb_inst_goto(TB_Function* f, TB_Label id);
TB_API TB_Register tb_inst_if(TB_Function* f, TB_Register cond, TB_Label if_true, TB_Label if_false);
TB_API void tb_inst_switch(TB_Function* f, TB_DataType dt, TB_Register key, TB_Label default_label, size_t entry_count, const TB_SwitchEntry* entries);
TB_API void tb_inst_ret(TB_Function* f, TB_DataType dt, TB_Register value);
TB_API void tb_function_print(TB_Function* f);
#endif /* _TINYBACKEND_H_ */