From 041c09680102fb717e5d881901ebed1b5e090721 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 9 Jan 2022 21:23:37 +0100 Subject: [PATCH] Refactored some of the llvm lowering. Work on TB. Disabled mimalloc due to issues with LLVM. --- CMakeLists.txt | 16 +- src/compiler/ast.c | 14 + src/compiler/codegen_internal.h | 4 + src/compiler/compiler_internal.h | 6 +- src/compiler/llvm_codegen.c | 272 +----- src/compiler/llvm_codegen_expr.c | 488 +++++----- src/compiler/llvm_codegen_function.c | 40 +- src/compiler/llvm_codegen_instr.c | 52 + src/compiler/llvm_codegen_internal.h | 51 +- src/compiler/llvm_codegen_stmt.c | 33 +- src/compiler/llvm_codegen_storeload.c | 174 ++++ src/compiler/llvm_codegen_value.c | 110 +++ src/compiler/sema_decls.c | 1 + src/compiler/sema_expr.c | 19 +- src/compiler/tb_codegen.c | 667 +++++++------ src/compiler/tilde_codegen.c | 1219 ++++++++++++++++++++++++ src/compiler/tilde_codegen_expr.c | 758 +++++++++++++++ src/compiler/tilde_codegen_instr.c | 43 + src/compiler/tilde_codegen_stmt.c | 122 +++ src/compiler/tilde_codegen_storeload.c | 105 ++ src/compiler/tilde_codegen_value.c | 102 ++ src/compiler/tilde_internal.h | 125 +++ 22 files changed, 3571 insertions(+), 850 deletions(-) create mode 100644 src/compiler/llvm_codegen_instr.c create mode 100644 src/compiler/llvm_codegen_storeload.c create mode 100644 src/compiler/llvm_codegen_value.c create mode 100644 src/compiler/tilde_codegen.c create mode 100644 src/compiler/tilde_codegen_expr.c create mode 100644 src/compiler/tilde_codegen_instr.c create mode 100644 src/compiler/tilde_codegen_stmt.c create mode 100644 src/compiler/tilde_codegen_storeload.c create mode 100644 src/compiler/tilde_codegen_value.c create mode 100644 src/compiler/tilde_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 118d0faaf..695edde51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,7 @@ set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]") option(C3_USE_MIMALLOC "Use built-in mimalloc" ON) set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc") -if(WIN32) - set(C3_USE_MIMALLOC OFF) -endif() +set(C3_USE_MIMALLOC OFF) if(C3_USE_MIMALLOC) option(MI_BUILD_TESTS OFF) @@ -150,9 +148,11 @@ add_executable(c3c src/compiler/llvm_codegen_debug_info.c src/compiler/llvm_codegen_expr.c src/compiler/llvm_codegen_function.c + src/compiler/llvm_codegen_instr.c src/compiler/llvm_codegen_module.c src/compiler/llvm_codegen_stmt.c src/compiler/llvm_codegen_type.c + src/compiler/llvm_codegen_value.c src/compiler/module.c src/compiler/number.c src/compiler/parse_expr.c @@ -173,6 +173,10 @@ add_executable(c3c src/compiler/symtab.c src/compiler/target.c src/compiler/tb_codegen.c + src/compiler/tilde_codegen.c + src/compiler/tilde_codegen_instr.c + src/compiler/tilde_codegen_value.c + src/compiler/tilde_codegen_storeload.c src/compiler_tests/benchmark.c src/compiler_tests/tests.c src/compiler/tokens.c @@ -188,7 +192,11 @@ add_executable(c3c src/utils/toml.c src/build/project.c src/utils/vmem.c src/utils/vmem.h - src/utils/whereami.c) + src/utils/whereami.c + src/compiler/tilde_codegen_storeload.c + src/compiler/llvm_codegen_storeload.c + src/compiler/tilde_codegen_expr.c + src/compiler/tilde_codegen_stmt.c) if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC") message(STATUS "using gcc/clang warning switches") diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 9dc665c77..184876c48 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -480,3 +480,17 @@ static Ast poison_ast = { .ast_kind = AST_POISONED }; Ast *poisoned_ast = &poison_ast; +bool ast_is_not_empty(Ast *ast) +{ + if (!ast) return false; + if (ast->ast_kind != AST_COMPOUND_STMT) return true; + AstId first = ast->compound_stmt.first_stmt; + if (first) + { + Ast *stmt = astptr(first); + if (stmt->next) return true; + if (ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end) return true; + return ast_is_not_empty(stmt); + } + return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end; +} \ No newline at end of file diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index f9051523c..c9d14c6d4 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -32,4 +32,8 @@ static inline bool abi_type_is_valid(AbiType type) return type.int_bits_plus_1 != 0; } +static inline bool abi_info_should_flatten(ABIArgInfo *info) +{ + return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten; +} diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d974940b6..0028cb9f8 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -372,6 +372,7 @@ typedef struct VarDecl_ { // Variable void *failable_ref; + int tb_failable_reg; }; }; struct @@ -587,6 +588,7 @@ typedef struct Decl_ union { void *backend_ref; + int tb_register; void *backend_value; }; const char *extname; @@ -1125,6 +1127,7 @@ typedef struct struct { void *exit_block; + uint64_t exit_val; } codegen; } AstDeferStmt; @@ -1562,6 +1565,7 @@ typedef struct FunctionPrototype_ ABIArgInfo *ret_abi_info; ABIArgInfo *ret_by_ref_abi_info; ABIArgInfo **abi_args; + void *tb_prototype; } FunctionPrototype; extern GlobalContext global_context; @@ -1660,7 +1664,7 @@ ARENA_DEF(type_info, TypeInfo) static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; } static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return false; } - +bool ast_is_not_empty(Ast *ast); static inline Ast *new_ast(AstKind kind, SourceSpan range) { Ast *ast = ast_calloc(); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3864af281..244711132 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -55,6 +55,12 @@ static void gencontext_destroy(GenContext *context) free(context); } +LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error_value) +{ + return LLVMBuildICmp(c->builder, LLVMIntEQ, error_value, llvm_get_zero(c, type_anyerr), "not_err"); +} + + LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, AlignSize align, bool bitcast) { @@ -64,56 +70,6 @@ LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint } -void llvm_emit_memclear(GenContext *c, BEValue *ref) -{ - llvm_value_addr(c, ref); - Type *type = ref->type; - if (!type_is_abi_aggregate(type)) - { - llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, type)); - return; - } - Type *single_type = type_abi_find_single_struct_element(type); - - if (single_type && !type_is_abi_aggregate(single_type)) - { - BEValue element; - llvm_value_set_address_align(&element, llvm_emit_bitcast(c, ref->value, type_get_ptr(single_type)), single_type, (unsigned)ref->alignment); - llvm_emit_memclear(c, &element); - return; - } - if (type_size(type) <= 16) - { - if (type->type_kind == TYPE_STRUCT) - { - Decl *decl = type->decl; - Decl **members = decl->strukt.members; - VECEACH(members, i) - { - if (!type_size(members[i]->type)) continue; - BEValue member_ref; - llvm_emit_struct_member_ref(c, ref, &member_ref, i); - llvm_emit_memclear(c, &member_ref); - } - return; - } - if (type->type_kind == TYPE_ARRAY) - { - LLVMTypeRef array_type = llvm_get_type(c, type); - for (unsigned i = 0; i < type->array.len; i++) - { - AlignSize align; - LLVMValueRef element_ptr = llvm_emit_array_gep_raw(c, ref->value, array_type, i, ref->alignment, &align); - BEValue be_value; - llvm_value_set_address_align(&be_value, element_ptr, type->array.base, align); - llvm_emit_memclear(c, &be_value); - } - return; - } - } - llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true); -} - LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified) { if (diff == 1) return LLVMConstNull(element_type); @@ -290,7 +246,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ { BEValue value; llvm_emit_expr(c, &value, const_init->init_value); - return llvm_value_rvalue_store(c, &value); + return llvm_load_value_store(c, &value); } } UNREACHABLE @@ -337,7 +293,10 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) BEValue member; llvm_emit_subarray_pointer(c, value, &member); llvm_value_rvalue(c, &member); - llvm_value_set_address_align(value, member.value, type_get_ptr(value->type->array.base), type_abi_alignment(value->type->array.base)); + llvm_value_set_address(value, + member.value, + type_get_ptr(value->type->array.base), + type_abi_alignment(value->type->array.base)); return; } default: @@ -388,7 +347,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) else { llvm_emit_expr(c, &value, decl->var.init_expr); - init_value = llvm_value_rvalue_store(c, &value); + init_value = llvm_load_value_store(c, &value); } } } @@ -535,7 +494,6 @@ void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) { llvm_emit_debug_local_var(c, decl); } - } /** @@ -740,28 +698,13 @@ void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i) llvm_value_set(value, llvm_const_int(c, type, i), type); } -void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type) -{ - type = type_lowering(type); - assert(llvm_value || type == type_void); - value->value = llvm_value; - value->alignment = type_abi_alignment(type); - value->kind = BE_VALUE; - value->type = type; -} + bool llvm_value_is_const(BEValue *value) { return LLVMIsConstant(value->value); } -void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment) -{ - value->value = llvm_value; - value->alignment = alignment; - value->kind = BE_ADDRESS; - value->type = type_lowering(type); -} void llvm_value_set_decl(BEValue *value, Decl *decl) { @@ -774,73 +717,10 @@ void llvm_value_set_decl(BEValue *value, Decl *decl) llvm_value_set_decl_address(value, decl); } -void llvm_value_set_decl_address(BEValue *value, Decl *decl) -{ - decl = decl_flatten(decl); - llvm_value_set_address(value, decl_ref(decl), decl->type); - value->alignment = decl->alignment; - if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) - { - value->kind = BE_ADDRESS_FAILABLE; - value->failable = decl->var.failable_ref; - } -} -void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type) -{ - llvm_value_set_address_align(value, llvm_value, type_lowering(type), type_abi_alignment(type)); -} -void llvm_value_fold_failable(GenContext *c, BEValue *value) -{ - if (value->kind == BE_ADDRESS_FAILABLE) - { - LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); - BEValue error_value; - llvm_value_set_address(&error_value, value->failable, type_anyerr); - BEValue comp; - llvm_value_set_bool(&comp, llvm_emit_is_no_error_value(c, &error_value)); - if (c->error_var) - { - LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); - llvm_emit_cond_br(c, &comp, after_block, error_block); - llvm_emit_block(c, error_block); - llvm_store_bevalue_dest_aligned(c, c->error_var, &error_value); - llvm_emit_br(c, c->catch_block); - } - else - { - assert(c->catch_block); - llvm_emit_cond_br(c, &comp, after_block, c->catch_block); - } - llvm_emit_block(c, after_block); - value->kind = BE_ADDRESS; - } -} -LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value) -{ - llvm_value_fold_failable(c, value); - switch (value->kind) - { - case BE_VALUE: - return value->value; - case BE_ADDRESS_FAILABLE: - UNREACHABLE - case BE_ADDRESS: - return llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, - value->alignment ? value->alignment : type_abi_alignment(value->type), - ""); - case BE_BOOLEAN: - if (LLVMIsConstant(value->value)) - { - return LLVMConstZExt(value->value, c->byte_type); - } - return LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); - } - UNREACHABLE -} LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name) @@ -848,53 +728,7 @@ LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name) return LLVMCreateBasicBlockInContext(c->context, name); } -void llvm_value_addr(GenContext *c, BEValue *value) -{ - llvm_value_fold_failable(c, value); - if (value->kind == BE_ADDRESS) return; - if (!c->builder) - { - LLVMValueRef val = llvm_value_rvalue_store(c, value); - LLVMValueRef ref = LLVMAddGlobal(c->module, LLVMTypeOf(val), ".taddr"); - llvm_set_alignment(ref, llvm_abi_alignment(c, LLVMTypeOf(val))); - llvm_set_private_linkage(ref); - LLVMSetInitializer(ref, val); - llvm_emit_bitcast(c, ref, type_get_ptr(value->type)); - llvm_value_set_address(value, ref, value->type); - } - else - { - LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr"); - llvm_store_bevalue_dest_aligned(c, temp, value); - llvm_value_set_address(value, temp, value->type); - } -} -void llvm_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; - } - return; - } - llvm_value_fold_failable(c, value); - value->value = llvm_emit_load_aligned(c, - llvm_get_type(c, 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; - } - value->kind = BE_VALUE; -} static void llvm_emit_type_decls(GenContext *context, Decl *decl) @@ -1127,79 +961,8 @@ AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type) return (AlignSize)LLVMABIAlignmentOfType(c->target_data, type); } -LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment) -{ - // If we have an address but not an aggregate, do a load. - llvm_value_fold_failable(c, value); - if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type)) - { - value->value = llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, value->alignment, ""); - value->kind = BE_VALUE; - } - switch (value->kind) - { - case BE_BOOLEAN: - value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); - value->kind = BE_VALUE; - FALLTHROUGH; - case BE_VALUE: - return llvm_store_aligned(c, destination, value->value, alignment ? alignment : type_abi_alignment(value->type)); - case BE_ADDRESS_FAILABLE: - UNREACHABLE - case BE_ADDRESS: - { - // Here we do an optimized(?) memcopy. - ByteSize size = type_size(value->type); - LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size); - destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), ""); - LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); - LLVMValueRef copy = LLVMBuildMemCpy(c->builder, - destination, - alignment ? alignment : type_abi_alignment(value->type), - source, - value->alignment ? value->alignment : type_abi_alignment(value->type), - copy_size); - return copy; - } - } - UNREACHABLE -} -void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value) -{ - llvm_store_bevalue_aligned(c, destination, value, LLVMGetAlignment(destination)); -} -LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value) -{ - if (value->type == type_void) return NULL; - assert(llvm_value_is_addr(destination)); - return llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment); -} - -void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value) -{ - assert(llvm_value_is_addr(destination)); - llvm_store_aligned(c, destination->value, raw_value, destination->alignment); -} - -void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type) -{ - llvm_store_aligned(context, pointer, value, type_abi_alignment(type)); -} - -LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) -{ - LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer); - if (alignment) llvm_set_alignment(ref, alignment); - return ref; -} - -void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value) -{ - assert(!decl->is_value); - llvm_store_aligned(context, decl->backend_ref, value, decl->alignment); -} void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len) { @@ -1219,14 +982,7 @@ void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, un llvm_emit_memcpy(c, decl->backend_ref, decl->alignment, source, source_alignment, type_size(decl->type)); } -LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name) -{ - assert(c->builder); - LLVMValueRef value = LLVMBuildLoad2(c->builder, type, pointer, name); - assert(LLVMGetTypeContext(type) == c->context); - llvm_set_alignment(value, alignment ? alignment : llvm_abi_alignment(c, type)); - return value; -} + TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type) { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 9b03c979b..0075cb5a5 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -16,10 +16,75 @@ static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Ex static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *result, BEValue *parent, BEValue *index, SourceLocation *loc); static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSize offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value); static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); -LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value) +static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); + +BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable) { - llvm_value_rvalue(c, value); - return LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type_anyerr), "not_err"); + assert(ref->kind == BE_ADDRESS || ref->kind == BE_ADDRESS_FAILABLE); + LLVMBasicBlockRef assign_block = NULL; + + PUSH_ERROR(); + + if (failable) + { + if (IS_FAILABLE(expr)) + { + if (expr->expr_kind == EXPR_FAILABLE) + { + c->error_var = NULL; + c->catch_block = NULL; + BEValue result; + llvm_emit_expr(c, &result, expr->inner_expr); + llvm_store_value_dest_aligned(c, failable, &result); + llvm_value_set(&result, LLVMGetUndef(llvm_get_type(c, ref->type)), ref->type); + POP_ERROR(); + return result; + } + assign_block = llvm_basic_block_new(c, "after_assign"); + c->error_var = failable; + c->catch_block = assign_block; + } + else + { + c->error_var = NULL; + c->catch_block = NULL; + } + } + BEValue value; + if (type_is_vector(expr->type)) + { + llvm_emit_expr(c, &value, expr); + llvm_store_value(c, ref, &value); + } + else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) + { + llvm_emit_const_initialize_reference(c, ref, expr); + value = *ref; + } + else if (expr_is_init_list(expr)) + { + llvm_emit_initialize_reference(c, ref, expr); + value = *ref; + } + else + { + llvm_emit_expr(c, &value, expr); + llvm_store_value(c, ref, &value); + } + + if (failable) + { + llvm_store_raw_abi_alignment(c, failable, llvm_get_zero(c, type_anyerr), type_anyerr); + } + POP_ERROR(); + + if (failable && IS_FAILABLE(expr)) + { + llvm_emit_br(c, assign_block); + llvm_emit_block(c, assign_block); + } + + return value; } static inline LLVMValueRef llvm_emit_extract_value(GenContext *c, LLVMValueRef agg, unsigned index) @@ -80,33 +145,7 @@ static inline LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLV return LLVMBuildTrunc(c->builder, data, type, ""); } -static inline LLVMValueRef llvm_emit_lshr(GenContext *c, LLVMValueRef data, int shift) -{ - assert(shift >= 0); - if (shift == 0) return data; - LLVMTypeRef type = LLVMTypeOf(data); - BitSize bit_width = llvm_bitsize(c, type); - if (shift >= bit_width) return LLVMConstNull(type); - if (LLVMIsAConstant(data)) - { - return LLVMConstLShr(data, LLVMConstInt(type, (unsigned)shift, false)); - } - return LLVMBuildLShr(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), ""); -} -static inline LLVMValueRef llvm_emit_shl(GenContext *c, LLVMValueRef data, int shift) -{ - assert(shift >= 0); - if (shift == 0) return data; - LLVMTypeRef type = LLVMTypeOf(data); - BitSize bit_width = llvm_bitsize(c, type); - if (shift >= bit_width) return LLVMConstNull(type); - if (LLVMIsAConstant(data)) - { - return LLVMConstShl(data, LLVMConstInt(type, (unsigned)shift, false)); - } - return LLVMBuildShl(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), ""); -} void llvm_convert_vector_comparison(GenContext *c, BEValue *be_value, LLVMValueRef val, Type *vector_type) { @@ -122,7 +161,7 @@ LLVMValueRef llvm_emit_coerce_alignment(GenContext *c, BEValue *be_value, LLVMTy { LLVMValueRef cast = llvm_emit_alloca(c, llvm_get_type(c, be_value->type), target_alignment, "coerce"); LLVMValueRef target = LLVMBuildBitCast(c->builder, cast, LLVMPointerType(coerce_type, 0), ""); - llvm_store_bevalue_aligned(c, target, be_value, target_alignment); + llvm_store_value_aligned(c, target, be_value, target_alignment); *resulting_alignment = target_alignment; return cast; } @@ -326,8 +365,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value // 1. If the types match then we're done, just load. if (llvm_source_type == coerced) { - llvm_value_rvalue_store(c, value); - return value->value; + return llvm_load_value(c, value); } // 2. Both are integer types and values, then just truncate / extend @@ -359,7 +397,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value if ((coerced_type_kind == LLVMPointerTypeKind || coerced_type_kind == LLVMIntegerTypeKind) && (source_type_kind == LLVMPointerTypeKind || source_type_kind == LLVMIntegerTypeKind)) { - LLVMValueRef val = llvm_emit_load_aligned(c, llvm_source_type, addr, value->alignment, ""); + LLVMValueRef val = llvm_load(c, llvm_source_type, addr, value->alignment, ""); return llvm_coerce_int_ptr(c, val, llvm_source_type, coerced); } @@ -367,7 +405,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value if (source_size > target_size) { LLVMValueRef val = LLVMBuildBitCast(c->builder, addr, LLVMPointerType(coerced, 0), ""); - return llvm_emit_load_aligned(c, coerced, val, value->alignment, ""); + return llvm_load(c, coerced, val, value->alignment, ""); } // Otherwise, do it through memory. @@ -375,7 +413,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value LLVMValueRef temp = llvm_emit_alloca(c, coerced, max_align, "tempcoerce"); llvm_emit_memcpy(c, temp, max_align, addr, value->alignment, source_size); - return llvm_emit_load_aligned(c, coerced, temp, max_align, ""); + return llvm_load(c, coerced, temp, max_align, ""); } @@ -385,7 +423,7 @@ void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignmen // 1. Simplest case, the underlying types match. if (coerced == target_type) { - llvm_store_aligned(c, addr, value, alignment); + llvm_store(c, addr, value, alignment); return; } @@ -404,7 +442,7 @@ void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignmen && (source_type_kind == LLVMPointerTypeKind || source_type_kind == LLVMIntegerTypeKind)) { value = llvm_coerce_int_ptr(c, value, coerced, target_type); - llvm_store_aligned(c, addr, value, alignment); + llvm_store(c, addr, value, alignment); return; } @@ -413,14 +451,14 @@ void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignmen if (src_size <= target_size) { LLVMValueRef val = LLVMBuildBitCast(c->builder, addr, LLVMPointerType(coerced, 0), ""); - llvm_store_aligned(c, val, value, alignment); + llvm_store(c, val, value, alignment); return; } // Otherwise, do it through memory. AlignSize coerce_align = llvm_abi_alignment(c, coerced); LLVMValueRef temp = llvm_emit_alloca(c, coerced, coerce_align, "tempcoerce"); - llvm_store_aligned(c, temp, value, coerce_align); + llvm_store(c, temp, value, coerce_align); llvm_emit_memcpy(c, addr, alignment, temp, coerce_align, target_size); } @@ -429,7 +467,7 @@ void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTy LLVMTypeRef target_type = llvm_get_type(c, original_type); LLVMValueRef addr = llvm_emit_alloca(c, target_type, type_abi_alignment(original_type), "result"); llvm_emit_coerce_store(c, addr, type_abi_alignment(original_type), coerced, value, target_type); - llvm_value_set_address(result, addr, original_type); + llvm_value_set_address_abi_aligned(result, addr, original_type); } static inline LLVMValueRef llvm_emit_sub_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceLocation *loc) @@ -489,13 +527,19 @@ static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *re switch (type->type_kind) { case TYPE_POINTER: - llvm_value_set_address(result, llvm_emit_pointer_inbounds_gep_raw(c, llvm_get_pointee_type(c, parent->type), parent->value, index->value), type->pointer); + llvm_value_set_address_abi_aligned(result, + llvm_emit_pointer_inbounds_gep_raw(c, + llvm_get_pointee_type(c, + parent->type), + parent->value, + index->value), + type->pointer); return; case TYPE_FLEXIBLE_ARRAY: { AlignSize alignment; LLVMValueRef ptr = llvm_emit_array_gep_raw_index(c, parent->value, llvm_get_type(c, type), index->value, parent->alignment, &alignment); - llvm_value_set_address_align(result, ptr, type->array.base, alignment); + llvm_value_set_address(result, ptr, type->array.base, alignment); return; } case TYPE_ARRAY: @@ -508,7 +552,7 @@ static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *re { AlignSize alignment; LLVMValueRef ptr = llvm_emit_array_gep_raw_index(c, parent->value, llvm_get_type(c, type), index->value, parent->alignment, &alignment); - llvm_value_set_address_align(result, ptr, type->array.base, alignment); + llvm_value_set_address(result, ptr, type->array.base, alignment); } return; case TYPE_SUBARRAY: @@ -518,7 +562,7 @@ static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *re } { LLVMValueRef ptr = llvm_emit_pointer_inbounds_gep_raw(c, llvm_get_type(c, type->array.base), parent->value, index->value); - llvm_value_set_address_align(result, ptr, type->array.base, type_abi_alignment(type->array.base)); + llvm_value_set_address(result, ptr, type->array.base, type_abi_alignment(type->array.base)); } return; default: @@ -612,7 +656,10 @@ static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *par { case TYPE_UNION: llvm_value_addr(c, value); - llvm_value_set_address_align(value, llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), found->type, value->alignment); + llvm_value_set_address(value, + llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), + found->type, + value->alignment); break; case TYPE_STRUCT: llvm_value_struct_gep(c, value, value, (unsigned)index); @@ -637,10 +684,10 @@ static void llvm_emit_bitstruct_member(GenContext *c, BEValue *value, Decl *pare { case TYPE_UNION: llvm_value_addr(c, value); - llvm_value_set_address_align(value, - llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), - found->type, - value->alignment); + llvm_value_set_address(value, + llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), + found->type, + value->alignment); break; case TYPE_STRUCT: llvm_value_struct_gep(c, value, value, (unsigned)index); @@ -682,9 +729,9 @@ static inline void llvm_extract_bool_bit_from_array(GenContext *c, BEValue *be_v // Grab the byte LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, llvm_get_type(c, be_value->type), start_bit / 8, be_value->alignment, &alignment); - LLVMValueRef element = llvm_emit_load_aligned(c, array_type, byte_ptr, alignment, ""); + LLVMValueRef element = llvm_load(c, array_type, byte_ptr, alignment, ""); // Shift the bit to the zero position. - element = llvm_emit_lshr(c, element, start_bit % 8); + element = llvm_emit_lshr_fixed(c, element, start_bit % 8); // Truncate to i1. element = LLVMBuildTrunc(c->builder, element, c->bool_type, ""); // Done! @@ -694,7 +741,7 @@ static inline void llvm_extract_bool_bit_from_array(GenContext *c, BEValue *be_v static inline LLVMValueRef llvm_bswap_non_integral(GenContext *c, LLVMValueRef value, unsigned bitsize) { if (bitsize <= 8) return value; - LLVMValueRef shifted = llvm_emit_shl(c, value, (int)llvm_bitsize(c, LLVMTypeOf(value)) - (int)bitsize); + LLVMValueRef shifted = llvm_emit_shl_fixed(c, value, (int)llvm_bitsize(c, LLVMTypeOf(value)) - (int)bitsize); return llvm_emit_bswap(c, shifted); } @@ -726,16 +773,16 @@ static inline void llvm_extract_bitvalue_from_array(GenContext *c, BEValue *be_v AlignSize alignment; LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, llvm_get_type(c, be_value->type), (unsigned)i, be_value->alignment, &alignment); - LLVMValueRef element = llvm_emit_load_aligned(c, array_type, byte_ptr, alignment, ""); + LLVMValueRef element = llvm_load(c, array_type, byte_ptr, alignment, ""); element = llvm_zext_trunc(c, element, llvm_member_type); int current_offset = 8 * (i - start_byte) - offset; if (current_offset < 0) { - element = llvm_emit_lshr(c, element, -current_offset); + element = llvm_emit_lshr_fixed(c, element, -current_offset); } else if (current_offset > 0) { - element = llvm_emit_shl(c, element, current_offset); + element = llvm_emit_shl_fixed(c, element, current_offset); } if (res == NULL) { @@ -777,7 +824,7 @@ static inline void llvm_extract_bitvalue(GenContext *c, BEValue *be_value, Expr bool bswap = false; if (parent_decl->bitstruct.big_endian && !platform_target.big_endian) bswap = true; if (parent_decl->bitstruct.little_endian && platform_target.big_endian) bswap = true; - LLVMValueRef value = llvm_value_rvalue_store(c, be_value); + LLVMValueRef value = llvm_load_value_store(c, be_value); if (bswap) value = llvm_emit_bswap(c, value); LLVMTypeRef container_type = LLVMTypeOf(value); BitSize container_size = type_size(be_value->type); @@ -835,18 +882,18 @@ static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEV unsigned end_bit = member->var.end_bit; Type *member_type = type_flatten(member->type); - LLVMValueRef value = llvm_value_rvalue_store(c, result); + LLVMValueRef value = llvm_load_value_store(c, result); if (member_type == type_bool) { assert(start_bit == end_bit); - value = llvm_emit_shl(c, value, start_bit % 8); + value = llvm_emit_shl_fixed(c, value, start_bit % 8); AlignSize alignment; LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, array_type, start_bit / 8, parent.alignment, &alignment); - LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); - LLVMValueRef bit = llvm_emit_shl(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); + LLVMValueRef current = llvm_load(c, c->byte_type, byte_ptr, alignment, ""); + LLVMValueRef bit = llvm_emit_shl_fixed(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); current = LLVMBuildAnd(c->builder, current, LLVMConstNot(bit), ""); current = LLVMBuildOr(c->builder, current, value, ""); - llvm_store_aligned(c, byte_ptr, current, alignment); + llvm_store(c, byte_ptr, current, alignment); return; } @@ -874,7 +921,7 @@ static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEV { int skipped_bits = start_mod; // Shift the lower bits into the top of the byte. - LLVMValueRef res = llvm_emit_shl(c, value, skipped_bits); + LLVMValueRef res = llvm_emit_shl_fixed(c, value, skipped_bits); // Then truncate. if (member_type_bitsize > 8) { @@ -890,15 +937,15 @@ static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEV mask = LLVMConstOr(mask, llvm_const_high_bitmask(c->byte_type, 8, 7 - (int)end_bit)); } // Load the current value. - LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); + LLVMValueRef current = llvm_load(c, c->byte_type, byte_ptr, alignment, ""); // Empty the top bits. current = LLVMBuildAnd(c->builder, current, mask, ""); // Use *or* with the top bits from "res": current = LLVMBuildOr(c->builder, current, res, ""); // And store it back. - llvm_store_aligned(c, byte_ptr, current, alignment); + llvm_store(c, byte_ptr, current, alignment); // We now shift the value by the number of bits we used. - value = llvm_emit_lshr(c, value, 8 - skipped_bits); + value = llvm_emit_lshr_fixed(c, value, 8 - skipped_bits); // ... and we're done with the first byte. continue; } @@ -910,19 +957,19 @@ static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEV LLVMValueRef mask = llvm_const_low_bitmask(c->byte_type, 8, end_mod + 1); value = LLVMBuildAnd(c->builder, value, mask, ""); // Load the current value. - LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); + LLVMValueRef current = llvm_load(c, c->byte_type, byte_ptr, alignment, ""); // Clear the lower bits. current = LLVMBuildAnd(c->builder, current, LLVMConstNot(mask), ""); // Use *or* with the bottom bits from "value": current = LLVMBuildOr(c->builder, current, value, ""); // And store it back. - llvm_store_aligned(c, byte_ptr, current, alignment); + llvm_store(c, byte_ptr, current, alignment); continue; } // All others are simple: truncate & store - llvm_store_aligned(c, byte_ptr, llvm_zext_trunc(c, value, c->byte_type), alignment); + llvm_store(c, byte_ptr, llvm_zext_trunc(c, value, c->byte_type), alignment); // Then shift - value = llvm_emit_lshr(c, value, 8); + value = llvm_emit_lshr_fixed(c, value, 8); } } static inline void llvm_emit_bitassign_expr(GenContext *c, BEValue *be_value, Expr *expr) @@ -958,7 +1005,7 @@ static inline void llvm_emit_bitassign_expr(GenContext *c, BEValue *be_value, Ex } // To start the assign, pull out the current value. - LLVMValueRef current_value = llvm_value_rvalue_store(c, &parent); + LLVMValueRef current_value = llvm_load_value_store(c, &parent); // Get the type. LLVMTypeRef struct_type = LLVMTypeOf(current_value); @@ -970,23 +1017,23 @@ static inline void llvm_emit_bitassign_expr(GenContext *c, BEValue *be_value, Ex int end_bit = (int)member->var.end_bit; // Let's say we want to create 00111000 => start: 3 end: 5 int left_shift = (int)bits - end_bit - 1; - mask = llvm_emit_shl(c, mask, left_shift); + mask = llvm_emit_shl_fixed(c, mask, left_shift); // => shift 2: 11111100 - mask = llvm_emit_lshr(c, mask, left_shift + start_bit); + mask = llvm_emit_lshr_fixed(c, mask, left_shift + start_bit); // => shift 5: 00000111 - mask = llvm_emit_shl(c, mask, start_bit); + mask = llvm_emit_shl_fixed(c, mask, start_bit); // => shift 3: 00111000 // Now we might need to truncate or widen the value to insert: - LLVMValueRef value = llvm_value_rvalue_store(c, be_value); + LLVMValueRef value = llvm_load_value_store(c, be_value); value = llvm_zext_trunc(c, value, struct_type); // Shift to the correct location. - value = llvm_emit_shl(c, value, start_bit); + value = llvm_emit_shl_fixed(c, value, start_bit); // And combine using ((current_value & ~mask) | (value & mask)) value = LLVMBuildAnd(c->builder, value, mask, ""); current_value = LLVMBuildAnd(c->builder, current_value, LLVMConstNot(mask), ""); current_value = LLVMBuildOr(c->builder, current_value, value, ""); - llvm_store_bevalue_raw(c, &parent, current_value); + llvm_store_value_raw(c, &parent, current_value); } static inline void llvm_emit_bitaccess(GenContext *c, BEValue *be_value, Expr *expr) { @@ -1334,7 +1381,7 @@ static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement ** BEValue res; llvm_emit_expr(c, &res, value); unsigned index = (unsigned)current_element->index; - LLVMValueRef val = llvm_value_rvalue_store(c, &res); + LLVMValueRef val = llvm_load_value_store(c, &res); switch (current_element->kind) { case DESIGNATOR_FIELD: @@ -1425,10 +1472,10 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, case CONST_INIT_ZERO: if (type_is_builtin(ref->type->type_kind) || ref->type->type_kind == TYPE_ARRAY) { - llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, ref->type)); + llvm_store_value_raw(c, ref, llvm_get_zero(c, ref->type)); return; } - llvm_emit_memclear(c, ref); + llvm_store_zero(c, ref); return; case CONST_INIT_ARRAY_VALUE: UNREACHABLE @@ -1445,7 +1492,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, AlignSize alignment; LLVMValueRef array_pointer = llvm_emit_array_gep_raw(c, array_ref, array_type_llvm, (unsigned)i, ref->alignment, &alignment); BEValue value; - llvm_value_set_address_align(&value, array_pointer, element_type, alignment); + llvm_value_set_address(&value, array_pointer, element_type, alignment); llvm_emit_inititialize_reference_const(c, &value, const_init->init_array_full[i]); } return; @@ -1453,7 +1500,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, case CONST_INIT_ARRAY: { LLVMValueRef array_ref = ref->value; - llvm_emit_memclear(c, ref); + llvm_store_zero(c, ref); Type *array_type = const_init->type; Type *element_type = array_type->array.base; LLVMTypeRef array_type_llvm = llvm_get_type(c, array_type); @@ -1468,7 +1515,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, AlignSize alignment; LLVMValueRef array_pointer = llvm_emit_array_gep_raw(c, array_ref, array_type_llvm, (unsigned)element_index, ref->alignment, &alignment); BEValue value; - llvm_value_set_address_align(&value, array_pointer, element_type, alignment); + llvm_value_set_address(&value, array_pointer, element_type, alignment); llvm_emit_inititialize_reference_const(c, &value, element->init_array_value.element); } return; @@ -1480,7 +1527,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, Type *type = decl->strukt.members[index]->type->canonical; // Bitcast. BEValue value = *ref; - llvm_value_set_address(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type); + llvm_value_set_address_abi_aligned(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type); // Emit our value. llvm_emit_inititialize_reference_const(c, &value, const_init->init_union.element); return; @@ -1502,7 +1549,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, { BEValue value; llvm_emit_expr(c, &value, const_init->init_value); - llvm_store_bevalue(c, ref, &value); + llvm_store_value(c, ref, &value); return; } } @@ -1556,11 +1603,11 @@ static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *r // Todo optimize AlignSize alignment; LLVMValueRef ptr = llvm_emit_array_gep_raw(c, value, llvm_type, i, ref->alignment, &alignment); - llvm_value_set_address_align(&pointer, ptr, element->type, alignment); + llvm_value_set_address(&pointer, ptr, element->type, alignment); } else { - llvm_value_set_address_align(&pointer, value, element->type, ref->alignment); + llvm_value_set_address(&pointer, value, element->type, ref->alignment); } // If this is an initializer, we want to actually run the initialization recursively. if (element->expr_kind == EXPR_CONST && element->const_expr.const_kind == CONST_LIST) @@ -1575,7 +1622,7 @@ static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *r } BEValue init_value; llvm_emit_expr(c, &init_value, element); - llvm_store_bevalue(c, &pointer, &init_value); + llvm_store_value(c, &pointer, &init_value); } } @@ -1598,7 +1645,7 @@ static void llvm_emit_initialize_designated_const_range(GenContext *c, BEValue * BEValue new_ref; AlignSize alignment; LLVMValueRef ptr = llvm_emit_array_gep_raw(c, ref->value, ref_type, (unsigned)i, ref->alignment, &alignment); - llvm_value_set_address_align(&new_ref, ptr, type_get_indexed_type(ref->type), alignment); + llvm_value_set_address(&new_ref, ptr, type_get_indexed_type(ref->type), alignment); llvm_emit_initialize_designated(c, &new_ref, offset, current + 1, last, expr, emitted_value); } } @@ -1611,7 +1658,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSi { if (emitted_value) { - llvm_store_bevalue(c, ref, emitted_value); + llvm_store_value(c, ref, emitted_value); return; } if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) @@ -1626,7 +1673,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSi } BEValue val; llvm_emit_expr(c, &val, expr); - llvm_store_bevalue(c, ref, &val); + llvm_store_value(c, ref, &val); return; } DesignatorElement *curr = current[0]; @@ -1640,7 +1687,10 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSi unsigned decl_alignment = decl->alignment; if (ref->type->type_kind == TYPE_UNION) { - llvm_value_set_address_align(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type, type_min_alignment(offset, decl_alignment)); + llvm_value_set_address(&value, + llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), + type, + type_min_alignment(offset, decl_alignment)); } else { @@ -1655,7 +1705,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSi offset += (unsigned)curr->index * type_size(type); AlignSize alignment; LLVMValueRef ptr = llvm_emit_array_gep_raw(c, ref->value, llvm_get_type(c, ref->type), (unsigned)curr->index, ref->alignment, &alignment); - llvm_value_set_address_align(&value, ptr, type, alignment); + llvm_value_set_address(&value, ptr, type, alignment); llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value); break; } @@ -1678,7 +1728,7 @@ static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEVa llvm_value_addr(c, ref); // Clear the memory if not union. - if (real_type->type_kind != TYPE_UNION) llvm_emit_memclear(c, ref); + if (real_type->type_kind != TYPE_UNION) llvm_store_zero(c, ref); // Now walk through the elements. VECEACH(elements, i) @@ -1716,7 +1766,7 @@ LLVMValueRef llvm_emit_const_bitstruct_array(GenContext *c, ConstInitializer *in // Completely skip zero. if (!expr->const_expr.b) continue; - LLVMValueRef bit = llvm_emit_shl(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); + LLVMValueRef bit = llvm_emit_shl_fixed(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); unsigned byte = start_bit / 8; LLVMValueRef current_value = llvm_emit_extract_value(c, data, byte); data = llvm_emit_insert_value(c, data, LLVMConstOr(current_value, bit), byte); @@ -1742,11 +1792,11 @@ LLVMValueRef llvm_emit_const_bitstruct_array(GenContext *c, ConstInitializer *in LLVMValueRef to_or; if (j == start_byte) { - to_or = llvm_emit_shl(c, value, bit_offset); + to_or = llvm_emit_shl_fixed(c, value, bit_offset); } else { - to_or = llvm_emit_lshr(c, value, j * 8 - (int)start_bit); + to_or = llvm_emit_lshr_fixed(c, value, j * 8 - (int)start_bit); } if (j == end_byte) { @@ -1786,7 +1836,7 @@ LLVMValueRef llvm_emit_const_bitstruct(GenContext *c, ConstInitializer *initiali assert(initializer->init_struct[i]->kind == CONST_INIT_VALUE); BEValue entry; llvm_emit_const_expr(c, &entry, initializer->init_struct[i]->init_value); - LLVMValueRef value = llvm_value_rvalue_store(c, &entry); + LLVMValueRef value = llvm_load_value_store(c, &entry); value = llvm_zext_trunc(c, value, llvm_base_type); if (bit_size < base_type_bitsize) { @@ -1815,11 +1865,11 @@ static inline void llvm_emit_const_initialize_bitstruct_ref(GenContext *c, BEVal { if (initializer->kind == CONST_INIT_ZERO) { - llvm_emit_memclear(c, ref); + llvm_store_zero(c, ref); return; } assert(initializer->kind == CONST_INIT_STRUCT); - llvm_store_bevalue_raw(c, ref, llvm_emit_const_bitstruct(c, initializer)); + llvm_store_value_raw(c, ref, llvm_emit_const_bitstruct(c, initializer)); } /** @@ -1838,7 +1888,7 @@ static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue * if (initializer->kind == CONST_INIT_ZERO) { // In case of a zero, optimize. - llvm_emit_memclear(c, ref); + llvm_store_zero(c, ref); return; } // In case of small const initializers, or full arrays - use copy. @@ -1952,7 +2002,7 @@ static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue } // Store the result aligned. - llvm_store_bevalue_raw(c, addr, after_value); + llvm_store_value_raw(c, addr, after_value); if (after) llvm_value_set(after, after_value, addr->type); } @@ -2125,7 +2175,7 @@ void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_l 1, expr_to_len->alignment, &alignment); - llvm_value_set_address_align(be_value, len_addr, type_usize, alignment); + llvm_value_set_address(be_value, len_addr, type_usize, alignment); break; } case TYPE_ARRAY: @@ -2300,7 +2350,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r llvm_value_set(end_ref, end_index.value, end_type); llvm_value_set(start_ref, start_index.value, start_type); - llvm_value_set_address_align(parent_ref, parent_base, parent_type, type_abi_alignment(parent_type)); + llvm_value_set_address(parent_ref, parent_base, parent_type, type_abi_alignment(parent_type)); } static void gencontext_emit_slice(GenContext *c, BEValue *be_value, Expr *expr) @@ -2387,7 +2437,7 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, TOKLOC(expr->span.loc)); // And store the value. - llvm_store_bevalue(c, &addr, be_value); + llvm_store_value(c, &addr, be_value); } return; } @@ -2425,7 +2475,7 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, TOKLOC(expr->span.loc)); // And store the value. - llvm_store_bevalue(c, &addr, be_value); + llvm_store_value(c, &addr, be_value); // Create the new offset LLVMValueRef next_offset = llvm_emit_add_int(c, start.type, offset, llvm_const_int(c, start.type, 1), TOKLOC(expr->span.loc)); @@ -2805,9 +2855,9 @@ static void llvm_emit_subarray_comp(GenContext *c, BEValue *be_value, BEValue *l llvm_emit_block(c, value_cmp); BEValue index_var; - llvm_value_set_address(&index_var, llvm_emit_alloca_aligned(c, type_usize, "cmp.idx"), type_usize); + llvm_value_set_address_abi_aligned(&index_var, llvm_emit_alloca_aligned(c, type_usize, "cmp.idx"), type_usize); LLVMValueRef one = llvm_const_int(c, type_usize, 1); - llvm_store_bevalue_raw(c, &index_var, llvm_get_zero(c, type_usize)); + llvm_store_value_raw(c, &index_var, llvm_get_zero(c, type_usize)); llvm_emit_br(c, loop_begin); llvm_emit_block(c, loop_begin); @@ -2821,15 +2871,21 @@ static void llvm_emit_subarray_comp(GenContext *c, BEValue *be_value, BEValue *l llvm_emit_block(c, comparison); BEValue lhs_to_compare; BEValue rhs_to_compare; - llvm_value_set_address(&lhs_to_compare, - llvm_emit_pointer_inbounds_gep_raw(c, llvm_base_type, lhs_value.value, current_index.value), - array_base_type); - llvm_value_set_address(&rhs_to_compare, - llvm_emit_pointer_inbounds_gep_raw(c, llvm_base_type, rhs_value.value, current_index.value), - array_base_type); + llvm_value_set_address_abi_aligned(&lhs_to_compare, + llvm_emit_pointer_inbounds_gep_raw(c, + llvm_base_type, + lhs_value.value, + current_index.value), + array_base_type); + llvm_value_set_address_abi_aligned(&rhs_to_compare, + llvm_emit_pointer_inbounds_gep_raw(c, + llvm_base_type, + rhs_value.value, + current_index.value), + array_base_type); llvm_emit_comparison(c, &cmp, &lhs_to_compare, &rhs_to_compare, BINARYOP_EQ); match_fail_block = c->current_block; - llvm_store_bevalue_raw(c, &index_var, LLVMBuildAdd(c->builder, current_index.value, one, "")); + llvm_store_value_raw(c, &index_var, LLVMBuildAdd(c->builder, current_index.value, one, "")); llvm_emit_cond_br(c, &cmp, loop_begin, exit); llvm_emit_block(c, exit); @@ -3190,7 +3246,7 @@ void llvm_emit_try_assign_try_catch(GenContext *c, bool is_try, BEValue *be_valu if (var_addr) { assert(is_try && "Storing will only happen on try."); - llvm_store_bevalue(c, var_addr, be_value); + llvm_store_value(c, var_addr, be_value); } // 8. Restore the error stack. @@ -3230,7 +3286,7 @@ static inline void llvm_emit_catch_expr(GenContext *c, BEValue *value, Expr *exp { Decl *decl = inner->identifier_expr.decl; assert(IS_FAILABLE(decl)); - llvm_value_set_address(value, decl_failable_ref(decl), type_anyerr); + llvm_value_set_address_abi_aligned(value, decl_failable_ref(decl), type_anyerr); return; } @@ -3246,8 +3302,8 @@ static inline void llvm_emit_catch_expr(GenContext *c, BEValue *value, Expr *exp PUSH_ERROR(); LLVMValueRef error_var = llvm_emit_alloca_aligned(c, type_anyerr, "error_var"); - llvm_value_set_address(value, error_var, type_anyerr); - llvm_store_bevalue_raw(c, value, llvm_get_zero(c, type_anyerr)); + llvm_value_set_address_abi_aligned(value, error_var, type_anyerr); + llvm_store_value_raw(c, value, llvm_get_zero(c, type_anyerr)); c->error_var = error_var; c->catch_block = end_block; @@ -3435,7 +3491,7 @@ static inline void llvm_emit_rethrow_expr(GenContext *c, BEValue *be_value, Expr { llvm_emit_defer(c, expr->rethrow_expr.defer, 0); BEValue value; - llvm_value_set_address(&value, error_var, type_anyerr); + llvm_value_set_address_abi_aligned(&value, error_var, type_anyerr); llvm_emit_return_abi(c, NULL, &value); c->current_block = NULL; c->current_block_is_target = NULL; @@ -3458,7 +3514,7 @@ static inline void llvm_emit_typeofany(GenContext *c, BEValue *be_value, Expr *e 1, be_value->alignment, &alignment); - llvm_value_set_address_align(be_value, pointer_addr, type_typeid, alignment); + llvm_value_set_address(be_value, pointer_addr, type_typeid, alignment); } else { @@ -3530,11 +3586,11 @@ static void llvm_emit_vector_assign_expr(GenContext *c, BEValue *be_value, Expr // Emit the variable llvm_emit_expr(c, &addr, left->subscript_expr.expr); llvm_value_addr(c, &addr); - LLVMValueRef vector_value = llvm_value_rvalue_store(c, &addr); + LLVMValueRef vector_value = llvm_load_value_store(c, &addr); // Emit the index llvm_emit_expr(c, &index, left->subscript_expr.index); - LLVMValueRef index_val = llvm_value_rvalue_store(c, &index); + LLVMValueRef index_val = llvm_load_value_store(c, &index); if (binary_op > BINARYOP_ASSIGN) { @@ -3549,8 +3605,8 @@ static void llvm_emit_vector_assign_expr(GenContext *c, BEValue *be_value, Expr llvm_emit_expr(c, be_value, expr->binary_expr.right); } - LLVMValueRef new_value = LLVMBuildInsertElement(c->builder, vector_value, llvm_value_rvalue_store(c, be_value), index_val, "elemset"); - llvm_store_bevalue_raw(c, &addr, new_value); + LLVMValueRef new_value = LLVMBuildInsertElement(c->builder, vector_value, llvm_load_value_store(c, be_value), index_val, "elemset"); + llvm_store_value_raw(c, &addr, new_value); } static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) @@ -3569,7 +3625,7 @@ static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_expr(c, &addr, expr->binary_expr.left); llvm_value_addr(c, &addr); gencontext_emit_binary(c, be_value, expr, &addr, base_op); - llvm_store_bevalue(c, &addr, be_value); + llvm_store_value(c, &addr, be_value); return; } if (binary_op == BINARYOP_ASSIGN) @@ -3659,7 +3715,7 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) llvm_emit_block(c, lhs_block); BEValue lhs; llvm_emit_expr(c, &lhs, expr->ternary_expr.then_expr); - LLVMValueRef lhs_value = llvm_value_rvalue_store(c, &lhs); + LLVMValueRef lhs_value = llvm_load_value_store(c, &lhs); LLVMBasicBlockRef lhs_exit = llvm_get_current_block_if_in_use(c); if (lhs_exit) llvm_emit_br(c, phi_block); @@ -3667,7 +3723,7 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) llvm_emit_block(c, rhs_block); BEValue rhs; llvm_emit_expr(c, &rhs, expr->ternary_expr.else_expr); - LLVMValueRef rhs_value = llvm_value_rvalue_store(c, &rhs); + LLVMValueRef rhs_value = llvm_load_value_store(c, &rhs); LLVMBasicBlockRef rhs_exit = llvm_get_current_block_if_in_use(c); if (rhs_exit) llvm_emit_br(c, phi_block); @@ -3717,7 +3773,7 @@ static inline void llvm_emit_const_initializer_list_expr(GenContext *c, BEValue llvm_value_set(value, llvm_emit_const_initializer(c, expr->const_expr.list), expr->type); return; } - llvm_value_set_address(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type); + llvm_value_set_address_abi_aligned(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type); llvm_emit_initialize_reference_const(c, value, expr); } @@ -3739,7 +3795,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) 1)); llvm_set_alignment(global_name, 1); global_name = LLVMConstBitCast(global_name, LLVMPointerType(llvm_get_type(c, type_char), 0)); - llvm_value_set_address(be_value, global_name, type); + llvm_value_set_address_abi_aligned(be_value, global_name, type); return; } case CONST_INTEGER: @@ -3877,7 +3933,12 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case ALL_INTS: case ALL_FLOATS: case TYPE_POINTER: - vec_add(*values, llvm_emit_load_aligned(context, llvm_get_type(context, param_type), expand_ptr, alignment, "loadexpanded")); + vec_add(*values, + llvm_load(context, + llvm_get_type(context, param_type), + expand_ptr, + alignment, + "loadexpanded")); return; case TYPE_TYPEDEF: param_type = param_type->canonical; @@ -3904,7 +3965,7 @@ void llvm_emit_struct_member_ref(GenContext *c, BEValue *struct_ref, BEValue *me assert(struct_ref->type->type_kind == TYPE_STRUCT); AlignSize align; LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, struct_ref->value, llvm_get_type(c, struct_ref->type), member_id, struct_ref->alignment, &align); - llvm_value_set_address_align(member_ref, ptr, struct_ref->type->decl->strukt.members[member_id]->type, align); + llvm_value_set_address(member_ref, ptr, struct_ref->type->decl->strukt.members[member_id]->type, align); } LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, @@ -3940,7 +4001,7 @@ LLVMValueRef llvm_emit_array_load(GenContext *c, LLVMValueRef ptr, LLVMTypeRef a { AlignSize alignment; LLVMValueRef element_ptr = llvm_emit_array_gep_raw_index(c, ptr, array_type, llvm_const_int(c, type_usize, index), array_alignment, &alignment); - return llvm_emit_load_aligned(c, LLVMGetElementType(array_type), element_ptr, alignment, ""); + return llvm_load(c, LLVMGetElementType(array_type), element_ptr, alignment, ""); } LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset) @@ -3971,7 +4032,7 @@ void llvm_emit_subarray_len(GenContext *c, BEValue *subarray, BEValue *len) 1, subarray->alignment, &alignment); - llvm_value_set_address_align(len, len_addr, type_usize, alignment); + llvm_value_set_address(len, len_addr, type_usize, alignment); } void llvm_emit_subarray_pointer(GenContext *c, BEValue *value, BEValue *pointer) @@ -3982,7 +4043,7 @@ void llvm_emit_subarray_pointer(GenContext *c, BEValue *value, BEValue *pointer) { AlignSize alignment; LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, value->value, llvm_get_type(c, value->type), 0, value->alignment, &alignment); - llvm_value_set_address_align(pointer, ptr, ptr_type, alignment); + llvm_value_set_address(pointer, ptr, ptr_type, alignment); return; } LLVMValueRef ptr = llvm_emit_extract_value(c, value->value, 0); @@ -3996,7 +4057,7 @@ static void llvm_emit_any_pointer(GenContext *c, BEValue *value, BEValue *pointe { AlignSize alignment; LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, value->value, llvm_get_type(c, value->type), 0, value->alignment, &alignment); - llvm_value_set_address_align(pointer, ptr, type_voidptr, alignment); + llvm_value_set_address(pointer, ptr, type_voidptr, alignment); return; } LLVMValueRef ptr = llvm_emit_extract_value(c, value->value, 0); @@ -4024,7 +4085,7 @@ void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_poin (unsigned)actual_index, struct_pointer->alignment, &alignment); - llvm_value_set_address(element, ref, member->type); + llvm_value_set_address_abi_aligned(element, ref, member->type); element->alignment = alignment; } @@ -4062,19 +4123,19 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B llvm_get_type(c, type), info->indirect.alignment, "indirectarg"); - llvm_store_bevalue_aligned(c, indirect, be_value, info->indirect.alignment); + llvm_store_value_aligned(c, indirect, be_value, info->indirect.alignment); vec_add(*args, indirect); return; } case ABI_ARG_DIRECT: - vec_add(*args, llvm_value_rvalue_store(c, be_value)); + vec_add(*args, llvm_load_value_store(c, be_value)); return; case ABI_ARG_DIRECT_COERCE: { LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); if (!coerce_type || coerce_type == llvm_get_type(c, type)) { - vec_add(*args, llvm_value_rvalue_store(c, be_value)); + vec_add(*args, llvm_load_value_store(c, be_value)); return; } if (!abi_info_should_flatten(info)) @@ -4091,7 +4152,7 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B { AlignSize load_align; LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, alignment, &load_align); - vec_add(*args, llvm_emit_load_aligned(c, element, element_ptr, load_align, "")); + vec_add(*args, llvm_load(c, element, element_ptr, load_align, "")); } return; } @@ -4111,10 +4172,10 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B AlignSize alignment; LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, struct_align, &alignment); - vec_add(*args, llvm_emit_load_aligned(c, lo, lo_ptr, alignment, "lo")); + vec_add(*args, llvm_load(c, lo, lo_ptr, alignment, "lo")); // Get the hi value. LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, struct_align, &alignment); - vec_add(*args, llvm_emit_load_aligned(c, hi, hi_ptr, alignment, "hi")); + vec_add(*args, llvm_load(c, hi, hi_ptr, alignment, "hi")); return; } case ABI_ARG_EXPAND_COERCE: @@ -4127,11 +4188,11 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B AlignSize align; LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.lo_index, alignment, &align); - vec_add(*args, llvm_emit_load_aligned(c, llvm_abi_type(c, info->coerce_expand.lo), gep_first, align, "")); + vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.lo), gep_first, 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, &align); - vec_add(*args, llvm_emit_load_aligned(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, "")); + vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, "")); } return; } @@ -4163,16 +4224,18 @@ static void llvm_emit_unpacked_variadic_arg(GenContext *c, Expr *expr, BEValue * { case TYPE_ARRAY: { - llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, type->array.len)); + llvm_store_value_raw(c, &len_addr, llvm_const_int(c, type_usize, type->array.len)); llvm_value_addr(c, &value); - llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, value.value, type_get_ptr(type->array.base))); + llvm_store_value_raw(c, &pointer_addr, llvm_emit_bitcast(c, value.value, type_get_ptr(type->array.base))); return; } case TYPE_POINTER: // Load the pointer llvm_value_rvalue(c, &value); - llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, type->pointer->array.len)); - llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, value.value, type_get_ptr(type->pointer->array.base))); + llvm_store_value_raw(c, &len_addr, llvm_const_int(c, type_usize, type->pointer->array.len)); + llvm_store_value_raw(c, + &pointer_addr, + llvm_emit_bitcast(c, value.value, type_get_ptr(type->pointer->array.base))); return; case TYPE_SUBARRAY: *subarray = value; @@ -4249,7 +4312,7 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) llvm_value_rvalue(c, &value); value.kind = BE_ADDRESS; BEValue store_value = *result_value; - LLVMValueRef store = llvm_store_bevalue(c, &value, &store_value); + LLVMValueRef store = llvm_store_value(c, &value, &store_value); if (store) LLVMSetVolatile(store, true); return; } @@ -4296,7 +4359,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_expr(c, &func_value, expr->call_expr.function); // 1d. Load it as a value - func = llvm_value_rvalue_store(c, &func_value); + func = llvm_load_value_store(c, &func_value); // 1e. Calculate the function type func_type = llvm_get_type(c, type); @@ -4342,7 +4405,10 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side. assert(ret_info->attributes.realign || ret_info->indirect.alignment == type_abi_alignment(call_return_type)); AlignSize alignment = ret_info->indirect.alignment; - llvm_value_set_address_align(result_value, llvm_emit_alloca(c, llvm_get_type(c, call_return_type), alignment, "sretparam"), call_return_type, alignment); + llvm_value_set_address(result_value, + llvm_emit_alloca(c, llvm_get_type(c, call_return_type), alignment, "sretparam"), + call_return_type, + alignment); // 6c. Add the pointer to the list of arguments. vec_add(values, result_value->value); @@ -4369,7 +4435,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list) llvm_emit_parameter(c, &values, prototype->ret_by_ref_abi_info, &synthetic_return_param, synthetic_return_param.type); // 7d. Update the be_value to actually be an address. - llvm_value_set_address(&synthetic_return_param, synthetic_return_param.value, actual_return_type); + llvm_value_set_address_abi_aligned(&synthetic_return_param, synthetic_return_param.value, actual_return_type); } @@ -4397,7 +4463,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) BEValue subarray; - llvm_value_set_address(&subarray, llvm_emit_alloca_aligned(c, vararg_param, "vararg"), vararg_param); + llvm_value_set_address_abi_aligned(&subarray, llvm_emit_alloca_aligned(c, vararg_param, "vararg"), vararg_param); // 9a. Special case, empty argument if (arguments == non_variadic_params) @@ -4405,7 +4471,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // Just set the size to zero. BEValue len_addr; llvm_emit_subarray_len(c, &subarray, &len_addr); - llvm_store_bevalue_raw(c, &len_addr, llvm_get_zero(c, type_usize)); + llvm_store_value_raw(c, &len_addr, llvm_get_zero(c, type_usize)); } else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last) { @@ -4426,15 +4492,15 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_expr(c, &temp_value, arg_expr); AlignSize store_alignment; LLVMValueRef slot = llvm_emit_array_gep_raw(c, array_ref, llvm_array_type, i - non_variadic_params, alignment, &store_alignment); - llvm_store_bevalue_aligned(c, slot, &temp_value, store_alignment); + llvm_store_value_aligned(c, slot, &temp_value, store_alignment); } BEValue len_addr; llvm_emit_subarray_len(c, &subarray, &len_addr); - llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params)); + llvm_store_value_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params)); BEValue pointer_addr; llvm_emit_subarray_pointer(c, &subarray, &pointer_addr); Type *array_as_pointer_type = type_get_ptr(pointee_type); - llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, array_ref, array_as_pointer_type)); + llvm_store_value_raw(c, &pointer_addr, llvm_emit_bitcast(c, array_ref, array_as_pointer_type)); } llvm_emit_parameter(c, &values, vararg_info, &subarray, vararg_param); } @@ -4446,7 +4512,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) Expr *arg_expr = expr->call_expr.arguments[i]; llvm_emit_expr(c, &temp_value, arg_expr); REMINDER("Varargs should be expanded correctly"); - vec_add(values, llvm_value_rvalue_store(c, &temp_value)); + vec_add(values, llvm_load_value_store(c, &temp_value)); } } @@ -4537,7 +4603,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 15a. Create memory to hold the return type. LLVMValueRef ret = llvm_emit_alloca_aligned(c, call_return_type, ""); - llvm_value_set_address(result_value, ret, call_return_type); + llvm_value_set_address_abi_aligned(result_value, ret, call_return_type); // 15b. "Convert" this return type pointer in memory to our coerce type which is { pad, lo, pad, hi } LLVMTypeRef coerce_type = llvm_get_coerce_type(c, ret_info); @@ -4553,7 +4619,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) if (!abi_type_is_valid(ret_info->coerce_expand.hi)) { // Here we do a store to call -> lo (leaving the rest undefined) - llvm_store_aligned(c, lo, call_value, alignment); + llvm_store(c, lo, call_value, alignment); break; } @@ -4562,14 +4628,14 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) LLVMValueRef hi_value = LLVMBuildExtractValue(c->builder, call_value, 1, ""); // 15h. Store lo_value into the { pad, lo, pad, hi } struct. - llvm_store_aligned(c, lo, lo_value, alignment); + llvm_store(c, lo, lo_value, alignment); // 15i. Calculate the address to the high value (like for the low in 15d. LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.hi_index, type_abi_alignment(call_return_type), &alignment); // 15h. Store the high value. - llvm_store_aligned(c, hi, hi_value, alignment); + llvm_store(c, hi, hi_value, alignment); break; } @@ -4607,10 +4673,10 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) BEValue error_holder = *result_value; if (error_var) { - llvm_value_set_address(&error_holder, c->error_var, type_anyerr); + llvm_value_set_address_abi_aligned(&error_holder, c->error_var, type_anyerr); } // 17b. Generate a boolean switch. - llvm_value_set_bool(&no_err, llvm_emit_is_no_error_value(c, &error_holder)); + llvm_value_set_bool(&no_err, llvm_emit_is_no_error(c, llvm_load_value(c, &error_holder))); // 17c. If we have an error var, or we aren't interested in the error variable // - then it's straightforward. We just jump to the catch block. @@ -4626,7 +4692,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); llvm_emit_cond_br(c, &no_err, after_block, error_block); llvm_emit_block(c, error_block); - llvm_store_bevalue_aligned(c, c->error_var, result_value, 0); + llvm_store_value_aligned(c, c->error_var, result_value, type_alloca_alignment(type_anyerr)); // 17e. Then jump to the catch. llvm_emit_br(c, c->catch_block); } @@ -4756,7 +4822,7 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value if (return_out) { - llvm_value_set_address(be_value, return_out, type_lowered); + llvm_value_set_address_abi_aligned(be_value, return_out, type_lowered); } else { @@ -4816,7 +4882,7 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, BEValue value; llvm_emit_expr(context, &value, expr->macro_block.args[i]); - llvm_store_aligned_decl(context, decl, llvm_value_rvalue_store(context, &value)); + llvm_store_decl_raw(context, decl, llvm_load_value_store(context, &value)); } if (expr->macro_block.no_scope) { @@ -4839,74 +4905,6 @@ LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic, L return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); } -BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable) -{ - assert(ref->kind == BE_ADDRESS || ref->kind == BE_ADDRESS_FAILABLE); - LLVMBasicBlockRef assign_block = NULL; - - PUSH_ERROR(); - - if (failable) - { - if (IS_FAILABLE(expr)) - { - if (expr->expr_kind == EXPR_FAILABLE) - { - c->error_var = NULL; - c->catch_block = NULL; - BEValue result; - llvm_emit_expr(c, &result, expr->inner_expr); - llvm_store_bevalue_dest_aligned(c, failable, &result); - llvm_value_set(&result, LLVMGetUndef(llvm_get_type(c, ref->type)), ref->type); - POP_ERROR(); - return result; - } - assign_block = llvm_basic_block_new(c, "after_assign"); - c->error_var = failable; - c->catch_block = assign_block; - } - else - { - c->error_var = NULL; - c->catch_block = NULL; - } - } - BEValue value; - if (type_is_vector(expr->type)) - { - llvm_emit_expr(c, &value, expr); - llvm_store_bevalue(c, ref, &value); - } - else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) - { - llvm_emit_const_initialize_reference(c, ref, expr); - value = *ref; - } - else if (expr_is_init_list(expr)) - { - llvm_emit_initialize_reference(c, ref, expr); - value = *ref; - } - else - { - llvm_emit_expr(c, &value, expr); - llvm_store_bevalue(c, ref, &value); - } - - if (failable) - { - llvm_store_self_aligned(c, failable, llvm_get_zero(c, type_anyerr), type_anyerr); - } - POP_ERROR(); - - if (failable && IS_FAILABLE(expr)) - { - llvm_emit_br(c, assign_block); - llvm_emit_block(c, assign_block); - } - - return value; -} @@ -4918,7 +4916,7 @@ static inline void llvm_emit_failable(GenContext *c, BEValue *be_value, Expr *ex { assert(c->error_var); llvm_emit_expr(c, be_value, fail); - llvm_store_bevalue_dest_aligned(c, c->error_var, be_value); + llvm_store_value_dest_aligned(c, c->error_var, be_value); } // Branch to the catch llvm_emit_br(c, c->catch_block); @@ -5019,7 +5017,7 @@ static inline void llvm_emit_initializer_list_expr(GenContext *c, BEValue *value return; } assert(!IS_FAILABLE(expr) || c->catch_block); - llvm_value_set_address(value, llvm_emit_alloca_aligned(c, type, "literal"), type); + llvm_value_set_address_abi_aligned(value, llvm_emit_alloca_aligned(c, type, "literal"), type); llvm_emit_initialize_reference(c, value, expr); } @@ -5038,7 +5036,7 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr * { Expr *expr = values[i]; llvm_emit_expr(c, value, expr); - llvm_store_bevalue_aligned(c, declarations[i]->backend_ref, value, declarations[i]->alignment); + llvm_store_value_aligned(c, declarations[i]->backend_ref, value, declarations[i]->alignment); } llvm_emit_stmt(c, body_expr->body_expansion_expr.ast); } @@ -5048,7 +5046,7 @@ void llvm_emit_try_unwrap(GenContext *c, BEValue *value, Expr *expr) if (!expr->try_unwrap_expr.failable) { LLVMValueRef fail_ref = decl_failable_ref(expr->try_unwrap_expr.decl); - LLVMValueRef errv = llvm_emit_load_aligned(c, llvm_get_type(c, type_anyerr), fail_ref, 0, "load.err"); + LLVMValueRef errv = llvm_load(c, llvm_get_type(c, type_anyerr), fail_ref, type_abi_alignment(type_anyerr), "load.err"); LLVMValueRef result = LLVMBuildICmp(c->builder, LLVMIntEQ, errv, llvm_get_zero(c, type_anyerr), "result"); llvm_value_set_bool(value, result); return; @@ -5082,7 +5080,7 @@ void llvm_emit_catch_unwrap(GenContext *c, BEValue *value, Expr *expr) else { LLVMValueRef temp_err = llvm_emit_alloca_aligned(c, type_anyerr, "temp_err"); - llvm_value_set_address(&addr, temp_err, type_anyerr); + llvm_value_set_address_abi_aligned(&addr, temp_err, type_anyerr); } PUSH_ERROR(); @@ -5104,7 +5102,7 @@ void llvm_emit_catch_unwrap(GenContext *c, BEValue *value, Expr *expr) POP_ERROR(); - llvm_store_bevalue_raw(c, &addr, llvm_get_zero(c, type_anyerr)); + llvm_store_value_raw(c, &addr, llvm_get_zero(c, type_anyerr)); llvm_emit_br(c, catch_block); llvm_emit_block(c, catch_block); llvm_value_rvalue(c, &addr); @@ -5222,8 +5220,8 @@ static inline void llvm_emit_argv_to_subarray(GenContext *c, BEValue *value, Exp LLVMValueRef pointer_to_arg = LLVMBuildInBoundsGEP2(c->builder, char_ptr_type, argv_ptr, &index_var, 1, ""); // Get the char* to the argument - LLVMValueRef pointer_value = llvm_emit_load_aligned(c, llvm_get_ptr_type(c, type_char), pointer_to_arg, - type_abi_alignment(type_get_ptr(type_char)), ""); + LLVMValueRef pointer_value = llvm_load(c, llvm_get_ptr_type(c, type_char), pointer_to_arg, + type_abi_alignment(type_get_ptr(type_char)), ""); AlignSize index_align; // Get strlen to calculate length @@ -5238,10 +5236,10 @@ static inline void llvm_emit_argv_to_subarray(GenContext *c, BEValue *value, Exp // We first set the pointer AlignSize align = type_abi_alignment(arg_array_type); LLVMValueRef ptr_loc = llvm_emit_struct_gep_raw(c, index, arg_array_elem_type, 0, align, &index_align); - llvm_store_aligned(c, ptr_loc, pointer_value, index_align); + llvm_store(c, ptr_loc, pointer_value, index_align); // Then the length LLVMValueRef len_loc = llvm_emit_struct_gep_raw(c, index, arg_array_elem_type, 1, align, &index_align); - llvm_store_aligned(c, len_loc, len, index_align); + llvm_store(c, len_loc, len, index_align); // Add index LLVMValueRef index_plus = LLVMBuildNUWAdd(c->builder, index_var, llvm_const_int(c, type_usize, 1), ""); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 3576374ea..ba6130f0e 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -55,18 +55,6 @@ void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block) } - -void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block) -{ - assert(context->current_block); - assert(value->kind == BE_BOOLEAN); - LLVMBuildCondBr(context->builder, value->value, then_block, else_block); - LLVMClearInsertionPosition(context->builder); - context->current_block = NULL; - context->current_block_is_target = false; -} - - void llvm_emit_block(GenContext *context, LLVMBasicBlockRef next_block) { assert(context->current_block == NULL); @@ -111,7 +99,7 @@ static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, u return; } default: - llvm_store_aligned(c, ref, llvm_get_next_param(c, index), alignment); + llvm_store(c, ref, llvm_get_next_param(c, index), alignment); return; } } @@ -146,11 +134,11 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr 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_aligned(c, gep_first, llvm_get_next_param(c, index), element_align); + llvm_store(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_aligned(c, gep_second, llvm_get_next_param(c, index), element_align); + llvm_store(c, gep_second, llvm_get_next_param(c, index), element_align); } break; } @@ -169,11 +157,11 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr AlignSize element_align; LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, decl_alignment, &element_align); // Store it in the struct. - llvm_store_aligned(c, lo_ptr, llvm_get_next_param(c, index), element_align); + llvm_store(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, cast, struct_type, 1, decl_alignment, &element_align); // Store it in the struct. - llvm_store_aligned(c, hi_ptr, llvm_get_next_param(c, index), element_align); + llvm_store(c, hi_ptr, llvm_get_next_param(c, index), element_align); return; } case ABI_ARG_DIRECT: @@ -185,7 +173,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr return; } llvm_emit_and_set_decl_alloca(c, decl); - llvm_store_aligned_decl(c, decl, llvm_get_next_param(c, index)); + llvm_store_decl_raw(c, decl, llvm_get_next_param(c, index)); return; case ABI_ARG_DIRECT_COERCE: { @@ -218,7 +206,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr 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_aligned(c, element_ptr, value, align); + llvm_store(c, element_ptr, value, align); } return; } @@ -277,7 +265,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl { if (return_value && return_value->value) { - llvm_store_bevalue_aligned(c, c->return_out, return_value, 0); + llvm_store_value_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type)); } return_out = c->failable_out; if (!failable) @@ -291,7 +279,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl switch (info->kind) { case ABI_ARG_INDIRECT: - llvm_store_bevalue_aligned(c, return_out, return_value, info->indirect.alignment); + llvm_store_value_aligned(c, return_out, return_value, info->indirect.alignment); llvm_emit_return_value(c, NULL); return; case ABI_ARG_IGNORE: @@ -315,7 +303,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl 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_emit_load_aligned(c, lo_type, lo, alignment, ""); + 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)) @@ -328,7 +316,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl 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_emit_load_aligned(c, hi_type, hi, alignment, ""); + LLVMValueRef hi_val = llvm_load(c, hi_type, hi, alignment, ""); LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type); LLVMValueRef composite = LLVMGetUndef(unpadded_type); @@ -342,7 +330,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl } case ABI_ARG_DIRECT: // The normal return - llvm_emit_return_value(c, llvm_value_rvalue_store(c, return_value)); + llvm_emit_return_value(c, llvm_load_value_store(c, return_value)); return; case ABI_ARG_DIRECT_PAIR: case ABI_ARG_DIRECT_COERCE: @@ -351,7 +339,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl if (!coerce_type || coerce_type == llvm_get_type(c, call_return_type)) { // The normal return - llvm_emit_return_value(c, llvm_value_rvalue_store(c, return_value)); + llvm_emit_return_value(c, llvm_load_value_store(c, return_value)); return; } assert(!abi_info_should_flatten(info)); @@ -568,7 +556,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) { llvm_attribute_add(c, function, attribute_id.noreturn, -1); } - if (decl->alignment) + if (decl->alignment != type_abi_alignment(decl->type)) { llvm_set_alignment(function, decl->alignment); } diff --git a/src/compiler/llvm_codegen_instr.c b/src/compiler/llvm_codegen_instr.c new file mode 100644 index 000000000..c1b6f1d5c --- /dev/null +++ b/src/compiler/llvm_codegen_instr.c @@ -0,0 +1,52 @@ +// 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 "llvm_codegen_internal.h" + +void llvm_emit_cond_br_raw(GenContext *context, LLVMValueRef b, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block) +{ + assert(context->current_block); + LLVMBuildCondBr(context->builder, b, then_block, else_block); + LLVMClearInsertionPosition(context->builder); + context->current_block = NULL; + context->current_block_is_target = false; +} + +void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block) +{ + assert(context->current_block); + assert(value->kind == BE_BOOLEAN); + LLVMBuildCondBr(context->builder, value->value, then_block, else_block); + LLVMClearInsertionPosition(context->builder); + context->current_block = NULL; + context->current_block_is_target = false; +} + +LLVMValueRef llvm_emit_lshr_fixed(GenContext *c, LLVMValueRef data, int shift) +{ + assert(shift >= 0); + if (shift == 0) return data; + LLVMTypeRef type = LLVMTypeOf(data); + BitSize bit_width = llvm_bitsize(c, type); + if (shift >= bit_width) return LLVMConstNull(type); + if (LLVMIsAConstant(data)) + { + return LLVMConstLShr(data, LLVMConstInt(type, (unsigned)shift, false)); + } + return LLVMBuildLShr(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), ""); +} + +LLVMValueRef llvm_emit_shl_fixed(GenContext *c, LLVMValueRef data, int shift) +{ + assert(shift >= 0); + if (shift == 0) return data; + LLVMTypeRef type = LLVMTypeOf(data); + BitSize bit_width = llvm_bitsize(c, type); + if (shift >= bit_width) return LLVMConstNull(type); + if (LLVMIsAConstant(data)) + { + return LLVMConstShl(data, LLVMConstInt(type, (unsigned)shift, false)); + } + return LLVMBuildShl(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), ""); +} diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 9a89fb087..839aca4b0 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -210,14 +210,13 @@ void llvm_value_rvalue(GenContext *context, BEValue *value); void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value); void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i); -void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment); -void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type); +void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment); +void llvm_value_set_address_abi_aligned(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_set_decl_address(BEValue *value, Decl *decl); void llvm_value_set_decl(BEValue *value, Decl *decl); void llvm_value_fold_failable(GenContext *c, BEValue *value); void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index); -LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value); LLVMTypeRef llvm_abi_type(GenContext *c, AbiType type); TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type); @@ -250,7 +249,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl); void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type); -void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); + void llvm_emit_debug_function(GenContext *c, Decl *decl); void llvm_emit_debug_location(GenContext *context, SourceSpan location); void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index); @@ -269,14 +268,32 @@ void llvm_emit_int_comp_zero(GenContext *c, BEValue *result, BEValue *lhs, Binar void llvm_emit_int_comparison(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op); void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op); void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op); -LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value); void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len); -LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name); + +// -- instr --- +void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); +void llvm_emit_cond_br_raw(GenContext *context, LLVMValueRef b, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); + +// -- general -- +LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error_value); + + +// -- load --- +LLVMValueRef llvm_load(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name); +LLVMValueRef llvm_load_natural_alignment(GenContext *c, Type *type, LLVMValueRef pointer, const char *name); +LLVMValueRef llvm_load_value(GenContext *c, BEValue *value); +LLVMValueRef llvm_load_value_store(GenContext *c, BEValue *value); + +// -- expr --- +LLVMValueRef llvm_emit_shl_fixed(GenContext *c, LLVMValueRef data, int shift); +LLVMValueRef llvm_emit_lshr_fixed(GenContext *c, LLVMValueRef data, int shift); + +// -- general -- void llvm_emit_local_var_alloca(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_aggregate_value(GenContext *c, Type *type, ...); LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, AlignSize align, bool bitcast); -void llvm_emit_memclear(GenContext *c, BEValue *ref); +void llvm_store_zero(GenContext *c, BEValue *ref); void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len); void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment); void llvm_emit_stmt(GenContext *c, Ast *ast); @@ -318,13 +335,13 @@ bool llvm_emit_check_block_branch(GenContext *context); TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type); -LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value); -void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value); -void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value); -LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment); -void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type); -LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment); -void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value); +LLVMValueRef llvm_store_value(GenContext *c, BEValue *destination, BEValue *value); +void llvm_store_value_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value); +void llvm_store_value_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value); +LLVMValueRef llvm_store_value_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment); +void llvm_store_raw_abi_alignment(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type); +LLVMValueRef llvm_store(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment); +void llvm_store_decl_raw(GenContext *context, Decl *decl, LLVMValueRef value); LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi); LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value, Type *original_type); @@ -467,10 +484,4 @@ void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMV c->error_var = _old_error_var -static inline bool abi_info_should_flatten(ABIArgInfo *info); - -static inline bool abi_info_should_flatten(ABIArgInfo *info) -{ - return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten; -} diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 1c513a429..d1eb1e828 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -6,21 +6,6 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast); -static bool ast_is_not_empty(Ast *ast) -{ - if (!ast) return false; - if (ast->ast_kind != AST_COMPOUND_STMT) return true; - AstId first = ast->compound_stmt.first_stmt; - if (first) - { - Ast *stmt = astptr(first); - if (stmt->next) return true; - if (ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end) return true; - return ast_is_not_empty(stmt); - } - return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end; -} - void llvm_emit_compound_stmt(GenContext *context, Ast *ast) { @@ -41,7 +26,7 @@ void llvm_emit_compound_stmt(GenContext *context, Ast *ast) } } -void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast) +static void llvm_emit_ct_compound_stmt(GenContext *context, Ast *ast) { assert(ast->ast_kind == AST_CT_COMPOUND_STMT); AstId current = ast->compound_stmt.first_stmt; @@ -119,7 +104,7 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) BEValue value; llvm_value_set_decl_address(&value, decl); value.kind = BE_ADDRESS; - llvm_emit_memclear(c, &value); + llvm_store_zero(c, &value); } } return decl->backend_ref; @@ -157,7 +142,7 @@ void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false)); return; } - llvm_value_set_address(be_value, decl_value, type); + llvm_value_set_address_abi_aligned(be_value, decl_value, type); } if (bool_cast) { @@ -216,7 +201,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) { if (c->return_out) { - llvm_store_bevalue_aligned(c, c->return_out, &return_value, 0); + llvm_store_value_aligned(c, c->return_out, &return_value, type_alloca_alignment(return_value.type)); } llvm_emit_jmp(c, c->block_return_exit); return; @@ -235,7 +220,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) { llvm_emit_block(c, error_return_block); BEValue value; - llvm_value_set_address(&value, error_out, type_anyerr); + llvm_value_set_address_abi_aligned(&value, error_out, type_anyerr); llvm_emit_return_abi(c, NULL, &value); c->current_block = NULL; } @@ -765,9 +750,9 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, As Type *switch_type = switch_ast->ast_kind == AST_IF_CATCH_SWITCH_STMT ? type_lowering(type_anyerr) : switch_ast->switch_stmt.cond->type; BEValue switch_var; - llvm_value_set_address(&switch_var, llvm_emit_alloca_aligned(c, switch_type, "switch"), switch_type); + llvm_value_set_address_abi_aligned(&switch_var, llvm_emit_alloca_aligned(c, switch_type, "switch"), switch_type); switch_ast->switch_stmt.codegen.retry_var = &switch_var; - llvm_store_bevalue(c, &switch_var, switch_value); + llvm_store_value(c, &switch_var, switch_value); llvm_emit_br(c, switch_block); llvm_emit_block(c, switch_block); @@ -917,7 +902,7 @@ void gencontext_emit_next_stmt(GenContext *context, Ast *ast) } BEValue be_value; llvm_emit_expr(context, &be_value, ast->nextcase_stmt.switch_expr); - llvm_store_bevalue(context, jump_target->switch_stmt.codegen.retry_var, &be_value); + llvm_store_value(context, jump_target->switch_stmt.codegen.retry_var, &be_value); llvm_emit_defer(context, ast->nextcase_stmt.defers.start, ast->nextcase_stmt.defers.end); llvm_emit_jmp(context, jump_target->switch_stmt.codegen.retry_block); } @@ -1289,7 +1274,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) llvm_emit_compound_stmt(c, ast); break; case AST_CT_COMPOUND_STMT: - gencontext_emit_ct_compound_stmt(c, ast); + llvm_emit_ct_compound_stmt(c, ast); break; case AST_FOR_STMT: llvm_emit_for_stmt(c, ast); diff --git a/src/compiler/llvm_codegen_storeload.c b/src/compiler/llvm_codegen_storeload.c new file mode 100644 index 000000000..5f35ba08b --- /dev/null +++ b/src/compiler/llvm_codegen_storeload.c @@ -0,0 +1,174 @@ +// 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 "llvm_codegen_internal.h" + +LLVMValueRef llvm_store(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) +{ + assert(alignment > 0); + LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer); + llvm_set_alignment(ref, alignment); + return ref; +} + +void llvm_store_raw_abi_alignment(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type) +{ + llvm_store(context, pointer, value, type_abi_alignment(type)); +} + +void llvm_store_value_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value) +{ + assert(llvm_value_is_addr(destination)); + llvm_store(c, destination->value, raw_value, destination->alignment); +} + +void llvm_store_decl_raw(GenContext *context, Decl *decl, LLVMValueRef value) +{ + assert(!decl->is_value); + llvm_store(context, decl->backend_ref, value, decl->alignment); +} + +void llvm_store_value_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value) +{ + llvm_store_value_aligned(c, destination, value, LLVMGetAlignment(destination)); +} + +LLVMValueRef llvm_store_value_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment) +{ + // If we have an address but not an aggregate, do a load. + assert(alignment); + llvm_value_fold_failable(c, value); + if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type)) + { + value->value = llvm_load_value_store(c, value); + value->kind = BE_VALUE; + } + switch (value->kind) + { + case BE_BOOLEAN: + value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); + value->kind = BE_VALUE; + FALLTHROUGH; + case BE_VALUE: + return llvm_store(c, destination, value->value, alignment); + case BE_ADDRESS_FAILABLE: + UNREACHABLE + case BE_ADDRESS: + { + // Here we do an optimized(?) memcopy. + ByteSize size = type_size(value->type); + LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size); + destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), ""); + LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); + LLVMValueRef copy = LLVMBuildMemCpy(c->builder, + destination, + alignment, + source, + value->alignment ? value->alignment : type_abi_alignment(value->type), + copy_size); + return copy; + } + } + UNREACHABLE +} + +LLVMValueRef llvm_store_value(GenContext *c, BEValue *destination, BEValue *value) +{ + if (value->type == type_void) return NULL; + assert(llvm_value_is_addr(destination)); + return llvm_store_value_aligned(c, destination->value, value, destination->alignment); +} + +LLVMValueRef llvm_load(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name) +{ + assert(alignment > 0); + assert(c->builder); + assert(LLVMGetTypeContext(type) == c->context); + LLVMValueRef value = LLVMBuildLoad2(c->builder, type, pointer, name); + llvm_set_alignment(value, alignment ? alignment : llvm_abi_alignment(c, type)); + return value; +} + +LLVMValueRef llvm_load_natural_alignment(GenContext *c, Type *type, LLVMValueRef pointer, const char *name) +{ + return llvm_load(c, llvm_get_type(c, type), pointer, type_abi_alignment(type), name); +} + + +LLVMValueRef llvm_load_value(GenContext *c, BEValue *value) +{ + llvm_value_fold_failable(c, value); + switch (value->kind) + { + case BE_BOOLEAN: + case BE_VALUE: + return value->value; + case BE_ADDRESS_FAILABLE: + UNREACHABLE + case BE_ADDRESS: + return llvm_load(c, llvm_get_type(c, value->type), value->value, value->alignment, ""); + } + UNREACHABLE +} + +LLVMValueRef llvm_load_value_store(GenContext *c, BEValue *value) +{ + LLVMValueRef val = llvm_load_value(c, value); + if (value->kind != BE_BOOLEAN) return val; + return LLVMIsConstant(val) ? LLVMConstZExt(val, c->byte_type) : LLVMBuildZExt(c->builder, val, c->byte_type, ""); +} + + +void llvm_store_zero(GenContext *c, BEValue *ref) +{ + llvm_value_addr(c, ref); + Type *type = ref->type; + if (!type_is_abi_aggregate(type)) + { + llvm_store_value_raw(c, ref, llvm_get_zero(c, type)); + return; + } + Type *single_type = type_abi_find_single_struct_element(type); + + if (single_type && !type_is_abi_aggregate(single_type)) + { + BEValue element; + llvm_value_set_address(&element, + llvm_emit_bitcast(c, ref->value, type_get_ptr(single_type)), + single_type, + (unsigned)ref->alignment); + llvm_store_zero(c, &element); + return; + } + if (type_size(type) <= 16) + { + if (type->type_kind == TYPE_STRUCT) + { + Decl *decl = type->decl; + Decl **members = decl->strukt.members; + VECEACH(members, i) + { + if (!type_size(members[i]->type)) continue; + BEValue member_ref; + llvm_emit_struct_member_ref(c, ref, &member_ref, i); + llvm_store_zero(c, &member_ref); + } + return; + } + if (type->type_kind == TYPE_ARRAY) + { + LLVMTypeRef array_type = llvm_get_type(c, type); + for (unsigned i = 0; i < type->array.len; i++) + { + AlignSize align; + LLVMValueRef element_ptr = llvm_emit_array_gep_raw(c, ref->value, array_type, i, ref->alignment, &align); + BEValue be_value; + llvm_value_set_address(&be_value, element_ptr, type->array.base, align); + llvm_store_zero(c, &be_value); + } + return; + } + } + llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true); +} diff --git a/src/compiler/llvm_codegen_value.c b/src/compiler/llvm_codegen_value.c new file mode 100644 index 000000000..d88b4cde5 --- /dev/null +++ b/src/compiler/llvm_codegen_value.c @@ -0,0 +1,110 @@ +#include "llvm_codegen_internal.h" + +void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type) +{ + type = type_lowering(type); + assert(llvm_value || type == type_void); + value->value = llvm_value; + value->alignment = type_abi_alignment(type); + value->kind = BE_VALUE; + value->type = type; +} + +void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment) +{ + assert(alignment > 0); + value->value = llvm_value; + value->alignment = alignment; + value->kind = BE_ADDRESS; + value->type = type_lowering(type); +} + +void llvm_value_set_address_abi_aligned(BEValue *value, LLVMValueRef llvm_value, Type *type) +{ + llvm_value_set_address(value, llvm_value, type_lowering(type), type_abi_alignment(type)); +} + +void llvm_value_addr(GenContext *c, BEValue *value) +{ + llvm_value_fold_failable(c, value); + if (value->kind == BE_ADDRESS) return; + if (!c->builder) + { + LLVMValueRef val = llvm_load_value_store(c, value); + LLVMValueRef ref = LLVMAddGlobal(c->module, LLVMTypeOf(val), ".taddr"); + llvm_set_alignment(ref, llvm_abi_alignment(c, LLVMTypeOf(val))); + llvm_set_private_linkage(ref); + LLVMSetInitializer(ref, val); + llvm_emit_bitcast(c, ref, type_get_ptr(value->type)); + llvm_value_set_address_abi_aligned(value, ref, value->type); + } + else + { + LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr"); + llvm_store_value_dest_aligned(c, temp, value); + llvm_value_set_address_abi_aligned(value, temp, value->type); + } +} + +void llvm_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; + } + return; + } + llvm_value_fold_failable(c, value); + value->value = llvm_load(c, + llvm_get_type(c, 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; + } + value->kind = BE_VALUE; +} + +void llvm_value_fold_failable(GenContext *c, BEValue *value) +{ + if (value->kind == BE_ADDRESS_FAILABLE) + { + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); + LLVMValueRef err_value = llvm_load_natural_alignment(c, type_anyerr, value->failable, ""); + LLVMValueRef was_ok = llvm_emit_is_no_error(c, err_value); + if (c->error_var) + { + LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); + llvm_emit_cond_br_raw(c, was_ok, after_block, error_block); + llvm_emit_block(c, error_block); + llvm_store_raw_abi_alignment(c, c->error_var, err_value, type_anyerr); + llvm_emit_br(c, c->catch_block); + } + else + { + assert(c->catch_block); + llvm_emit_cond_br_raw(c, was_ok, after_block, c->catch_block); + } + llvm_emit_block(c, after_block); + value->kind = BE_ADDRESS; + } +} + +void llvm_value_set_decl_address(BEValue *value, Decl *decl) +{ + decl = decl_flatten(decl); + llvm_value_set_address(value, decl_ref(decl), decl->type, decl->alignment); + + if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) + { + value->kind = BE_ADDRESS_FAILABLE; + value->failable = decl->var.failable_ref; + } +} diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c2ed92a54..64bd99528 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1511,6 +1511,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) decl_set_external_name(decl); } if (!sema_analyse_doc_header(decl->docs, decl->func_decl.function_signature.params, NULL)) return decl_poison(decl); + decl->alignment = type_alloca_alignment(decl->type); DEBUG_LOG("Function analysis done."); return true; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index dc5c355bc..084e6d490 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1487,22 +1487,31 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr { case VARDECL_PARAM_REF: // &foo - { if (!sema_analyse_expr_lvalue(context, arg)) return false; - } if (type && type->canonical != arg->type->canonical) { SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type)); return false; } + if (param && !param->alignment) + { + if (arg->expr_kind == EXPR_IDENTIFIER || arg->expr_kind == EXPR_CONST) + { + param->alignment = arg->identifier_expr.decl->alignment; + } + else + { + param->alignment = type_alloca_alignment(arg->type); + } + } break; case VARDECL_PARAM: // foo if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; if (IS_FAILABLE(arg)) *failable = true; - if (callee.macro) + if (param && !param->alignment) { - param->alignment = type_abi_alignment(type ? type : arg->type); + param->alignment = type_alloca_alignment(arg->type); } break; case VARDECL_PARAM_EXPR: @@ -1521,9 +1530,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr break; case VARDECL_PARAM_CT_TYPE: // $Foo - { if (!sema_analyse_expr_lvalue(context, arg)) return false; - } if (arg->expr_kind != EXPR_TYPEINFO) { SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); diff --git a/src/compiler/tb_codegen.c b/src/compiler/tb_codegen.c index ece93f315..07413e354 100644 --- a/src/compiler/tb_codegen.c +++ b/src/compiler/tb_codegen.c @@ -1,55 +1,36 @@ -#include "codegen_internal.h" +#include "tilde_internal.h" #if TB_BACKEND -#include - -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); +static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr); +static inline void tilde_emit_block(TbContext *c, TB_Label label); +static TB_Register tilde_value_rvalue_get(TbContext *c, TBEValue *value); +static void TBE_VALUE_set_reg(TBEValue *value, TB_Register reg, Type *type); // 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_addr(TBEValue *value) +{ return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_FAILABLE; } -static inline bool tinybackend_value_is_bool(BEValue *value) -{ return value->kind == BE_BOOLEAN; } -static TB_DataType tbc3_get_type(Type *type) +static TB_CallingConv tilde_call_convention(CallABI abi) +{ + switch (abi) + { + case CALL_C: + return TB_CDECL; + case CALL_X86_STD: + return TB_STDCALL; + default: + FATAL_ERROR("Unsupported call convention for TildeBE"); + } +} + +TB_DataType tbtype(Type *type) { type = type_lowering(type); uint8_t elements = 1; @@ -124,63 +105,247 @@ static TB_DataType tbc3_get_type(Type *type) } } +static TB_DataType tilde_get_abi_type(AbiType type) +{ + if (abi_type_is_type(type)) return tbtype(type.type); + TODO +} + +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_ANYERR: + case TYPE_ERRTYPE: + 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, tbtype(type)); + return; + } + +} + +static inline void add_func_type_param(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_get_abi_type(arg_info->coerce_expand.lo)); + if (abi_type_is_valid(arg_info->coerce_expand.hi)) + { + vec_add(*params, tilde_get_abi_type(arg_info->coerce_expand.hi)); + } + break; + case ABI_ARG_EXPAND: + // Expanding a structs + param_expand(params, param_type->canonical); + // If we have padding, add it here. + if (arg_info->expand.padding_type) + { + vec_add(*params, tbtype(arg_info->expand.padding_type)); + } + break; + case ABI_ARG_DIRECT: + vec_add(*params, tbtype(param_type)); + break; + case ABI_ARG_DIRECT_COERCE: + { + // Normal direct. + if (!abi_type_is_valid(arg_info->direct_coerce.type)) + { + vec_add(*params, tbtype(param_type)); + break; + } + TB_DataType coerce_type = tilde_get_abi_type(arg_info->direct_coerce.type); + if (!abi_info_should_flatten(arg_info)) + { + vec_add(*params, coerce_type); + break; + } + for (unsigned idx = 0; idx < arg_info->direct_coerce.elements; idx++) + { + vec_add(*params, coerce_type); + } + break; + } + case ABI_ARG_DIRECT_PAIR: + // Pairs are passed by param. + vec_add(*params, tilde_get_abi_type(arg_info->direct_pair.lo)); + vec_add(*params, tilde_get_abi_type(arg_info->direct_pair.hi)); + break; + } + arg_info->param_index_end = (MemberIndex)vec_size(*params); +} + +static TB_FunctionPrototype *tilde_get_function_type(TB_Module *module, FunctionPrototype *prototype) +{ + if (prototype->tb_prototype) return prototype->tb_prototype; + + TB_DataType *params = NULL; + TB_DataType return_type = TB_TYPE_VOID; + + 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); + return_type = TB_TYPE_VOID; + break; + case ABI_ARG_EXPAND_COERCE: + { + TB_DataType lo = tilde_get_abi_type(ret_arg_info->direct_pair.lo); + if (!abi_type_is_valid(ret_arg_info->direct_pair.hi)) + { + return_type = lo; + break; + } + TB_DataType hi = tilde_get_abi_type(ret_arg_info->direct_pair.hi); + TODO + // return_type = llvm_get_twostruct(context, lo, hi); + break; + } + case ABI_ARG_IGNORE: + return_type = TB_TYPE_VOID; + break; + case ABI_ARG_DIRECT_PAIR: + { + TB_DataType lo = tilde_get_abi_type(ret_arg_info->direct_pair.lo); + TB_DataType hi = tilde_get_abi_type(ret_arg_info->direct_pair.hi); + TODO // return_type = llvm_get_twostruct(context, lo, hi); + break; + } + case ABI_ARG_DIRECT: + return_type = tbtype(call_return_type); + break; + case ABI_ARG_DIRECT_COERCE: + assert(!abi_info_should_flatten(ret_arg_info)); + TODO + /* + return_type = llvm_get_coerce_type(context, ret_arg_info); + if (!return_type) return_type = llvm_get_type(context, call_return_type);*/ + break; + } + + // If it's failable and it's not void (meaning ret_abi_info will be NULL) + if (prototype->ret_by_ref) + { + add_func_type_param(type_get_ptr(type_lowering(prototype->ret_by_ref_type)), + prototype->ret_by_ref_abi_info, + ¶ms); + } + + // Add in all of the required arguments. + VECEACH(prototype->params, i) + { + add_func_type_param(prototype->params[i], prototype->abi_args[i], ¶ms); + } + + unsigned param_count = vec_size(params); + TB_FunctionPrototype *tb_proto = tb_prototype_create(module, + tilde_call_convention(prototype->call_abi), + return_type, param_count, prototype->variadic == VARIADIC_RAW); + tb_prototype_add_params(tb_proto, param_count, params); + prototype->tb_prototype = tb_proto; + return tb_proto; +} + 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); + return decl->tb_register; } -TB_Register tinybackend_emit_load_aligned(GenContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment) +TB_Register tilde_load_aligned(TbContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment) { - TB_Register value = tb_inst_load(c->function, dt, pointer, alignment); + TB_Register value = tb_inst_load(c->f, dt, pointer, alignment); return value; } -void tinybackend_store_self_aligned(GenContext *c, TB_Register pointer, TB_Register value, Type *type) +void tilde_store_self_aligned(TbContext *c, TB_Register pointer, TB_Register value, Type *type) { - tb_inst_store(c->function, tbc3_get_type(type), pointer, value, type_abi_alignment(type)); + tb_inst_store(c->f, tbtype(type), pointer, value, type_abi_alignment(type)); } -void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destination, BEValue *value) + + +void tinybackend_store_value(TbContext *c, TBEValue *destination, TBEValue *value) { assert(tinybackend_value_is_addr(destination)); - if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type)) + TB_DataType type = tbtype(destination->type); + if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type)) { - value->value = tinybackend_emit_load_aligned(c, - tbc3_get_type(value->type), - value->value, - value->alignment); - value->kind = BE_VALUE; + value->reg = tilde_load_aligned(c, type, value->reg, value->alignment); + value->kind = TBE_VALUE; } AlignSize alignment = destination->alignment; + assert(alignment); switch (value->kind) { - case BE_BOOLEAN: - value->value = tb_inst_zxt(c->function, value->value, TB_TYPE_I8); - value->kind = BE_VALUE; - FALLTHROUGH; - case BE_VALUE: - tb_inst_store(c->function, - tbc3_get_type(destination->type), - destination->value, - value->value, - alignment ? alignment : type_abi_alignment(value->type)); + case TBE_VALUE: + tb_inst_store(c->f, type, destination->reg, value->reg, alignment); return; - case BE_ADDRESS: + case TBE_ADDRESS: { // Here we do an optimized(?) memcopy. ByteSize size = type_size(value->type); - TB_Register copy_size = tb_inst_iconst(c->function, + TB_Register copy_size = tb_inst_iconst(c->f, size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size); - TB_Register source = value->value; - tb_inst_memcpy(c->function, - destination->value, - value->value, copy_size, - value->alignment ? (int)value->alignment : (int)type_abi_alignment(value->type)); + alignment = type_min_alignment(destination->alignment, value->alignment); + tb_inst_memcpy(c->f, destination->reg, value->reg, copy_size, alignment); return; } default: @@ -188,43 +353,32 @@ void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destina } } -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) +void TBE_VALUE_set(TBEValue *value, TB_Register reg, Type *type) { type = type_lowering(type); assert(reg || type == type_void); - value->value = reg; + value->reg = reg; value->alignment = type_abi_alignment(type); - value->kind = BE_VALUE; + value->kind = TBE_VALUE; value->type = type; } -static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *expr) +static void tinybackend_emit_const_expr(TbContext *c, TBEValue *value, Expr *expr) { - Type *type = type_reduced_from_expr(expr)->canonical; - + Type *type = type_flatten(expr->type); switch (expr->const_expr.const_kind) { + case CONST_FLOAT: + TBE_VALUE_set(value, tb_inst_fconst(c->f, tbtype(type), expr->const_expr.fxx.f), type); + return; case CONST_INTEGER: { TB_Register reg; Int128 i = expr->const_expr.ixx.i; - TB_DataType dt = tbc3_get_type(type); + TB_DataType dt = tbtype(type); printf("Const int type: %d\n", dt.type); switch (expr->const_expr.ixx.type) @@ -232,15 +386,14 @@ static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *exp case TYPE_I128: case TYPE_U128: - TODO + FATAL_ERROR("TB does not yet support int128"); //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); + reg = tb_inst_iconst(c->f, dt, i.low); break; } - - tinybackend_value_set(value, reg, type); + TBE_VALUE_set(value, reg, type); return; } default: @@ -248,17 +401,6 @@ static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *exp } } -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, tbc3_get_type(ref->type), ref, &value); - - return value; -} - static int find_member_index(Decl *parent, Decl *member) { VECEACH(parent->strukt.members, i) @@ -276,7 +418,7 @@ static int find_member_index(Decl *parent, Decl *member) return -1; } -static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member) +static void tinybackend_emit_member_addr(TbContext *c, TBEValue *value, Decl *parent, Decl *member) { assert(member->resolve_status == RESOLVE_DONE); @@ -298,9 +440,9 @@ static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *pa break; case TYPE_STRUCT: { - TB_Register ref = tb_inst_member_access(c->function, value->value, (int32_t)found->offset); + TB_Register ref = tb_inst_member_access(c->f, value->reg, (int32_t)found->offset); - tinybackend_value_set_address(value, ref, member->type); + value_set_address_abi_aligned(value, ref, member->type); value->alignment = found->alignment; } break; @@ -311,13 +453,13 @@ static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *pa } while (found != member); } -void tinybackend_emit_access_addr(GenContext *c, BEValue *be_value, Expr *expr) +void tinybackend_emit_access_addr(TbContext *c, TBEValue *TBE_VALUE, Expr *expr) { Expr *parent = expr->access_expr.parent; - tinybackend_emit_expr(c, be_value, parent); + tinybackend_emit_expr(c, TBE_VALUE, parent); Decl *member = expr->access_expr.ref; - tinybackend_emit_member_addr(c, be_value, type_lowering(parent->type)->decl, member); + tinybackend_emit_member_addr(c, TBE_VALUE, type_lowering(parent->type)->decl, member); } static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type) @@ -330,44 +472,35 @@ static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type) 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, - tbc3_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; + + +static inline void tilde_emit_block(TbContext *c, TB_Label label) +{ + tb_inst_label(c->f, label); } -void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op) +TB_Register tilde_value_rvalue_get(TbContext *c, TBEValue *value) +{ + if (value->kind == TBE_VALUE) return value->reg; + //llvm_value_fold_failable(c, value); + return tilde_load_aligned(c, + tbtype(value->type), + value->reg, + value->alignment ? value->alignment : type_abi_alignment(value->type)); + +} + +void tinybackend_emit_binary(TbContext *c, TBEValue *TBE_VALUE, Expr *expr, TBEValue *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); + //TbContext_emit_logical_and_or(c, TBE_VALUE, expr, binary_op); //return; TODO } - BEValue lhs; + TBEValue lhs; if (lhs_loaded) { lhs = *lhs_loaded; @@ -376,16 +509,16 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal { tinybackend_emit_expr(c, &lhs, expr->binary_expr.left); } - tinybackend_value_rvalue(c, &lhs); + value_rvalue(c, &lhs); - BEValue rhs; + TBEValue rhs; tinybackend_emit_expr(c, &rhs, expr->binary_expr.right); - tinybackend_value_rvalue(c, &rhs); + 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); + llvm_emit_comparison(c, TBE_VALUE, &lhs, &rhs, binary_op); return; }*/ @@ -395,10 +528,10 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal 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_Register lhs_value = lhs.reg; + TB_Register rhs_value = rhs.reg; - TB_DataType dt = tbc3_get_type(lhs_type); + TB_DataType dt = tbtype(lhs_type); switch (binary_op) { case BINARYOP_ERROR: @@ -406,12 +539,12 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal case BINARYOP_MULT: if (is_float) { - val = tb_inst_fmul(c->function, dt, lhs_value, rhs_value); + val = tb_inst_fmul(c->f, 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); + val = tb_inst_mul(c->f, dt, lhs_value, rhs_value, TB_ASSUME_NUW); break; case BINARYOP_SUB: if (lhs_type->type_kind == TYPE_POINTER) @@ -431,10 +564,10 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal } if (is_float) { - val = tb_inst_fsub(c->function, dt, lhs_value, rhs_value); + val = tb_inst_fsub(c->f, dt, lhs_value, rhs_value); break; } - val = tb_inst_mul(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type)); + val = tb_inst_mul(c->f, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type)); break; case BINARYOP_ADD: if (lhs_type->type_kind == TYPE_POINTER) @@ -443,20 +576,20 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal } if (is_float) { - val = tb_inst_fadd(c->function, dt, lhs_value, rhs_value); + val = tb_inst_fadd(c->f, dt, lhs_value, rhs_value); break; } - val = tb_inst_add(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type)); + val = tb_inst_add(c->f, 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); + val = tb_inst_fdiv(c->f, dt, lhs_value, rhs_value); break; } - val = tb_inst_div(c->function, dt, lhs_value, rhs_value, !type_is_unsigned(lhs_type)); + val = tb_inst_div(c->f, dt, lhs_value, rhs_value, !type_is_unsigned(lhs_type)); break; case BINARYOP_MOD: { @@ -465,20 +598,20 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal case BINARYOP_SHR: if (type_is_unsigned(lhs_type)) { - val = tb_inst_shr(c->function, dt, lhs_value, rhs_value); + val = tb_inst_shr(c->f, dt, lhs_value, rhs_value); return; } - val = tb_inst_sar(c->function, dt, lhs_value, rhs_value); + val = tb_inst_sar(c->f, dt, lhs_value, rhs_value); break; case BINARYOP_SHL: - val = tb_inst_shl(c->function, dt, lhs_value, rhs_value, TB_ASSUME_NUW); + val = tb_inst_shl(c->f, dt, lhs_value, rhs_value, TB_ASSUME_NUW); break; case BINARYOP_BIT_AND: - val = tb_inst_and(c->function, dt, lhs_value, rhs_value); + val = tb_inst_and(c->f, dt, lhs_value, rhs_value); break; case BINARYOP_BIT_OR: - val = tb_inst_or(c->function, dt, lhs_value, rhs_value); + val = tb_inst_or(c->f, dt, lhs_value, rhs_value); break; case BINARYOP_BIT_XOR: TODO @@ -506,24 +639,16 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal UNREACHABLE } assert(val); - tinybackend_value_set(be_value, val, expr->type); + TBE_VALUE_set(TBE_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) +static void tinybackend_emit_binary_expr(TbContext *c, TBEValue *TBE_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); + tinybackend_emit_vector_assign_expr(c, TBE_VALUE, expr); return; }*/ printf("A\n"); @@ -531,28 +656,28 @@ static void tinybackend_emit_binary_expr(GenContext *c, BEValue *be_value, Expr { BinaryOp base_op = binaryop_assign_base_op(binary_op); assert(base_op != BINARYOP_ERROR); - BEValue addr; + TBEValue 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, tbc3_get_type(addr.type), &addr, be_value); + value_rvalue(c, &addr); + tinybackend_emit_binary(c, TBE_VALUE, expr, &addr, base_op); + tinybackend_store_value(c, &addr, TBE_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)); + tinybackend_emit_expr(c, TBE_VALUE, expr->binary_expr.left); + assert(tinybackend_value_is_addr(TBE_VALUE)); - *be_value = tinybackend_emit_assign_expr(c, be_value, expr->binary_expr.right, TB_NULL_REG /* failable_ref */); + *TBE_VALUE = tilde_emit_assign_expr(c, TBE_VALUE, expr->binary_expr.right, TB_NULL_REG /* failable_ref */); return; } - tinybackend_emit_binary(c, be_value, expr, NULL, binary_op); + tinybackend_emit_binary(c, TBE_VALUE, expr, NULL, binary_op); } -static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr) +static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr) { printf("expr->expr_kind = %d\n", expr->expr_kind); @@ -569,125 +694,134 @@ static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr) return; case EXPR_IDENTIFIER: case EXPR_CONST_IDENTIFIER: - tinybackend_value_set_decl_address(value, expr->identifier_expr.decl); + value_set_decl(value, expr->identifier_expr.decl); return; default: TODO } } -static TB_Register tinybackend_emit_local_decl(GenContext *c, Decl *decl) + + +static void TBE_VALUE_set_reg(TBEValue *value, TB_Register reg, Type *type) { + value->reg = reg; + value->kind = TBE_ADDRESS; + value->type = type_lowering(type); +} + + +static TB_Register tilde_emit_local_decl(TbContext *c, Decl *decl) +{ + // 1. Get the declaration and the LLVM type. + Type *var_type = type_lowering(type_no_fail(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 + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + scratch_buffer_append(".f"); + TB_InitializerID initializer = tb_initializer_create(c->module, type_size(type_anyerr), type_alloca_alignment(type_anyerr), 1); + decl->var.tb_failable_reg = tb_global_create(c->module, scratch_buffer_to_string(), initializer); + } + TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1); + decl->tb_register = tb_global_create(c->module, "tempglobal", static_initializer); + tilde_emit_global_initializer(c, decl); + return decl->tb_register; } - - TB_DataType dt = tbc3_get_type(decl->type); - TypeSize 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 - } - + tilde_emit_local_var_alloca(c, decl); + TB_Register reg = decl->tb_register; Expr *init = decl->var.init_expr; + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->name); + scratch_buffer_append(".f"); + decl->var.tb_failable_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr)); + // Only clear out the result if the assignment isn't a failable. + } + + TBEValue value; + value_set_decl(&value, decl); 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)); + tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg); } + // TODO trap on undef in debug mode. } else { - Type *type = type_lowering(decl->type); - - // Normal case, zero init. - if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER) + if (decl->var.tb_failable_reg) { - tb_inst_store(c->function, dt, reg, tb_inst_iconst(c->function, dt, 0), align); - } - else - { - TODO + tilde_store_self_aligned(c, tilde_get_zero(c, type_anyerr), decl->var.tb_failable_reg, type_anyerr); } + tilde_store_value_zero(c, &value); } - 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_NULL_REG); - break; - default: - break; - } -} -static TB_CallingConv tbc3_call_convention(CallABI abi) -{ - switch (abi) - { - case CALL_C: - return TB_CDECL; - case CALL_X86_STD: - return TB_STDCALL; - default: - FATAL_ERROR("Unsupported call convention for TildeBE"); - } -} -static void tbc3_emit_function_body(GenContext *c, Decl *decl) + + + +static void tilde_emit_function_body(TbContext *c, Decl *decl) { printf("Function: %s\n", decl->external_name); c->current_func_decl = decl; - FunctionSignature *func = &decl->func_decl.function_signature; - unsigned params = vec_size(func->params); - /* - TB_FunctionPrototype *p = tb_prototype_create(c->module, - tbc3_call_convention(func->call_abi), - tbc3_get_type(abi_returntype(func->rtype->type)), - params, func->variadic == VARIADIC_RAW); + TB_FunctionPrototype *prototype = tilde_get_function_type(c->module, decl->type->func.prototype); + TB_Function *function = tb_prototype_build(c->module, prototype, decl->external_name); + c->f = function; - TB_Function *f = tb_prototype_add_param()*/ - /* - VECEACH(decl->func_decl.body->compound_stmt.stmts, i) + AstId current = decl->func_decl.body->compound_stmt.first_stmt; + while (current) { - tinybackend_emit_stmt(c, decl->func_decl.body->compound_stmt.stmts[i]); - }*/ + tilde_emit_stmt(c, ast_next(¤t)); + } - tb_function_print(c->function, stdout); + // Insert a return (and defer) if needed. + /* + if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block)) + { + assert(!decl->func_decl.body->compound_stmt.defer_list.end); + llvm_emit_defer(context, decl->func_decl.body->compound_stmt.defer_list.start, 0); + llvm_emit_return_implicit(context); + } + + // erase alloca point + if (LLVMGetInstructionParent(alloca_point)) + { + context->alloca_point = NULL; + LLVMInstructionEraseFromParent(alloca_point); + } + + LLVMDisposeBuilder(context->builder); + context->builder = NULL; + + if (llvm_use_debug(context)) + { + llvm_debug_scope_pop(context); + } + + context->builder = prev_builder; + context->function = prev_function; +*/ + + tb_function_print(c->f, stdout); +// TODO } -static void tbc3_gen_context(GenContext *c) -{ - const char *result = module_create_object_file_name(c->code_module); - c->object_filename = strformat("%s%s", result, get_object_extension()); -} -static TB_Arch tbc3_get_arch(void) + +static TB_Arch tilde_get_arch(void) { switch (platform_target.arch) { @@ -700,7 +834,7 @@ static TB_Arch tbc3_get_arch(void) } } -static TB_System tbc3_get_system(void) +static TB_System tilde_get_system(void) { switch (platform_target.os) { @@ -723,12 +857,13 @@ static TB_System tbc3_get_system(void) void *tinybackend_gen(Module *module) { if (!vec_size(module->units)) return NULL; - GenContext *c = ccalloc(sizeof(GenContext), 1); + TbContext *c = ccalloc(sizeof(TbContext), 1); c->code_module = module; - c->module = tb_module_create(tbc3_get_arch(), tbc3_get_system(), &c->features); + c->module = tb_module_create(tilde_get_arch(), tilde_get_system(), &c->features); - tbc3_gen_context(c); + const char *result = module_create_object_file_name(module); + c->object_filename = strformat("%s%s", result, get_object_extension()); printf("Module: %.*s\n", module->name->len, module->name->module); // Forward decls @@ -748,7 +883,7 @@ void *tinybackend_gen(Module *module) VECEACH(unit->functions, i) { Decl *decl = unit->functions[i]; - if (decl->func_decl.body) tbc3_emit_function_body(c, decl); + if (decl->func_decl.body) tilde_emit_function_body(c, decl); } } @@ -759,7 +894,7 @@ void *tinybackend_gen(Module *module) // Compile module (multi threaded) const char *tinybackend_codegen(void *context) { - GenContext *c = (GenContext *)context; + TbContext *c = (TbContext *)context; // Write out the object file tb_module_export(c->module, c->object_filename); diff --git a/src/compiler/tilde_codegen.c b/src/compiler/tilde_codegen.c new file mode 100644 index 000000000..92b0cb0ee --- /dev/null +++ b/src/compiler/tilde_codegen.c @@ -0,0 +1,1219 @@ +#include "tilde_internal.h" + +#if TB_BACKEND +static void tbcontext_destroy(TbContext *context) +{ + free(context); +} + +TB_Register tilde_emit_is_no_error(TbContext *c, TB_Reg reg) +{ + return tb_inst_cmp_eq(c->f, tbtype(type_anyerr), reg, tilde_get_zero(c, type_anyerr)); +} + +void tilde_emit_global_initializer(TbContext *c, Decl *decl) +{ + TODO; +} + + +void tilde_emit_and_set_decl_alloca(TbContext *c, Decl *decl) +{ + Type *type = type_lowering(decl->type); + if (type == type_void) return; + decl->tb_register = tb_inst_local(c->f, type_size(type), decl->alignment); +} + + +void tilde_emit_local_var_alloca(TbContext *c, Decl *decl) +{ + assert(!decl->var.is_static); + tilde_emit_and_set_decl_alloca(c, decl); + /* Emit debug here */ +} +#ifdef NEVER +void tilde_emit_memclear(TbContext *c, TBEValue *ref) +{ + assert(ref->kind == TBE_VALUE); + Type *type = ref->type; + if (!type_is_abi_aggregate(type)) + { + tilde_store_bevalue_raw(c, ref, tilde_get_zero(c, type)); + return; + } + Type *single_type = type_abi_find_single_struct_element(type); + + if (single_type && !type_is_abi_aggregate(single_type)) + { + TBEValue element; + value_set_address_align(&element, ref->reg, single_type, ref->alignment); + tilde_emit_memclear(c, &element); + return; + } + if (type_size(type) <= 16) + { + if (type->type_kind == TYPE_STRUCT) + { + Decl *decl = type->decl; + Decl **members = decl->strukt.members; + VECEACH(members, i) + { + if (!type_size(members[i]->type)) continue; + BEValue member_ref; + llvm_emit_struct_member_ref(c, ref, &member_ref, i); + llvm_emit_memclear(c, &member_ref); + } + return; + } + if (type->type_kind == TYPE_ARRAY) + { + LLVMTypeRef array_type = llvm_get_type(c, type); + for (unsigned i = 0; i < type->array.len; i++) + { + AlignSize align; + LLVMValueRef element_ptr = llvm_emit_array_gep_raw(c, ref->value, array_type, i, ref->alignment, &align); + BEValue be_value; + llvm_value_set_address_align(&be_value, element_ptr, type->array.base, align); + llvm_emit_memclear(c, &be_value); + } + return; + } + } + llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true); +} + + + +LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified) +{ + if (diff == 1) return LLVMConstNull(element_type); + *modified = true; + LLVMTypeRef padding_type = LLVMArrayType(element_type, (unsigned)diff); + return LLVMConstNull(padding_type); +} + +LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init) +{ + switch (const_init->kind) + { + case CONST_INIT_ZERO: + return LLVMConstNull(llvm_get_type(c, const_init->type)); + case CONST_INIT_ARRAY_VALUE: + UNREACHABLE + case CONST_INIT_ARRAY_FULL: + { + bool was_modified = false; + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + ConstInitializer **elements = const_init->init_array_full; + assert(array_type->type_kind == TYPE_ARRAY || array_type->type_kind == TYPE_VECTOR); + ArraySize size = array_type->array.len; + assert(size > 0); + LLVMValueRef *parts = VECNEW(LLVMValueRef, size); + for (MemberIndex i = 0; i < size; i++) + { + LLVMValueRef element = llvm_emit_const_initializer(c, elements[i]); + if (element_type_llvm != LLVMTypeOf(element)) was_modified = true; + vec_add(parts, element); + } + if (array_type->type_kind == TYPE_VECTOR) + { + return LLVMConstVector(parts, vec_size(parts)); + } + if (was_modified) + { + return LLVMConstStructInContext(c->context, parts, vec_size(parts), true); + } + return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); + } + + case CONST_INIT_ARRAY: + { + bool was_modified = false; + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + AlignSize expected_align = llvm_abi_alignment(c, element_type_llvm); + ConstInitializer **elements = const_init->init_array.elements; + unsigned element_count = vec_size(elements); + assert(element_count > 0 && "Array should always have gotten at least one element."); + MemberIndex current_index = 0; + unsigned alignment = 0; + LLVMValueRef *parts = NULL; + bool pack = false; + VECEACH(elements, i) + { + ConstInitializer *element = elements[i]; + assert(element->kind == CONST_INIT_ARRAY_VALUE); + MemberIndex element_index = element->init_array_value.index; + IndexDiff diff = element_index - current_index; + if (alignment && expected_align != alignment) + { + pack = true; + } + alignment = expected_align; + // Add zeroes + if (diff > 0) + { + vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified)); + } + LLVMValueRef value = llvm_emit_const_initializer(c, element->init_array_value.element); + if (LLVMTypeOf(value) == element_type_llvm) was_modified = true; + vec_add(parts, value); + current_index = element_index + 1; + } + + IndexDiff end_diff = (MemberIndex)array_type->array.len - current_index; + if (end_diff > 0) + { + vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, end_diff, &was_modified)); + } + if (was_modified) + { + return LLVMConstStructInContext(c->context, parts, vec_size(parts), pack); + } + return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); + } + case CONST_INIT_UNION: + { + Decl *decl = const_init->type->decl; + + // Emit our value. + LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element); + LLVMTypeRef result_type = LLVMTypeOf(result); + + // Get the union value + LLVMTypeRef union_type_llvm = llvm_get_type(c, decl->type); + + // Take the first type in the union (note that there may be padding!) + LLVMTypeRef first_type = LLVMStructGetTypeAtIndex(union_type_llvm, 0); + + // We need to calculate some possible padding. + TypeSize union_size = type_size(const_init->type); + TypeSize member_size = llvm_abi_size(c, result_type); + + // Create the resulting values: + LLVMValueRef values[2] = { result, NULL }; + unsigned value_count = 1; + + // Add possible padding as and i8 array. + if (union_size > member_size) + { + values[1] = llvm_emit_const_padding(c, union_size - member_size); + value_count = 2; + } + + // Is this another type than usual for the union? + if (first_type != result_type) + { + // Yes, so the type needs to be modified. + return LLVMConstStructInContext(c->context, values, value_count, false); + } + + return LLVMConstNamedStruct(union_type_llvm, values, value_count); + } + case CONST_INIT_STRUCT: + { + if (const_init->type->type_kind == TYPE_BITSTRUCT) + { + return llvm_emit_const_bitstruct(c, const_init); + } + Decl *decl = const_init->type->decl; + Decl **members = decl->strukt.members; + uint32_t count = vec_size(members); + LLVMValueRef *entries = NULL; + bool was_modified = false; + for (MemberIndex i = 0; i < count; i++) + { + if (members[i]->padding) + { + vec_add(entries, llvm_emit_const_padding(c, members[i]->padding)); + } + LLVMTypeRef expected_type = llvm_get_type(c, const_init->init_struct[i]->type); + LLVMValueRef element = llvm_emit_const_initializer(c, const_init->init_struct[i]); + LLVMTypeRef element_type = LLVMTypeOf(element); + if (expected_type != element_type) + { + was_modified = true; + } + AlignSize new_align = llvm_abi_alignment(c, element_type); + AlignSize expected_align = llvm_abi_alignment(c, expected_type); + if (i != 0 && new_align < expected_align) + { + vec_add(entries, llvm_emit_const_padding(c, expected_align - new_align)); + } + vec_add(entries, element); + } + if (decl->strukt.padding) + { + vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding)); + } + if (was_modified) + { + LLVMValueRef value = LLVMConstStructInContext(c->context, entries, vec_size(entries), decl->is_packed); + return value; + } + return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, vec_size(entries)); + } + case CONST_INIT_VALUE: + { + BEValue value; + llvm_emit_expr(c, &value, const_init->init_value); + return llvm_load_value_store(c, &value); + } + } + UNREACHABLE +} + + +static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl) +{ + assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); + + // Skip real constants. + if (!decl->type) return; + + if (decl->type != type_void) + { + decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal"); + } + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + scratch_buffer_append(".f"); + decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string()); + } +} + + +void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) +{ + switch (value->type->type_kind) + { + case TYPE_POINTER: + llvm_value_rvalue(c, value); + value->kind = BE_ADDRESS; + return; + case TYPE_ARRAY: + case TYPE_VECTOR: + case TYPE_FLEXIBLE_ARRAY: + return; + case TYPE_SUBARRAY: + { + // TODO insert trap on overflow. + assert(value->kind == BE_ADDRESS); + BEValue member; + llvm_emit_subarray_pointer(c, value, &member); + llvm_value_rvalue(c, &member); + llvm_value_set_address_align(value, member.value, type_get_ptr(value->type->array.base), type_abi_alignment(value->type->array.base)); + return; + } + default: + UNREACHABLE + } +} + +void llvm_set_internal_linkage(LLVMValueRef alloc) +{ + LLVMSetLinkage(alloc, LLVMInternalLinkage); + LLVMSetVisibility(alloc, LLVMDefaultVisibility); +} +void llvm_set_private_linkage(LLVMValueRef alloc) +{ + LLVMSetLinkage(alloc, LLVMPrivateLinkage); + LLVMSetVisibility(alloc, LLVMDefaultVisibility); +} +void llvm_emit_global_variable_init(GenContext *c, Decl *decl) +{ + assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST || decl->var.is_static); + + // Skip real constants. + if (!decl->type) return; + + LLVMValueRef init_value; + + Type *var_type = type_lowering(decl->type); + AlignSize alignment = type_alloca_alignment(var_type); + + Expr *init_expr = decl->var.init_expr; + if (init_expr) + { + if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_LIST) + { + ConstInitializer *list = init_expr->const_expr.list; + init_value = llvm_emit_const_initializer(c, list); + } + else + { + BEValue value; + if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_BYTES) + { + init_value = LLVMConstStringInContext(c->context, + init_expr->const_expr.bytes.ptr, + init_expr->const_expr.bytes.len, + 1); + } + else + { + llvm_emit_expr(c, &value, decl->var.init_expr); + init_value = llvm_load_value_store(c, &value); + } + } + } + else + { + init_value = LLVMConstNull(llvm_get_type(c, var_type)); + } + + + // TODO fix name + LLVMValueRef old = decl->backend_ref; + decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->extname ? decl->extname : decl->external_name); + LLVMSetThreadLocal(decl->backend_ref, decl->var.is_threadlocal); + if (decl->section) + { + LLVMSetSection(decl->backend_ref, decl->section); + } + llvm_set_alignment(decl->backend_ref, alignment); + + LLVMValueRef failable_ref = decl->var.failable_ref; + if (failable_ref) + { + llvm_set_alignment(failable_ref, type_alloca_alignment(type_anyerr)); + LLVMSetThreadLocal(failable_ref, decl->var.is_threadlocal); + } + if (init_expr && IS_FAILABLE(init_expr) && init_expr->expr_kind == EXPR_FAILABLE) + { + UNREACHABLE + } + if (decl->visibility != VISIBLE_EXTERN) + { + LLVMSetInitializer(decl->backend_ref, init_value); + if (failable_ref) + { + LLVMSetInitializer(failable_ref, LLVMConstNull(llvm_get_type(c, type_anyerr))); + } + } + + LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST); + + switch (decl->visibility) + { + case VISIBLE_MODULE: + LLVMSetVisibility(decl->backend_ref, LLVMProtectedVisibility); + if (failable_ref) LLVMSetVisibility(failable_ref, LLVMProtectedVisibility); + break; + case VISIBLE_PUBLIC: + LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); + if (failable_ref) LLVMSetVisibility(failable_ref, LLVMDefaultVisibility); + break; + case VISIBLE_EXTERN: + LLVMSetLinkage(decl->backend_ref, LLVMExternalLinkage); + if (failable_ref) LLVMSetLinkage(failable_ref, LLVMExternalLinkage); + //LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); + break; + case VISIBLE_LOCAL: + LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility); + if (failable_ref) LLVMSetVisibility(failable_ref, LLVMHiddenVisibility); + break; + } + + if (init_value && LLVMTypeOf(init_value) != llvm_get_type(c, var_type)) + { + decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, var_type)); + } + LLVMReplaceAllUsesWith(old, decl->backend_ref); + LLVMDeleteGlobal(old); + + // Should we set linkage here? + if (llvm_use_debug(c)) + { + llvm_emit_debug_global_var(c, decl); + } +} +static void gencontext_verify_ir(GenContext *context) +{ + char *error = NULL; + assert(context->module); + if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error)) + { + if (*error) + { + error_exit("Could not verify IR: %s", error); + } + error_exit("Could not verify module IR."); + } +} + +void gencontext_emit_object_file(GenContext *context) +{ + char *err = ""; + LLVMSetTarget(context->module, platform_target.target_triple); + char *layout = LLVMCopyStringRepOfTargetData(context->target_data); + LLVMSetDataLayout(context->module, layout); + LLVMDisposeMessage(layout); + + // Generate .o or .obj file + if (LLVMTargetMachineEmitToFile(context->machine, context->module, context->object_filename, LLVMObjectFile, &err)) + { + error_exit("Could not emit object file: %s", err); + } +} + +void gencontext_print_llvm_ir(GenContext *context) +{ + char *err = NULL; + if (LLVMPrintModuleToFile(context->module, context->ir_filename, &err)) + { + error_exit("Could not emit ir to file: %s", err); + } +} + + +LLVMValueRef llvm_emit_alloca(GenContext *c, LLVMTypeRef type, unsigned alignment, const char *name) +{ + assert(c->builder); + assert(alignment > 0); + LLVMBasicBlockRef current_block = LLVMGetInsertBlock(c->builder); + LLVMPositionBuilderBefore(c->builder, c->alloca_point); + assert(LLVMGetTypeContext(type) == c->context); + LLVMValueRef alloca = LLVMBuildAlloca(c->builder, type, name); + llvm_set_alignment(alloca, alignment); + LLVMPositionBuilderAtEnd(c->builder, current_block); + return alloca; +} + +LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name) +{ + return llvm_emit_alloca(c, llvm_get_type(c, type), type_alloca_alignment(type), name); +} + +void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl) +{ + Type *type = type_lowering(decl->type); + if (type == type_void) return; + decl->backend_ref = llvm_emit_alloca(c, llvm_get_type(c, type), decl->alignment, decl->name ? decl->name : "anon"); +} + +void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) +{ + assert(!decl->var.is_static); + llvm_emit_and_set_decl_alloca(c, decl); + if (llvm_use_debug(c)) + { + llvm_emit_debug_local_var(c, decl); + } +} + +/** + * Values here taken from LLVM. + * @return return the inlining threshold given the build options. + */ +static int get_inlining_threshold() +{ + if (active_target.optimization_level == OPTIMIZATION_AGGRESSIVE) + { + return 250; + } + switch (active_target.size_optimization_level) + { + case SIZE_OPTIMIZATION_TINY: + return 5; + case SIZE_OPTIMIZATION_SMALL: + return 50; + default: + return 250; + } +} + + +static inline unsigned lookup_intrinsic(const char *name) +{ + return LLVMLookupIntrinsicID(name, strlen(name)); +} + +static inline unsigned lookup_attribute(const char *name) +{ + return LLVMGetEnumAttributeKindForName(name, strlen(name)); +} + +static bool intrinsics_setup = false; +LLVMAttributes attribute_id; +LLVMIntrinsics intrinsic_id; + +void llvm_codegen_setup() +{ + if (intrinsics_setup) return; + + intrinsic_id.trap = lookup_intrinsic("llvm.trap"); + intrinsic_id.assume = lookup_intrinsic("llvm.assume"); + + intrinsic_id.bswap = lookup_intrinsic("llvm.bswap"); + + intrinsic_id.lifetime_start = lookup_intrinsic("llvm.lifetime.start"); + intrinsic_id.lifetime_end = lookup_intrinsic("llvm.lifetime.end"); + + intrinsic_id.ssub_overflow = lookup_intrinsic("llvm.ssub.with.overflow"); + intrinsic_id.ssub_sat = lookup_intrinsic("llvm.ssub.sat"); + intrinsic_id.usub_overflow = lookup_intrinsic("llvm.usub.with.overflow"); + intrinsic_id.usub_sat = lookup_intrinsic("llvm.usub.sat"); + intrinsic_id.sadd_overflow = lookup_intrinsic("llvm.sadd.with.overflow"); + intrinsic_id.sadd_sat = lookup_intrinsic("llvm.sadd.sat"); + intrinsic_id.uadd_overflow = lookup_intrinsic("llvm.uadd.with.overflow"); + intrinsic_id.uadd_sat = lookup_intrinsic("llvm.uadd.sat"); + intrinsic_id.smul_overflow = lookup_intrinsic("llvm.smul.with.overflow"); + intrinsic_id.umul_overflow = lookup_intrinsic("llvm.umul.with.overflow"); + //intrinsic_id.sshl_sat = lookup_intrinsic("llvm.sshl.sat"); + //intrinsic_id.ushl_sat = lookup_intrinsic("llvm.ushl.sat"); + intrinsic_id.fshl = lookup_intrinsic("llvm.fshl"); + intrinsic_id.fshr = lookup_intrinsic("llvm.fshr"); + intrinsic_id.bitreverse = lookup_intrinsic("llvm.bitreverse"); + intrinsic_id.bswap = lookup_intrinsic("llvm.bswap"); + intrinsic_id.ctpop = lookup_intrinsic("llvm.ctpop"); + intrinsic_id.cttz = lookup_intrinsic("llvm.cttz"); + intrinsic_id.ctlz = lookup_intrinsic("llvm.ctlz"); + + intrinsic_id.rint = lookup_intrinsic("llvm.rint"); + intrinsic_id.trunc = lookup_intrinsic("llvm.trunc"); + intrinsic_id.ceil = lookup_intrinsic("llvm.ceil"); + intrinsic_id.floor = lookup_intrinsic("llvm.floor"); + intrinsic_id.sqrt = lookup_intrinsic("llvm.sqrt"); + intrinsic_id.powi = lookup_intrinsic("llvm.powi"); + intrinsic_id.pow = lookup_intrinsic("llvm.pow"); + intrinsic_id.sin = lookup_intrinsic("llvm.sin"); + intrinsic_id.cos = lookup_intrinsic("llvm.cos"); + intrinsic_id.exp = lookup_intrinsic("llvm.exp"); + intrinsic_id.exp2 = lookup_intrinsic("llvm.exp2"); + intrinsic_id.log = lookup_intrinsic("llvm.log"); + intrinsic_id.log2 = lookup_intrinsic("llvm.log2"); + intrinsic_id.log10 = lookup_intrinsic("llvm.log10"); + intrinsic_id.fabs = lookup_intrinsic("llvm.fabs"); + intrinsic_id.fma = lookup_intrinsic("llvm.fma"); + intrinsic_id.copysign = lookup_intrinsic("llvm.copysign"); + intrinsic_id.minnum = lookup_intrinsic("llvm.minnum"); + intrinsic_id.maxnum = lookup_intrinsic("llvm.maxnum"); + intrinsic_id.minimum = lookup_intrinsic("llvm.minimum"); + intrinsic_id.maximum = lookup_intrinsic("llvm.maximum"); + intrinsic_id.convert_to_fp16 = lookup_intrinsic("llvm.convert.to.fp16"); + intrinsic_id.convert_from_fp16 = lookup_intrinsic("llvm.convert.from.fp16"); + intrinsic_id.nearbyint = lookup_intrinsic("llvm.nearbyint"); + intrinsic_id.roundeven = lookup_intrinsic("llvm.roundeven"); + intrinsic_id.lround = lookup_intrinsic("llvm.lround"); + intrinsic_id.llround = lookup_intrinsic("llvm.llround"); + intrinsic_id.lrint = lookup_intrinsic("llvm.lrint"); + intrinsic_id.llrint = lookup_intrinsic("llvm.llrint"); + + //intrinsic_id.abs = lookup_intrinsic("llvm.abs"); + intrinsic_id.smax = lookup_intrinsic("llvm.smax"); + intrinsic_id.smin = lookup_intrinsic("llvm.smin"); + intrinsic_id.umax = lookup_intrinsic("llvm.umax"); + intrinsic_id.umin = lookup_intrinsic("llvm.umin"); + + attribute_id.noinline = lookup_attribute("noinline"); + attribute_id.optnone = lookup_attribute("optnone"); + attribute_id.alwaysinline = lookup_attribute("alwaysinline"); + attribute_id.inlinehint = lookup_attribute("inlinehint"); + attribute_id.noreturn = lookup_attribute("noreturn"); + attribute_id.nounwind = lookup_attribute("nounwind"); + attribute_id.writeonly = lookup_attribute("writeonly"); + attribute_id.readonly = lookup_attribute("readonly"); + attribute_id.optnone = lookup_attribute("optnone"); + attribute_id.sret = lookup_attribute("sret"); + attribute_id.noalias = lookup_attribute("noalias"); + attribute_id.zext = lookup_attribute("zeroext"); + attribute_id.sext = lookup_attribute("signext"); + attribute_id.align = lookup_attribute("align"); + attribute_id.byval = lookup_attribute("byval"); + attribute_id.inreg = lookup_attribute("inreg"); + attribute_id.naked = lookup_attribute("naked"); + intrinsics_setup = true; +} + +void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) +{ + if (decl->module != c->code_module) + { + LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); + LLVMSetVisibility(value, LLVMDefaultVisibility); + return; + } + switch (decl->visibility) + { + case VISIBLE_MODULE: + case VISIBLE_PUBLIC: + LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); + LLVMSetVisibility(value, LLVMDefaultVisibility); + break; + case VISIBLE_EXTERN: + case VISIBLE_LOCAL: + LLVMSetVisibility(value, LLVMHiddenVisibility); + LLVMSetLinkage(value, LLVMLinkerPrivateLinkage); + break; + } + +} +void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl) +{ + llvm_get_type(c, decl->type); + if (decl_is_struct_type(decl)) + { + Decl **decls = decl->strukt.members; + VECEACH(decls, i) + { + Decl *member_decl = decls[i]; + if (decl_is_struct_type(member_decl)) + { + llvm_emit_introspection_type_from_decl(c, member_decl); + } + } + } + if (decl_is_enum_kind(decl)) + { + unsigned elements = vec_size(decl->enums.values); + LLVMTypeRef element_type = llvm_get_type(c, type_voidptr); + LLVMTypeRef elements_type = LLVMArrayType(element_type, elements); + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + scratch_buffer_append("$elements"); + LLVMValueRef enum_elements = LLVMAddGlobal(c->module, elements_type, scratch_buffer_to_string()); + LLVMSetGlobalConstant(enum_elements, 1); + llvm_set_linkage(c, decl, enum_elements); + LLVMSetInitializer(enum_elements, LLVMConstNull(elements_type)); + AlignSize alignment = type_alloca_alignment(type_voidptr); + for (unsigned i = 0; i < elements; i++) + { + AlignSize store_align; + decl->enums.values[i]->backend_ref = llvm_emit_array_gep_raw(c, enum_elements, elements_type, i, alignment, &store_align); + } + } + LLVMValueRef global_name = LLVMAddGlobal(c->module, llvm_get_type(c, type_char), decl->name ? decl->name : "anon"); + LLVMSetGlobalConstant(global_name, 1); + LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); + decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); + llvm_set_linkage(c, decl, global_name); +} + + +void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value) +{ + value->value = llvm_value; + value->alignment = type_abi_alignment(type_bool); + value->kind = BE_BOOLEAN; + value->type = type_bool; +} + +void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i) +{ + llvm_value_set(value, llvm_const_int(c, type, i), type); +} + +void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type) +{ + type = type_lowering(type); + assert(llvm_value || type == type_void); + value->value = llvm_value; + value->alignment = type_abi_alignment(type); + value->kind = BE_VALUE; + value->type = type; +} + +bool llvm_value_is_const(BEValue *value) +{ + return LLVMIsConstant(value->value); +} + +void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment) +{ + value->value = llvm_value; + value->alignment = alignment; + value->kind = BE_ADDRESS; + value->type = type_lowering(type); +} + +void llvm_value_set_decl(BEValue *value, Decl *decl) +{ + decl = decl_flatten(decl); + if (decl->is_value) + { + llvm_value_set(value, decl->backend_value, decl->type); + return; + } + llvm_value_set_decl_address(value, decl); +} + +void llvm_value_set_decl_address(BEValue *value, Decl *decl) +{ + decl = decl_flatten(decl); + llvm_value_set_address(value, decl_ref(decl), decl->type); + value->alignment = decl->alignment; + + if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) + { + value->kind = BE_ADDRESS_FAILABLE; + value->failable = decl->var.failable_ref; + } +} + +void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type) +{ + llvm_value_set_address_align(value, llvm_value, type_lowering(type), type_abi_alignment(type)); +} + +void llvm_value_fold_failable(GenContext *c, BEValue *value) +{ + if (value->kind == BE_ADDRESS_FAILABLE) + { + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); + BEValue error_value; + llvm_value_set_address(&error_value, value->failable, type_anyerr); + BEValue comp; + llvm_value_set_bool(&comp, llvm_emit_is_no_error_value(c, &error_value)); + if (c->error_var) + { + LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); + llvm_emit_cond_br(c, &comp, after_block, error_block); + llvm_emit_block(c, error_block); + llvm_store_bevalue_dest_aligned(c, c->error_var, &error_value); + llvm_emit_br(c, c->catch_block); + } + else + { + assert(c->catch_block); + llvm_emit_cond_br(c, &comp, after_block, c->catch_block); + } + llvm_emit_block(c, after_block); + value->kind = BE_ADDRESS; + } +} + +LLVMValueRef llvm_load_value_store(GenContext *c, BEValue *value) +{ + llvm_value_fold_failable(c, value); + switch (value->kind) + { + case BE_VALUE: + return value->value; + case BE_ADDRESS_FAILABLE: + UNREACHABLE + case BE_ADDRESS: + return llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, + value->alignment ? value->alignment : type_abi_alignment(value->type), + ""); + case BE_BOOLEAN: + if (LLVMIsConstant(value->value)) + { + return LLVMConstZExt(value->value, c->byte_type); + } + return LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); + } + UNREACHABLE +} + + +LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name) +{ + return LLVMCreateBasicBlockInContext(c->context, name); +} + +void llvm_value_addr(GenContext *c, BEValue *value) +{ + llvm_value_fold_failable(c, value); + if (value->kind == BE_ADDRESS) return; + if (!c->builder) + { + LLVMValueRef val = llvm_load_value_store(c, value); + LLVMValueRef ref = LLVMAddGlobal(c->module, LLVMTypeOf(val), ".taddr"); + llvm_set_alignment(ref, llvm_abi_alignment(c, LLVMTypeOf(val))); + llvm_set_private_linkage(ref); + LLVMSetInitializer(ref, val); + llvm_emit_bitcast(c, ref, type_get_ptr(value->type)); + llvm_value_set_address(value, ref, value->type); + } + else + { + LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr"); + llvm_store_bevalue_dest_aligned(c, temp, value); + llvm_value_set_address(value, temp, value->type); + } +} + +void llvm_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; + } + return; + } + llvm_value_fold_failable(c, value); + value->value = llvm_emit_load_aligned(c, + llvm_get_type(c, 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; + } + value->kind = BE_VALUE; +} + + +static void llvm_emit_type_decls(GenContext *context, 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_DISTINCT: + // TODO + break; + case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: + // TODO + break;; + case DECL_STRUCT: + case DECL_UNION: + case DECL_ENUM: + case DECL_ERRTYPE: + case DECL_BITSTRUCT: + llvm_emit_introspection_type_from_decl(context, decl); + break; + case NON_TYPE_DECLS: + UNREACHABLE + } +} + +const char *llvm_codegen(void *context) +{ + GenContext *c = context; + LLVMModuleRef module = c->module; + // Starting from here we could potentially thread this: + LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate(); + LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, (unsigned)active_target.optimization_level); + LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, (unsigned)active_target.size_optimization_level); + LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, active_target.optimization_level == OPTIMIZATION_NONE); + if (active_target.optimization_level != OPTIMIZATION_NONE) + { + LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, (unsigned)get_inlining_threshold()); + } + LLVMPassManagerRef pass_manager = LLVMCreatePassManager(); + LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(module); + LLVMAddAnalysisPasses(c->machine, function_pass_manager); + LLVMAddAnalysisPasses(c->machine, pass_manager); + LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager); + LLVMPassManagerBuilderPopulateFunctionPassManager(pass_manager_builder, function_pass_manager); + + // IMPROVE + // In LLVM Opt, LoopVectorize and SLPVectorize settings are part of the PassManagerBuilder + // Anything else we need to manually add? + + LLVMPassManagerBuilderDispose(pass_manager_builder); + + // Run function passes + LLVMInitializeFunctionPassManager(function_pass_manager); + LLVMValueRef current_function = LLVMGetFirstFunction(module); + while (current_function) + { + LLVMRunFunctionPassManager(function_pass_manager, current_function); + current_function = LLVMGetNextFunction(current_function); + } + LLVMFinalizeFunctionPassManager(function_pass_manager); + LLVMDisposePassManager(function_pass_manager); + + // Run module pass + LLVMRunPassManager(pass_manager, module); + LLVMDisposePassManager(pass_manager); + + // Serialize the LLVM IR, if requested, also verify the IR in this case + if (active_target.emit_llvm) + { + gencontext_print_llvm_ir(c); + gencontext_verify_ir(c); + } + + const char *object_name = NULL; + if (active_target.emit_object_files) + { + gencontext_emit_object_file(c); + object_name = c->object_filename; + } + + gencontext_end_module(c); + gencontext_destroy(c); + + return object_name; +} + +void *llvm_gen(Module *module) +{ + if (!vec_size(module->units)) return NULL; + assert(intrinsics_setup); + GenContext *gen_context = cmalloc(sizeof(GenContext)); + gencontext_init(gen_context, module); + gencontext_begin_module(gen_context); + + VECEACH(module->units, j) + { + CompilationUnit *unit = module->units[j]; + gencontext_init_file_emit(gen_context, unit); + gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; + gen_context->debug.file = unit->llvm.debug_file; + + VECEACH(unit->external_symbol_list, i) + { + Decl *d = unit->external_symbol_list[i]; + // Avoid duplicating symbol + if (d->module == unit->module) continue; + llvm_emit_extern_decl(gen_context, unit->external_symbol_list[i]); + } + VECEACH(unit->methods, i) + { + llvm_emit_function_decl(gen_context, unit->methods[i]); + } + VECEACH(unit->types, i) + { + llvm_emit_type_decls(gen_context, unit->types[i]); + } + VECEACH(unit->functions, i) + { + llvm_emit_function_decl(gen_context, unit->functions[i]); + } + if (unit->main_function) llvm_emit_function_decl(gen_context, unit->main_function); + } + + VECEACH(module->units, j) + { + CompilationUnit *unit = module->units[j]; + gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; + gen_context->debug.file = unit->llvm.debug_file; + + VECEACH(unit->vars, i) + { + gencontext_emit_global_variable_definition(gen_context, unit->vars[i]); + } + VECEACH(unit->vars, i) + { + llvm_emit_global_variable_init(gen_context, unit->vars[i]); + } + VECEACH(unit->functions, i) + { + Decl *decl = unit->functions[i]; + if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); + } + if (unit->main_function) llvm_emit_function_body(gen_context, unit->main_function); + + VECEACH(unit->methods, i) + { + Decl *decl = unit->methods[i]; + if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); + } + + gencontext_end_file_emit(gen_context, unit); + } + // EmitDeferred() + + if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(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 gen_context; +} + +void llvm_attribute_add_int(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, uint64_t val, int index) +{ + LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute, val); + LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); +} + +void llvm_attribute_add_type(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute, LLVMTypeRef type, int index) +{ + LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type); + LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); +} + +void llvm_attribute_add(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index) +{ + llvm_attribute_add_int(context, value_to_add_attribute_to, attribute, 0, index); +} + +void llvm_attribute_add_call_type(GenContext *c, LLVMValueRef call, unsigned attribute, int index, LLVMTypeRef type) +{ + LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type); + LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr); +} + +void llvm_attribute_add_call(GenContext *context, LLVMValueRef call, unsigned attribute, int index, int64_t value) +{ + LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute, (uint64_t)value); + LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr); +} + +void llvm_attribute_add_range(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index_start, int index_end) +{ + for (int i = index_start; i <= index_end; i++) + { + llvm_attribute_add_int(context, value_to_add_attribute_to, attribute, 0, i); + } +} + +void llvm_attribute_add_string(GenContext *context, LLVMValueRef value_to_add_attribute_to, const char *attribute, const char *value, int index) +{ + LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(context->context, attribute, (unsigned)strlen(attribute), value, (unsigned)strlen(value)); + LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); +} + +BitSize llvm_bitsize(GenContext *c, LLVMTypeRef type) +{ + return LLVMSizeOfTypeInBits(c->target_data, type); +} +TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type) +{ + return (TypeSize)LLVMABISizeOfType(c->target_data, type); +} + +AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type) +{ + return (AlignSize)LLVMABIAlignmentOfType(c->target_data, type); +} + +LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment) +{ + // If we have an address but not an aggregate, do a load. + llvm_value_fold_failable(c, value); + if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type)) + { + value->value = llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, value->alignment, ""); + value->kind = BE_VALUE; + } + switch (value->kind) + { + case BE_BOOLEAN: + value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); + value->kind = BE_VALUE; + FALLTHROUGH; + case BE_VALUE: + return llvm_store_aligned(c, destination, value->value, alignment ? alignment : type_abi_alignment(value->type)); + case BE_ADDRESS_FAILABLE: + UNREACHABLE + case BE_ADDRESS: + { + // Here we do an optimized(?) memcopy. + ByteSize size = type_size(value->type); + LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size); + destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), ""); + LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); + LLVMValueRef copy = LLVMBuildMemCpy(c->builder, + destination, + alignment ? alignment : type_abi_alignment(value->type), + source, + value->alignment ? value->alignment : type_abi_alignment(value->type), + copy_size); + return copy; + } + } + UNREACHABLE +} + +void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value) +{ + llvm_store_bevalue_aligned(c, destination, value, LLVMGetAlignment(destination)); +} + +LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value) +{ + if (value->type == type_void) return NULL; + assert(llvm_value_is_addr(destination)); + return llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment); +} + +void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value) +{ + assert(llvm_value_is_addr(destination)); + llvm_store_aligned(c, destination->value, raw_value, destination->alignment); +} + +void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type) +{ + llvm_store_aligned(context, pointer, value, type_abi_alignment(type)); +} + +LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) +{ + LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer); + if (alignment) llvm_set_alignment(ref, alignment); + return ref; +} + +void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value) +{ + assert(!decl->is_value); + llvm_store_aligned(context, decl->backend_ref, value, decl->alignment); +} + +void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len) +{ + assert(dest_align && src_align); + if (len <= UINT32_MAX) + { + LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len)); + return; + } + LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len)); +} + +void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment) +{ + if (source_alignment == 0) source_alignment = type_abi_alignment(decl->type); + assert(!decl->is_value); + llvm_emit_memcpy(c, decl->backend_ref, decl->alignment, source, source_alignment, type_size(decl->type)); +} + +LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name) +{ + assert(c->builder); + LLVMValueRef value = LLVMBuildLoad2(c->builder, type, pointer, name); + assert(LLVMGetTypeContext(type) == c->context); + llvm_set_alignment(value, alignment ? alignment : llvm_abi_alignment(c, type)); + return value; +} + +TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type) +{ + return (TypeSize)LLVMStoreSizeOfType(c->target_data, type); +} + +void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block) +{ + c->catch_block = block; + c->error_var = NULL; +} + +void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef ref) +{ + c->catch_block = block; + c->error_var = ref; +} + +#endif + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_codegen_expr.c b/src/compiler/tilde_codegen_expr.c new file mode 100644 index 000000000..d9ef7ad0d --- /dev/null +++ b/src/compiler/tilde_codegen_expr.c @@ -0,0 +1,758 @@ +// 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" + +#if TB_BACKEND + +TB_Register tilde_get_zero(TbContext *c, Type *type) +{ + type = type_lowering(type); + TB_DataType data_type = tbtype(type); + if (type_is_float(type)) + { + return tb_inst_fconst(c->f, data_type, 0); + } + return tb_inst_iconst(c->f, data_type, 0); +} + +TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable) +{ + assert(ref->kind == TBE_ADDRESS || ref->kind == TBE_ADDRESS_FAILABLE); + TB_Label assign_block = 0; + + PUSH_ERROR(); + + TBEValue result; + if (failable) + { + if (IS_FAILABLE(expr)) + { + if (expr->expr_kind == EXPR_FAILABLE) + { + c->error_var = 0; + c->catch_block = 0; + tilde_emit_expr(c, &result, expr->inner_expr); + tilde_store_value_aligned(c, failable, &result, type_abi_alignment(type_anyerr)); + POP_ERROR(); + return (TBEValue) { .kind = TBE_VALUE }; + } + assign_block = tb_inst_new_label_id(c->f); + c->error_var = failable; + c->catch_block = assign_block; + } + else + { + c->error_var = 0; + c->catch_block = 0; + } + } + if (type_is_vector(expr->type)) + { + tilde_emit_expr(c, &result, expr); + tilde_store_value(c, ref, &result); + } + else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) + { + TODO + //llvm_emit_const_initialize_reference(c, ref, expr); + //value = *ref; + } + else if (expr_is_init_list(expr)) + { + TODO + //llvm_emit_initialize_reference(c, ref, expr); + //value = *ref; + } + else + { + tilde_emit_expr(c, &result, expr); + tilde_store_value(c, ref, &result); + } + + if (failable) + { + tilde_store_zero(c, type_anyerr, failable, type_alloca_alignment(type_anyerr)); + } + POP_ERROR(); + + if (assign_block) + { + tb_inst_goto(c->f, assign_block); + tb_inst_label(c->f, assign_block); + } + + return result; +} + +static void tilde_emit_parameter(TbContext *c, TB_Reg **args, ABIArgInfo *info, TBEValue *be_value, Type *type) +{ + type = type_lowering(type); + assert(be_value->type->canonical == type); + switch (info->kind) + { + case ABI_ARG_IGNORE: + // Skip. + return; + case ABI_ARG_INDIRECT: + { + // If we want we could optimize for structs by doing it by reference here. + assert(info->indirect.alignment == type_abi_alignment(type) || info->attributes.realign); + TB_Reg indirect = tb_inst_local(c->f, type_size(type), info->indirect.alignment); + tilde_store_value_aligned(c, indirect, be_value, info->indirect.alignment); + vec_add(*args, indirect); + return; + } + case ABI_ARG_DIRECT: + vec_add(*args, tilde_load_value(c, be_value)); + return; + case ABI_ARG_DIRECT_COERCE: + { + TODO + /* + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); + if (!coerce_type || coerce_type == llvm_get_type(c, type)) + { + vec_add(*args, llvm_load_value_store(c, be_value)); + return; + } + if (!abi_info_should_flatten(info)) + { + vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type)); + return; + } + AlignSize target_alignment = llvm_abi_alignment(c, coerce_type); + + AlignSize alignment; + LLVMValueRef cast = llvm_emit_coerce_alignment(c, be_value, coerce_type, target_alignment, &alignment); + LLVMTypeRef element = llvm_abi_type(c, info->direct_coerce.type); + for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++) + { + AlignSize load_align; + LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, alignment, &load_align); + vec_add(*args, llvm_load(c, element, element_ptr, load_align, "")); + }*/ + return; + } + case ABI_ARG_DIRECT_PAIR: + { + /* + llvm_value_addr(c, be_value); + REMINDER("Handle invalid alignment"); + // Here we do the following transform: + // struct -> { lo, hi } -> lo, hi + 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_coerce_type(c, info); + + AlignSize struct_align; + LLVMValueRef cast = llvm_emit_coerce_alignment(c, + be_value, + struct_type, + llvm_abi_alignment(c, struct_type), + &struct_align); + // Get the lo value. + + AlignSize alignment; + LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, struct_align, &alignment); + vec_add(*args, llvm_load(c, lo, lo_ptr, alignment, "lo")); + // Get the hi value. + LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, struct_align, &alignment); + vec_add(*args, llvm_load(c, hi, hi_ptr, alignment, "hi")); + return;*/ + } + case ABI_ARG_EXPAND_COERCE: + { + // Move this to an address (if needed) + value_addr(c, be_value); + /* + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); + AlignSize alignment; + LLVMValueRef temp = llvm_emit_coerce_alignment(c, + be_value, + coerce_type, + llvm_abi_alignment(c, coerce_type), + &alignment); + + AlignSize align; + LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c, + temp, + coerce_type, + info->coerce_expand.lo_index, + alignment, + &align); + vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.lo), gep_first, 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, + &align); + vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, "")); + }*/ + return; + } + case ABI_ARG_EXPAND: + { + // Move this to an address (if needed) + value_addr(c, be_value); + TODO + /* + llvm_expand_type_to_args(c, type, be_value->value, args, be_value->alignment); + // Expand the padding here. + if (info->expand.padding_type) + { + vec_add(*args, LLVMGetUndef(llvm_get_type(c, info->expand.padding_type))); + }*/ + return; + } + } + +} + +void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) +{ + if (expr->call_expr.is_builtin) + { + TODO + // llvm_emit_builtin_call(c, result_value, expr); + return; + } + TB_DataType func_type; + TB_Reg func; + TBEValue temp_value; + + bool always_inline = false; + + FunctionPrototype *prototype; + // 1. Call through a pointer. + if (expr->call_expr.is_pointer_call) + { + Expr *function = expr->call_expr.function; + + // 1a. Find the pointee type for the function pointer: + Type *type = function->type->canonical->pointer; + + // 1b. Find the type signature using the underlying pointer. + prototype = type->func.prototype; + + // 1c. Evaluate the pointer expression. + TBEValue func_value; + tilde_emit_expr(c, &func_value, expr->call_expr.function); + + // 1d. Load it as a value + func = tilde_load_value(c, &func_value); + + // 1e. Calculate the function type + func_type = tbtype(type); + } + else + { + // 2a. Get the function declaration + + Decl *function_decl = expr->call_expr.func_ref; + always_inline = function_decl->func_decl.attr_inline; + + // 2b. Set signature, function and function type + prototype = function_decl->type->func.prototype; + func = function_decl->tb_register; + assert(func); + func_type = tbtype(function_decl->type); + } + + TB_Reg *values = NULL; + Type **params = prototype->params; + ABIArgInfo **abi_args = prototype->abi_args; + unsigned param_count = vec_size(params); + unsigned non_variadic_params = param_count; + if (prototype->variadic == VARIADIC_TYPED) non_variadic_params--; + ABIArgInfo *ret_info = prototype->ret_abi_info; + Type *call_return_type = prototype->abi_ret_type; + + // 5. In the case of a failable, the error is replacing the regular return abi. + TB_Reg error_var = 0; + + *result_value = (TBEValue){ .kind = TBE_VALUE, .reg = 0 }; + // 6. Generate data for the return value. + switch (ret_info->kind) + { + case ABI_ARG_INDIRECT: + // 6a. We can use the stored error var if there is no redirect. + if (prototype->is_failable && c->error_var && !ret_info->attributes.realign) + { + error_var = c->error_var; + vec_add(values, error_var); + break; + } + // 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side. + assert(ret_info->attributes.realign || ret_info->indirect.alignment == type_abi_alignment(call_return_type)); + AlignSize alignment = ret_info->indirect.alignment; + value_set_address(result_value, tilde_emit_alloca(c, call_return_type), call_return_type, alignment); + + // 6c. Add the pointer to the list of arguments. + vec_add(values, result_value->reg); + break; + case ABI_ARG_EXPAND: + UNREACHABLE + case ABI_ARG_DIRECT_PAIR: + case ABI_ARG_IGNORE: + case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_DIRECT: + case ABI_ARG_EXPAND_COERCE: + break; + } + + + // 7. We might have a failable indirect return and a normal return. + // In this case we need to add it by hand. + TBEValue synthetic_return_param = { 0 }; + if (prototype->ret_by_ref) + { + // 7b. Create the address to hold the return. + Type *actual_return_type = type_lowering(prototype->ret_by_ref_type); + value_set(&synthetic_return_param, tilde_emit_alloca(c, actual_return_type), type_get_ptr(actual_return_type)); + // 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list) + tilde_emit_parameter(c, + &values, + prototype->ret_by_ref_abi_info, + &synthetic_return_param, + synthetic_return_param.type); + // 7d. Update the be_value to actually be an address. + value_set_address_abi_aligned(&synthetic_return_param, synthetic_return_param.reg, actual_return_type); + } + + + // 8. Add all other arguments. + unsigned arguments = vec_size(expr->call_expr.arguments); + assert(arguments >= non_variadic_params); + for (unsigned i = 0; i < non_variadic_params; i++) + { + // 8a. Evaluate the expression. + Expr *arg_expr = expr->call_expr.arguments[i]; + tilde_emit_expr(c, &temp_value, arg_expr); + + // 8b. Emit the parameter according to ABI rules. + Type *param = params[i]; + ABIArgInfo *info = abi_args[i]; + tilde_emit_parameter(c, &values, info, &temp_value, param); + } + + // 9. Typed varargs + if (prototype->variadic == VARIADIC_TYPED) + { + REMINDER("All varargs should be called with non-alias!"); + Type *vararg_param = params[non_variadic_params]; + ABIArgInfo *vararg_info = abi_args[non_variadic_params]; + + TBEValue subarray; + + value_set_address_abi_aligned(&subarray, tilde_emit_alloca(c, vararg_param), vararg_param); + + // 9a. Special case, empty argument + if (arguments == non_variadic_params) + { + // Just set the size to zero. + TBEValue len_addr; + TODO + // llvm_emit_subarray_len(c, &subarray, &len_addr); + // llvm_store_value_raw(c, &len_addr, llvm_get_zero(c, type_usize)); + } + else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last) + { + // 9b. We unpack the last type which is either a slice, an array or a dynamic array. + TODO + // llvm_emit_unpacked_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); + } + else + { + // 9b. Otherwise we also need to allocate memory for the arguments: + Type *pointee_type = vararg_param->array.base; + Type *array = type_get_array(pointee_type, arguments - non_variadic_params); + TB_DataType llvm_array_type = tbtype(array); + AlignSize alignment = type_alloca_alignment(array); + TB_Reg array_ref = tilde_emit_alloca(c, array); + for (unsigned i = non_variadic_params; i < arguments; i++) + { + Expr *arg_expr = expr->call_expr.arguments[i]; + tilde_emit_expr(c, &temp_value, arg_expr); + AlignSize store_alignment; + TODO + /* + LLVMValueRef slot = llvm_emit_array_gep_raw(c, array_ref, llvm_array_type, i - non_variadic_params, alignment, &store_alignment); + llvm_store_value_aligned(c, slot, &temp_value, store_alignment);*/ + } + TODO + /* + BEValue len_addr; + llvm_emit_subarray_len(c, &subarray, &len_addr); + llvm_store_value_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params)); + BEValue pointer_addr; + llvm_emit_subarray_pointer(c, &subarray, &pointer_addr); + Type *array_as_pointer_type = type_get_ptr(pointee_type); + llvm_store_value_raw(c, &pointer_addr, llvm_emit_bitcast(c, array_ref, array_as_pointer_type));*/ + } + tilde_emit_parameter(c, &values, vararg_info, &subarray, vararg_param); + } + else + { + // 9. Emit varargs. + for (unsigned i = param_count; i < arguments; i++) + { + Expr *arg_expr = expr->call_expr.arguments[i]; + tilde_emit_expr(c, &temp_value, arg_expr); + REMINDER("Varargs should be expanded correctly"); + vec_add(values, tilde_load_value(c, &temp_value)); + } + } + + + // 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params) + //EMIT_LOC(c, expr); + TODO + TB_Reg call_value; + // = tb_inst_call(c->f, func_type, func, vec_size(values), values); + if (prototype->call_abi) + { + // LLVMSetInstructionCallConv(call_value, llvm_call_convention_from_call(prototype->call_abi, platform_target.arch, platform_target.os)); + } + if (expr->call_expr.force_noinline) + { + // llvm_attribute_add_call(c, call_value, attribute_id.noinline, -1, 0); + } + else + { + if (expr->call_expr.force_inline || always_inline) + { + // llvm_attribute_add_call(c, call_value, attribute_id.alwaysinline, -1, 0); + } + } + + /* + for (unsigned i = 0; i < non_variadic_params; i++) + { + Type *param = params[i]; + ABIArgInfo *info = abi_args[i]; + switch (info->kind) + { + case ABI_ARG_INDIRECT: + if (info->attributes.by_val) + { + // llvm_attribute_add_call_type(c, call_value, attribute_id.byval, (int)i + 1, llvm_get_type(c, info->indirect.type)); + } + llvm_attribute_add_call(c, call_value, attribute_id.align, (int)i + 1, info->indirect.alignment); + break; + default: + break; + } + } + + // 11. Process the return value. + switch (ret_info->kind) + { + case ABI_ARG_EXPAND: + UNREACHABLE + case ABI_ARG_IGNORE: + // 12. Basically void returns or empty structs. + // Here we know we don't have a failable or any return value that can be used. + assert(!prototype->is_failable && "Failable should have produced a return value."); + *result_value = (BEValue) { .type = type_void, .kind = BE_VALUE }; + return; + case ABI_ARG_INDIRECT: + llvm_attribute_add_call_type(c, call_value, attribute_id.sret, 1, llvm_get_type(c, ret_info->indirect.type)); + llvm_attribute_add_call(c, call_value, attribute_id.align, 1, ret_info->indirect.alignment); + // 13. Indirect, that is passing the result through an out parameter. + + // 13a. In the case of an already present error_var, we don't need to do a load here. + if (error_var) break; + + // 13b. Otherwise it will be contained in a be_value that is an address + // so we don't need to do anything more. + assert(result_value->kind == BE_ADDRESS); + + break; + case ABI_ARG_DIRECT_PAIR: + { + // 14. A direct pair, in this case the data is stored like { lo, hi } + // For example we might have { int, int, short, short, int }, + // this then gets bitcast to { long, long }, so we recover it by loading + // { long, long } into memory, then performing a bitcast to { int, int, short, short, int } + + // 14a. Generate the type. + LLVMTypeRef lo = llvm_abi_type(c, ret_info->direct_pair.lo); + LLVMTypeRef hi = llvm_abi_type(c, ret_info->direct_pair.hi); + LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); + + // 14b. Use the coerce method to go from the struct to the actual type + // by storing the { lo, hi } struct to memory, then loading it + // again using a bitcast. + llvm_emit_convert_value_from_coerced(c, result_value, struct_type, call_value, call_return_type); + break; + } + case ABI_ARG_EXPAND_COERCE: + { + // 15. Expand-coerce, this is similar to "direct pair", but looks like this: + // { lo, hi } set into { pad, lo, pad, hi } -> original type. + + // 15a. Create memory to hold the return type. + LLVMValueRef ret = llvm_emit_alloca_aligned(c, call_return_type, ""); + llvm_value_set_address_abi_aligned(result_value, ret, call_return_type); + + // 15b. "Convert" this return type pointer in memory to our coerce type which is { pad, lo, pad, hi } + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, ret_info); + LLVMValueRef coerce = LLVMBuildBitCast(c->builder, ret, coerce_type, ""); + + // 15d. Find the address to the low value + AlignSize alignment; + LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.lo_index, + type_abi_alignment(call_return_type), &alignment); + + // 15e. If there is only a single field, we simply store the value, + // so { lo } set into { pad, lo, pad } -> original type. + if (!abi_type_is_valid(ret_info->coerce_expand.hi)) + { + // Here we do a store to call -> lo (leaving the rest undefined) + llvm_store(c, lo, call_value, alignment); + break; + } + + // 15g. We can now extract { lo, hi } to lo_value and hi_value. + LLVMValueRef lo_value = LLVMBuildExtractValue(c->builder, call_value, 0, ""); + LLVMValueRef hi_value = LLVMBuildExtractValue(c->builder, call_value, 1, ""); + + // 15h. Store lo_value into the { pad, lo, pad, hi } struct. + llvm_store(c, lo, lo_value, alignment); + + // 15i. Calculate the address to the high value (like for the low in 15d. + LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.hi_index, + type_abi_alignment(call_return_type), &alignment); + + // 15h. Store the high value. + llvm_store(c, hi, hi_value, alignment); + + break; + } + case ABI_ARG_DIRECT: + llvm_value_set(result_value, call_value, call_return_type); + break; + case ABI_ARG_DIRECT_COERCE: + { + // 16. A direct coerce, this is basically "call result" bitcast return type. + + // 16a. Get the type of the return. + LLVMTypeRef coerce = llvm_get_coerce_type(c, ret_info); + + // 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done. + if (!coerce || coerce == llvm_get_type(c, call_return_type)) + { + // 16c. We just set as a value in be_value. + llvm_value_set(result_value, call_value, call_return_type); + break; + } + // 16c. We use a normal bitcast coerce. + assert(!abi_info_should_flatten(ret_info) && "Did not expect flattening on return types."); + llvm_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type); + break; + } + } + + // 17. Handle failables. + if (prototype->is_failable) + { + BEValue no_err; + + // 17a. If we used the error var as the indirect recipient, then that will hold the error. + // otherwise it's whatever value in be_value. + BEValue error_holder = *result_value; + if (error_var) + { + llvm_value_set_address_abi_aligned(&error_holder, c->error_var, type_anyerr); + } + // 17b. Generate a boolean switch. + llvm_value_set_bool(&no_err, llvm_emit_is_no_error(c, llvm_load_value(c, &error_holder))); + + // 17c. If we have an error var, or we aren't interested in the error variable + // - then it's straightforward. We just jump to the catch block. + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after.errcheck"); + if (error_var || !c->error_var) + { + llvm_emit_cond_br(c, &no_err, after_block, c->catch_block); + } + else + { + // 17d. If we have an error var we need to assign, then we need to + // first jump to an error block, where we do the copy. + LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); + llvm_emit_cond_br(c, &no_err, after_block, error_block); + llvm_emit_block(c, error_block); + llvm_store_value_aligned(c, c->error_var, result_value, 0); + // 17e. Then jump to the catch. + llvm_emit_br(c, c->catch_block); + } + + // 17f. Emit the "after" block. + llvm_emit_block(c, after_block); + + // 17g. If void, be_value contents should be skipped. + if (!prototype->ret_by_ref) + { + *result_value = (BEValue) { .type = type_void, .kind = BE_VALUE }; + return; + } + + // 17h. Assign the return param to be_value. + *result_value = synthetic_return_param; + return; + } +*/ + // 17i. The simple case here is where there is a normal return. + // In this case be_value already holds the result + return; +} + +void tilde_emit_expr(TbContext *c, TBEValue *value, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_CALL: + tilde_emit_call_expr(c, value, expr); + return; + default: + TODO + /* + case EXPR_DESIGNATOR: + case EXPR_POISONED: + case EXPR_COND: + case EXPR_TYPEINFO: + case EXPR_MACRO_EXPANSION: + case EXPR_CT_IDENT: + case EXPR_HASH_IDENT: + case EXPR_PLACEHOLDER: + case EXPR_CT_CALL: + case EXPR_FLATPATH: + case EXPR_VARIANTSWITCH: + UNREACHABLE + case EXPR_ARGV_TO_SUBARRAY: + llvm_emit_argv_to_subarray(c, value, expr); + return; + case EXPR_TRY_UNWRAP_CHAIN: + llvm_emit_try_unwrap_chain(c, value, expr); + return; + case EXPR_TRY_UNWRAP: + llvm_emit_try_unwrap(c, value, expr); + return; + case EXPR_CATCH_UNWRAP: + llvm_emit_catch_unwrap(c, value, expr); + return; + case EXPR_UNDEF: + // Should never reach this. + UNREACHABLE + case EXPR_PTR: + llvm_emit_ptr(c, value, expr); + return; + case EXPR_BUILTIN: + UNREACHABLE; + case EXPR_DECL: + llvm_emit_local_decl(c, expr->decl_expr); + llvm_value_set_decl_address(value, expr->decl_expr); + return; + case EXPR_SLICE_ASSIGN: + llvm_emit_slice_assign(c, value, expr); + return; + case EXPR_SLICE: + gencontext_emit_slice(c, value, expr); + return; + case EXPR_LEN: + llvm_emit_len(c, value, expr); + return; + case EXPR_FAILABLE: + llvm_emit_failable(c, value, expr); + return; + case EXPR_TRY: + llvm_emit_try_expr(c, value, expr); + return; + case EXPR_CATCH: + llvm_emit_catch_expr(c, value, expr); + return; + case EXPR_NOP: + llvm_value_set(value, NULL, type_void); + return; + case EXPR_OR_ERROR: + gencontext_emit_or_error(c, value, expr); + return; + case EXPR_MACRO_BLOCK: + llvm_emit_macro_block(c, value, expr); + return; + case EXPR_COMPOUND_LITERAL: + UNREACHABLE + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + llvm_emit_initializer_list_expr(c, value, expr); + return; + case EXPR_EXPR_BLOCK: + llvm_emit_expr_block(c, value, expr); + return; + case EXPR_SCOPED_EXPR: + gencontext_emit_scoped_expr(c, value, expr); + return; + case EXPR_UNARY: + gencontext_emit_unary_expr(c, value, expr); + return; + case EXPR_CONST: + llvm_emit_const_expr(c, value, expr); + return; + case EXPR_MACRO_BODY_EXPANSION: + llvm_emit_macro_body_expansion(c, value, expr); + return; + case EXPR_BITASSIGN: + llvm_emit_bitassign_expr(c, value, expr); + return; + case EXPR_BINARY: + llvm_emit_binary_expr(c, value, expr); + return; + case EXPR_TERNARY: + gencontext_emit_ternary_expr(c, value, expr); + return; + case EXPR_POST_UNARY: + llvm_emit_post_unary_expr(c, value, expr); + return; + case EXPR_FORCE_UNWRAP: + llvm_emit_force_unwrap_expr(c, value, expr); + return; + case EXPR_RETHROW: + llvm_emit_rethrow_expr(c, value, expr); + return; + case EXPR_TYPEOFANY: + llvm_emit_typeofany(c, value, expr); + return; + case EXPR_TYPEID: + case EXPR_GROUP: + // These are folded in the semantic analysis step. + UNREACHABLE + case EXPR_IDENTIFIER: + case EXPR_CONST_IDENTIFIER: + llvm_value_set_decl(value, expr->identifier_expr.decl); + return; + case EXPR_SUBSCRIPT: + case EXPR_SUBSCRIPT_ADDR: + gencontext_emit_subscript(c, value, expr); + return; + case EXPR_ACCESS: + gencontext_emit_access_addr(c, value, expr); + return; + case EXPR_EXPRESSION_LIST: + gencontext_emit_expression_list_expr(c, value, expr); + return; + case EXPR_CAST: + gencontext_emit_cast_expr(c, value, expr); + return; + case EXPR_BITACCESS: + llvm_emit_bitaccess(c, value, expr); + return;*/ + } + UNREACHABLE +} + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_codegen_instr.c b/src/compiler/tilde_codegen_instr.c new file mode 100644 index 000000000..87b8d1fb5 --- /dev/null +++ b/src/compiler/tilde_codegen_instr.c @@ -0,0 +1,43 @@ +#include "tilde_internal.h" + +#if TB_BACKEND + +void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align) +{ + ByteSize min = type_min_alignment(align, size); + TB_Register zero = tb_inst_iconst(c->f, TB_TYPE_I8, 0); + TB_Register elements = tb_inst_iconst(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(TbContext *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(TbContext *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 = tbtype(type); + return tb_inst_shl(c->f, int_type, reg, tb_inst_iconst(c->f, int_type, (unsigned)shift), type_is_signed(type) ? TB_ASSUME_NSW : TB_ASSUME_NUW); +} + +TB_Reg tilde_emit_lshr_fixed(TbContext *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 = tbtype(type); + return tb_inst_shr(c->f, int_type, reg, tb_inst_iconst(c->f, int_type, (unsigned)shift)); +} + +TB_Reg tilde_emit_alloca(TbContext *c, Type *type) +{ + return tb_inst_local(c->f, type_size(type), type_alloca_alignment(type)); +} + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_codegen_stmt.c b/src/compiler/tilde_codegen_stmt.c new file mode 100644 index 000000000..80c21d9ae --- /dev/null +++ b/src/compiler/tilde_codegen_stmt.c @@ -0,0 +1,122 @@ +// 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" + +#if TB_BACKEND + +void tilde_emit_compound_stmt(TbContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_COMPOUND_STMT); + AstId current = ast->compound_stmt.first_stmt; + while (current) + { + tilde_emit_stmt(context, ast_next(¤t)); + } + tilde_emit_defer(context, ast->compound_stmt.defer_list.start, ast->compound_stmt.defer_list.end); +} + +static void llvm_emit_ct_compound_stmt(TbContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_CT_COMPOUND_STMT); + AstId current = ast->compound_stmt.first_stmt; + while (current) + { + tilde_emit_stmt(context, ast_next(¤t)); + } +} + +TB_Reg tilde_emit_local_decl(TbContext *c, Decl *decl) +{ + // 1. Get the declaration and the TB type. + Type *var_type = type_lowering(type_no_fail(decl->type)); + + // 2. In the case we have a static variable, + // then we essentially treat this as a global. + if (decl->var.is_static) + { + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + scratch_buffer_append(".f"); + TB_InitializerID initializer = tb_initializer_create(c->module, type_size(type_anyerr), type_alloca_alignment(type_anyerr), 1); + decl->var.tb_failable_reg = tb_global_create(c->module, scratch_buffer_to_string(), initializer); + } + TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1); + decl->tb_register = tb_global_create(c->module, "tempglobal", static_initializer); + tilde_emit_global_initializer(c, decl); + return decl->tb_register; + } + tilde_emit_local_var_alloca(c, decl); + TB_Register reg = decl->tb_register; + Expr *init = decl->var.init_expr; + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->name); + scratch_buffer_append(".f"); + decl->var.tb_failable_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr)); + // Only clear out the result if the assignment isn't a failable. + } + + TBEValue value; + value_set_decl(&value, decl); + if (init) + { + // If we don't have undef, then make an assign. + if (init->expr_kind != EXPR_UNDEF) + { + tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg); + } + // TODO trap on undef in debug mode. + } + else + { + if (decl->var.tb_failable_reg) + { + tilde_store_zero(c, type_anyerr, decl->var.tb_failable_reg, type_abi_alignment(type_anyerr)); + } + tilde_store_value_zero(c, &value); + } + return reg; + +} + +void tilde_emit_defer(TbContext *c, AstId defer_start, AstId defer_end) +{ + if (defer_start == defer_end) return; + AstId defer = defer_start; + while (defer && defer != defer_end) + { + Ast *def = astptr(defer); + TB_Label exit = tb_inst_new_label_id(c->f); + Ast *body = def->defer_stmt.body; + def->defer_stmt.codegen.exit_val = exit; + tilde_emit_stmt(c, body); + tb_inst_goto(c->f, exit); + tb_inst_label(c->f, exit); + defer = def->defer_stmt.prev_defer; + } +} + + + +void tilde_emit_stmt(TbContext *c, Ast *ast) +{ + switch (ast->ast_kind) + { + case AST_DECLARE_STMT: + tilde_emit_local_decl(c, ast->declare_stmt); + break; + case AST_RETURN_STMT: + //TbContext_emit_return(c, ast); + tb_inst_ret(c->f, TB_NULL_REG); + break; + default: + break; + } +} + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_codegen_storeload.c b/src/compiler/tilde_codegen_storeload.c new file mode 100644 index 000000000..52672272d --- /dev/null +++ b/src/compiler/tilde_codegen_storeload.c @@ -0,0 +1,105 @@ +// 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" + +#if TB_BACKEND + +void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment) +{ + tilde_store(c, tbtype(type), addr, value, alignment); +} + +void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value) +{ + tilde_store(c, tbtype(type), addr, value, type_abi_alignment(type)); +} + +void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value) +{ + assert(value_is_addr(destination)); + tilde_store(c, tbtype(destination->type), destination->reg, value, destination->alignment); +} + + +void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value) +{ + assert(!decl->is_value); + tilde_store(c, tbtype(decl->type), decl->tb_register, value, decl->alignment); +} + +void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment) +{ + assert(alignment); + value_fold_failable(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_FAILABLE: + UNREACHABLE + case TBE_ADDRESS: + { + ByteSize size = type_size(value->type); + TB_Reg copy_size = tb_inst_iconst(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_value(TbContext *c, TBEValue *dst, TBEValue *value) +{ + assert(dst->kind == TBE_ADDRESS); + tilde_store_value_aligned(c, dst->reg, value, dst->alignment); +} + +TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer) +{ + return tilde_load(c, tbtype(type), pointer, type_abi_alignment(type)); +} + +TB_Reg tilde_load_value(TbContext *c, TBEValue *value) +{ + value_fold_failable(c, value); + switch (value->kind) + { + case TBE_VALUE: + return value->reg; + case TBE_ADDRESS_FAILABLE: + UNREACHABLE + case TBE_ADDRESS: + return tilde_load(c, tbtype(value->type), value->reg, value->alignment); + } + UNREACHABLE +} + +void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment) +{ + type = type_lowering(type); + if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER) + { + tilde_store_raw(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_iconst(c->f, TB_TYPE_I8, 0); + TB_Register elements = tb_inst_iconst(c->f, tbtype(type_usize), size); + tb_inst_memset(c->f, addr, zero, elements, min); +} + +void tilde_store_value_zero(TbContext *c, TBEValue *to) +{ + assert(to->kind == TBE_ADDRESS); + tilde_store_zero(c, to->type, to->reg, to->alignment); +} + +#endif diff --git a/src/compiler/tilde_codegen_value.c b/src/compiler/tilde_codegen_value.c new file mode 100644 index 000000000..c492b8b5b --- /dev/null +++ b/src/compiler/tilde_codegen_value.c @@ -0,0 +1,102 @@ +#include "tilde_internal.h" + +#if TB_BACKEND + +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_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(TbContext *c, TBEValue *value) +{ + value_fold_failable(c, value); + if (value->kind == TBE_ADDRESS) return; + if (!c->f) + { + TODO + // TB_Register val = value_rvalue_get(c, value); + // TODO check whether new names must be added + TB_InitializerID initializer_id = tb_initializer_create(c->module, type_size(value->type), + type_alloca_alignment(value->type), 0); + tb_global_create(c->module, ".taddr", initializer_id); + // TODO set linkage + /* + llvm_set_private_linkage(ref); + llvm_emit_bitcast(c, ref, type_get_ptr(value->type)); + llvm_value_set_address(value, ref, value->type);*/ + } + else + { + TODO + /* + * + tilde_emit_alloca_aligned(c, value->type); + TB_Register reg = llvm_emit_alloca_aligned(c, value->type, "taddr"); + tilde_store_bevalue_dest_aligned(c, temp, value); + llvm_value_set_address(value, temp, value->type);*/ + } +} + +void value_rvalue(TbContext *c, TBEValue *value) +{ + if (value->kind == TBE_VALUE) return; + value_fold_failable(c, value); + value->reg = tilde_load_value(c, value); + value->kind = TBE_VALUE; +} + +void value_fold_failable(TbContext *c, TBEValue *value) +{ + if (value->kind != TBE_ADDRESS_FAILABLE) return; + TB_Label after_block = tb_inst_new_label_id(c->f); + TB_Reg error_val = tilde_load_abi_alignment(c, type_anyerr, value->failable); + TB_Reg comp = tilde_emit_is_no_error(c, error_val); + assert(c->catch_block); + if (c->error_var) + { + TB_Label err_block = tb_inst_new_label_id(c->f); + tb_inst_if(c->f, comp, after_block, err_block); + tb_inst_label(c->f, err_block); + tilde_store_raw_abi_alignment(c, type_anyerr, c->error_var, error_val); + tb_inst_goto(c->f, c->catch_block); + } + else + { + tb_inst_if(c->f, comp, after_block, c->catch_block); + } + tb_inst_label(c->f, after_block); + value->kind = TBE_ADDRESS; +} + +void value_set_decl(TBEValue *value, Decl *decl) +{ + decl = decl_flatten(decl); + value_set_address(value, decl_reg(decl), decl->type, decl->alignment); + + if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) + { + value->kind = TBE_ADDRESS_FAILABLE; + value->failable = decl->var.tb_failable_reg; + } +} + + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_internal.h b/src/compiler/tilde_internal.h new file mode 100644 index 000000000..2b1790097 --- /dev/null +++ b/src/compiler/tilde_internal.h @@ -0,0 +1,125 @@ +#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. + +#if TB_BACKEND + +#include + +typedef struct +{ + Module *code_module; + const char *object_filename; + + TB_Module *module; + TB_FeatureSet features; + + Decl *current_func_decl; + TB_Function *f; + TB_Register error_var; + TB_Label catch_block; +} TbContext; + +typedef enum +{ + TBE_VALUE, + TBE_ADDRESS, + TBE_ADDRESS_FAILABLE, +} TBBackendValueKind; + +typedef struct +{ + TBBackendValueKind kind: 5; + AlignSize alignment; + + Type *type; // Should never be a distinct or canonical type. + + TB_Register reg; + TB_Register failable; // TODO what even is a failable? +} TBEValue; + +TB_DataType tbtype(Type *type); + +#define PUSH_ERROR() \ +TB_Label _old_catch = c->catch_block; \ +TB_Reg _old_error_var = c->error_var +#define POP_ERROR() \ +c->catch_block = _old_catch; \ +c->error_var = _old_error_var + +// -- store --- +static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment); +void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment); +void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value); +void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value); +void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment); +void tilde_store_value(TbContext *c, TBEValue *dst, TBEValue *value); +void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value); +void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment); +void tilde_store_value_zero(TbContext *c, TBEValue *to); + +// -- load --- +static inline TB_Reg tilde_load(TbContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment); +TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer); +TB_Reg tilde_load_value(TbContext *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(TBEValue *value, Decl *decl); +void value_addr(TbContext *c, TBEValue *value); + +static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_FAILABLE; } +void value_fold_failable(TbContext *c, TBEValue *value); +void value_rvalue(TbContext *c, TBEValue *value); + + +TB_Register tilde_get_zero(TbContext *c, Type *type); + +// -- instructions -- +void tilde_emit_cond_br(TbContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block); +TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift); +TB_Reg tilde_emit_alloca(TbContext *c, Type *type); + +// -- stmt --- +void tilde_emit_stmt(TbContext *c, Ast *ast); +void tilde_emit_defer(TbContext *c, AstId defer_start, AstId defer_end); + +// -- general --- +TB_Register tilde_emit_is_no_error(TbContext *c, TB_Reg reg); +void tilde_emit_global_initializer(TbContext *c, Decl *decl); +void tilde_emit_local_var_alloca(TbContext *c, Decl *decl); +void tilde_emit_and_set_decl_alloca(TbContext *c, Decl *decl); + +// -- expr -- +void tilde_emit_expr(TbContext *c, TBEValue *result, Expr *expr); +TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable); + +void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align); + + +static inline void tilde_store(TbContext *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(TbContext *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; +} + +#endif // TB_BACKEND \ No newline at end of file