mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Removing tb codegen info, because it's sure to have code-rotted by now.
This commit is contained in:
@@ -82,7 +82,6 @@ static void usage(void)
|
||||
PRINTF(" project <subcommand> ... Manipulate or view project files.");
|
||||
PRINTF("");
|
||||
PRINTF("Options:");
|
||||
PRINTF(" --tb - Use Tilde Backend for compilation.");
|
||||
PRINTF(" --stdlib <dir> - Use this directory as the C3 standard library path.");
|
||||
PRINTF(" --no-entry - Do not generate (or require) a main function.");
|
||||
PRINTF(" --libdir <dir> - Add this directory to the C3 library search paths.");
|
||||
@@ -696,11 +695,6 @@ static void parse_option(BuildOptions *options)
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (match_longopt("tb"))
|
||||
{
|
||||
options->backend = BACKEND_TB;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("max-mem"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --max-mem needs a valid integer.");
|
||||
|
||||
@@ -1,658 +0,0 @@
|
||||
#include "tilde_internal.h"
|
||||
|
||||
|
||||
|
||||
static TB_System os_to_tilde_system(OsType target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case OS_TYPE_LINUX:
|
||||
return TB_SYSTEM_LINUX;
|
||||
case OS_TYPE_MACOSX:
|
||||
return TB_SYSTEM_MACOS;
|
||||
case OS_TYPE_WIN32:
|
||||
return TB_SYSTEM_WINDOWS;
|
||||
case OS_TYPE_WASI:
|
||||
case OS_TYPE_EMSCRIPTEN:
|
||||
return TB_SYSTEM_WEB;
|
||||
default:
|
||||
error_exit("Unsupported system for TB compilation, use LLVM.");
|
||||
}
|
||||
}
|
||||
static TB_Arch arch_to_tilde_arch(ArchType target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case ARCH_TYPE_AARCH64:
|
||||
return TB_ARCH_AARCH64;
|
||||
case ARCH_TYPE_X86_64:
|
||||
return TB_ARCH_X86_64;
|
||||
case ARCH_TYPE_WASM32:
|
||||
return TB_ARCH_WASM32;
|
||||
default:
|
||||
error_exit("Unsupported architecture for TB compilation, use LLVM.");
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static void tilde_emit_type_decls(TildeContext *c, Decl *decl)
|
||||
{
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_POISONED:
|
||||
UNREACHABLE;
|
||||
case DECL_FUNC:
|
||||
// TODO
|
||||
break;
|
||||
case DECL_VAR:
|
||||
// TODO
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
break;
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_FAULTVALUE:
|
||||
// TODO
|
||||
break;;
|
||||
case DECL_DISTINCT:
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
case DECL_ENUM:
|
||||
case DECL_FAULT:
|
||||
case DECL_BITSTRUCT:
|
||||
tilde_get_typeid(c, decl->type);
|
||||
break;
|
||||
case DECL_BODYPARAM:
|
||||
case NON_TYPE_DECLS:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
TB_Reg tilde_get_next_param(TildeContext *c, unsigned *index)
|
||||
{
|
||||
return tb_inst_param(c->f, (*index)++);
|
||||
}
|
||||
|
||||
static inline void tilde_process_parameter_value(TildeContext *c, Decl *decl, ABIArgInfo *info, unsigned *index)
|
||||
{
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
return;
|
||||
case ABI_ARG_INDIRECT:
|
||||
// Indirect is caller copied.
|
||||
decl->tb_register = tilde_get_next_param(c, index);
|
||||
return;
|
||||
/*
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
// Create the expand type:
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
LLVMValueRef temp = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce");
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
|
||||
AlignSize alignment = decl->alignment;
|
||||
AlignSize element_align;
|
||||
LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.lo_index, alignment, &element_align);
|
||||
llvm_store_to_ptr_raw_aligned(c, gep_first, llvm_get_next_param(c, index), element_align);
|
||||
if (abi_type_is_valid(info->coerce_expand.hi))
|
||||
{
|
||||
LLVMValueRef gep_second = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.hi_index, alignment, &element_align);
|
||||
llvm_store_to_ptr_raw_aligned(c, gep_second, llvm_get_next_param(c, index), element_align);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo);
|
||||
LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi);
|
||||
LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi);
|
||||
AlignSize decl_alignment = decl->alignment;
|
||||
LLVMValueRef coerce;
|
||||
if (llvm_store_size(c, struct_type) > type_size(decl->type))
|
||||
{
|
||||
AlignSize struct_alignment = llvm_abi_alignment(c, struct_type);
|
||||
if (decl_alignment < struct_alignment) decl->alignment = decl_alignment = struct_alignment;
|
||||
coerce = llvm_emit_alloca(c, struct_type, decl_alignment, "");
|
||||
decl->backend_ref = LLVMBuildBitCast(c->builder, coerce, llvm_get_ptr_type(c, decl->type), decl->name ? decl->name : ".anon");
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
// Here we do the following transform:
|
||||
// lo, hi -> { lo, hi } -> struct
|
||||
// Cast to { lo, hi }
|
||||
coerce = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair");
|
||||
}
|
||||
// Point to the lo value.
|
||||
AlignSize element_align;
|
||||
LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, coerce, struct_type, 0, decl_alignment, &element_align);
|
||||
// Store it in the struct.
|
||||
llvm_store_to_ptr_raw_aligned(c, lo_ptr, llvm_get_next_param(c, index), element_align);
|
||||
// Point to the hi value.
|
||||
LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, coerce, struct_type, 1, decl_alignment, &element_align);
|
||||
// Store it in the struct.
|
||||
llvm_store_to_ptr_raw_aligned(c, hi_ptr, llvm_get_next_param(c, index), element_align);
|
||||
return;
|
||||
}*/
|
||||
|
||||
case ABI_ARG_DIRECT:
|
||||
//DIRECT_FROM_COERCE:
|
||||
if (!decl->var.is_written && !decl->var.is_addr)
|
||||
{
|
||||
decl->tb_register = tilde_get_next_param(c, index);
|
||||
decl->is_value = true;
|
||||
return;
|
||||
}
|
||||
tilde_emit_and_set_decl_alloca(c, decl);
|
||||
tilde_store_decl_raw(c, decl, tilde_get_next_param(c, index));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_SPLIT_STRUCT:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
// In this case we've been flattening the parameter into multiple registers.
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
|
||||
// Cast to the coerce type.
|
||||
LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce");
|
||||
|
||||
AlignSize decl_alignment = decl->alignment;
|
||||
// Store each expanded parameter.
|
||||
for (unsigned idx = 0; idx < info->direct_struct_expand.elements; idx++)
|
||||
{
|
||||
AlignSize align;
|
||||
LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, decl_alignment, &align);
|
||||
LLVMValueRef value = llvm_get_next_param(c, index);
|
||||
llvm_store_to_ptr_raw_aligned(c, element_ptr, value, align);
|
||||
}
|
||||
return;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type);
|
||||
if (coerce_type == llvm_get_type(c, decl->type))
|
||||
{
|
||||
goto DIRECT_FROM_COERCE;
|
||||
}
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
|
||||
LLVMValueRef param = llvm_get_next_param(c, index);
|
||||
// Store it with the alignment of the decl.
|
||||
llvm_emit_coerce_store(c, decl->backend_ref, decl->alignment, coerce_type, param, llvm_get_type(c, decl->type));
|
||||
return;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE_INT:
|
||||
{
|
||||
TODO
|
||||
/* TODO
|
||||
LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(decl->type) * 8);
|
||||
if (coerce_type == llvm_get_type(c, decl->type))
|
||||
{
|
||||
goto DIRECT_FROM_COERCE;
|
||||
}
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
|
||||
LLVMValueRef param = llvm_get_next_param(c, index);
|
||||
// Store it with the alignment of the decl.
|
||||
llvm_emit_coerce_store(c, decl->backend_ref, decl->alignment, coerce_type, param, llvm_get_type(c, decl->type));
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_EXPAND:
|
||||
{
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
llvm_expand_from_args(c, decl->type, decl->backend_ref, index, decl->alignment);
|
||||
if (info->expand.padding_type)
|
||||
{
|
||||
// Skip the pad.
|
||||
llvm_get_next_param(c, index);
|
||||
}*/
|
||||
}
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tilde_emit_func_parameter(TildeContext *c, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||
|
||||
// Allocate room on stack, but do not copy.
|
||||
tilde_process_parameter_value(c, decl, abi_info, index);
|
||||
if (tilde_use_debug(c))
|
||||
{
|
||||
TODO
|
||||
// TODO llvm_emit_debug_parameter(context, decl, real_index);
|
||||
}
|
||||
}
|
||||
|
||||
void tilde_emit_body(TildeContext *c, TB_Function *function, const char *module_name, const char *function_name,
|
||||
FileId file_id, FunctionPrototype *prototype, Signature *signature, Ast *body)
|
||||
{
|
||||
|
||||
bool emit_debug = tilde_use_debug(c);
|
||||
TB_Function *prev_function = c->f;
|
||||
|
||||
c->f = function;
|
||||
c->opt_var = TB_NULL_REG;
|
||||
c->catch_block = 0;
|
||||
|
||||
if (!function_name) function_name = "anonymous function";
|
||||
if (emit_debug)
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
c->debug.function = LLVMGetSubprogram(function);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(module_name);
|
||||
scratch_buffer_append("::");
|
||||
scratch_buffer_append(function_name);
|
||||
c->debug.func_name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".funcname");
|
||||
|
||||
File *file = source_file_by_id(file_id);
|
||||
c->debug.file_name = llvm_emit_string_const(c, file->name, ".filename");
|
||||
}*/
|
||||
}
|
||||
|
||||
c->cur_func.name = function_name;
|
||||
c->cur_func.prototype = prototype;
|
||||
|
||||
|
||||
unsigned arg = 0;
|
||||
|
||||
if (emit_debug)
|
||||
{
|
||||
TODO /*
|
||||
llvm_debug_scope_push(c, c->debug.function);
|
||||
EMIT_LOC(c, body);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
LLVMTypeRef slot_type = c->debug.stack_type;
|
||||
LLVMTypeRef ptr_to_slot_type = LLVMPointerType(slot_type, 0);
|
||||
if (!c->debug.last_ptr)
|
||||
{
|
||||
const char *name = ".$last_stack";
|
||||
LLVMValueRef last_stack = c->debug.last_ptr = llvm_add_global_raw(c, name, ptr_to_slot_type, 0);
|
||||
LLVMSetThreadLocal(last_stack, true);
|
||||
LLVMSetInitializer(last_stack, llvm_get_zero_raw(ptr_to_slot_type));
|
||||
llvm_set_weak(c, last_stack);
|
||||
}
|
||||
AlignSize alignment = llvm_abi_alignment(c, slot_type);
|
||||
c->debug.stack_slot = llvm_emit_alloca(c, slot_type, alignment, ".$stackslot");
|
||||
AlignSize align_to_use;
|
||||
LLVMValueRef prev_ptr = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 0, alignment, &align_to_use);
|
||||
llvm_store_to_ptr_raw_aligned(c,
|
||||
prev_ptr,
|
||||
LLVMBuildLoad2(c->builder, ptr_to_slot_type, c->debug.last_ptr, ""),
|
||||
align_to_use);
|
||||
LLVMValueRef func_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 1, alignment, &align_to_use);
|
||||
llvm_store_to_ptr_raw_aligned(c, func_name, c->debug.func_name, align_to_use);
|
||||
LLVMValueRef file_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 2, alignment, &align_to_use);
|
||||
llvm_store_to_ptr_raw_aligned(c, file_name, c->debug.file_name, align_to_use);
|
||||
c->debug.stack_slot_row = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 3, alignment, &align_to_use);
|
||||
LLVMValueRef last_ptr = NULL;
|
||||
if (function_name != kw_main && function_name != kw_mainstub)
|
||||
{
|
||||
last_ptr = c->debug.last_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_ptr = prev_ptr;
|
||||
}
|
||||
llvm_store_to_ptr_raw_aligned(c,
|
||||
last_ptr,
|
||||
c->debug.stack_slot,
|
||||
type_alloca_alignment(type_voidptr));
|
||||
}*/
|
||||
}
|
||||
|
||||
c->optional_out = TB_NULL_REG;
|
||||
c->return_out = TB_NULL_REG;
|
||||
if (prototype && prototype->ret_abi_info->kind == ABI_ARG_INDIRECT)
|
||||
{
|
||||
if (prototype->is_optional)
|
||||
{
|
||||
c->optional_out = tb_inst_param(c->f, arg++);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->return_out = tb_inst_param(c->f, arg++);
|
||||
}
|
||||
}
|
||||
if (prototype && prototype->ret_by_ref_abi_info)
|
||||
{
|
||||
assert(!c->return_out);
|
||||
c->return_out = tb_inst_param(c->f, arg++);
|
||||
}
|
||||
|
||||
|
||||
if (signature)
|
||||
{
|
||||
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
||||
FOREACH_BEGIN_IDX(i, Decl *param, signature->params)
|
||||
tilde_emit_func_parameter(c, param, prototype->abi_args[i], &arg, i);
|
||||
FOREACH_END();
|
||||
}
|
||||
|
||||
/*-- TODO
|
||||
LLVMSetCurrentDebugLocation2(c->builder, NULL);*/
|
||||
|
||||
AstId current = body->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
tilde_emit_stmt(c, ast_next(¤t));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
if (c->current_block && llvm_basic_block_is_unused(c->current_block))
|
||||
{
|
||||
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(c->current_block);
|
||||
LLVMDeleteBasicBlock(c->current_block);
|
||||
c->current_block = prev_block;
|
||||
LLVMPositionBuilderAtEnd(c->builder, c->current_block);
|
||||
}*/
|
||||
|
||||
// Insert a return (and defer) if needed.
|
||||
if (!tb_basic_block_is_complete(c->f, tb_inst_get_label(c->f)))
|
||||
{
|
||||
tilde_emit_return_implicit(c);
|
||||
}
|
||||
if (tilde_use_debug(c))
|
||||
{
|
||||
TODO
|
||||
//llvm_debug_scope_pop(c);
|
||||
}
|
||||
|
||||
c->f = prev_function;
|
||||
}
|
||||
|
||||
static void tilde_emit_function_body(TildeContext *c, Decl *decl)
|
||||
{
|
||||
TB_Function *fn = decl->tb_symbol;
|
||||
vec_add(c->functions, fn);
|
||||
DEBUG_LOG("Generating function %s.", decl->extname);
|
||||
assert(decl->backend_ref);
|
||||
tilde_emit_body(c,
|
||||
fn,
|
||||
decl->unit->module->name->module,
|
||||
decl->name,
|
||||
decl->span.file_id,
|
||||
decl->type->function.prototype,
|
||||
decl->func_decl.attr_naked ? NULL : &decl->func_decl.signature,
|
||||
astptr(decl->func_decl.body));
|
||||
}
|
||||
|
||||
static TildeContext *tilde_gen_module(Module *module, TB_FeatureSet *feature_set)
|
||||
{
|
||||
if (!vec_size(module->units)) return NULL;
|
||||
TildeContext *context = CALLOCS(TildeContext);
|
||||
TB_Module *tb_module = tb_module_create(arch_to_tilde_arch(platform_target.arch),
|
||||
os_to_tilde_system(platform_target.os),
|
||||
feature_set, false);
|
||||
codegen_setup_object_names(module, &context->ir_filename, &context->asm_filename, &context->object_filename);
|
||||
context->module = tb_module;
|
||||
|
||||
FOREACH(CompilationUnit *, unit, module->units)
|
||||
{
|
||||
REMINDER("Add debug info");
|
||||
/* gencontext_init_file_emit(gen_context, unit);
|
||||
context->debug.compile_unit = unit->llvm.debug_compile_unit;
|
||||
gen_context->debug.file = unit->llvm.debug_file;*/
|
||||
|
||||
FOREACH(Decl *, initializer, unit->xxlizers)
|
||||
{
|
||||
REMINDER("Add xxlizer");
|
||||
//tilde_emit_xxlizer(gen_context, initializer);
|
||||
}
|
||||
|
||||
FOREACH(Decl *, method, unit->methods)
|
||||
{
|
||||
tilde_emit_function_decl(context, method);
|
||||
}
|
||||
|
||||
FOREACH(Decl *, type_decl, unit->types)
|
||||
{
|
||||
tilde_emit_type_decls(context, type_decl);
|
||||
}
|
||||
|
||||
FOREACH(Decl *, enum_decl, unit->enums)
|
||||
{
|
||||
tilde_emit_type_decls(context, enum_decl);
|
||||
}
|
||||
|
||||
FOREACH(Decl *, func, unit->functions)
|
||||
if (func->func_decl.attr_test)
|
||||
{
|
||||
if (!active_target.testing) continue;
|
||||
vec_add(module->tests, func);
|
||||
}
|
||||
if (func->func_decl.attr_benchmark)
|
||||
{
|
||||
if (!active_target.benchmarking) continue;
|
||||
vec_add(module->benchmarks, func);
|
||||
}
|
||||
tilde_emit_function_decl(context, func);
|
||||
FOREACH_END();
|
||||
|
||||
if (active_target.type != TARGET_TYPE_TEST && active_target.type != TARGET_TYPE_BENCHMARK && unit->main_function && unit->main_function->is_synthetic)
|
||||
{
|
||||
tilde_emit_function_decl(context, unit->main_function);
|
||||
}
|
||||
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH(CompilationUnit *, unit, module->units)
|
||||
|
||||
/*--- TODO
|
||||
context->debug.compile_unit = unit->llvm.debug_compile_unit;
|
||||
context->debug.file = unit->llvm.debug_file;
|
||||
*/
|
||||
FOREACH(Decl *, var, unit->vars)
|
||||
tilde_get_ref(context, var);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH(Decl *, var, unit->vars)
|
||||
// TODO tilde_emit_global_variable_init(context, var);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH(Decl *, decl, unit->functions)
|
||||
if (decl->func_decl.attr_test && !active_target.testing) continue;
|
||||
if (decl->func_decl.body) tilde_emit_function_body(context, decl);
|
||||
FOREACH_END();
|
||||
|
||||
if (active_target.type != TARGET_TYPE_TEST && active_target.type != TARGET_TYPE_BENCHMARK && unit->main_function && unit->main_function->is_synthetic)
|
||||
{
|
||||
tilde_emit_function_body(context, unit->main_function);
|
||||
}
|
||||
|
||||
FOREACH(Decl *, decl, unit->methods)
|
||||
if (decl->func_decl.body) tilde_emit_function_body(context, decl);
|
||||
FOREACH_END();
|
||||
|
||||
// gencontext_end_file_emit(gen_context, unit);
|
||||
|
||||
FOREACH_END();
|
||||
|
||||
/*-- TODO
|
||||
tilde_emit_constructors_and_destructors(context); */
|
||||
|
||||
// EmitDeferred()
|
||||
|
||||
/*-- TODO
|
||||
if (llvm_use_debug(gen_context))
|
||||
{
|
||||
LLVMDIBuilderFinalize(gen_context->debug.builder);
|
||||
LLVMDisposeDIBuilder(gen_context->debug.builder);
|
||||
}*/
|
||||
|
||||
// If it's in test, then we want to serialize the IR before it is optimized.
|
||||
/*--if (active_target.test_output)
|
||||
{
|
||||
gencontext_print_llvm_ir(gen_context);
|
||||
gencontext_verify_ir(gen_context);
|
||||
}--*/
|
||||
return context;
|
||||
}
|
||||
|
||||
void **tilde_gen(Module **modules, unsigned module_count)
|
||||
{
|
||||
if (!module_count) return NULL;
|
||||
TB_FeatureSet feature_set;
|
||||
switch (platform_target.arch)
|
||||
{
|
||||
case ARCH_TYPE_AARCH64:
|
||||
feature_set.aarch64.bf16 = false;
|
||||
case ARCH_TYPE_X86_64:
|
||||
feature_set.x64.avx = platform_target.x64.x86_vector_capability >= X86VECTOR_AVX;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
TildeContext **contexts = NULL;
|
||||
for (unsigned i = 0; i < module_count; i++)
|
||||
{
|
||||
TildeContext *c = tilde_gen_module(modules[i], &feature_set);
|
||||
if (c) vec_add(contexts, c);
|
||||
}
|
||||
return (void**)contexts;
|
||||
}
|
||||
|
||||
void tinybackend_codegen_setup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static TB_DataType tilde_get_abi_type(AbiType type)
|
||||
{
|
||||
if (abi_type_is_type(type)) return tildetype(type.type);
|
||||
TODO
|
||||
}
|
||||
|
||||
TB_DataType tilde_get_int_type_of_bytesize(int byte_size)
|
||||
{
|
||||
switch (byte_size)
|
||||
{
|
||||
case 1:
|
||||
return TB_TYPE_I8;
|
||||
case 2:
|
||||
return TB_TYPE_I16;
|
||||
case 3:
|
||||
case 4:
|
||||
return TB_TYPE_I32;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
return TB_TYPE_I64;
|
||||
case 16:
|
||||
return (TB_DataType) { .type = TB_INT, .width = 0, .data = 128 };
|
||||
default:
|
||||
FATAL_ERROR("Unsupported size");
|
||||
}
|
||||
|
||||
}
|
||||
static void param_expand(TB_DataType **params_ref, Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEDEF:
|
||||
UNREACHABLE
|
||||
case TYPE_ARRAY:
|
||||
for (ArraySize i = type->array.len; i > 0; i--)
|
||||
{
|
||||
param_expand(params_ref, type->array.base);
|
||||
}
|
||||
return;
|
||||
case TYPE_STRUCT:
|
||||
{
|
||||
Decl **members = type->decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
param_expand(params_ref, members[i]->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ANYFAULT:
|
||||
case TYPE_FAULTTYPE:
|
||||
param_expand(params_ref, type_lowering(type));
|
||||
return;
|
||||
case TYPE_UNION:
|
||||
{
|
||||
ByteSize largest = 0;
|
||||
Type *largest_type = NULL;
|
||||
Decl **members = type->decl->strukt.members;
|
||||
// Clang: Unions can be here only in degenerative cases - all the fields are same
|
||||
// after flattening. Thus we have to use the "largest" field.
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (type_size(type) > largest)
|
||||
{
|
||||
largest = type_size(type);
|
||||
type = type->canonical;
|
||||
}
|
||||
}
|
||||
if (!largest) return;
|
||||
param_expand(params_ref, largest_type);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
vec_add(*params_ref, tildetype(type));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void callback(void* user_data, const char* fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
vprintf(fmt, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
// Compile module (multi threaded)
|
||||
const char *tilde_codegen(void *context)
|
||||
{
|
||||
TildeContext *c = (TildeContext *)context;
|
||||
bool is_win32 = platform_target.os == OS_TYPE_WIN32;
|
||||
TB_DebugFormat debug_format = is_win32 ? TB_DEBUGFMT_CODEVIEW : TB_DEBUGFMT_DWARF;
|
||||
if (active_target.debug_info == DEBUG_INFO_NONE) debug_format = TB_DEBUGFMT_NONE;
|
||||
|
||||
FOREACH(TB_Function *, function, c->functions)
|
||||
if (!tb_module_compile_function(c->module, function, TB_ISEL_FAST))
|
||||
{
|
||||
error_exit("Failed to compile function.");
|
||||
}
|
||||
tb_function_print(function, &callback, NULL, true);
|
||||
FOREACH_END();
|
||||
|
||||
const char *object_name = NULL;
|
||||
if (active_target.emit_object_files)
|
||||
{
|
||||
if (!tb_exporter_write_files(c->module, TB_FLAVOR_OBJECT, debug_format, 1, &c->object_filename))
|
||||
{
|
||||
error_exit("Failed to create object file %s.", c->object_filename);
|
||||
}
|
||||
object_name = c->object_filename;
|
||||
}
|
||||
|
||||
if (active_target.emit_asm && false)
|
||||
{
|
||||
if (!tb_exporter_write_files(c->module, TB_FLAVOR_ASSEMBLY, debug_format, 1, &c->asm_filename))
|
||||
{
|
||||
error_exit("Failed to create asm %s.", c->asm_filename);
|
||||
}
|
||||
}
|
||||
|
||||
tb_module_destroy(c->module);
|
||||
return object_name;
|
||||
|
||||
|
||||
return c->object_filename;
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +0,0 @@
|
||||
#include "tilde_internal.h"
|
||||
|
||||
|
||||
void tilde_emit_memclear_size_align(TildeContext *c, TB_Register ref, uint64_t size, AlignSize align)
|
||||
{
|
||||
ByteSize min = type_min_alignment(align, size);
|
||||
TB_Register zero = tb_inst_uint(c->f, TB_TYPE_I8, 0);
|
||||
TB_Register elements = tb_inst_uint(c->f, size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size);
|
||||
tb_inst_memset(c->f, ref, zero, elements, min);
|
||||
}
|
||||
|
||||
void tilde_emit_cond_br(TildeContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block)
|
||||
{
|
||||
tb_inst_if(c->f, tilde_load_value(c, value), then_block, else_block);
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_shl_fixed(TildeContext *c, Type *type, TB_Reg reg, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return reg;
|
||||
BitSize bit_width = type_kind_bitsize(type->type_kind);
|
||||
if (shift >= bit_width) return tilde_get_zero(c, type);
|
||||
TB_DataType int_type = tildetype(type);
|
||||
return tb_inst_shl(c->f, reg, tb_inst_uint(c->f, int_type, (unsigned)shift), type_is_signed(type) ? TB_ARITHMATIC_NSW : TB_ARITHMATIC_NUW);
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_lshr_fixed(TildeContext *c, Type *type, TB_Reg reg, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return reg;
|
||||
BitSize bit_width = type_kind_bitsize(type->type_kind);
|
||||
if (shift >= bit_width) return tilde_get_zero(c, type);
|
||||
TB_DataType int_type = tildetype(type);
|
||||
return tb_inst_shr(c->f, reg, tb_inst_uint(c->f, int_type, (unsigned)shift));
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_alloca(TildeContext *c, Type *type, AlignSize alignment)
|
||||
{
|
||||
return tb_inst_local(c->f, type_size(type), alignment ? alignment : type_alloca_alignment(type));
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_is_no_opt(TildeContext *c, TB_Reg error_value)
|
||||
{
|
||||
return tb_inst_cmp_eq(c->f, error_value, tilde_get_zero(c, type_anyfault));
|
||||
}
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
|
||||
bool tilde_emit_goto_if_needed(TildeContext *c, TB_Label jump)
|
||||
{
|
||||
if (tb_basic_block_is_complete(c->f, tb_inst_get_label(c->f))) return false;
|
||||
tb_inst_goto(c->f, jump);
|
||||
return true;
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_statement_chain(TildeContext *c, AstId current)
|
||||
{
|
||||
while (current)
|
||||
{
|
||||
tilde_emit_stmt(c, ast_next(¤t));
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_compound_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
// Push debug scope
|
||||
tilde_emit_statement_chain(c, ast->compound_stmt.first_stmt);
|
||||
// Pop debug scope
|
||||
}
|
||||
|
||||
static void tilde_emit_return_abi(TildeContext *c, TBEValue *return_value, TBEValue *optional)
|
||||
{
|
||||
FunctionPrototype *prototype = c->cur_func.prototype;
|
||||
|
||||
// If there is no prototype, this is a static initializer, so bail.
|
||||
if (!prototype)
|
||||
{
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
}
|
||||
|
||||
ABIArgInfo *info = prototype->ret_abi_info;
|
||||
|
||||
// If we have an optional it's always the return argument, so we need to copy
|
||||
// the return value into the return value holder.
|
||||
TB_Reg return_out = c->return_out;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
|
||||
TBEValue no_fail;
|
||||
|
||||
// In this case we use the optional as the actual return.
|
||||
if (prototype->is_optional)
|
||||
{
|
||||
if (return_value && return_value->type != type_void)
|
||||
{
|
||||
assert(return_value->type);
|
||||
tilde_store_to_ptr_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type));
|
||||
}
|
||||
return_out = c->optional_out;
|
||||
if (!optional)
|
||||
{
|
||||
value_set(&no_fail, tilde_get_zero(c, type_anyfault), type_anyfault);
|
||||
optional = &no_fail;
|
||||
}
|
||||
return_value = optional;
|
||||
}
|
||||
assert(return_value || info->kind == ABI_ARG_IGNORE);
|
||||
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
assert(return_value);
|
||||
tilde_store_to_ptr_aligned(c, return_out, return_value, info->indirect.alignment);
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
case ABI_ARG_IGNORE:
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
case ABI_ARG_DIRECT_SPLIT_STRUCT:
|
||||
case ABI_ARG_EXPAND:
|
||||
// Expands to multiple slots -
|
||||
// Not applicable to return values.
|
||||
UNREACHABLE
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
// Pick the return as an address.
|
||||
value_addr(c, return_value);
|
||||
// Get the coerce type.
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
// Create the new pointer
|
||||
assert(return_value);
|
||||
LLVMValueRef coerce = LLVMBuildBitCast(c->builder, return_value->value, coerce_type, "");
|
||||
// We might have only one value, in that case, build a GEP to that one.
|
||||
LLVMValueRef lo_val;
|
||||
AlignSize alignment;
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo);
|
||||
lo_val = llvm_load(c, lo_type, lo, alignment, "");
|
||||
|
||||
// We're done if there's a single field.
|
||||
if (!abi_type_is_valid(info->coerce_expand.hi))
|
||||
{
|
||||
llvm_emit_return_value(c, lo_val);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's make a first class aggregate
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi);
|
||||
LLVMValueRef hi_val = llvm_load(c, hi_type, hi, alignment, "");
|
||||
|
||||
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
|
||||
LLVMValueRef composite = llvm_get_undef_raw(unpadded_type);
|
||||
|
||||
composite = llvm_emit_insert_value(c, composite, lo_val, 0);
|
||||
composite = llvm_emit_insert_value(c, composite, hi_val, 1);
|
||||
|
||||
// And return that unpadded result
|
||||
llvm_emit_return_value(c, composite);
|
||||
break;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
DIRECT_RETURN:
|
||||
// The normal return
|
||||
tb_inst_ret(c->f, tilde_load_value_store(c, return_value));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
TODO
|
||||
goto DIRECT_RETURN;
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));
|
||||
return;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE_INT:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(call_return_type) * 8);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tilde_emit_return_implicit(TildeContext *c)
|
||||
{
|
||||
Type *rtype_real = c->cur_func.prototype ? c->cur_func.prototype->rtype : type_void;
|
||||
if (type_lowering(type_no_optional(rtype_real)) != type_void)
|
||||
{
|
||||
tb_inst_unreachable(c->f);
|
||||
return;
|
||||
}
|
||||
if (type_is_optional(rtype_real))
|
||||
{
|
||||
tilde_emit_return_abi(c, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
TBEValue value;
|
||||
value_set(&value, tb_inst_ptr(c->f, 0), type_anyfault);
|
||||
tilde_emit_return_abi(c, NULL, &value);
|
||||
}
|
||||
|
||||
static void tilde_emit_decl_expr_list(TildeContext *c, TBEValue *be_value, Expr *expr, bool bool_cast)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_COND);
|
||||
ByteSize size = vec_size(expr->cond_expr);
|
||||
ByteSize last_index = size - 1;
|
||||
for (ByteSize i = 0; i < last_index; i++)
|
||||
{
|
||||
TBEValue value;
|
||||
tilde_emit_expr(c, &value, expr->cond_expr[i]);
|
||||
}
|
||||
Expr *last = expr->cond_expr[last_index];
|
||||
Type *type = last->type;
|
||||
tilde_emit_expr(c, be_value, last);
|
||||
if (last->expr_kind == EXPR_DECL)
|
||||
{
|
||||
type = last->decl_expr->var.type_info->type;
|
||||
|
||||
TB_Reg decl_value = tilde_get_ref(c, last->decl_expr);
|
||||
if (bool_cast && last->decl_expr->var.unwrap)
|
||||
{
|
||||
value_set(be_value, tb_inst_bool(c->f, true), type_bool);
|
||||
return;
|
||||
}
|
||||
value_set_address_abi_aligned(be_value, decl_value, type);
|
||||
}
|
||||
if (bool_cast)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (type->type_kind != TYPE_BOOL)
|
||||
{
|
||||
CastKind cast = cast_to_bool_kind(type);
|
||||
tilde_emit_cast(c, cast, last, be_value, type, type_bool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_return_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
PUSH_OPT();
|
||||
|
||||
Expr *expr = ast->return_stmt.expr;
|
||||
if (expr && expr->expr_kind == EXPR_OPTIONAL)
|
||||
{
|
||||
TBEValue be_value;
|
||||
tilde_emit_expr(c, &be_value, expr->inner_expr);
|
||||
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
|
||||
tilde_emit_return_abi(c, NULL, &be_value);
|
||||
return;
|
||||
}
|
||||
|
||||
TB_Label error_return_block = 0;
|
||||
TB_Reg error_out = TB_NULL_REG;
|
||||
if (c->cur_func.prototype && type_is_optional(c->cur_func.prototype->rtype))
|
||||
{
|
||||
error_return_block = tb_basic_block_create(c->f);
|
||||
error_out = tilde_emit_alloca(c, type_anyfault, 0);
|
||||
c->opt_var = error_out;
|
||||
c->catch_block = error_return_block;
|
||||
}
|
||||
|
||||
bool has_return_value = ast->return_stmt.expr != NULL;
|
||||
TBEValue return_value = { 0 };
|
||||
if (has_return_value)
|
||||
{
|
||||
tilde_emit_expr(c, &return_value, ast->return_stmt.expr);
|
||||
value_fold_optional(c, &return_value);
|
||||
c->retval = return_value;
|
||||
}
|
||||
|
||||
POP_OPT();
|
||||
|
||||
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
|
||||
|
||||
// Are we in an expression block?
|
||||
if (!has_return_value)
|
||||
{
|
||||
tilde_emit_return_implicit(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
tilde_emit_return_abi(c, &return_value, NULL);
|
||||
}
|
||||
if (error_return_block)
|
||||
{
|
||||
tilde_emit_block(c, error_return_block);
|
||||
TBEValue value;
|
||||
value_set_address_abi_aligned(&value, error_out, type_anyfault);
|
||||
tilde_emit_return_abi(c, NULL, &value);
|
||||
}
|
||||
}
|
||||
|
||||
void tilde_emit_local_decl(TildeContext *c, Decl *decl, TBEValue *value)
|
||||
{
|
||||
// 1. Get the declaration and the LLVM type.
|
||||
Type *var_type = type_lowering(type_no_optional(decl->type));
|
||||
|
||||
// 2. In the case we have a static variable,
|
||||
// then we essentially treat this as a global.
|
||||
if (decl->var.is_static)
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
// In defers we might already have generated this variable.
|
||||
if (decl->backend_ref)
|
||||
{
|
||||
llvm_value_set_decl(c, value, decl);
|
||||
return;
|
||||
}
|
||||
void *builder = c->builder;
|
||||
c->builder = c->global_builder;
|
||||
decl->backend_ref = llvm_add_global(c, "tempglobal", var_type, decl->alignment);
|
||||
if (IS_OPTIONAL(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->extname);
|
||||
scratch_buffer_append("$f");
|
||||
decl->var.optional_ref = llvm_add_global(c, scratch_buffer_to_string(), type_anyfault, 0);
|
||||
}
|
||||
llvm_emit_global_variable_init(c, decl);
|
||||
c->builder = builder;
|
||||
llvm_value_set_decl(c, value, decl);
|
||||
return;*/
|
||||
}
|
||||
assert(!decl->backend_ref);
|
||||
decl->tb_register = tb_inst_local(c->f, type_size(var_type), type_alloca_alignment(var_type));
|
||||
Expr *init = decl->var.init_expr;
|
||||
bool is_optional = IS_OPTIONAL(decl);
|
||||
if (is_optional)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->name);
|
||||
scratch_buffer_append(".f");
|
||||
decl->var.tb_optional_reg = tb_inst_local(c->f, type_size(type_anyfault), type_alloca_alignment(type_anyfault));
|
||||
// Only clear out the result if the assignment isn't an optional.
|
||||
}
|
||||
|
||||
if (init)
|
||||
{
|
||||
value_set_decl_address(c, value, decl);
|
||||
value->kind = TBE_ADDRESS;
|
||||
TBEValue val = tilde_emit_assign_expr(c, value, decl->var.init_expr, decl->var.tb_optional_reg);
|
||||
if (!is_optional) *value = val;
|
||||
}
|
||||
else if (decl->var.no_init)
|
||||
{
|
||||
value_set(value, tb_inst_poison(c->f), decl->type);
|
||||
if (decl->var.tb_optional_reg)
|
||||
{
|
||||
tilde_store_to_ptr_raw(c, decl->var.tb_optional_reg, tb_inst_poison(c->f), type_anyfault);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (decl->var.tb_optional_reg)
|
||||
{
|
||||
tilde_store_zero(c, type_anyfault, decl->var.tb_optional_reg, 0);
|
||||
}
|
||||
|
||||
Type *type = type_lowering(decl->type);
|
||||
// Normal case, zero init.
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
tilde_store_zero(c, type_anyfault, decl->tb_register, decl->alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_inst_memclr(c->f, decl->tb_register, type_bit_size(type), decl->alignment);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void tilde_emit_expr_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
TBEValue value;
|
||||
if (IS_OPTIONAL(ast->expr_stmt))
|
||||
{
|
||||
PUSH_OPT();
|
||||
TB_Label discard_fail = tb_basic_block_create(c->f);
|
||||
c->catch_block = discard_fail;
|
||||
c->opt_var = TB_NULL_REG;
|
||||
tilde_emit_expr(c, &value, ast->expr_stmt);
|
||||
value_fold_optional(c, &value);
|
||||
EMIT_LOC(c, ast);
|
||||
tilde_emit_goto_if_needed(c, discard_fail);
|
||||
tilde_emit_block(c, discard_fail);
|
||||
POP_OPT();
|
||||
return;
|
||||
}
|
||||
|
||||
tilde_emit_expr(c, &value, ast->expr_stmt);
|
||||
}
|
||||
|
||||
// See llvm_emit_if_stmt
|
||||
static void tilde_emit_if_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
// We need at least the exit block and the "then" block.
|
||||
TB_Label exit_block = tb_basic_block_create(c->f);
|
||||
TB_Label then_block = exit_block;
|
||||
TB_Label else_block = exit_block;
|
||||
|
||||
Ast *then_body = astptr(ast->if_stmt.then_body);
|
||||
// Only generate a target if
|
||||
if (ast_is_not_empty(then_body))
|
||||
{
|
||||
then_block = tb_basic_block_create(c->f);
|
||||
}
|
||||
|
||||
// We have an optional else block.
|
||||
AstId else_id = ast->if_stmt.else_body;
|
||||
Ast *else_body = else_id ? astptr(else_id) : NULL;
|
||||
if (ast_is_not_empty(else_body))
|
||||
{
|
||||
else_block = tb_basic_block_create(c->f);
|
||||
}
|
||||
|
||||
Expr *cond = exprptr(ast->if_stmt.cond);
|
||||
ast->if_stmt.codegen.tb_break_block = exit_block;
|
||||
|
||||
// Output boolean value and switch.
|
||||
|
||||
Decl *label = ast->if_stmt.flow.label;
|
||||
if (label)
|
||||
{
|
||||
label->label.tb_break_target = exit_block;
|
||||
}
|
||||
|
||||
TBEValue be_value = { 0 };
|
||||
|
||||
bool exit_in_use = true;
|
||||
|
||||
if (then_body->ast_kind == AST_IF_CATCH_SWITCH_STMT)
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
tilde_emit_decl_expr_list(c, &be_value, cond, false);
|
||||
value_rvalue(c, &be_value);
|
||||
TBEValue comp;
|
||||
tilde_emit_int_comp_zero(c, &comp, &be_value, BINARYOP_NE);
|
||||
tb_inst_if(c->f, comp.reg, then_block, else_block);
|
||||
tb_inst_set_label(c->f, then_block);
|
||||
tilde_emit_switch_body(c, &be_value, then_body);
|
||||
tb_inst_goto(c->f, exit_block);
|
||||
goto EMIT_ELSE;*/
|
||||
}
|
||||
|
||||
tilde_emit_decl_expr_list(c, &be_value, cond, true);
|
||||
|
||||
value_rvalue(c, &be_value);
|
||||
|
||||
if (then_block != else_block)
|
||||
{
|
||||
tb_inst_if(c->f, be_value.reg, then_block, else_block);
|
||||
}
|
||||
|
||||
// Emit the 'then' code.
|
||||
if (then_block != exit_block)
|
||||
{
|
||||
tilde_emit_block(c, then_block);
|
||||
tilde_emit_stmt(c, then_body);
|
||||
|
||||
// Jump to exit.
|
||||
tilde_emit_goto_if_needed(c, exit_block);
|
||||
}
|
||||
|
||||
// Emit the 'else' branch if present.
|
||||
if (else_block != exit_block)
|
||||
{
|
||||
tilde_emit_block(c, else_block);
|
||||
tilde_emit_stmt(c, else_body);
|
||||
tilde_emit_goto_if_needed(c, exit_block);
|
||||
}
|
||||
|
||||
tilde_emit_block(c, exit_block);
|
||||
}
|
||||
|
||||
void tilde_emit_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_COMPOUND_STMT:
|
||||
tilde_emit_compound_stmt(c, ast);
|
||||
return;
|
||||
case AST_RETURN_STMT:
|
||||
tilde_emit_return_stmt(c, ast);
|
||||
return;
|
||||
case AST_IF_STMT:
|
||||
tilde_emit_if_stmt(c, ast);
|
||||
return;
|
||||
case AST_DECLARE_STMT:
|
||||
{
|
||||
TBEValue value;
|
||||
tilde_emit_local_decl(c, ast->declare_stmt, &value);
|
||||
return;
|
||||
}
|
||||
case AST_EXPR_STMT:
|
||||
tilde_emit_expr_stmt(c, ast);
|
||||
return;
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value)
|
||||
{
|
||||
assert(c->catch_block && "unexpected emit");
|
||||
bool is_constant_opt_zero = tb_node_is_constant_zero(c->f, opt_value);
|
||||
|
||||
// Maybe we don't need to emit anything?
|
||||
if (is_constant_opt_zero) return;
|
||||
|
||||
bool is_constant_opt = false;
|
||||
|
||||
TB_Label after_block = tb_basic_block_create(c->f);
|
||||
|
||||
// No error variable
|
||||
if (!c->opt_var)
|
||||
{
|
||||
// No error var and a constant error means jumping to the "catch" block
|
||||
if (is_constant_opt)
|
||||
{
|
||||
tb_inst_goto(c->f, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_inst_if(c->f, tilde_emit_is_no_opt(c, opt_value), after_block, c->catch_block);
|
||||
}
|
||||
tilde_emit_block(c, after_block);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's not a constant, then jump conditionally
|
||||
if (!is_constant_opt)
|
||||
{
|
||||
TB_Reg was_ok = tilde_emit_is_no_opt(c, opt_value);
|
||||
TB_Label error_block = tb_basic_block_create(c->f);
|
||||
tb_inst_if(c->f, was_ok, after_block, error_block);
|
||||
tilde_emit_block(c, error_block);
|
||||
}
|
||||
|
||||
tilde_store_to_ptr_raw(c, c->opt_var, opt_value, type_anyfault);
|
||||
tb_inst_goto(c->f, c->catch_block);
|
||||
tilde_emit_block(c, after_block);
|
||||
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
|
||||
|
||||
void tilde_store_to_ptr_raw_aligned(TildeContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
{
|
||||
tilde_store_internal(c, tildetype(type), addr, value, alignment);
|
||||
}
|
||||
|
||||
void tilde_store_to_ptr_raw(TildeContext *c, TB_Reg addr, TB_Reg value, Type *type)
|
||||
{
|
||||
tilde_store_internal(c, tildetype(type), addr, value, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
|
||||
void tilde_store_value_raw(TildeContext *c, TBEValue *destination, TB_Reg value)
|
||||
{
|
||||
assert(value_is_addr(destination));
|
||||
tilde_store_internal(c, tildetype(destination->type), destination->reg, value, destination->alignment);
|
||||
}
|
||||
|
||||
|
||||
void tilde_store_decl_raw(TildeContext *c, Decl *decl, TB_Reg value)
|
||||
{
|
||||
assert(!decl->is_value);
|
||||
tilde_store_internal(c, tildetype(decl->type), decl->tb_register, value, decl->alignment);
|
||||
}
|
||||
|
||||
TB_Reg tilde_load_value_store(TildeContext *c, TBEValue *value)
|
||||
{
|
||||
TB_Reg val = tilde_load_value(c, value);
|
||||
return val;
|
||||
/*
|
||||
if (value->kind == BE_BOOLVECTOR)
|
||||
{
|
||||
return LLVMBuildSExt(c->builder, val, llvm_get_type(c, type_get_vector_bool(value->type)), "");
|
||||
}
|
||||
if (value->kind != BE_BOOLEAN) return val;
|
||||
return LLVMBuildZExt(c->builder, val, c->byte_type, "");*/
|
||||
}
|
||||
|
||||
void tilde_store_to_ptr_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
|
||||
{
|
||||
// If we have an address but not an aggregate, do a load.
|
||||
assert(alignment);
|
||||
value_fold_optional(c, value);
|
||||
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value->reg = tilde_load_value_store(c, value);
|
||||
value->kind = TBE_VALUE;
|
||||
}
|
||||
switch (value->kind)
|
||||
{
|
||||
/*
|
||||
case BE_BOOLVECTOR:
|
||||
value->value = LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, value->type), "");
|
||||
value->kind = BE_VALUE;
|
||||
return llvm_store_to_ptr_raw_aligned(c, destination, value->value, alignment);
|
||||
case BE_BOOLEAN:
|
||||
value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
|
||||
value->kind = BE_VALUE;
|
||||
FALLTHROUGH;*/
|
||||
case TBE_VALUE:
|
||||
tilde_store_to_ptr_raw_aligned(c, value->type, destination, value->reg, alignment);
|
||||
return;
|
||||
case TBE_ADDRESS_OPTIONAL:
|
||||
UNREACHABLE
|
||||
case TBE_ADDRESS:
|
||||
tb_inst_memcpy(c->f, destination, value->reg, tb_inst_uint(c->f, TB_TYPE_I32, type_size(value->type)), alignment);
|
||||
return;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void tilde_store_value_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment);
|
||||
value_fold_optional(c, value);
|
||||
// If we have an address but not an aggregate, do a load.
|
||||
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value_rvalue(c, value);
|
||||
}
|
||||
switch (value->kind)
|
||||
{
|
||||
case TBE_VALUE:
|
||||
tilde_store_value_raw(c, value, value->reg);
|
||||
return;
|
||||
case TBE_ADDRESS_OPTIONAL:
|
||||
UNREACHABLE
|
||||
case TBE_ADDRESS:
|
||||
{
|
||||
ByteSize size = type_size(value->type);
|
||||
TB_Reg copy_size = tb_inst_uint(c->f, size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size);
|
||||
tb_inst_memcpy(c->f, destination, value->reg, copy_size, type_min_alignment(alignment, value->alignment));
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void tilde_store(TildeContext *c, TBEValue *dst, TBEValue *value)
|
||||
{
|
||||
if (value->type == type_void) return;
|
||||
assert(value_is_addr(dst));
|
||||
tilde_store_to_ptr_aligned(c, dst->reg, value, dst->alignment);
|
||||
}
|
||||
|
||||
TB_Reg tilde_load_abi_alignment(TildeContext *c, Type *type, TB_Reg pointer)
|
||||
{
|
||||
return tilde_load(c, tildetype(type), pointer, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
TB_Reg tilde_load_value(TildeContext *c, TBEValue *value)
|
||||
{
|
||||
value_fold_optional(c, value);
|
||||
switch (value->kind)
|
||||
{
|
||||
case TBE_VALUE:
|
||||
return value->reg;
|
||||
case TBE_ADDRESS_OPTIONAL:
|
||||
UNREACHABLE
|
||||
case TBE_ADDRESS:
|
||||
return tilde_load(c, tildetype(value->type), value->reg, value->alignment);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void tilde_store_zero(TildeContext *c, Type *type, TB_Reg addr, AlignSize alignment)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (alignment == 0) alignment = type_alloca_alignment(type);
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
tilde_store_to_ptr_raw_aligned(c, type, addr, tilde_get_zero(c, type), alignment);
|
||||
return;
|
||||
}
|
||||
ByteSize size = type_size(type);
|
||||
ByteSize min = type_min_alignment(alignment, size);
|
||||
TB_Register zero = tb_inst_uint(c->f, TB_TYPE_I8, 0);
|
||||
TB_Register elements = tb_inst_uint(c->f, tildetype(type_usz), size);
|
||||
tb_inst_memset(c->f, addr, zero, elements, min);
|
||||
}
|
||||
|
||||
void tilde_store_value_zero(TildeContext *c, TBEValue *to)
|
||||
{
|
||||
assert(to->kind == TBE_ADDRESS);
|
||||
tilde_store_zero(c, to->type, to->reg, to->alignment);
|
||||
}
|
||||
|
||||
|
||||
void tilde_emit_and_set_decl_alloca(TildeContext *c, Decl *decl)
|
||||
{
|
||||
Type *type = type_lowering(decl->type);
|
||||
if (type == type_void) return;
|
||||
decl->tb_register = tilde_emit_alloca(c, type, decl->alignment);
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
TB_CallingConv calling_conv_from(CallABI abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
case CALL_X86_STD:
|
||||
return TB_STDCALL;
|
||||
case CALL_C:
|
||||
return TB_CDECL;
|
||||
case CALL_X86_FAST:
|
||||
case CALL_X86_THIS:
|
||||
case CALL_X86_VECTOR:
|
||||
case CALL_X86_REG:
|
||||
case CALL_AAPCS:
|
||||
case CALL_AAPCS_VFP:
|
||||
REMINDER("Using C decl even though actual calling convention is different.");
|
||||
return TB_CDECL;
|
||||
}
|
||||
}
|
||||
|
||||
TB_DebugType *tilde_get_debug_type(Type *type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype)
|
||||
{
|
||||
if (prototype->tb_prototype) return prototype->tb_prototype;
|
||||
int actual_arg_count = 0;
|
||||
ABIArgInfo **abi_args = prototype->abi_args;
|
||||
for (unsigned i = vec_size(prototype->param_types); i > 0; i--)
|
||||
{
|
||||
ABIArgInfo *info = abi_args[i - 1];
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
continue;
|
||||
case ABI_ARG_INDIRECT:
|
||||
case ABI_ARG_DIRECT:
|
||||
actual_arg_count++;
|
||||
break;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
actual_arg_count += 2;
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
}
|
||||
TB_FunctionPrototype *proto =
|
||||
tb_prototype_create(c->module,
|
||||
calling_conv_from(prototype->call_abi),
|
||||
tildetype(prototype->abi_ret_type),
|
||||
tilde_get_debug_type(prototype->rtype),
|
||||
actual_arg_count,
|
||||
prototype->variadic == VARIADIC_RAW);
|
||||
FOREACH_BEGIN_IDX(i, Decl *param, prototype->param_copy)
|
||||
ABIArgInfo *abi_info = prototype->abi_args[i];
|
||||
switch (abi_info->kind)
|
||||
{
|
||||
case ABI_ARG_DIRECT:
|
||||
if (param->name)
|
||||
{
|
||||
tb_prototype_add_param_named(proto, tildetype(param->type), param->name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_prototype_add_param(proto, tildetype(param->type));
|
||||
}
|
||||
break;
|
||||
case ABI_ARG_IGNORE:
|
||||
continue;
|
||||
case ABI_ARG_INDIRECT:
|
||||
tb_prototype_add_param(proto, TB_TYPE_PTR);
|
||||
break;
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
FOREACH_END();
|
||||
prototype->tb_prototype = proto;
|
||||
return proto;
|
||||
}
|
||||
|
||||
TB_DataType tilde_abi_type(AbiType type)
|
||||
{
|
||||
if (abi_type_is_type(type)) return tildetype(type.type);
|
||||
return tilde_get_int_type_of_bytesize((type.int_bits_plus_1 - 1) / 8);
|
||||
}
|
||||
|
||||
static void param_expand(TildeContext *context, TB_DataType** params_ref, Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEDEF:
|
||||
UNREACHABLE
|
||||
case TYPE_ARRAY:
|
||||
for (ArraySize i = type->array.len; i > 0; i--)
|
||||
{
|
||||
param_expand(context, params_ref, type->array.base);
|
||||
}
|
||||
return;
|
||||
case TYPE_STRUCT:
|
||||
{
|
||||
Decl **members = type->decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
param_expand(context, params_ref, members[i]->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ANYFAULT:
|
||||
case TYPE_FAULTTYPE:
|
||||
param_expand(context, params_ref, type_lowering(type));
|
||||
return;
|
||||
case TYPE_UNION:
|
||||
{
|
||||
ByteSize largest = 0;
|
||||
Type *largest_type = NULL;
|
||||
Decl **members = type->decl->strukt.members;
|
||||
// Clang: Unions can be here only in degenerative cases - all the fields are same
|
||||
// after flattening. Thus we have to use the "largest" field.
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (type_size(type) > largest)
|
||||
{
|
||||
largest = type_size(type);
|
||||
type = type->canonical;
|
||||
}
|
||||
}
|
||||
if (!largest) return;
|
||||
param_expand(context, params_ref, largest_type);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
// Type complex: return 2;
|
||||
vec_add(*params_ref, tildetype(type));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void add_func_type_param(TildeContext *context, Type *param_type, ABIArgInfo *arg_info, TB_DataType **params)
|
||||
{
|
||||
arg_info->param_index_start = (MemberIndex)vec_size(*params);
|
||||
switch (arg_info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
break;
|
||||
case ABI_ARG_INDIRECT:
|
||||
vec_add(*params, TB_TYPE_PTR);
|
||||
break;
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
vec_add(*params, tilde_abi_type(arg_info->coerce_expand.lo));
|
||||
if (abi_type_is_valid(arg_info->coerce_expand.hi))
|
||||
{
|
||||
vec_add(*params, tilde_abi_type(arg_info->coerce_expand.hi));
|
||||
}
|
||||
break;
|
||||
case ABI_ARG_EXPAND:
|
||||
// Expanding a structs
|
||||
param_expand(context, params, param_type->canonical);
|
||||
// If we have padding, add it here.
|
||||
if (arg_info->expand.padding_type)
|
||||
{
|
||||
vec_add(*params, tildetype(arg_info->expand.padding_type));
|
||||
}
|
||||
break;
|
||||
case ABI_ARG_DIRECT:
|
||||
vec_add(*params, tildetype(param_type));
|
||||
break;
|
||||
case ABI_ARG_DIRECT_SPLIT_STRUCT:
|
||||
{
|
||||
// Normal direct.
|
||||
TB_DataType coerce_type = tildetype(arg_info->direct_struct_expand.type);
|
||||
for (unsigned idx = 0; idx < arg_info->direct_struct_expand.elements; idx++)
|
||||
{
|
||||
vec_add(*params, coerce_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE_INT:
|
||||
{
|
||||
// Normal direct.
|
||||
TB_DataType coerce_type = tilde_get_int_type_of_bytesize(type_size(param_type));
|
||||
vec_add(*params, coerce_type);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
// Normal direct.
|
||||
TB_DataType coerce_type = tildetype(arg_info->direct_coerce_type);
|
||||
vec_add(*params, coerce_type);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
// Pairs are passed by param.
|
||||
vec_add(*params, tilde_abi_type(arg_info->direct_pair.lo));
|
||||
vec_add(*params, tilde_abi_type(arg_info->direct_pair.hi));
|
||||
break;
|
||||
}
|
||||
arg_info->param_index_end = (MemberIndex)vec_size(*params);
|
||||
}
|
||||
|
||||
TB_DataType tilde_update_prototype_abi(TildeContext *context, FunctionPrototype *prototype, TB_DataType **params)
|
||||
{
|
||||
TB_DataType retval;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
ABIArgInfo *ret_arg_info = prototype->ret_abi_info;
|
||||
|
||||
ret_arg_info->param_index_end = 0;
|
||||
ret_arg_info->param_index_start = 0;
|
||||
|
||||
switch (ret_arg_info->kind)
|
||||
{
|
||||
case ABI_ARG_EXPAND:
|
||||
UNREACHABLE;
|
||||
case ABI_ARG_INDIRECT:
|
||||
vec_add(*params, TB_TYPE_PTR);
|
||||
retval = TB_TYPE_VOID;
|
||||
break;
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
TB_DataType lo = tilde_abi_type(ret_arg_info->direct_pair.lo);
|
||||
if (!abi_type_is_valid(ret_arg_info->direct_pair.hi))
|
||||
{
|
||||
retval = lo;
|
||||
break;
|
||||
}
|
||||
TB_DataType hi = tilde_abi_type(ret_arg_info->direct_pair.hi);
|
||||
TODO // retval = llvm_get_twostruct(context, lo, hi);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_IGNORE:
|
||||
retval = TB_TYPE_VOID;
|
||||
break;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
TODO /*---
|
||||
LLVMTypeRef lo = llvm_abi_type(context, ret_arg_info->direct_pair.lo);
|
||||
LLVMTypeRef hi = llvm_abi_type(context, ret_arg_info->direct_pair.hi);
|
||||
retval = llvm_get_twostruct(context, lo, hi);*/
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
retval = tildetype(call_return_type);
|
||||
break;
|
||||
case ABI_ARG_DIRECT_SPLIT_STRUCT:
|
||||
UNREACHABLE
|
||||
case ABI_ARG_DIRECT_COERCE_INT:
|
||||
retval = tilde_get_int_type_of_bytesize(type_size(call_return_type));
|
||||
break;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
retval = tildetype(ret_arg_info->direct_coerce_type);
|
||||
break;
|
||||
}
|
||||
|
||||
// If it's optional and it's not void (meaning ret_abi_info will be NULL)
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
add_func_type_param(context, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), prototype->ret_by_ref_abi_info, params);
|
||||
}
|
||||
|
||||
// Add in all of the required arguments.
|
||||
VECEACH(prototype->param_types, i)
|
||||
{
|
||||
add_func_type_param(context, prototype->param_types[i], prototype->abi_args[i], params);
|
||||
}
|
||||
|
||||
VECEACH(prototype->varargs, i)
|
||||
{
|
||||
add_func_type_param(context, prototype->varargs[i], prototype->abi_varargs[i], params);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
TB_Global *tilde_get_typeid(TildeContext *c, Type *type)
|
||||
{
|
||||
return NULL;
|
||||
/*
|
||||
if (type->backend_typeid) return type->backend_typeid;
|
||||
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_OPTIONAL:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_OPTIONAL, type->optional, 0, NULL, false);
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_ARRAY, type->array.base, 0, NULL, false);
|
||||
case TYPE_VECTOR:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_VECTOR, type->array.base, type->array.len, NULL, false);
|
||||
case TYPE_ARRAY:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_ARRAY, type->array.base, type->array.len, NULL, false);
|
||||
case TYPE_SUBARRAY:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_SUBARRAY, type->array.base, 0, NULL, false);
|
||||
case TYPE_POINTER:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_POINTER, type->pointer, 0, NULL, false);
|
||||
case TYPE_DISTINCT:
|
||||
return tilde_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_DISTINCT, type->decl->distinct->type, 0, NULL, false);
|
||||
case TYPE_ENUM:
|
||||
return tilde_generate_introspection_global(c, type);
|
||||
case TYPE_FAULTTYPE:
|
||||
return tilde_generate_introspection_global(c, type);
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
return tilde_generate_introspection_global(c, type);
|
||||
case TYPE_FUNC:
|
||||
if (type->function.prototype->raw_type == type)
|
||||
{
|
||||
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
|
||||
return tilde_generate_introspection_global(c, ref, type, INTROSPECT_TYPE_FUNC, NULL, 0, NULL, false);
|
||||
}
|
||||
return llvm_get_typeid(c, type->function.prototype->raw_type);
|
||||
case TYPE_BITSTRUCT:
|
||||
{
|
||||
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
|
||||
return tilde_generate_introspection_global(c, ref, type, INTROSPECT_TYPE_BITSTRUCT, NULL, 0, NULL, false);
|
||||
}
|
||||
case TYPE_TYPEDEF:
|
||||
return tilde_get_typeid(c, type->canonical);
|
||||
case TYPE_INFERRED_ARRAY:
|
||||
case TYPE_INFERRED_VECTOR:
|
||||
case TYPE_UNTYPED_LIST:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case TYPE_TYPEINFO:
|
||||
case TYPE_MEMBER:
|
||||
UNREACHABLE
|
||||
case TYPE_VOID:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_VOID, 0);
|
||||
case TYPE_BOOL:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_BOOL, 0);
|
||||
case ALL_SIGNED_INTS:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_SIGNED_INT,
|
||||
type_kind_bitsize(type->type_kind));
|
||||
case ALL_UNSIGNED_INTS:
|
||||
return tilde_get_introspection_for_builtin_type(c,
|
||||
type,
|
||||
INTROSPECT_TYPE_UNSIGNED_INT,
|
||||
type_kind_bitsize(type->type_kind));
|
||||
case ALL_FLOATS:
|
||||
return tilde_get_introspection_for_builtin_type(c,
|
||||
type,
|
||||
INTROSPECT_TYPE_FLOAT,
|
||||
type_kind_bitsize(type->type_kind));
|
||||
case TYPE_ANYERR:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_ANYERR, 0);
|
||||
case TYPE_ANY:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_ANY, 0);
|
||||
case TYPE_TYPEID:
|
||||
return tilde_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_TYPEID, 0);
|
||||
case TYPE_POISONED:
|
||||
UNREACHABLE
|
||||
case TYPE_SCALED_VECTOR:
|
||||
TODO
|
||||
}
|
||||
UNREACHABLE*/
|
||||
}
|
||||
|
||||
void tilde_emit_function_decl(TildeContext *c, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
// Resolve function backend type for function.
|
||||
|
||||
TB_Function *fn = tilde_get_function(c, decl);
|
||||
(void)fn;
|
||||
/// FunctionPrototype *prototype = decl->type->function.prototype;
|
||||
|
||||
/*
|
||||
ABIArgInfo *ret_abi_info = prototype->ret_abi_info;
|
||||
llvm_emit_param_attributes(c, function, ret_abi_info, true, 0, 0);
|
||||
unsigned params = vec_size(prototype->param_types);
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
ABIArgInfo *info = prototype->ret_by_ref_abi_info;
|
||||
llvm_emit_param_attributes(c, function, prototype->ret_by_ref_abi_info, false, info->param_index_start + 1, info->param_index_end);
|
||||
}
|
||||
for (unsigned i = 0; i < params; i++)
|
||||
{
|
||||
ABIArgInfo *info = prototype->abi_args[i];
|
||||
llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end);
|
||||
}
|
||||
// We ignore decl->func_decl.attr_inline and place it in every call instead.
|
||||
if (decl->func_decl.attr_noinline)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_id.noinline, -1);
|
||||
}
|
||||
if (decl->func_decl.signature.attrs.noreturn)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_id.noreturn, -1);
|
||||
}
|
||||
if (decl->alignment != type_abi_alignment(decl->type))
|
||||
{
|
||||
llvm_set_alignment(function, decl->alignment);
|
||||
}
|
||||
if (decl->section)
|
||||
{
|
||||
LLVMSetSection(function, decl->section);
|
||||
}
|
||||
llvm_attribute_add(c, function, attribute_id.nounwind, -1);
|
||||
if (decl->func_decl.attr_naked)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_id.naked, -1);
|
||||
}
|
||||
LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(prototype->call_abi));
|
||||
|
||||
Visibility visibility = decl->visibility;
|
||||
if (decl->is_external_visible) visibility = VISIBLE_PUBLIC;
|
||||
switch (visibility)
|
||||
{
|
||||
case VISIBLE_EXTERN:
|
||||
if (decl->is_weak)
|
||||
{
|
||||
LLVMSetLinkage(function, LLVMExternalWeakLinkage);
|
||||
llvm_set_comdat(c, function);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMSetLinkage(function, LLVMExternalLinkage);
|
||||
}
|
||||
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
||||
if (prototype->call_abi == CALL_X86_STD && platform_target.os == OS_TYPE_WIN32)
|
||||
{
|
||||
LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass);
|
||||
}
|
||||
break;
|
||||
case VISIBLE_PUBLIC:
|
||||
case VISIBLE_MODULE:
|
||||
if (decl->is_weak) llvm_set_weak(c, function);
|
||||
break;
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetLinkage(function, decl->is_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
|
||||
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
||||
break;;
|
||||
}
|
||||
if (llvm_use_debug(c))
|
||||
{
|
||||
llvm_emit_debug_function(c, decl);
|
||||
}*/
|
||||
}
|
||||
|
||||
TB_DataType tildetype(Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (type->tb_set) return (TB_DataType) { .raw = type->tb_type };
|
||||
TB_DataType tb_type;
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEID:
|
||||
case TYPE_ANYFAULT:
|
||||
UNREACHABLE;
|
||||
case TYPE_FUNC:
|
||||
TODO
|
||||
case TYPE_VECTOR:
|
||||
tb_type = tildetype(type->array.base);
|
||||
tb_type.width = next_highest_power_of_2(type->array.len);
|
||||
break;
|
||||
case TYPE_F32:
|
||||
tb_type = TB_TYPE_F32;
|
||||
break;
|
||||
case TYPE_F64:
|
||||
tb_type = TB_TYPE_F64;
|
||||
break;
|
||||
case TYPE_VOID:
|
||||
tb_type = TB_TYPE_VOID;
|
||||
break;
|
||||
case TYPE_I8:
|
||||
case TYPE_U8:
|
||||
tb_type = TB_TYPE_I8;
|
||||
break;
|
||||
case TYPE_I16:
|
||||
case TYPE_U16:
|
||||
tb_type = TB_TYPE_I16;
|
||||
break;
|
||||
case TYPE_I32:
|
||||
case TYPE_U32:
|
||||
tb_type = TB_TYPE_I32;
|
||||
break;
|
||||
case TYPE_I64:
|
||||
case TYPE_U64:
|
||||
tb_type = TB_TYPE_I64;
|
||||
break;
|
||||
case TYPE_I128:
|
||||
case TYPE_U128:
|
||||
tb_type = (TB_DataType) { { TB_INT, 0, 128 } };
|
||||
break;
|
||||
case TYPE_POINTER:
|
||||
tb_type = TB_TYPE_PTR;
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
tb_type = TB_TYPE_BOOL;
|
||||
break;
|
||||
case TYPE_ANY:
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
type->tb_set = 1;
|
||||
type->tb_type = tb_type.raw;
|
||||
return tb_type;
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
#include "tilde_internal.h"
|
||||
|
||||
|
||||
void value_set(TBEValue *value, TB_Reg val, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(val || type == type_void);
|
||||
value->reg = val;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = TBE_VALUE;
|
||||
value->type = type;
|
||||
}
|
||||
|
||||
void value_set_decl(TildeContext *c, TBEValue *value, Decl *decl)
|
||||
{
|
||||
decl = decl_flatten(decl);
|
||||
if (decl->is_value)
|
||||
{
|
||||
value_set(value, decl->tb_register, decl->type);
|
||||
return;
|
||||
}
|
||||
value_set_decl_address(c, value, decl);
|
||||
}
|
||||
|
||||
|
||||
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment)
|
||||
{
|
||||
value->reg = addr;
|
||||
value->alignment = alignment;
|
||||
value->kind = TBE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
void value_set_address_abi_aligned(TBEValue *value, TB_Reg val, Type *type)
|
||||
{
|
||||
value_set_address(value, val, type, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void value_addr(TildeContext *c, TBEValue *value)
|
||||
{
|
||||
value_fold_optional(c, value);
|
||||
if (value->kind == TBE_ADDRESS) return;
|
||||
if (!c->f)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
TB_Reg tilde_get_opt_ref(TildeContext *c, Decl *decl)
|
||||
{
|
||||
tilde_get_ref(c, decl);
|
||||
decl = decl_flatten(decl);
|
||||
if (decl->decl_kind != DECL_VAR) return TB_NULL_REG;
|
||||
return decl->var.tb_optional_reg;
|
||||
}
|
||||
|
||||
TB_Function *tilde_get_function(TildeContext *c, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
if (decl->tb_symbol && ((TB_Symbol *)decl->tb_symbol)->module == c->module)
|
||||
{
|
||||
return decl->tb_symbol;
|
||||
}
|
||||
bool is_internal = decl->unit->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility);
|
||||
decl->tb_symbol = tb_function_create(c->module, decl_get_extname(decl), is_internal ? TB_LINKAGE_PRIVATE : TB_LINKAGE_PUBLIC);
|
||||
tb_function_set_prototype(decl->tb_symbol, tilde_get_func_prototype(c, decl->type->function.prototype));
|
||||
return decl->tb_symbol;
|
||||
}
|
||||
|
||||
TB_Reg tilde_get_ref(TildeContext *c, Decl *decl)
|
||||
{
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_VAR:
|
||||
if (decl_is_local(decl))
|
||||
{
|
||||
return decl->tb_register;
|
||||
}
|
||||
if (decl->var.kind == VARDECL_UNWRAPPED) return tilde_get_ref(c, decl->var.alias);
|
||||
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
|
||||
{
|
||||
TB_Symbol *symbol = decl->backend_value;
|
||||
if (symbol->module != c->module || !symbol)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
return tb_inst_get_symbol_address(c->f, decl->backend_value);
|
||||
}
|
||||
case DECL_FUNC:
|
||||
/*
|
||||
backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type));
|
||||
if (decl->unit->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility))
|
||||
{
|
||||
llvm_set_internal_linkage(backend_ref);
|
||||
}
|
||||
return backend_ref;*/
|
||||
case DECL_DEFINE:
|
||||
if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) return tilde_get_ref(c, decl->define_decl.alias);
|
||||
UNREACHABLE
|
||||
case DECL_FAULTVALUE:
|
||||
/*
|
||||
if (!decl->backend_ref)
|
||||
{
|
||||
llvm_get_typeid(c, declptr(decl->enum_constant.parent)->type);
|
||||
}
|
||||
assert(decl->backend_ref);
|
||||
return decl->backend_ref;*/
|
||||
TODO
|
||||
case DECL_POISONED:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_BITSTRUCT:
|
||||
case DECL_CT_CASE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_IF:
|
||||
case DECL_CT_SWITCH:
|
||||
case DECL_CT_ASSERT:
|
||||
case DECL_DISTINCT:
|
||||
case DECL_ENUM:
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_FAULT:
|
||||
case DECL_GENERIC:
|
||||
case DECL_IMPORT:
|
||||
case DECL_LABEL:
|
||||
case DECL_MACRO:
|
||||
case DECL_STRUCT:
|
||||
case DECL_TYPEDEF:
|
||||
case DECL_UNION:
|
||||
case DECL_DECLARRAY:
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
case DECL_BODYPARAM:
|
||||
case DECL_CT_ECHO:
|
||||
case DECL_CT_INCLUDE:
|
||||
UNREACHABLE;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
void value_set_decl_address(TildeContext *c, TBEValue *value, Decl *decl)
|
||||
{
|
||||
TB_Reg backend_ref = tilde_get_ref(c, decl);
|
||||
value_set_address(value, backend_ref, decl->type, decl->alignment);
|
||||
|
||||
if ((value->failable = tilde_get_opt_ref(c, decl)))
|
||||
{
|
||||
value->kind = TBE_ADDRESS_OPTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
void value_fold_optional(TildeContext *c, TBEValue *value)
|
||||
{
|
||||
if (value->kind == TBE_ADDRESS_OPTIONAL)
|
||||
{
|
||||
TODO
|
||||
//tilde_emit_jump_to_optional_exit(c, tilde_load_natural_alignment(c, type_anyfault, value->optional, "optval"));
|
||||
value->kind = TBE_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
void value_rvalue(TildeContext *c, TBEValue *value)
|
||||
{
|
||||
if (value->kind == TBE_VALUE) return;
|
||||
value_fold_optional(c, value);
|
||||
value->reg = tilde_load_value(c, value);
|
||||
value->kind = TBE_VALUE;
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "codegen_internal.h"
|
||||
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#undef TB_Reg
|
||||
#include <tb.h>
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TBE_VALUE,
|
||||
TBE_ADDRESS,
|
||||
TBE_ADDRESS_OPTIONAL,
|
||||
} TBBackendValueKind;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TBBackendValueKind kind: 5;
|
||||
AlignSize alignment;
|
||||
|
||||
Type *type; // Should never be a distinct or canonical type.
|
||||
|
||||
TB_Reg reg;
|
||||
TB_Reg failable;
|
||||
} TBEValue;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Module *code_module;
|
||||
const char *object_filename;
|
||||
const char *ir_filename;
|
||||
const char *asm_filename;
|
||||
|
||||
TB_Function **functions;
|
||||
TB_Module *module;
|
||||
TB_FeatureSet features;
|
||||
|
||||
struct
|
||||
{
|
||||
const char *name;
|
||||
FunctionPrototype *prototype;
|
||||
Type *rtype;
|
||||
} cur_func;
|
||||
Decl *curr_func;
|
||||
TB_Function *f;
|
||||
TB_Reg opt_var;
|
||||
TB_Label catch_block;
|
||||
TBEValue retval;
|
||||
TB_Reg return_out;
|
||||
TB_Reg optional_out;
|
||||
|
||||
struct
|
||||
{
|
||||
TB_Reg last_ptr;
|
||||
TB_Reg stack_slot;
|
||||
CompilationUnit *compile_unit;
|
||||
} debug;
|
||||
} TildeContext;
|
||||
|
||||
|
||||
|
||||
|
||||
#define PUSH_OPT() TB_Label _old_catch = c->catch_block; TB_Reg _old_opt_var = c->opt_var
|
||||
#define POP_OPT() c->catch_block = _old_catch; c->opt_var = _old_opt_var
|
||||
|
||||
|
||||
// -- store ---
|
||||
static inline void tilde_store_internal(TildeContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_to_ptr_raw_aligned(TildeContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_to_ptr_raw(TildeContext *c, TB_Reg addr, TB_Reg value, Type *type);
|
||||
void tilde_store_value_raw(TildeContext *c, TBEValue *destination, TB_Reg value);
|
||||
void tilde_store_to_ptr_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store_value_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store(TildeContext *c, TBEValue *dst, TBEValue *value);
|
||||
void tilde_store_decl_raw(TildeContext *c, Decl *decl, TB_Reg value);
|
||||
TB_Reg tilde_load_natural_alignment(TildeContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value_store(TildeContext *c, TBEValue *value);
|
||||
void tilde_store_zero(TildeContext *c, Type *type, TB_Reg addr, AlignSize alignment);
|
||||
void tilde_store_value_zero(TildeContext *c, TBEValue *to);
|
||||
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value);
|
||||
|
||||
// -- load ---
|
||||
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment);
|
||||
TB_Reg tilde_load_abi_alignment(TildeContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value(TildeContext *c, TBEValue *value);
|
||||
|
||||
// -- value --
|
||||
void value_set(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment);
|
||||
void value_set_address_abi_aligned(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_decl_address(TildeContext *c, TBEValue *value, Decl *decl);
|
||||
void value_set_decl(TildeContext *c, TBEValue *value, Decl *decl);
|
||||
void value_addr(TildeContext *c, TBEValue *value);
|
||||
|
||||
static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_OPTIONAL; }
|
||||
void value_fold_optional(TildeContext *c, TBEValue *value);
|
||||
void value_rvalue(TildeContext *c, TBEValue *value);
|
||||
|
||||
TB_Function *tilde_get_function(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_get_ref(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_get_opt_ref(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_get_const_int(TildeContext *c, Type *type, uint64_t i);
|
||||
TB_Reg tilde_get_const_float(TildeContext *c, Type *type, double d);
|
||||
TB_Register tilde_get_zero(TildeContext *c, Type *type);
|
||||
|
||||
// -- type ---
|
||||
TB_DataType tilde_abi_type(AbiType type);
|
||||
TB_DataType tildetype(Type *type);
|
||||
TB_Global *tilde_get_typeid(TildeContext *c, Type *type);
|
||||
TB_DataType tilde_update_prototype_abi(TildeContext *context, FunctionPrototype *prototype, TB_DataType **params);
|
||||
void tilde_emit_function_decl(TildeContext *c, Decl *decl);
|
||||
|
||||
// -- instructions --
|
||||
void tilde_emit_cond_br(TildeContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block);
|
||||
TB_Reg tilde_emit_lshr_fixed(TildeContext *c, Type *type, TB_Reg reg, int shift);
|
||||
|
||||
// -- stmt ---
|
||||
void tilde_emit_stmt(TildeContext *c, Ast *ast);
|
||||
void tilde_emit_return_implicit(TildeContext *c);
|
||||
|
||||
// -- general ---
|
||||
TB_Register tilde_emit_is_no_error(TildeContext *c, TB_Reg reg);
|
||||
void tilde_emit_global_initializer(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_emit_alloca(TildeContext *c, Type *type, AlignSize alignment);
|
||||
void tilde_emit_local_var_alloca(TildeContext *c, Decl *decl);
|
||||
void tilde_emit_and_set_decl_alloca(TildeContext *c, Decl *decl);
|
||||
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl);
|
||||
void tilde_emit_parameter(TildeContext *c, TB_Reg *args, unsigned *arg_count_ref, ABIArgInfo *info, TBEValue *be_value, Type *type);
|
||||
INLINE bool tilde_use_debug(TildeContext *context);
|
||||
|
||||
|
||||
// -- expr --
|
||||
void tilde_emit_expr(TildeContext *c, TBEValue *result, Expr *expr);
|
||||
TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_Reg optional);
|
||||
void tilde_emit_cast(TildeContext *c, CastKind cast_kind, Expr *expr, TBEValue *value, Type *to_type, Type *from_type);
|
||||
|
||||
// -- comparisons --
|
||||
void tilde_emit_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp_zero(TildeContext *c, TBEValue *result, TBEValue *lhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp_raw(TildeContext *c, TBEValue *result, Type *lhs_type, Type *rhs_type, TB_Reg lhs_value, TB_Reg rhs_value, BinaryOp binary_op);
|
||||
|
||||
// -- optional --
|
||||
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value);
|
||||
TB_Reg tilde_emit_is_no_opt(TildeContext *c, TB_Reg error_value);
|
||||
|
||||
|
||||
// -- jumps ---
|
||||
bool tilde_emit_goto_if_needed(TildeContext *c, TB_Label jump);
|
||||
INLINE void tilde_emit_block(TildeContext *c, TB_Label block);
|
||||
|
||||
void tilde_emit_memclear_size_align(TildeContext *c, TB_Register ref, uint64_t size, AlignSize align);
|
||||
|
||||
#define EMIT_LOC(x, y) do { } while(0)
|
||||
|
||||
|
||||
static inline void tilde_store_internal(TildeContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
tb_inst_store(c->f, type, addr, value, alignment);
|
||||
}
|
||||
|
||||
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
return tb_inst_load(c->f, type, addr, alignment);
|
||||
}
|
||||
|
||||
static inline TB_Reg decl_reg(Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_reg(decl->var.alias);
|
||||
assert(!decl->is_value);
|
||||
return decl->tb_register;
|
||||
}
|
||||
|
||||
TB_DataType tilde_get_int_type_of_bytesize(int byte_size);
|
||||
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype);
|
||||
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl)
|
||||
{
|
||||
if (!decl->is_external_visible && decl->visibility == VISIBLE_LOCAL) return TB_LINKAGE_PRIVATE;
|
||||
return TB_LINKAGE_PUBLIC;
|
||||
}
|
||||
|
||||
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value)
|
||||
{
|
||||
return tilde_store_to_ptr_aligned(c, destination, value, type_alloca_alignment(value->type));
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_block(TildeContext *c, TB_Label block)
|
||||
{
|
||||
tb_inst_set_label(c->f, block);
|
||||
}
|
||||
|
||||
INLINE bool tilde_use_debug(TildeContext *context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user