mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Start integration of tiny backend.
This commit is contained in:
@@ -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/")
|
||||
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
695
src/compiler/tb_codegen.c
Normal 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
315
tb/tb.h
Normal 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_ */
|
||||
Reference in New Issue
Block a user