diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index 48928c4eb..198112228 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -47,12 +47,12 @@ typedef struct ABIArgInfo *c_abi_classify_return_type_default(Type *type); ABIArgInfo *c_abi_classify_argument_type_default(Type *type); -void c_abi_func_create_win64(FunctionSignature *signature); -void c_abi_func_create_x86(FunctionSignature *signature); -void c_abi_func_create_x64(FunctionSignature *signature); -void c_abi_func_create_aarch64(FunctionSignature *signature); -void c_abi_func_create_riscv(FunctionSignature *signature); -void c_abi_func_create_wasm(FunctionSignature *signature); +void c_abi_func_create_win64(FunctionPrototype *prototype); +void c_abi_func_create_x86(FunctionPrototype *prototype); +void c_abi_func_create_x64(FunctionPrototype *prototype); +void c_abi_func_create_aarch64(FunctionPrototype *prototype); +void c_abi_func_create_riscv(FunctionPrototype *prototype); +void c_abi_func_create_wasm(FunctionPrototype *prototype); static inline AbiType abi_type_get(Type *type) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index f70a3e5fb..4942796f3 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -391,6 +391,19 @@ void global_context_add_type(Type *type) vec_add(global_context.type, type); } +const char *get_object_extension(void) +{ + switch (active_target.arch_os_target) + { + case X64_WINDOWS: + case X86_WINDOWS: + case X64_WINDOWS_GNU: + return ".obj"; + default: + return ".o"; + } +} + Module *global_context_find_module(const char *name) { return stable_get(&global_context.modules, name); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index fbb2955d4..d974940b6 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -252,8 +252,7 @@ typedef struct typedef struct { - struct FunctionSignature_ *signature; - const char *mangled_function_signature; + struct FunctionPrototype_ *prototype; } TypeFunc; struct Type_ @@ -371,8 +370,6 @@ typedef struct VarDecl_ void *backend_debug_ref; union { - // Param - struct ABIArgInfo_ *abi_info; // Variable void *failable_ref; }; @@ -438,15 +435,13 @@ typedef enum VARIADIC_RAW, } Variadic; + typedef struct FunctionSignature_ { - CallABI call_abi : 4; Variadic variadic : 3; bool has_default : 1; bool use_win64 : 1; - TypeInfo *rtype; - struct ABIArgInfo_ *ret_abi_info; - struct ABIArgInfo_ *failable_abi_info; + TypeInfo *returntype; Decl** params; } FunctionSignature; @@ -1553,6 +1548,22 @@ typedef struct ABIArgInfo_ } ABIArgInfo; +typedef struct FunctionPrototype_ +{ + CallABI call_abi : 4; + Variadic variadic : 3; + bool use_win64 : 1; + bool is_failable : 1; + bool ret_by_ref : 1; + Type *rtype; + Type **params; + Type *ret_by_ref_type; + Type *abi_ret_type; + ABIArgInfo *ret_abi_info; + ABIArgInfo *ret_by_ref_abi_info; + ABIArgInfo **abi_args; +} FunctionPrototype; + extern GlobalContext global_context; extern BuildTarget active_target; extern Ast *poisoned_ast; @@ -1809,6 +1820,7 @@ void global_context_add_type(Type *type); Module *compiler_find_or_create_module(Path *module_name, TokenId *parameters, bool is_private); Module *global_context_find_module(const char *name); +const char *get_object_extension(void); CompilationUnit * unit_create(File *file); void unit_register_global_decl(CompilationUnit *unit, Decl *decl); @@ -1941,6 +1953,7 @@ static inline TokenType token_type(Token token) { return (TokenType)toktypeptr(t #define TOKLEN(T) TOKLOC(T)->length Decl *module_find_symbol(Module *module, const char *symbol); +const char *module_create_object_file_name(Module *module); bool parse_file(File *file); Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span); @@ -1952,6 +1965,8 @@ Path *path_find_parent_path(Path *path); #define SEMA_PREV(_node, ...) sema_prev_at_range3((_node)->span, __VA_ARGS__) #define SEMA_TOKID_PREV(_tok_id, ...) sema_prev_at_range3(source_span_from_token_id(_tok_id), __VA_ARGS__) +#define TABLE_MAX_LOAD 0.5 + void sema_analysis_run(void); bool sema_failed_cast(Expr *expr, Type *from, Type *to); @@ -2051,7 +2066,7 @@ static inline TokenType advance_token(TokenId *token) AlignSize type_abi_alignment(Type *type); AlignSize type_alloca_alignment(Type *type); -void type_append_signature_name(Type *type, char *dst, size_t *offset); + static inline bool type_convert_will_trunc(Type *destination, Type *source); bool type_is_comparable(Type *type); bool type_is_ordered(Type *type); @@ -2074,11 +2089,12 @@ Type *type_get_vector_bool(Type *original_type); Type *type_int_signed_by_bitsize(unsigned bitsize); Type *type_int_unsigned_by_bitsize(unsigned bytesize); void type_init_cint(void); +void type_func_prototype_init(uint32_t capacity); static inline bool type_is_builtin(TypeKind kind); bool type_is_abi_aggregate(Type *type); static inline bool type_is_float(Type *type); bool type_is_int128(Type *type); -Type *type_find_function_type(FunctionSignature *signature); +Type *type_get_func(FunctionSignature *signature, CallABI abi); static inline bool type_is_integer(Type *type); static inline bool type_is_integer_unsigned(Type *type); static inline bool type_is_integer_signed(Type *type); @@ -2501,18 +2517,6 @@ void platform_compiler(const char **files, unsigned file_count, const char* flag #define ASSIGN_TYPE_ELSE(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = TEMP(_type) #define ASSIGN_DECL_ELSE(_assign, _decl_stmt, _res) Decl* TEMP(_decl) = (_decl_stmt); if (!decl_ok(TEMP(_decl))) return _res; _assign = TEMP(_decl) -static inline Type *abi_rtype(FunctionSignature *signature) -{ - Type *type = signature->rtype->type; - if (type->type_kind == TYPE_FAILABLE) return type->failable; - return type; -} - -static inline Type *abi_returntype(FunctionSignature *signature) -{ - Type *type = signature->rtype->type; - return type->type_kind == TYPE_FAILABLE ? type_anyerr : type; -} static inline void global_context_clear_errors(void) { diff --git a/src/compiler/copying.c b/src/compiler/copying.c index d200eb548..fa2e7c043 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -440,9 +440,7 @@ TypeInfo *copy_type_info(TypeInfo *source) static void copy_function_signature_deep(FunctionSignature *signature) { MACRO_COPY_DECL_LIST(signature->params); - MACRO_COPY_TYPE(signature->rtype); - assert(!signature->failable_abi_info); - assert(!signature->ret_abi_info); + MACRO_COPY_TYPE(signature->returntype); } void copy_decl_type(Decl *decl) { diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index f12f6f55e..90670fe93 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -196,27 +196,27 @@ ABIArgInfo *abi_arg_new_expand_padded(Type *padding) } -void c_abi_func_create(FunctionSignature *signature) +void c_abi_func_create(FunctionPrototype *proto) { switch (platform_target.abi) { case ABI_X64: - c_abi_func_create_x64(signature); + c_abi_func_create_x64(proto); break; case ABI_X86: - c_abi_func_create_x86(signature); + c_abi_func_create_x86(proto); break; case ABI_WIN64: - c_abi_func_create_win64(signature); + c_abi_func_create_win64(proto); break; case ABI_AARCH64: - c_abi_func_create_aarch64(signature); + c_abi_func_create_aarch64(proto); break; case ABI_RISCV: - c_abi_func_create_riscv(signature); + c_abi_func_create_riscv(proto); break; case ABI_WASM: - c_abi_func_create_wasm(signature); + c_abi_func_create_wasm(proto); break; default: FATAL_ERROR("Unsupported ABI"); diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index 6ab551938..1e38fd76f 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -140,25 +140,24 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) } -void c_abi_func_create_aarch64(FunctionSignature *signature) +void c_abi_func_create_aarch64(FunctionPrototype *prototype) { - Type *rtype = abi_rtype(signature); - if (IS_FAILABLE(signature->rtype)) + + prototype->ret_abi_info = aarch64_classify_return_type(prototype->abi_ret_type, prototype->variadic == VARIADIC_RAW); + if (prototype->ret_by_ref) { - signature->failable_abi_info = aarch64_classify_return_type(rtype, signature->variadic == VARIADIC_RAW); - if (rtype->type_kind != TYPE_VOID) - { - signature->ret_abi_info = aarch64_classify_argument_type(type_get_ptr(type_lowering(rtype))); - } - } - else - { - signature->ret_abi_info = aarch64_classify_return_type(rtype, signature->variadic == VARIADIC_RAW); - } - Decl **params = signature->params; - VECEACH(params, i) - { - params[i]->var.abi_info = aarch64_classify_argument_type(params[i]->type); + prototype->ret_by_ref_abi_info = aarch64_classify_argument_type(type_get_ptr(type_flatten(prototype->ret_by_ref_type))); } + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) + { + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = aarch64_classify_argument_type(params[i]); + } + prototype->abi_args = args; + } } diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index bf6c8b2f1..4a1643355 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -248,33 +248,22 @@ static ABIArgInfo *riscv_classify_return(Type *return_type) return riscv_classify_argument_type(return_type, true, &arg_gpr_left, &arg_fpr_left); } -void c_abi_func_create_riscv(FunctionSignature *signature) +void c_abi_func_create_riscv(FunctionPrototype *prototype) { // Registers unsigned gpr = 8; unsigned fpr = 8; - bool failable = IS_FAILABLE(signature->rtype); - Type *rtype = abi_rtype(signature); - Type *return_type = abi_returntype(signature); - return_type = type_lowering(return_type); - ABIArgInfo *return_abi = riscv_classify_return(return_type); - if (failable) - { - signature->failable_abi_info = return_abi; - } - else - { - signature->ret_abi_info = return_abi; - } + Type *ret_type = type_lowering(prototype->abi_ret_type); + ABIArgInfo *ret_abi = prototype->ret_abi_info = riscv_classify_return(prototype->abi_ret_type); // IsRetIndirect is true if classifyArgumentType indicated the value should // be passed indirect, or if the type size is a scalar greater than 2*XLen // and not a complex type with elements <= FLen. e.g. fp128 is passed direct // in LLVM IR, relying on the backend lowering code to rewrite the argument // list and pass indirectly on RV32. - bool is_ret_indirect = abi_arg_is_indirect(return_abi); - if (type_is_scalar(return_type) && type_size(return_type) > 2 * platform_target.riscv.xlen) + bool is_ret_indirect = abi_arg_is_indirect(ret_abi); + if (type_is_scalar(ret_type) && type_size(ret_type) > 2 * platform_target.riscv.xlen) { // Normal scalar > 2 * XLen, e.g. f128 on RV32 is_ret_indirect = true; @@ -288,16 +277,22 @@ void c_abi_func_create_riscv(FunctionSignature *signature) unsigned arg_fprs_left = platform_target.riscv.flen ? fpr : 0; // If we have a failable, then the return type is a parameter. - if (IS_FAILABLE(signature->rtype) && rtype != type_void) + if (prototype->ret_by_ref) { - signature->ret_abi_info = riscv_classify_argument_type(type_get_ptr(type_lowering(rtype)), - true, &arg_gprs_left, &arg_fprs_left); + prototype->ret_by_ref = riscv_classify_argument_type(type_get_ptr(prototype->ret_by_ref_type), + true, &arg_gprs_left, &arg_fprs_left); } - Decl **params = signature->params; - VECEACH(params, i) + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { bool is_fixed = true; - params[i]->var.abi_info = riscv_classify_argument_type(type_lowering(params[i]->type), is_fixed, &arg_gprs_left, &arg_fprs_left); + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = riscv_classify_argument_type(type_lowering(params[i]), is_fixed, &arg_gprs_left, &arg_fprs_left); + } + prototype->abi_args = args; } } diff --git a/src/compiler/llvm_codegen_c_abi_wasm.c b/src/compiler/llvm_codegen_c_abi_wasm.c index 30735bfdb..d32f217f8 100644 --- a/src/compiler/llvm_codegen_c_abi_wasm.c +++ b/src/compiler/llvm_codegen_c_abi_wasm.c @@ -51,28 +51,23 @@ static ABIArgInfo *wasm_classify_return(Type *type) return c_abi_classify_return_type_default(type); } -void c_abi_func_create_wasm(FunctionSignature *signature) +void c_abi_func_create_wasm(FunctionPrototype *prototype) { - Type *rtype = abi_rtype(signature); - Type *return_type = abi_returntype(signature); - if (IS_FAILABLE(signature->rtype)) + prototype->ret_abi_info = wasm_classify_return(type_lowering(prototype->abi_ret_type)); + if (prototype->ret_by_ref) { - signature->failable_abi_info = wasm_classify_return(type_lowering(type_anyerr)); - } - else - { - signature->ret_abi_info = wasm_classify_return(type_lowering(rtype)); + prototype->ret_by_ref_abi_info = wasm_classify_argument_type(type_get_ptr(prototype->ret_by_ref_type)); } - // If we have a failable, then the return type is a parameter. - if (IS_FAILABLE(signature->rtype) && rtype->type_kind != TYPE_VOID) + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { - signature->ret_abi_info = wasm_classify_argument_type(type_get_ptr(type_lowering(rtype))); - } - - Decl **params = signature->params; - VECEACH(params, i) - { - params[i]->var.abi_info = wasm_classify_argument_type(type_lowering(params[i]->type)); + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = wasm_classify_argument_type(type_lowering(params[i])); + } + prototype->abi_args = args; } } \ No newline at end of file diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index 71c46acda..12212a1e9 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -106,37 +106,41 @@ ABIArgInfo *win64_reclassify_hva_arg(Regs *regs, Type *type, ABIArgInfo *info) return info; } -void win64_vector_call_args(Regs *regs, FunctionSignature *signature, bool is_vector, bool is_reg) +void win64_vector_call_args(Regs *regs, FunctionPrototype *prototype, bool is_vector, bool is_reg) { static const unsigned max_param_vector_calls_as_reg = 6; unsigned count = 0; - Decl **params = signature->params; - VECEACH(params, i) + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { - Decl *param = params[i]; - if (count < max_param_vector_calls_as_reg) + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) { - param->var.abi_info = win64_classify(regs, param->type, false, is_vector, is_reg); + Type *type = params[i]; + if (count < max_param_vector_calls_as_reg) + { + args[i] = win64_classify(regs, type, false, is_vector, is_reg); + } + else + { + // Cannot be passed in registers pretend no registers. + unsigned float_regs = regs->float_regs; + regs->float_regs = 0; + args[i] = win64_classify(regs, type, false, is_vector, is_reg); + regs->float_regs = float_regs; + } + count++; } - else + for (unsigned i = 0; i < param_count; i++) { - // Cannot be passed in registers pretend no registers. - unsigned float_regs = regs->float_regs; - regs->float_regs = 0; - param->var.abi_info = win64_classify(regs, param->type, false, is_vector, is_reg); - regs->float_regs = float_regs; + args[i] = win64_reclassify_hva_arg(regs, params[i], args[i]); } - count++; + prototype->abi_args = args; } - VECEACH(params, i) - { - Decl *param = params[i]; - param->var.abi_info = win64_reclassify_hva_arg(regs, param->type, param->var.abi_info); - } - } -void c_abi_func_create_win64(FunctionSignature *signature) +void c_abi_func_create_win64(FunctionPrototype *prototype) { // allow calling sysv? @@ -144,7 +148,7 @@ void c_abi_func_create_win64(FunctionSignature *signature) Regs regs = { 0, 0 }; bool is_reg_call = false; bool is_vector_call = false; - switch (signature->call_abi) + switch (prototype->call_abi) { case CALL_X86_VECTOR: regs.float_regs = 4; @@ -159,22 +163,14 @@ void c_abi_func_create_win64(FunctionSignature *signature) break; } - Type *rtype = abi_rtype(signature); - if (IS_FAILABLE(signature->rtype)) + prototype->ret_abi_info = win64_classify(®s, prototype->abi_ret_type, true, is_vector_call, is_reg_call); + if (prototype->ret_by_ref) { - signature->failable_abi_info = win64_classify(®s, type_anyerr, true, is_vector_call, is_reg_call); - if (rtype->type_kind != TYPE_VOID) - { - signature->ret_abi_info = win64_classify(®s, type_get_ptr(type_lowering(rtype)), false, is_vector_call, is_reg_call); - } - } - else - { - signature->ret_abi_info = win64_classify(®s, rtype, true, is_vector_call, is_reg_call); + prototype->ret_by_ref_abi_info = win64_classify(®s, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), false, is_vector_call, is_reg_call); } // Set up parameter registers. - switch (signature->call_abi) + switch (prototype->call_abi) { case CALL_X86_VECTOR: regs.float_regs = 6; @@ -190,12 +186,19 @@ void c_abi_func_create_win64(FunctionSignature *signature) } if (is_vector_call) { - win64_vector_call_args(®s, signature, is_vector_call, is_reg_call); + win64_vector_call_args(®s, prototype, is_vector_call, is_reg_call); return; } - Decl **params = signature->params; - VECEACH(params, i) + + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { - params[i]->var.abi_info = win64_classify(®s, params[i]->type, false, is_vector_call, is_reg_call); + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = win64_classify(®s, params[i], false, is_vector_call, is_reg_call); + } + prototype->abi_args = args; } } \ No newline at end of file diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index fbf7c52f4..2e92b8cc9 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -894,45 +894,37 @@ static ABIArgInfo *x64_classify_parameter(Type *type, Registers *available_regis } -void c_abi_func_create_x64(FunctionSignature *signature) +void c_abi_func_create_x64(FunctionPrototype *prototype) { - if (signature->use_win64) + if (prototype->use_win64) { - return c_abi_func_create_win64(signature); + return c_abi_func_create_win64(prototype); } // TODO 32 bit pointers - bool is_regcall = signature->call_abi == CALL_X86_REG; + bool is_regcall = prototype->call_abi == CALL_X86_REG; Registers available_registers = { .int_registers = is_regcall ? 11 : 16, .sse_registers = is_regcall ? 16 : 8 }; - Type *rtype = abi_rtype(signature); - if (IS_FAILABLE(signature->rtype)) + prototype->ret_abi_info = x64_classify_return_type(prototype->abi_ret_type, &available_registers, is_regcall); + if (abi_arg_is_indirect(prototype->ret_abi_info)) available_registers.int_registers--; + + if (prototype->ret_by_ref) { - signature->failable_abi_info = x64_classify_return_type(type_anyerr, &available_registers, is_regcall); - if (abi_arg_is_indirect(signature->failable_abi_info)) - { - available_registers.int_registers--; - } - if (rtype->type_kind != TYPE_VOID) - { - signature->ret_abi_info = x64_classify_parameter(type_get_ptr(type_lowering(rtype)), &available_registers, is_regcall, NAMED); - } - } - else - { - signature->ret_abi_info = x64_classify_return_type(rtype, &available_registers, is_regcall); - if (abi_arg_is_indirect(signature->ret_abi_info)) - { - available_registers.int_registers--; - } + prototype->ret_by_ref_abi_info = x64_classify_parameter(type_get_ptr(type_lowering(prototype->ret_by_ref_type)), &available_registers, is_regcall, NAMED); } - Decl **params = signature->params; - VECEACH(params, i) + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { - params[i]->var.abi_info = x64_classify_parameter(params[i]->type, &available_registers, is_regcall, NAMED); + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = x64_classify_parameter(params[i], &available_registers, is_regcall, NAMED); + } + prototype->abi_args = args; } } diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 6926964a1..868f7c41c 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -600,7 +600,7 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type) UNREACHABLE } -void c_abi_func_create_x86(FunctionSignature *signature) +void c_abi_func_create_x86(FunctionPrototype *prototype) { // 1. Calculate the registers we have available // Normal: 0 / 0 (3 on win32 struct ABI) @@ -608,7 +608,7 @@ void c_abi_func_create_x86(FunctionSignature *signature) // Vector: 2 / 6 // Fast: 2 / 3 Regs regs = { 0, 0 }; - switch (signature->call_abi) + switch (prototype->call_abi) { case CALL_C: if (platform_target.x86.is_win32_float_struct_abi) @@ -641,18 +641,10 @@ void c_abi_func_create_x86(FunctionSignature *signature) // 4. Classify the return type. In the case of failable, we need to classify the failable itself as the // return type. - Type *rtype = abi_rtype(signature); - if (IS_FAILABLE(signature->rtype)) + prototype->ret_abi_info = x86_classify_return(prototype->call_abi, ®s, prototype->abi_ret_type); + if (prototype->ret_by_ref) { - signature->failable_abi_info = x86_classify_return(signature->call_abi, ®s, type_anyerr); - if (rtype->type_kind != TYPE_VOID) - { - signature->ret_abi_info = x86_classify_argument(signature->call_abi, ®s, type_get_ptr(type_lowering(rtype))); - } - } - else - { - signature->ret_abi_info = x86_classify_return(signature->call_abi, ®s, rtype); + prototype->ret_by_ref_abi_info = x86_classify_argument(prototype->call_abi, ®s, type_get_ptr(type_lowering(prototype->ret_by_ref_type))); } /* @@ -666,16 +658,22 @@ void c_abi_func_create_x86(FunctionSignature *signature) runVectorCallFirstPass(FI, State); */ - if (signature->call_abi == CALL_X86_VECTOR) + if (prototype->call_abi == CALL_X86_VECTOR) { FATAL_ERROR("X86 vector call not supported"); } else { - Decl **params = signature->params; - VECEACH(params, i) + Type **params = prototype->params; + unsigned param_count = vec_size(prototype->params); + if (param_count) { - params[i]->var.abi_info = x86_classify_argument(signature->call_abi, ®s, params[i]->type); + ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count); + for (unsigned i = 0; i < param_count; i++) + { + args[i] = x86_classify_argument(prototype->call_abi, ®s, params[i]); + } + prototype->abi_args = args; } } } diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 648dc7045..62278e385 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -468,13 +468,13 @@ static LLVMMetadataRef llvm_debug_vector_type(GenContext *c, Type *type) static LLVMMetadataRef llvm_debug_func_type(GenContext *c, Type *type) { - FunctionSignature *sig = type->func.signature; + FunctionPrototype *prototype = type->func.prototype; static LLVMMetadataRef *buffer = NULL; vec_resize(buffer, 0); - vec_add(buffer, llvm_get_debug_type(c, sig->rtype->type)); - VECEACH(sig->params, i) + vec_add(buffer, llvm_get_debug_type(c, prototype->rtype)); + VECEACH(prototype->params, i) { - vec_add(buffer, llvm_get_debug_type(c, sig->params[i]->type)); + vec_add(buffer, llvm_get_debug_type(c, prototype->params[i])); } return LLVMDIBuilderCreateSubroutineType(c->debug.builder, c->debug.file, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 8ac6e4901..9b03c979b 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4273,13 +4273,13 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_builtin_call(c, result_value, expr); return; } - FunctionSignature *signature; LLVMTypeRef func_type; LLVMValueRef func; BEValue temp_value; bool always_inline = false; + FunctionPrototype *prototype; // 1. Call through a pointer. if (expr->call_expr.is_pointer_call) { @@ -4289,7 +4289,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) Type *type = function->type->canonical->pointer; // 1b. Find the type signature using the underlying pointer. - signature = type->func.signature; + prototype = type->func.prototype; // 1c. Evaluate the pointer expression. BEValue func_value; @@ -4309,25 +4309,23 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) always_inline = function_decl->func_decl.attr_inline; // 2b. Set signature, function and function type - signature = &function_decl->func_decl.function_signature; + prototype = function_decl->type->func.prototype; func = function_decl->backend_ref; assert(func); func_type = llvm_get_type(c, function_decl->type); } LLVMValueRef *values = NULL; - - // 4. Prepare the return abi data. - ABIArgInfo *ret_info = signature->ret_abi_info; - Type *return_type = abi_returntype(signature); - bool is_failable = IS_FAILABLE(signature->rtype); + 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. LLVMValueRef error_var = NULL; - if (is_failable) - { - ret_info = signature->failable_abi_info; - } *result_value = (BEValue){ .kind = BE_VALUE, .value = NULL }; // 6. Generate data for the return value. @@ -4335,16 +4333,16 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) { case ABI_ARG_INDIRECT: // 6a. We can use the stored error var if there is no redirect. - if (is_failable && c->error_var && !ret_info->attributes.realign) + 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(return_type)); + 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, return_type), alignment, "sretparam"), return_type, 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); // 6c. Add the pointer to the list of arguments. vec_add(values, result_value->value); @@ -4363,13 +4361,13 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 7. We might have a failable indirect return and a normal return. // In this case we need to add it by hand. BEValue synthetic_return_param = { 0 }; - if (is_failable && signature->ret_abi_info) + if (prototype->ret_by_ref) { // 7b. Create the address to hold the return. - Type *actual_return_type = type_lowering(signature->rtype->type); + Type *actual_return_type = type_lowering(prototype->ret_by_ref_type); llvm_value_set(&synthetic_return_param, llvm_emit_alloca_aligned(c, actual_return_type, "retparam"), type_get_ptr(actual_return_type)); // 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list) - llvm_emit_parameter(c, &values, signature->ret_abi_info, &synthetic_return_param, synthetic_return_param.type); + 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); } @@ -4377,8 +4375,6 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 8. Add all other arguments. unsigned arguments = vec_size(expr->call_expr.arguments); - unsigned non_variadic_params = vec_size(signature->params); - if (signature->variadic == VARIADIC_TYPED) non_variadic_params--; assert(arguments >= non_variadic_params); for (unsigned i = 0; i < non_variadic_params; i++) { @@ -4387,20 +4383,21 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_expr(c, &temp_value, arg_expr); // 8b. Emit the parameter according to ABI rules. - Decl *param = signature->params[i]; - ABIArgInfo *info = param->var.abi_info; - llvm_emit_parameter(c, &values, info, &temp_value, param->type); + Type *param = params[i]; + ABIArgInfo *info = abi_args[i]; + llvm_emit_parameter(c, &values, info, &temp_value, param); } // 9. Typed varargs - if (signature->variadic == VARIADIC_TYPED) + if (prototype->variadic == VARIADIC_TYPED) { REMINDER("All varargs should be called with non-alias!"); - Decl *vararg_param = signature->params[non_variadic_params]; + Type *vararg_param = params[non_variadic_params]; + ABIArgInfo *vararg_info = abi_args[non_variadic_params]; BEValue subarray; - llvm_value_set_address(&subarray, llvm_emit_alloca_aligned(c, vararg_param->type, "vararg"), vararg_param->type); + llvm_value_set_address(&subarray, llvm_emit_alloca_aligned(c, vararg_param, "vararg"), vararg_param); // 9a. Special case, empty argument if (arguments == non_variadic_params) @@ -4418,8 +4415,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) else { // 9b. Otherwise we also need to allocate memory for the arguments: - Type *pointee_type = vararg_param->type->array.base; - LLVMTypeRef llvm_pointee = llvm_get_type(c, pointee_type); + Type *pointee_type = vararg_param->array.base; Type *array = type_get_array(pointee_type, arguments - non_variadic_params); LLVMTypeRef llvm_array_type = llvm_get_type(c, array); AlignSize alignment = type_alloca_alignment(array); @@ -4440,13 +4436,12 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) 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)); } - ABIArgInfo *info = vararg_param->var.abi_info; - llvm_emit_parameter(c, &values, info, &subarray, vararg_param->type); + llvm_emit_parameter(c, &values, vararg_info, &subarray, vararg_param); } else { // 9. Emit varargs. - for (unsigned i = vec_size(signature->params); i < arguments; i++) + for (unsigned i = param_count; i < arguments; i++) { Expr *arg_expr = expr->call_expr.arguments[i]; llvm_emit_expr(c, &temp_value, arg_expr); @@ -4459,9 +4454,9 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params) EMIT_LOC(c, expr); LLVMValueRef call_value = LLVMBuildCall2(c->builder, func_type, func, values, vec_size(values), ""); - if (signature->call_abi) + if (prototype->call_abi) { - LLVMSetInstructionCallConv(call_value, llvm_call_convention_from_call(signature->call_abi, platform_target.arch, platform_target.os)); + LLVMSetInstructionCallConv(call_value, llvm_call_convention_from_call(prototype->call_abi, platform_target.arch, platform_target.os)); } if (expr->call_expr.force_noinline) { @@ -4477,8 +4472,8 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) for (unsigned i = 0; i < non_variadic_params; i++) { - Decl *param = signature->params[i]; - ABIArgInfo *info = param->var.abi_info; + Type *param = params[i]; + ABIArgInfo *info = abi_args[i]; switch (info->kind) { case ABI_ARG_INDIRECT: @@ -4501,7 +4496,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) 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(!is_failable && "Failable should have produced a return value."); + 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: @@ -4532,7 +4527,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 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, return_type); + llvm_emit_convert_value_from_coerced(c, result_value, struct_type, call_value, call_return_type); break; } case ABI_ARG_EXPAND_COERCE: @@ -4541,8 +4536,8 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // { 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, return_type, ""); - llvm_value_set_address(result_value, ret, return_type); + LLVMValueRef ret = llvm_emit_alloca_aligned(c, call_return_type, ""); + llvm_value_set_address(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); @@ -4551,7 +4546,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 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(return_type), &alignment); + 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. @@ -4571,7 +4566,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 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(return_type), &alignment); + type_abi_alignment(call_return_type), &alignment); // 15h. Store the high value. llvm_store_aligned(c, hi, hi_value, alignment); @@ -4579,7 +4574,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) break; } case ABI_ARG_DIRECT: - llvm_value_set(result_value, call_value, return_type); + llvm_value_set(result_value, call_value, call_return_type); break; case ABI_ARG_DIRECT_COERCE: { @@ -4589,21 +4584,21 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) 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, return_type)) + 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, return_type); + 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, return_type); + llvm_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type); break; } } // 17. Handle failables. - if (is_failable) + if (prototype->is_failable) { BEValue no_err; @@ -4640,7 +4635,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_block(c, after_block); // 17g. If void, be_value contents should be skipped. - if (!signature->ret_abi_info) + if (!prototype->ret_by_ref) { *result_value = (BEValue) { .type = type_void, .kind = BE_VALUE }; return; diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 90e36e238..3576374ea 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -8,7 +8,7 @@ static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index); static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef value); static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index, AlignSize alignment); -static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsigned *index); +static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIArgInfo *info, unsigned *index); bool llvm_emit_check_block_branch(GenContext *context) { @@ -122,9 +122,8 @@ LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index) } -static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsigned *index) +static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIArgInfo *info, unsigned *index) { - ABIArgInfo *info = decl->var.abi_info; switch (info->kind) { case ABI_ARG_IGNORE: @@ -235,12 +234,12 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig } } } -static inline void llvm_emit_parameter(GenContext *context, Decl *decl, unsigned *index, unsigned real_index) +static inline void llvm_emit_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index) { assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM); // Allocate room on stack, but do not copy. - llvm_process_parameter_value(context, decl, index); + llvm_process_parameter_value(context, decl, abi_info, index); if (llvm_use_debug(context)) { llvm_emit_debug_parameter(context, decl, real_index); @@ -263,35 +262,30 @@ static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef valu void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable) { - FunctionSignature *signature = &c->cur_func_decl->func_decl.function_signature; - ABIArgInfo *info = signature->ret_abi_info; + FunctionPrototype *prototype = c->cur_func_decl->type->func.prototype; + ABIArgInfo *info = prototype->ret_abi_info; // If we have a failable it's always the return argument, so we need to copy // the return value into the return value holder. LLVMValueRef return_out = c->return_out; - bool is_failable = IS_FAILABLE(signature->rtype); - Type *return_type = signature->rtype->type; - if (is_failable) return_type = return_type->failable; + Type *call_return_type = prototype->abi_ret_type; BEValue no_fail; // In this case we use the failable as the actual return. - if (is_failable) + if (prototype->is_failable) { if (return_value && return_value->value) { - llvm_store_bevalue_aligned(c, c->return_out, return_value, 0); } return_out = c->failable_out; - return_type = type_anyerr; if (!failable) { llvm_value_set(&no_fail, LLVMConstNull(llvm_get_type(c, type_anyerr)), type_anyerr); failable = &no_fail; } return_value = failable; - info = signature->failable_abi_info; } switch (info->kind) @@ -354,14 +348,14 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl case ABI_ARG_DIRECT_COERCE: { LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); - if (!coerce_type || coerce_type == llvm_get_type(c, return_type)) + 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)); return; } assert(!abi_info_should_flatten(info)); - llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, return_type)); + llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type)); return; } } @@ -369,7 +363,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl void llvm_emit_return_implicit(GenContext *c) { - Type *rtype_real = c->cur_func_decl->func_decl.function_signature.rtype->type; + Type *rtype_real = c->cur_func_decl->type->func.prototype->rtype; if (type_lowering(type_no_fail(rtype_real)) != type_void) { LLVMBuildUnreachable(c->builder); @@ -416,7 +410,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point"); context->alloca_point = alloca_point; - FunctionSignature *signature = &decl->func_decl.function_signature; + FunctionPrototype *prototype = decl->type->func.prototype; unsigned arg = 0; if (emit_debug) @@ -424,27 +418,24 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) llvm_debug_scope_push(context, context->debug.function); } - bool is_failable = IS_FAILABLE(signature->rtype); - if (is_failable && signature->failable_abi_info->kind == ABI_ARG_INDIRECT) + context->failable_out = NULL; + context->return_out = NULL; + if (prototype->ret_abi_info->kind == ABI_ARG_INDIRECT) { - context->failable_out = LLVMGetParam(context->function, arg++); - } - else - { - context->failable_out = NULL; - } - if (signature->ret_abi_info && signature->ret_abi_info->kind == ABI_ARG_INDIRECT) - { - context->return_out = LLVMGetParam(context->function, arg++); - } - else - { - context->return_out = NULL; - if (signature->ret_abi_info && is_failable) + if (prototype->is_failable) + { + context->failable_out = LLVMGetParam(context->function, arg++); + } + else { context->return_out = LLVMGetParam(context->function, arg++); } } + if (prototype->ret_by_ref_abi_info) + { + assert(!context->return_out); + context->return_out = LLVMGetParam(context->function, arg++); + } if (!decl->func_decl.attr_naked) @@ -452,7 +443,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code VECEACH(decl->func_decl.function_signature.params, i) { - llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], &arg, i); + llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], prototype->abi_args[i], &arg, i); } } @@ -552,40 +543,20 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) // Resolve function backend type for function. LLVMValueRef function = LLVMAddFunction(c->module, decl->extname ? decl->extname : decl->external_name, llvm_get_type(c, decl->type)); decl->backend_ref = function; - FunctionSignature *signature = &decl->func_decl.function_signature; - FunctionSignature *type_signature = decl->type->func.signature; + FunctionPrototype *prototype = decl->type->func.prototype; - // We only resolve 1 function signature, so we might have functions - // with the same signature (but different default values!) - // that we have in common. So overwrite the data from the type here. - if (signature != type_signature) - { - // Store the params. - Decl **params = signature->params; - // Copy the rest. - *signature = *type_signature; - signature->params = params; - VECEACH(params, i) - { - Decl *sig_param = type_signature->params[i]; - Decl *param = params[i]; - param->var.abi_info = sig_param->var.abi_info; - } - signature->params = params; - } - ABIArgInfo *ret_abi_info = signature->failable_abi_info ? signature->failable_abi_info : signature->ret_abi_info; + ABIArgInfo *ret_abi_info = prototype->ret_abi_info; llvm_emit_param_attributes(c, function, ret_abi_info, true, 0, 0); - Decl **params = signature->params; - if (signature->failable_abi_info && signature->ret_abi_info) + unsigned params = vec_size(prototype->params); + if (prototype->ret_by_ref) { - ABIArgInfo *info = signature->ret_abi_info; - llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end); + ABIArgInfo *info = prototype->ret_by_ref_abi_info; + llvm_emit_param_attributes(c, function, prototype->ret_by_ref_abi_info, false, info->param_index_start + 1, info->param_index_end); } - VECEACH(params, i) + for (unsigned i = 0; i < params; i++) { - Decl *param = params[i]; - ABIArgInfo *info = param->var.abi_info; + ABIArgInfo *info = prototype->abi_args[i]; llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end); } // We ignore decl->func_decl.attr_inline and place it in every call instead. @@ -610,14 +581,14 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) { llvm_attribute_add(c, function, attribute_id.naked, -1); } - if (decl->func_decl.function_signature.call_abi == CALL_X86_STD) + if (prototype->call_abi == CALL_X86_STD) { if (platform_target.os == OS_TYPE_WIN32) { LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass); } } - LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(decl->func_decl.function_signature.call_abi, platform_target.arch, platform_target.os)); + LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(prototype->call_abi, platform_target.arch, platform_target.os)); switch (decl->visibility) { diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 0dd3224b2..90c28c786 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -4,35 +4,12 @@ #include "llvm_codegen_internal.h" -const char *get_object_extension(void) -{ - switch (active_target.arch_os_target) - { - case X64_WINDOWS: - case X86_WINDOWS: - case X64_WINDOWS_GNU: - return ".obj"; - default: - return ".o"; - } -} void gencontext_begin_module(GenContext *c) { assert(!c->module && "Expected no module"); - scratch_buffer_clear(); - StringSlice slice = strtoslice(c->code_module->name->module); - while (true) - { - StringSlice part = strnexttok(&slice, ':'); - scratch_buffer_append_len(part.ptr, part.len); - if (!slice.len) break; - slice.ptr++; - slice.len--; - scratch_buffer_append_char('.'); - } - const char *result = scratch_buffer_to_string(); + const char *result = module_create_object_file_name(c->code_module); c->ir_filename = strformat("%s.ll", result); c->object_filename = strformat("%s%s", result, get_object_extension()); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 9312e58ff..1c513a429 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -191,7 +191,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) c->error_var = c->block_error_var; c->catch_block = c->block_failable_exit; } - else if (IS_FAILABLE(c->cur_func_decl->func_decl.function_signature.rtype)) + else if (type_is_failable(c->cur_func_decl->type->func.prototype->rtype)) { error_return_block = llvm_basic_block_new(c, "err_retblock"); error_out = llvm_emit_alloca_aligned(c, type_anyerr, "reterr"); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 954e2f90f..5e762fa07 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -7,7 +7,6 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) { - static LLVMTypeRef params[MAX_PARAMS]; switch (decl->decl_kind) { case DECL_VAR: @@ -19,18 +18,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) case DECL_BITSTRUCT: return llvm_get_type(c, decl->bitstruct.base_type->type); case DECL_FUNC: - { - VECEACH(decl->func_decl.function_signature.params, i) - { - params[i] = llvm_get_type(c, decl->func_decl.function_signature.params[i]->type); - } - unsigned param_size = vec_size(decl->func_decl.function_signature.params); - return LLVMFunctionType(llvm_get_type(c, decl->func_decl.function_signature.rtype->type), - params, - param_size, - decl->func_decl.function_signature.variadic == VARIADIC_RAW); - - } + UNREACHABLE case DECL_TYPEDEF: return llvm_get_type(c, decl->typedef_decl.type_info->type); case DECL_DISTINCT: @@ -232,15 +220,12 @@ static inline void add_func_type_param(GenContext *context, Type *param_type, AB LLVMTypeRef llvm_func_type(GenContext *context, Type *type) { LLVMTypeRef *params = NULL; - FunctionSignature *signature = type->func.signature; + FunctionPrototype *prototype = type->func.prototype; LLVMTypeRef return_type = NULL; - Type *rtype = signature->rtype->type; - bool is_failable = type_is_failable(rtype); - if (is_failable) rtype = rtype->failable; - Type *real_return_type = is_failable ? type_anyerr : rtype; - ABIArgInfo *ret_arg_info = is_failable ? signature->failable_abi_info : signature->ret_abi_info; + 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; @@ -250,7 +235,7 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) case ABI_ARG_EXPAND: UNREACHABLE; case ABI_ARG_INDIRECT: - vec_add(params, llvm_get_ptr_type(context, real_return_type)); + vec_add(params, llvm_get_ptr_type(context, call_return_type)); return_type = llvm_get_type(context, type_void); break; case ABI_ARG_EXPAND_COERCE: @@ -276,28 +261,28 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) break; } case ABI_ARG_DIRECT: - return_type = llvm_get_type(context, real_return_type); + return_type = llvm_get_type(context, call_return_type); break; case ABI_ARG_DIRECT_COERCE: assert(!abi_info_should_flatten(ret_arg_info)); return_type = llvm_get_coerce_type(context, ret_arg_info); - if (!return_type) return_type = llvm_get_type(context, real_return_type); + 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 (is_failable && signature->ret_abi_info) + if (prototype->ret_by_ref) { - add_func_type_param(context, type_get_ptr(rtype), signature->ret_abi_info, ¶ms); + add_func_type_param(context, 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(signature->params, i) + VECEACH(prototype->params, i) { - add_func_type_param(context, signature->params[i]->type, signature->params[i]->var.abi_info, ¶ms); + add_func_type_param(context, prototype->params[i], prototype->abi_args[i], ¶ms); } - return LLVMFunctionType(return_type, params, vec_size(params), signature->variadic == VARIADIC_RAW); + return LLVMFunctionType(return_type, params, vec_size(params), prototype->variadic == VARIADIC_RAW); } diff --git a/src/compiler/module.c b/src/compiler/module.c index 85ed72798..0b2399ebd 100644 --- a/src/compiler/module.c +++ b/src/compiler/module.c @@ -9,6 +9,22 @@ Decl *module_find_symbol(Module *module, const char *symbol) return stable_get(&module->symbols, symbol); } +const char *module_create_object_file_name(Module *module) +{ + scratch_buffer_clear(); + StringSlice slice = strtoslice(module->name->module); + while (true) + { + StringSlice part = strnexttok(&slice, ':'); + scratch_buffer_append_len(part.ptr, part.len); + if (!slice.len) break; + slice.ptr++; + slice.len--; + scratch_buffer_append_char('.'); + } + return scratch_buffer_to_string(); +} + Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span) { Path *path = CALLOCS(Path); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 48c29657f..4840db858 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1479,7 +1479,7 @@ static inline Decl *parse_define_type(ParseContext *context, Visibility visibili decl->typedef_decl.is_func = true; decl->typedef_decl.is_distinct = distinct; ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_failable_type(context), poisoned_decl); - decl->typedef_decl.function_signature.rtype = type_info; + decl->typedef_decl.function_signature.returntype = type_info; if (!parse_parameter_list(context, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; @@ -1967,7 +1967,7 @@ static inline Decl *parse_func_definition(ParseContext *context, Visibility visi { advance_and_verify(context, TOKEN_FN); } - TypeInfo **rtype_ref = &func->func_decl.function_signature.rtype; + TypeInfo **rtype_ref = &func->func_decl.function_signature.returntype; TypeInfo **method_type_ref = &func->func_decl.type_parent; TokenId name; if (!parse_func_macro_header(context, false, rtype_ref, method_type_ref, &name)) return poisoned_decl; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index f7d8fb3c7..c2ed92a54 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5,8 +5,6 @@ #include "sema_internal.h" - - static bool sema_analyse_struct_union(SemaContext *context, Decl *decl); static bool sema_check_section(SemaContext *context, Decl *decl, Attr *attr) @@ -656,10 +654,10 @@ static inline bool sema_analyse_function_param(SemaContext *context, Decl *param return true; } -static inline Type *sema_analyse_function_signature(SemaContext *context, FunctionSignature *signature, bool is_real_function) +static inline Type *sema_analyse_function_signature(SemaContext *context, CallABI abi, FunctionSignature *signature, bool is_real_function) { bool all_ok = true; - all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok; + all_ok = sema_resolve_type_info(context, signature->returntype) && all_ok; if (vec_size(signature->params) > MAX_PARAMS) { SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); @@ -667,6 +665,7 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Functi } unsigned param_count = vec_size(signature->params); Decl **params = signature->params; + Type **types = NULL; for (unsigned i = 0; i < param_count; i++) { Decl *param = params[i]; @@ -686,18 +685,19 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Functi } signature->has_default = signature->has_default || has_default; param->resolve_status = RESOLVE_DONE; + vec_add(types, param->type); } if (!all_ok) return NULL; - c_abi_func_create(signature); - return type_find_function_type(signature); + + return type_get_func(signature, abi); } static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl) { if (decl->typedef_decl.is_func) { - Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false); + Type *func_type = sema_analyse_function_signature(context, CALL_C, &decl->typedef_decl.function_signature, false); if (!func_type) return false; decl->type->canonical = type_get_ptr(func_type); return true; @@ -718,7 +718,7 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl) { if (decl->distinct_decl.typedef_decl.is_func) { - Type *func_type = sema_analyse_function_signature(context, &decl->distinct_decl.typedef_decl.function_signature, false); + Type *func_type = sema_analyse_function_signature(context, CALL_C, &decl->distinct_decl.typedef_decl.function_signature, false); if (!func_type) return false; decl->distinct_decl.base_type = type_get_ptr(func_type); return true; @@ -906,7 +906,7 @@ static inline void find_operator_parameters(Decl *method, TypeInfo **rtype_ptr, break; case DECL_FUNC: *params_ptr = method->func_decl.function_signature.params; - *rtype_ptr = method->func_decl.function_signature.rtype; + *rtype_ptr = method->func_decl.function_signature.returntype; break; default: UNREACHABLE @@ -1258,12 +1258,6 @@ static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extr } return true; } -static inline bool sema_update_call_convention(Decl *decl, CallABI abi) -{ - bool had = decl->func_decl.function_signature.call_abi > 0; - decl->func_decl.function_signature.call_abi = abi; - return had; -} static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) { @@ -1275,7 +1269,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) return false; } FunctionSignature *signature = &decl->func_decl.function_signature; - Type *rtype = type_flatten_distinct(signature->rtype->type); + Type *rtype = type_flatten_distinct(signature->returntype->type); bool is_int_return = true; bool is_err_return = false; if (rtype->type_kind == TYPE_FAILABLE_ANY) is_err_return = true; @@ -1283,7 +1277,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) { if (rtype->failable->type_kind != TYPE_VOID) { - SEMA_ERROR(signature->rtype, "The return type of 'main' cannot be a failable, unless it is 'void!'."); + SEMA_ERROR(signature->returntype, "The return type of 'main' cannot be a failable, unless it is 'void!'."); return false; } is_int_return = false; @@ -1293,7 +1287,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) if (type_is_integer(rtype) && rtype != type_cint) { - SEMA_ERROR(signature->rtype, "Expected a return type of 'void' or %s.", type_quoted_error_string(type_cint)); + SEMA_ERROR(signature->returntype, "Expected a return type of 'void' or %s.", type_quoted_error_string(type_cint)); return false; } // At this point the style is either MAIN_INT_VOID, MAIN_VOID_VOID or MAIN_ERR_VOID @@ -1340,7 +1334,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) Decl *function = decl_new(DECL_FUNC, decl->name_token, VISIBLE_EXTERN); function->name = kw_mainstub; function->extname = kw_main; - function->func_decl.function_signature.rtype = type_info_new_base(type_cint, decl->span); + function->func_decl.function_signature.returntype = type_info_new_base(type_cint, decl->span); Decl *param1 = decl_new_generated_var(kw_argv, type_cint, VARDECL_PARAM, decl->span); Decl *param2 = decl_new_generated_var(kw_argc, type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span); Decl **main_params = NULL; @@ -1403,6 +1397,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); + CallABI call_abi = 0; VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; @@ -1431,29 +1426,34 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) case ATTRIBUTE_STDCALL: if (platform_target.arch == ARCH_TYPE_X86 || platform_target.arch == ARCH_TYPE_X86_64) { - had = sema_update_call_convention(decl, CALL_X86_STD); + had = call_abi > 0; + call_abi = CALL_X86_STD; } else if (platform_target.arch == ARCH_TYPE_ARM || platform_target.arch == ARCH_TYPE_ARMB) { - had = sema_update_call_convention(decl, CALL_AAPCS); + had = call_abi > 0; + call_abi = CALL_AAPCS; } break; case ATTRIBUTE_CDECL: - had = sema_update_call_convention(decl, CALL_C); + had = call_abi > 0; + call_abi = CALL_C; break; case ATTRIBUTE_VECCALL: switch (platform_target.arch) { case ARCH_TYPE_X86_64: case ARCH_TYPE_X86: - had = sema_update_call_convention(decl, CALL_X86_VECTOR); + had = call_abi > 0; + call_abi = CALL_X86_VECTOR; break; case ARCH_TYPE_ARM: case ARCH_TYPE_ARMB: case ARCH_TYPE_AARCH64: case ARCH_TYPE_AARCH64_32: case ARCH_TYPE_AARCH64_BE: - had = sema_update_call_convention(decl, CALL_AAPCS_VFP); + had = call_abi > 0; + call_abi = CALL_AAPCS_VFP; break; default: break; @@ -1462,14 +1462,16 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) case ATTRIBUTE_FASTCALL: if (platform_target.arch == ARCH_TYPE_X86 || (platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32)) { - had = sema_update_call_convention(decl, CALL_X86_FAST); + had = call_abi > 0; + call_abi = CALL_X86_FAST; } break; case ATTRIBUTE_REGCALL: - had = decl->func_decl.function_signature.call_abi > 0; + had = call_abi > 0; if (platform_target.arch == ARCH_TYPE_X86 || (platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32)) { - had = sema_update_call_convention(decl, CALL_X86_REG); + had = call_abi > 0; + call_abi = CALL_X86_REG; } break; case ATTRIBUTE_INLINE: SET_ATTR(attr_inline); @@ -1492,7 +1494,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) } } - Type *func_type = sema_analyse_function_signature(context, &decl->func_decl.function_signature, true); + Type *func_type = sema_analyse_function_signature(context, call_abi, &decl->func_decl.function_signature, true); decl->type = func_type; if (!func_type) return decl_poison(decl); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index eb80dc365..dc5c355bc 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1137,9 +1137,12 @@ static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_typ typedef struct { bool macro; + bool func_pointer; TokenId block_parameter; Decl **params; + Type **param_types; Expr *struct_var; + unsigned param_count; Variadic variadic; } CalledDecl; @@ -1212,15 +1215,18 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr } -static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Decl **params, Expr **args, unsigned func_param_count, bool variadic, bool *failable) +static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, bool variadic, bool *failable) { unsigned num_args = vec_size(args); + Decl **params = callee->params; + bool is_func_ptr = callee->func_pointer; // 1. We need at least as many function locations as we have parameters. unsigned entries_needed = func_param_count > num_args ? func_param_count : num_args; Expr **actual_args = VECNEW(Expr*, entries_needed); for (unsigned i = 0; i < entries_needed; i++) vec_add(actual_args, NULL); + // 2. Loop through the parameters. bool uses_named_parameters = false; for (unsigned i = 0; i < num_args; i++) @@ -1230,6 +1236,11 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * // 3. Handle named parameters if (arg->expr_kind == EXPR_DESIGNATOR) { + if (is_func_ptr) + { + SEMA_ERROR(arg, "Named parameters are not allowed with function pointer calls."); + return false; + } // 8a. We have named parameters, that will add some restrictions. uses_named_parameters = true; @@ -1301,6 +1312,7 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * // 17a. Assigned a value - skip if (actual_args[i]) continue; + if (is_func_ptr) goto FAIL_MISSING; // 17b. Set the init expression. Expr *init_expr = params[i]->var.init_expr; if (init_expr) @@ -1317,10 +1329,17 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * continue; } +FAIL_MISSING: + // 17c. Vararg not set? That's fine. if (params[i]->var.vararg) continue; // 17d. Argument missing, that's bad. + if (is_func_ptr) + { + SEMA_ERROR(call, "The call is missing parameter(s), please check the definition."); + return false; + } SEMA_ERROR(call, "The mandatory parameter '%s' was not set, please add it.", params[i]->name); return false; } @@ -1335,7 +1354,8 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr // 2. Pick out all the arguments and parameters. Expr **args = call->call_expr.arguments; - Decl **params = callee.params; + Decl **decl_params = callee.params; + Type **param_types = callee.param_types; unsigned num_args = vec_size(args); // 3. If this is a type call, then we have an implicit first argument. @@ -1371,7 +1391,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr } // 5. Zero out all argument slots. - unsigned func_param_count = vec_size(params); + unsigned func_param_count = callee.param_count; // 6. We might have a typed variadic call e.g. foo(int, double...) // get that type. @@ -1379,13 +1399,14 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr if (callee.variadic == VARIADIC_TYPED) { // 7a. The parameter type is [], so we get the - assert(params[func_param_count - 1]->type->type_kind == TYPE_SUBARRAY); - variadic_type = params[func_param_count - 1]->type->array.base; + Type *last_type = callee.macro ? callee.params[func_param_count - 1]->type : callee.param_types[func_param_count - 1]; + assert(last_type->type_kind == TYPE_SUBARRAY); + variadic_type = last_type->array.base; // 7b. The last function parameter is implicit so we will pretend it's not there. func_param_count--; } - if (!sema_expand_call_arguments(context, &callee, call, params, args, func_param_count, callee.variadic != VARIADIC_NONE, failable)) return false; + if (!sema_expand_call_arguments(context, &callee, call, args, func_param_count, callee.variadic != VARIADIC_NONE, failable)) return false; args = call->call_expr.arguments; num_args = vec_size(args); @@ -1445,29 +1466,43 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr UNREACHABLE } - Decl *param = params[i]; + Decl *param; + VarDeclKind kind; + Type *type; + if (decl_params) + { + param = decl_params[i]; + kind = param->var.kind; + type = param->type; + } + else + { + param = NULL; + kind = VARDECL_PARAM; + type = param_types[i]; + } // 16. Analyse a regular argument. - switch (param->var.kind) + switch (kind) { case VARDECL_PARAM_REF: // &foo { if (!sema_analyse_expr_lvalue(context, arg)) return false; } - if (param->type && param->type->canonical != arg->type->canonical) + 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(param->type)); + SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type)); return false; } break; case VARDECL_PARAM: // foo - if (!sema_analyse_expr_rhs(context, param->type, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; if (IS_FAILABLE(arg)) *failable = true; if (callee.macro) { - param->alignment = type_abi_alignment(param->type ? param->type : arg->type); + param->alignment = type_abi_alignment(type ? type : arg->type); } break; case VARDECL_PARAM_EXPR: @@ -1477,7 +1512,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr case VARDECL_PARAM_CT: // $foo assert(callee.macro); - if (!sema_analyse_expr_rhs(context, param->type, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; if (!expr_is_constant_eval(arg, CONSTANT_EVAL_ANY)) { SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); @@ -1507,23 +1542,26 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr case VARDECL_ERASE: UNREACHABLE } - if (!param->type) param->type = type_no_fail(arg->type); + if (param && !type) param->type = type_no_fail(arg->type); } return true; } -static inline bool sema_expr_analyse_func_invocation(SemaContext *context, FunctionSignature *signature, Expr *expr, Decl *decl, +static inline bool sema_expr_analyse_func_invocation(SemaContext *context, FunctionPrototype *prototype, FunctionSignature *sig, Expr *expr, Decl *decl, Expr *struct_var, bool failable) { CalledDecl callee = { .macro = false, + .func_pointer = sig ? 0 : 1, .block_parameter = NO_TOKEN_ID, .struct_var = struct_var, - .params = signature->params, - .variadic = signature->variadic, + .params = sig ? sig->params : NULL, + .param_types = prototype->params, + .param_count = vec_size(prototype->params), + .variadic = prototype->variadic, }; if (!sema_expr_analyse_call_invocation(context, expr, callee, &failable)) return false; - Type *rtype = signature->rtype->type; + Type *rtype = prototype->rtype; expr->type = type_get_opt_fail(rtype, failable); @@ -1539,7 +1577,8 @@ static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, } expr->call_expr.is_pointer_call = true; return sema_expr_analyse_func_invocation(context, - func_ptr_type->pointer->func.signature, + func_ptr_type->pointer->func.prototype, + NULL, expr, NULL, NULL, failable); @@ -1608,7 +1647,7 @@ static inline Type *unify_returns(SemaContext *context) static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool failable) { expr->call_expr.is_pointer_call = false; - return sema_expr_analyse_func_invocation(context, &decl->func_decl.function_signature, expr, decl, struct_var, failable); + return sema_expr_analyse_func_invocation(context, decl->type->func.prototype, &decl->func_decl.function_signature, expr, decl, struct_var, failable); } @@ -1668,6 +1707,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s .macro = true, .block_parameter = decl->macro_decl.block_parameter, .params = params, + .param_count = vec_size(params), .struct_var = struct_var }; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 2ec4d64e3..1dce279a7 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -52,7 +52,6 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags); #define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast) #define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK() -void c_abi_func_create(FunctionSignature *signature); AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain); bool expr_is_ltype(Expr *expr); @@ -70,4 +69,5 @@ void sema_analyze_stage(Module *module, AnalysisStage stage); Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw); bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable); -void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable); \ No newline at end of file +void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable); + diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 004c08463..88c358c07 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1023,7 +1023,7 @@ static Decl *find_iterator(SemaContext *context, Expr *enumerator) break; case DECL_FUNC: parameters = method->func_decl.function_signature.params; - iterator_type = method->func_decl.function_signature.rtype; + iterator_type = method->func_decl.function_signature.returntype; break; default: UNREACHABLE @@ -1089,7 +1089,7 @@ static Decl *find_iterator_next(SemaContext *context, Expr *enumerator) break; case DECL_FUNC: parameters = method->func_decl.function_signature.params; - rtype = method->func_decl.function_signature.rtype; + rtype = method->func_decl.function_signature.returntype; break; default: UNREACHABLE @@ -2513,8 +2513,9 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) { if (!decl_ok(func)) return false; FunctionSignature *signature = &func->func_decl.function_signature; + FunctionPrototype *prototype = func->type->func.prototype; context->current_function = func; - context->rtype = signature->rtype->type; + context->rtype = prototype->rtype; context->active_scope = (DynamicScope) { .scope_id = 0, .depth = 0, @@ -2560,7 +2561,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) assert(context->active_scope.depth == 1); if (!context->active_scope.jump_end) { - Type *canonical_rtype = type_no_fail(signature->rtype->type)->canonical; + Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical; if (canonical_rtype != type_void) { // IMPROVE better pointer to end. diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 7969589e6..2b2f280f8 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -315,6 +315,7 @@ void sema_analysis_run(void) global_context.locals_list = NULL; stable_init(&global_context.std_module.symbols, 0x10000); + type_func_prototype_init(0x10000); if (!global_context.module_list) { diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d9904fffe..afdd23140 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -4,10 +4,8 @@ #include "compiler_internal.h" -#define TABLE_MAX_LOAD 0.5 #define MAX_HASH_SIZE (512 * 1024 * 1024) - typedef struct _SymEntry { const char *value; diff --git a/src/compiler/tb_codegen.c b/src/compiler/tb_codegen.c index e2ba48f85..ece93f315 100644 --- a/src/compiler/tb_codegen.c +++ b/src/compiler/tb_codegen.c @@ -49,48 +49,73 @@ static inline bool tinybackend_value_is_addr(BEValue *value) static inline bool tinybackend_value_is_bool(BEValue *value) { return value->kind == BE_BOOLEAN; } -static TB_DataType tinybackend_get_type(Type *type) +static TB_DataType tbc3_get_type(Type *type) { type = type_lowering(type); uint8_t elements = 1; if (type->type_kind == TYPE_VECTOR) { elements = (uint8_t)type->vector.len; - type = type->vector.base; + switch (type->vector.base->type_kind) + { + case TYPE_U8: + case TYPE_I8: + return TB_TYPE_VEC_I8(elements); + case TYPE_U16: + case TYPE_I16: + return TB_TYPE_VEC_I16(elements); + case TYPE_U32: + case TYPE_I32: + return TB_TYPE_VEC_I32(elements); + case TYPE_U64: + case TYPE_I64: + return TB_TYPE_VEC_I64(elements); + case TYPE_I128: + case TYPE_U128: + FATAL_ERROR("Unsupported int128"); + case TYPE_F64: + return TB_TYPE_VEC_F64(elements); + case TYPE_F32: + return TB_TYPE_VEC_F32(elements); + case TYPE_F16: + FATAL_ERROR("Unsupported f16"); + case TYPE_F128: + FATAL_ERROR("Unsupported f128"); + default: + UNREACHABLE + } } switch (type->type_kind) { case TYPE_U8: case TYPE_I8: - return TB_TYPE_I8(elements); + return TB_TYPE_I8; case TYPE_U16: case TYPE_I16: - return TB_TYPE_I16(elements); + return TB_TYPE_I16; case TYPE_U32: case TYPE_I32: - return TB_TYPE_I32(elements); + return TB_TYPE_I32; case TYPE_U64: case TYPE_I64: - return TB_TYPE_I64(elements); + return TB_TYPE_I64; case TYPE_POINTER: - assert(elements == 1); - return TB_TYPE_PTR(); + return TB_TYPE_PTR; case TYPE_I128: case TYPE_U128: - return TB_TYPE_I128(elements); + FATAL_ERROR("Unsupported int128"); case TYPE_BOOL: - return TB_TYPE_BOOL(elements); + return TB_TYPE_BOOL; case TYPE_F64: - return TB_TYPE_F64(elements); + return TB_TYPE_F64; case TYPE_F32: - return TB_TYPE_F32(elements); + return TB_TYPE_F32; case TYPE_VOID: - assert(elements == 1); - return TB_TYPE_VOID(); + return TB_TYPE_VOID; case TYPE_F16: - TODO + FATAL_ERROR("Unsupported f16"); case TYPE_F128: - TODO + FATAL_ERROR("Unsupported f128"); case TYPE_VECTOR: UNREACHABLE default: @@ -114,7 +139,7 @@ TB_Register tinybackend_emit_load_aligned(GenContext *c, TB_DataType dt, TB_Regi void tinybackend_store_self_aligned(GenContext *c, TB_Register pointer, TB_Register value, Type *type) { - tb_inst_store(c->function, tinybackend_get_type(type), pointer, value, type_abi_alignment(type)); + tb_inst_store(c->function, tbc3_get_type(type), pointer, value, type_abi_alignment(type)); } void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destination, BEValue *value) @@ -123,7 +148,7 @@ void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destina if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type)) { value->value = tinybackend_emit_load_aligned(c, - tinybackend_get_type(value->type), + tbc3_get_type(value->type), value->value, value->alignment); value->kind = BE_VALUE; @@ -133,12 +158,12 @@ void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destina switch (value->kind) { case BE_BOOLEAN: - value->value = tb_inst_zxt(c->function, value->value, TB_TYPE_I8(1)); + 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, - tinybackend_get_type(destination->type), + tbc3_get_type(destination->type), destination->value, value->value, alignment ? alignment : type_abi_alignment(value->type)); @@ -148,7 +173,7 @@ void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destina // Here we do an optimized(?) memcopy. ByteSize size = type_size(value->type); TB_Register copy_size = tb_inst_iconst(c->function, - size <= UINT32_MAX ? TB_TYPE_I32(1) : TB_TYPE_I64(1), + size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size); TB_Register source = value->value; @@ -199,14 +224,16 @@ static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *exp TB_Register reg; Int128 i = expr->const_expr.ixx.i; - TB_DataType dt = tinybackend_get_type(type); + TB_DataType dt = tbc3_get_type(type); printf("Const int type: %d\n", dt.type); switch (expr->const_expr.ixx.type) { + case TYPE_I128: case TYPE_U128: - reg = tb_inst_iconst128(c->function, dt, (TB_Int128){ .lo = i.low, .hi = i.high }); + TODO + //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); @@ -227,7 +254,7 @@ BEValue tinybackend_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, TB BEValue value; tinybackend_emit_expr(c, &value, expr); - tinybackend_store_bevalue(c, tinybackend_get_type(ref->type), ref, &value); + tinybackend_store_bevalue(c, tbc3_get_type(ref->type), ref, &value); return value; } @@ -317,7 +344,7 @@ void tinybackend_value_rvalue(GenContext *c, BEValue *value) } //llvm_value_fold_failable(c, value); value->value = tinybackend_emit_load_aligned(c, - tinybackend_get_type(value->type), + tbc3_get_type(value->type), value->value, value->alignment ? value->alignment : type_abi_alignment(value->type)); @@ -371,7 +398,7 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal TB_Register lhs_value = lhs.value; TB_Register rhs_value = rhs.value; - TB_DataType dt = tinybackend_get_type(lhs_type); + TB_DataType dt = tbc3_get_type(lhs_type); switch (binary_op) { case BINARYOP_ERROR: @@ -508,7 +535,7 @@ static void tinybackend_emit_binary_expr(GenContext *c, BEValue *be_value, Expr tinybackend_emit_expr(c, &addr, expr->binary_expr.left); tinybackend_value_addr(c, &addr); tinybackend_emit_binary(c, be_value, expr, &addr, base_op); - tinybackend_store_bevalue(c, tinybackend_get_type(addr.type), &addr, be_value); + tinybackend_store_bevalue(c, tbc3_get_type(addr.type), &addr, be_value); return; } printf("B\n"); @@ -556,7 +583,7 @@ static TB_Register tinybackend_emit_local_decl(GenContext *c, Decl *decl) TODO } - TB_DataType dt = tinybackend_get_type(decl->type); + TB_DataType dt = tbc3_get_type(decl->type); TypeSize size = type_size(decl->type); AlignSize align = decl->alignment; @@ -611,78 +638,117 @@ static void tinybackend_emit_stmt(GenContext *c, Ast *ast) break; case AST_RETURN_STMT: //gencontext_emit_return(c, ast); - tb_inst_ret(c->function, TB_TYPE_VOID(), TB_NULL_REG); + tb_inst_ret(c->function, TB_NULL_REG); break; default: break; } } -static void tinybackend_emit_function_body(GenContext *c, Decl *decl) +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) { printf("Function: %s\n", decl->external_name); c->current_func_decl = decl; - // TODO(NeGate): Actually interpret the return value - c->function = tb_function_create(c->module, decl->external_name, TB_TYPE_VOID()); + 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_Function *f = tb_prototype_add_param()*/ + /* VECEACH(decl->func_decl.body->compound_stmt.stmts, i) { tinybackend_emit_stmt(c, decl->func_decl.body->compound_stmt.stmts[i]); - } + }*/ tb_function_print(c->function, stdout); } -static void tinybackend_gen_context(GenContext *c) +static void tbc3_gen_context(GenContext *c) { - scratch_buffer_clear(); - StringSlice slice = strtoslice(c->code_module->name->module); - while (true) + 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) +{ + switch (platform_target.arch) { - StringSlice part = strnexttok(&slice, ':'); - scratch_buffer_append_len(part.ptr, part.len); - if (!slice.len) break; - slice.ptr++; - slice.len--; - scratch_buffer_append_char('.'); + case ARCH_TYPE_AARCH64: + return TB_ARCH_AARCH64; + case ARCH_TYPE_X86_64: + return TB_ARCH_X86_64; + default: + FATAL_ERROR("Unsupported architecture for TB backend."); + } +} + +static TB_System tbc3_get_system(void) +{ + switch (platform_target.os) + { + case OS_TYPE_LINUX: + return TB_SYSTEM_LINUX; + case OS_TYPE_WIN32: + return TB_SYSTEM_WINDOWS; + case OS_TYPE_MACOSX: + // TODO add when support exists. + // return TB_SYSTEM_MACOS; + return TB_SYSTEM_LINUX; + case OS_TYPE_UNKNOWN: + return TB_SYSTEM_LINUX; + default: + FATAL_ERROR("Unsupported OS"); } - const char *result = scratch_buffer_to_string(); - // TODO filename should depend on platform. - c->object_filename = strformat("%s.o", result); } // Generate context per module (single threaded) void *tinybackend_gen(Module *module) { + if (!vec_size(module->units)) return NULL; GenContext *c = ccalloc(sizeof(GenContext), 1); c->code_module = module; - // TODO identify target architecture - c->module = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_LINUX, &c->features, TB_OPT_O0, 1, false); + c->module = tb_module_create(tbc3_get_arch(), tbc3_get_system(), &c->features); - tinybackend_gen_context(c); + tbc3_gen_context(c); printf("Module: %.*s\n", module->name->len, module->name->module); - // Forward decls - VECEACH(module->contexts, j) - { - Context *context = module->contexts[j]; - printf(" External symbols: %d\n", vec_size(context->external_symbol_list)); - printf(" Functions: %d\n", vec_size(context->functions)); + VECEACH(module->units, j) + { + CompilationUnit *unit = module->units[j]; + + printf(" External symbols: %d\n", vec_size(unit->external_symbol_list)); + printf(" Functions: %d\n", vec_size(unit->functions)); } - VECEACH(module->contexts, j) + VECEACH(module->units, j) { - Context *context = module->contexts[j]; + CompilationUnit *unit = module->units[j]; - VECEACH(context->functions, i) + VECEACH(unit->functions, i) { - Decl *decl = context->functions[i]; - if (decl->func_decl.body) tinybackend_emit_function_body(c, decl); + Decl *decl = unit->functions[i]; + if (decl->func_decl.body) tbc3_emit_function_body(c, decl); } } @@ -696,9 +762,7 @@ const char *tinybackend_codegen(void *context) GenContext *c = (GenContext *)context; // Write out the object file - FILE *file = fopen(c->object_filename, "wb"); - tb_module_export(c->module, file); - fclose(file); + tb_module_export(c->module, c->object_filename); return c->object_filename; } diff --git a/src/compiler/types.c b/src/compiler/types.c index 1da19082a..1431c3a5d 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -101,6 +101,112 @@ const char *type_quoted_error_string(Type *type) asprintf(&buffer, "'%s'", type_to_error_string(type)); return buffer; } + +static void type_append_func_to_scratch(FunctionPrototype *prototype); + +static void type_append_name_to_scratch(Type *type) +{ + type = type->canonical; + switch (type->type_kind) + { + case TYPE_POISONED: + case TYPE_TYPEDEF: + UNREACHABLE; + case TYPE_ERRTYPE: + case TYPE_ENUM: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_DISTINCT: + case TYPE_BITSTRUCT: + scratch_buffer_append(type->decl->external_name); + break; + case TYPE_POINTER: + type_append_name_to_scratch(type->pointer); + scratch_buffer_append_char('*'); + break; + case TYPE_FAILABLE_ANY: + scratch_buffer_append("void!"); + break; + case TYPE_FAILABLE: + if (type->failable) + { + type_append_name_to_scratch(type->failable); + } + else + { + scratch_buffer_append("void"); + } + scratch_buffer_append_char('!'); + break; + case TYPE_SUBARRAY: + type_append_name_to_scratch(type->array.base); + scratch_buffer_append("[]"); + case TYPE_FLEXIBLE_ARRAY: + type_append_name_to_scratch(type->array.base); + scratch_buffer_append("[*]"); + case TYPE_VOID: + case TYPE_BOOL: + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_I128: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + case TYPE_U128: + case TYPE_F16: + case TYPE_F32: + case TYPE_F64: + case TYPE_F128: + case TYPE_TYPEID: + case TYPE_ANYERR: + case TYPE_ANY: + case TYPE_VECTOR: + scratch_buffer_append(type->name); + break; + case TYPE_UNTYPED_LIST: + case TYPE_INFERRED_ARRAY: + case TYPE_TYPEINFO: + UNREACHABLE + break; + case TYPE_FUNC: + type_append_func_to_scratch(type->func.prototype); + break; + case TYPE_ARRAY: + type_append_name_to_scratch(type->array.base); + scratch_buffer_append_char('['); + scratch_buffer_append_signed_int(type->array.len); + scratch_buffer_append_char(']'); + break; + } +} + +static void type_append_func_to_scratch(FunctionPrototype *prototype) +{ + type_append_name_to_scratch(prototype->rtype); + scratch_buffer_append_char('('); + unsigned elements = vec_size(prototype->params); + for (unsigned i = 0; i < elements; i++) + { + if (i > 0) + { + scratch_buffer_append_char(','); + } + type_append_name_to_scratch(prototype->params[i]); + } + if (prototype->variadic == VARIADIC_RAW && elements > 0) + { + scratch_buffer_append_char(','); + } + if (prototype->variadic != VARIADIC_NONE) + { + scratch_buffer_append("..."); + } + scratch_buffer_append_char(')'); +} + const char *type_to_error_string(Type *type) { char *buffer = NULL; @@ -124,7 +230,10 @@ const char *type_to_error_string(Type *type) case TYPE_ANY: return type->name; case TYPE_FUNC: - return strcat_arena("func ", type->func.mangled_function_signature); + scratch_buffer_clear(); + type_append_func_to_scratch(type->func.prototype); + asprintf(&buffer, "fn %s", scratch_buffer_to_string()); + return buffer; case TYPE_VECTOR: asprintf(&buffer, "%s[<%llu>]", type_to_error_string(type->array.base), (unsigned long long)type->array.len); return buffer; @@ -159,31 +268,6 @@ const char *type_to_error_string(Type *type) UNREACHABLE } -void type_append_signature_name(Type *type, char *dst, size_t *offset) -{ - type = type->canonical; - assert(*offset < MAX_FUNCTION_SIGNATURE_SIZE); - const char *name; - switch (type->type_kind) - { - case TYPE_POISONED: - case TYPE_TYPEDEF: - UNREACHABLE; - case TYPE_ERRTYPE: - case TYPE_ENUM: - case TYPE_STRUCT: - case TYPE_UNION: - name = type->decl->external_name; - break; - default: - name = type->name; - break; - } - memcpy(dst + *offset, name, strlen(name)); - *offset += strlen(name); -} - - TypeSize type_size(Type *type) { @@ -896,120 +980,154 @@ static void type_create_alias(const char *name, Type *location, Type *canonical) } -static void type_append_name_to_scratch(Type *type) + +typedef struct { - type = type->canonical; - switch (type->type_kind) + uint32_t key; + Type *value; +} FuncTypeEntry; + +typedef struct +{ + uint32_t count; + uint32_t capacity; + uint32_t max_load; + FuncTypeEntry *entries; +} FuncMap; + +FuncMap map; + +void type_func_prototype_init(uint32_t capacity) +{ + assert(is_power_of_two(capacity) && capacity > 1); + map.entries = CALLOC(capacity * sizeof(FuncTypeEntry)); + map.capacity = capacity; + map.max_load = TABLE_MAX_LOAD * capacity; +} + +static uint32_t hash_function(FunctionSignature *sig) +{ + uintptr_t hash = (unsigned)sig->variadic; + hash = hash * 31 + (uintptr_t)sig->returntype->type->canonical; + Decl **params = sig->params; + VECEACH(params, i) { - case TYPE_POISONED: - case TYPE_TYPEDEF: - UNREACHABLE; - case TYPE_ERRTYPE: - case TYPE_ENUM: - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_DISTINCT: - case TYPE_BITSTRUCT: - scratch_buffer_append(type->decl->external_name); - break; - case TYPE_POINTER: - type_append_name_to_scratch(type->pointer); - scratch_buffer_append_char('*'); - break; - case TYPE_FAILABLE_ANY: - scratch_buffer_append("void!"); - break; - case TYPE_FAILABLE: - if (type->failable) + Decl *param = params[i]; + hash = hash * 31 + (uintptr_t)param->type->canonical; + } + return (uint32_t)((hash >> 16) ^ hash); +} + +static int compare_function(FunctionSignature *sig, FunctionPrototype *proto) +{ + if (sig->variadic != proto->variadic) return -1; + Decl **params = sig->params; + Type **other_params = proto->params; + unsigned param_count = vec_size(params); + if (param_count != vec_size(other_params)) return -1; + if (sig->returntype->type->canonical != proto->rtype->canonical) return -1; + VECEACH(params, i) + { + Decl *param = params[i]; + Type *other_param = other_params[i]; + if (param->type->canonical != other_param->canonical) return -1; + } + return 0; +} + +void c_abi_func_create(FunctionPrototype *proto); + +static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI abi, uint32_t hash, FuncTypeEntry *entry) +{ + unsigned param_count = vec_size(sig->params); + FunctionPrototype *proto = CALLOCS(FunctionPrototype); + proto->variadic = sig->variadic; + Type *rtype = sig->returntype->type; + proto->rtype = rtype; + if (type_is_failable(rtype)) + { + proto->is_failable = true; + Type *real_return_type = rtype->failable; + proto->ret_by_ref_type = rtype->failable; + proto->ret_by_ref = real_return_type->type_kind != TYPE_VOID; + proto->abi_ret_type = type_anyerr; + } + else + { + proto->ret_by_ref_type = proto->abi_ret_type = rtype; + } + proto->call_abi = abi; + if (param_count) + { + Type **params = VECNEW(Type*, param_count); + for (unsigned i = 0; i < param_count; i++) + { + vec_add(params, sig->params[i]->type); + } + proto->params = params; + } + c_abi_func_create(proto); + + + Type *type = type_new(TYPE_FUNC, "#Function"); + type->func.prototype = proto; + type->canonical = type; + entry->key = hash; + entry->value = type; + + map.count++; + if (map.count >= map.max_load) + { + FuncTypeEntry *entries = map.entries; + uint32_t old_capacity = map.capacity; + uint32_t new_capacity = map.capacity = old_capacity << 2; + map.max_load = new_capacity * TABLE_MAX_LOAD; + FuncTypeEntry *new_map = CALLOC(new_capacity * sizeof(FuncTypeEntry)); + uint32_t new_mask = new_capacity - 1; + for (int i = 0; i < old_capacity; i++) + { + uint32_t key = entries[i].key; + if (!key) continue; + uint32_t index = key & new_mask; + while (1) { - type_append_name_to_scratch(type->failable); + entry = &new_map[index]; + if (!entry->key) + { + entry->key = key; + entry->value = entries[i].value; + break; + } + index = (index + 1) & new_mask; } - else - { - scratch_buffer_append("void"); - } - scratch_buffer_append_char('!'); - break; - case TYPE_SUBARRAY: - type_append_name_to_scratch(type->array.base); - scratch_buffer_append("[]"); - case TYPE_FLEXIBLE_ARRAY: - type_append_name_to_scratch(type->array.base); - scratch_buffer_append("[*]"); - case TYPE_VOID: - case TYPE_BOOL: - case TYPE_I8: - case TYPE_I16: - case TYPE_I32: - case TYPE_I64: - case TYPE_I128: - case TYPE_U8: - case TYPE_U16: - case TYPE_U32: - case TYPE_U64: - case TYPE_U128: - case TYPE_F16: - case TYPE_F32: - case TYPE_F64: - case TYPE_F128: - case TYPE_TYPEID: - case TYPE_ANYERR: - case TYPE_ANY: - case TYPE_VECTOR: - scratch_buffer_append(type->name); - break; - case TYPE_UNTYPED_LIST: - case TYPE_INFERRED_ARRAY: - case TYPE_TYPEINFO: - UNREACHABLE - break; - case TYPE_FUNC: - scratch_buffer_append(type->func.mangled_function_signature); - break; - case TYPE_ARRAY: - type_append_name_to_scratch(type->array.base); - scratch_buffer_append_char('['); - scratch_buffer_append_signed_int(type->array.len); - scratch_buffer_append_char(']'); - break; + } + map.entries = new_map; + } + return type; +} + +Type *type_get_func(FunctionSignature *signature, CallABI abi) +{ + uint32_t hash = hash_function(signature); + uint32_t mask = map.capacity - 1; + uint32_t index = hash & mask; + FuncTypeEntry *entries = map.entries; + FuncTypeEntry *entry; + while (1) + { + entry = &entries[index]; + if (!entry->key) + { + return func_create_new_func_proto(signature, abi, hash, entry); + } + if (entry->key == hash && compare_function(signature, entry->value->func.prototype) == 0) + { + return entry->value; + } + index = (index + 1) & mask; } } -Type *type_find_function_type(FunctionSignature *signature) -{ - scratch_buffer_clear(); - type_append_name_to_scratch(signature->rtype->type); - scratch_buffer_append_char('('); - unsigned elements = vec_size(signature->params); - for (unsigned i = 0; i < elements; i++) - { - if (i > 0) - { - scratch_buffer_append_char(','); - } - type_append_name_to_scratch(signature->params[i]->var.type_info->type); - } - if (signature->variadic == VARIADIC_RAW && elements > 0) - { - scratch_buffer_append_char(','); - } - if (signature->variadic != VARIADIC_NONE) - { - scratch_buffer_append("..."); - } - scratch_buffer_append_char(')'); - const char *mangled_signature = scratch_buffer_interned(); - Type *func_type = stable_get(&function_types, mangled_signature); - if (!func_type) - { - func_type = type_new(TYPE_FUNC, mangled_signature); - func_type->canonical = func_type; - func_type->func.signature = signature; - func_type->func.mangled_function_signature = mangled_signature; - stable_set(&function_types, mangled_signature, func_type); - } - return func_type; -} static inline void type_init_int(const char *name, Type *type, TypeKind kind, BitSizes bits) { @@ -1049,8 +1167,6 @@ void type_setup(PlatformTarget *target) type_init_int("void", &t.u0, TYPE_VOID, BITS8); - - type_create("typeinfo", &t.typeinfo, TYPE_TYPEINFO, 1, 1, 1); type_create("complist", &t.ctlist, TYPE_UNTYPED_LIST, 1, 1, 1); type_create("void!", &t.anyfail, TYPE_FAILABLE_ANY, 1, 1, 1); diff --git a/src/version.h b/src/version.h index 689b1263f..bef80b7d9 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.11" \ No newline at end of file +#define COMPILER_VERSION "PRE.12" \ No newline at end of file diff --git a/tb/tb.h b/tb/tb.h index 47be3ad91..783fdfad2 100644 --- a/tb/tb.h +++ b/tb/tb.h @@ -1,17 +1,16 @@ -// _______ _ ____ _ _ -// |__ __(_) | _ \ | | | | -// | | _ _ __ _ _| |_) | __ _ ___| | _____ _ __ __| | -// | | | | '_ \| | | | _ < / _` |/ __| |/ / _ \ '_ \ / _` | -// | | | | | | | |_| | |_) | (_| | (__| < __/ | | | (_| | -// |_| |_|_| |_|\__, |____/ \__,_|\___|_|\_\___|_| |_|\__,_| -// __/ | -// |___/ // -// It's a relatively small compiler backend all behind a -// simple C API! To get started: TODO +// _______ _ _ _ ____ ______ +// |__ __(_) | | | | _ \| ____| +// | | _| | __| | ___| |_) | |__ +// | | | | |/ _` |/ _ \ _ <| __| +// | | | | | (_| | __/ |_) | |____ +// |_| |_|_|\__,_|\___|____/|______| // -#ifndef _TINYBACKEND_H_ -#define _TINYBACKEND_H_ +// It's a relatively small compiler backend all +// behind a simple C API! +// +#ifndef _TILDEBACKEND_H_ +#define _TILDEBACKEND_H_ #ifdef __cplusplus extern "C" { @@ -27,12 +26,15 @@ extern "C" { #include #include #include -#include #include #include -#include #include +// https://semver.org/ +#define TB_VERSION_MAJOR 0 +#define TB_VERSION_MINOR 1 +#define TB_VERSION_PATCH 0 + #ifndef TB_MAX_THREADS #define TB_MAX_THREADS 16 #endif @@ -46,19 +48,15 @@ extern "C" { #define TB_MAX_FUNCTIONS (1 << 22) #endif +#define TB_API extern + #define TB_HOST_UNKNOWN 0 #define TB_HOST_X86_64 1 -// While generating the IR, it's possible to -// perform some optimizations on the spot such -// as CSE and constant folding, if this define -// is 0, the CSE is disabled. -#define TB_FRONTEND_OPT 0 - // If on, the labels aren't marked in the object file // might save on performance at the cost of some assembly // readability. -#define TB_STRIP_LABELS 0 +#define TB_STRIP_LABELS 1 #if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) #define TB_HOST_ARCH TB_HOST_X86_64 @@ -66,10 +64,8 @@ extern "C" { #define TB_HOST_ARCH TB_HOST_UNKNOWN #endif -#define TB_API extern - #define TB_NULL_REG ((TB_Register)0) -#define TB_REG_MAX ((TB_Register)INT32_MAX) +#define TB_REG_MAX ((TB_Register)INT_MAX) typedef enum TB_ArithmaticBehavior { // No overflow will assume the value does not @@ -108,6 +104,11 @@ typedef enum TB_System { TB_SYSTEM_ANDROID } TB_System; +typedef enum TB_CallingConv { + TB_CDECL, + TB_STDCALL +} TB_CallingConv; + typedef enum TB_BranchHint { TB_BRANCH_HINT_NONE, TB_BRANCH_HINT_LIKELY, @@ -115,58 +116,44 @@ typedef enum TB_BranchHint { } TB_BranchHint; typedef enum TB_OptLevel { + // no optimizer run TB_OPT_O0, - // DCE - // CSE - // MEM2REG + // run optimizer with all optimizations TB_OPT_O1, - // DCE - // CSE - // MEM2REG + // same as O1 but favors size, will aggresively deduplicate + // (at least that's the plan :P) TB_OPT_SIZE, - // DCE - // CSE - // MEM2REG - // LOOP_UNROLL + // same as O1 but favors speed, will aggresively unroll + // sometimes (at least that's the plan :P) TB_OPT_SPEED, } TB_OptLevel; -enum { +typedef enum TB_DataTypeEnum { TB_VOID, // Boolean TB_BOOL, // Integers - TB_I8, TB_I16, TB_I32, TB_I64, TB_I128, + TB_I8, TB_I16, TB_I32, TB_I64, // IEEE 754 Floating point TB_F32, TB_F64, // Pointers - // NOTE(NeGate): consider support for multiple address spaces TB_PTR, TB_MAX_TYPES -}; +} TB_DataTypeEnum; -#define TB_IS_INTEGER_TYPE(x) ((x) >= TB_I8 && (x) <= TB_I128) +#define TB_IS_INTEGER_TYPE(x) ((x) >= TB_I8 && (x) <= TB_I64) #define TB_IS_FLOAT_TYPE(x) ((x) >= TB_F32 && (x) <= TB_F64) #define TB_IS_POINTER_TYPE(x) ((x) == TB_PTR) typedef struct TB_DataType { - uint8_t type; + TB_DataTypeEnum type : 8; uint8_t count; // 0 is illegal, except on VOID, it doesn't matter there } TB_DataType; -typedef struct TB_Int128 { - uint64_t lo; - uint64_t hi; -} TB_Int128; - -typedef struct TB_FeatureConstraints { - int max_vector_width[TB_MAX_TYPES]; -} TB_FeatureConstraints; - typedef struct TB_FeatureSet { struct { bool sse3 : 1; @@ -200,77 +187,185 @@ typedef struct TB_SwitchEntry { typedef int TB_Register; typedef int TB_Reg; // short-hand -typedef int TB_FileID; -typedef int TB_ExternalID; +typedef unsigned int TB_FileID; +typedef unsigned int TB_FunctionID; +typedef unsigned int TB_ExternalID; +typedef unsigned int TB_GlobalID; +typedef unsigned int TB_InitializerID; + typedef struct TB_Module TB_Module; typedef struct TB_Function TB_Function; -typedef struct TB_FunctionOutput TB_FunctionOutput; +typedef struct TB_FunctionPrototype TB_FunctionPrototype; // ******************************* // Public macros // ******************************* -#define TB_TYPE_VOID() (TB_DataType){ .type = TB_VOID } +#ifdef __cplusplus +#define TB_TYPE_VOID TB_DataType{ TB_VOID, 1 } -#define TB_TYPE_I8(c) (TB_DataType){ .type = TB_I8, .count = c } -#define TB_TYPE_I16(c) (TB_DataType){ .type = TB_I16, .count = c } -#define TB_TYPE_I32(c) (TB_DataType){ .type = TB_I32, .count = c } -#define TB_TYPE_I64(c) (TB_DataType){ .type = TB_I64, .count = c } -#define TB_TYPE_I128(c) (TB_DataType){ .type = TB_I128, .count = c } +#define TB_TYPE_I8 TB_DataType{ TB_I8, 1 } +#define TB_TYPE_I16 TB_DataType{ TB_I16, 1 } +#define TB_TYPE_I32 TB_DataType{ TB_I32, 1 } +#define TB_TYPE_I64 TB_DataType{ TB_I64, 1 } -#define TB_TYPE_F32(c) (TB_DataType){ .type = TB_F32, .count = c } -#define TB_TYPE_F64(c) (TB_DataType){ .type = TB_F64, .count = c } +#define TB_TYPE_F32 TB_DataType{ TB_F32, 1 } +#define TB_TYPE_F64 TB_DataType{ TB_F64, 1 } -#define TB_TYPE_BOOL(c) (TB_DataType){ .type = TB_BOOL, .count = c } -#define TB_TYPE_PTR() (TB_DataType){ .type = TB_PTR, .count = 1 } +#define TB_TYPE_BOOL TB_DataType{ TB_BOOL, 1 } +#define TB_TYPE_PTR TB_DataType{ TB_PTR, 1 } -// ******************************* -// Public functions -// ******************************* -TB_API void tb_get_constraints(TB_Arch target_arch, const TB_FeatureSet* features, TB_FeatureConstraints* constraints); +#define TB_TYPE_VEC_I8(c) TB_DataType{ TB_I8, c } +#define TB_TYPE_VEC_I16(c) TB_DataType{ TB_I16, c } +#define TB_TYPE_VEC_I32(c) TB_DataType{ TB_I32, c } +#define TB_TYPE_VEC_I64(c) TB_DataType{ TB_I64, c } +#define TB_TYPE_VEC_F32(c) TB_DataType{ TB_F32, c } +#define TB_TYPE_VEC_F64(c) TB_DataType{ TB_F64, c } +#define TB_TYPE_VEC_BOOL(c) TB_DataType{ TB_BOOL, c } -TB_API TB_Module* tb_module_create(TB_Arch target_arch, TB_System target_system, const TB_FeatureSet* features, int optimization_level, int max_threads, bool preserve_ir_after_submit); -// preserve_ir_after_submit means that after the tb_module_compile_func(...) you can -// still access the IR, this comes at a higher overall memory usage cost since the -// IR is kept in memory for the lifetime of the compile but this is not an issue when -// debugging. +#else +#define TB_TYPE_VOID (TB_DataType){ TB_VOID, 1 } + +#define TB_TYPE_I8 (TB_DataType){ TB_I8, 1 } +#define TB_TYPE_I16 (TB_DataType){ TB_I16, 1 } +#define TB_TYPE_I32 (TB_DataType){ TB_I32, 1 } +#define TB_TYPE_I64 (TB_DataType){ TB_I64, 1 } + +#define TB_TYPE_F32 (TB_DataType){ TB_F32, 1 } +#define TB_TYPE_F64 (TB_DataType){ TB_F64, 1 } + +#define TB_TYPE_BOOL (TB_DataType){ TB_BOOL, 1 } +#define TB_TYPE_PTR (TB_DataType){ TB_PTR, 1 } + +#define TB_TYPE_VEC_I8(c) (TB_DataType){ TB_I8, c } +#define TB_TYPE_VEC_I16(c) (TB_DataType){ TB_I16, c } +#define TB_TYPE_VEC_I32(c) (TB_DataType){ TB_I32, c } +#define TB_TYPE_VEC_I64(c) (TB_DataType){ TB_I64, c } +#define TB_TYPE_VEC_F32(c) (TB_DataType){ TB_F32, c } +#define TB_TYPE_VEC_F64(c) (TB_DataType){ TB_F64, c } + +#endif + +//////////////////////////////// +// Module management +//////////////////////////////// +// Creates a module with the correct target and settings +TB_API TB_Module* tb_module_create(TB_Arch target_arch, TB_System target_system, const TB_FeatureSet* features); + +// Validates IR & compiles the function into machine code. +// returns false if it fails. TB_API bool tb_module_compile_func(TB_Module* m, TB_Function* f); + +// NOTE: Don't use this it's for testing purposes. TB_API size_t tb_DEBUG_module_get_full_node_count(TB_Module* m); + +// Frees all resources for the TB_Module and it's functions, globals and compiled code. TB_API void tb_module_destroy(TB_Module* m); +// Waits for the machine code generation to finish before continuing. TB_API bool tb_module_compile(TB_Module* m); -TB_API bool tb_module_export(TB_Module* m, FILE* f); + +// Exports an object file with all the machine code and symbols generated. +TB_API bool tb_module_export(TB_Module* m, const char* path); + TB_API void tb_module_export_jit(TB_Module* m); TB_API void* tb_module_get_jit_func_by_name(TB_Module* m, const char* name); TB_API void* tb_module_get_jit_func_by_id(TB_Module* m, size_t i); TB_API void* tb_module_get_jit_func(TB_Module* m, TB_Function* f); -TB_API TB_Function* tb_function_create(TB_Module* m, const char* name, TB_DataType return_dt); -TB_API TB_ExternalID tb_module_extern(TB_Module* m, const char* name); -TB_API TB_FileID tb_register_file(TB_Module* m, const char* path); +// Binds an external to an address +TB_API bool tb_jit_import(TB_Module* m, const char* name, void* address); -TB_API TB_Label tb_get_current_label(TB_Function* f); +TB_API TB_ExternalID tb_extern_create(TB_Module* m, const char* name); +TB_API TB_FileID tb_file_create(TB_Module* m, const char* path); + +//////////////////////////////// +// Function Prototypes +//////////////////////////////// +// creates a function prototype used to define a function's parameters and +// return type. +// +// function prototypes do not get freed individually and last for the entire run +// of the backend, they can also be reused for multiple functions which have matching +// signatures. +TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv conv, TB_DataType return_dt, int num_params, bool has_varargs); + +// adds a parameter to the function prototype, TB doesn't support struct +// parameters so the frontend must lower them to pointers or any other type +// depending on their preferred ABI. +TB_API void tb_prototype_add_param(TB_FunctionPrototype* p, TB_DataType dt); + +// same as tb_prototype_add_param(...) but takes an array +TB_API void tb_prototype_add_params(TB_FunctionPrototype* p, size_t count, const TB_DataType* dt); + +// adds a parameter to the function prototype, TB doesn't support struct +// parameters so the frontend must lower them to pointers or any other type +// depending on their preferred ABI. +TB_API TB_Function* tb_prototype_build(TB_Module* m, TB_FunctionPrototype* p, const char* name); + +//////////////////////////////// +// Constant Initializers +//////////////////////////////// +// NOTE: the max objects is a cap and thus it can be bigger than the actual +// number used. +TB_API TB_InitializerID tb_initializer_create(TB_Module* m, size_t size, size_t align, size_t max_objects); + +// returns a buffer which the user can fill to then have represented in the initializer +TB_API void* tb_initializer_add_region(TB_Module* m, TB_InitializerID id, size_t offset, size_t size); + +//////////////////////////////// +// Constant Initializers +//////////////////////////////// +TB_API TB_GlobalID tb_global_create(TB_Module* m, const char* name, TB_InitializerID initializer); + +//////////////////////////////// +// Function IR Generation +//////////////////////////////// +TB_API TB_Function* tb_function_clone(TB_Module* m, TB_Function* f, const char* name); +TB_API void tb_function_print(TB_Function* f, FILE* out); +TB_API void tb_function_free(TB_Function* f); + +TB_API TB_Label tb_inst_get_current_label(TB_Function* f); TB_API void tb_inst_loc(TB_Function* f, TB_FileID file, int line); -TB_API TB_Register tb_inst_param(TB_Function* f, TB_DataType dt); -TB_API TB_Register tb_inst_param_addr(TB_Function* f, TB_Register param); +TB_API TB_Register tb_inst_param(TB_Function* f, int param_id); +TB_API TB_Register tb_inst_param_addr(TB_Function* f, int param_id); +TB_API TB_Register tb_inst_fpxt(TB_Function* f, TB_Register src, TB_DataType dt); TB_API TB_Register tb_inst_sxt(TB_Function* f, TB_Register src, TB_DataType dt); TB_API TB_Register tb_inst_zxt(TB_Function* f, TB_Register src, TB_DataType dt); +TB_API TB_Register tb_inst_trunc(TB_Function* f, TB_Register src, TB_DataType dt); +TB_API TB_Register tb_inst_int2ptr(TB_Function* f, TB_Register src); +TB_API TB_Register tb_inst_ptr2int(TB_Function* f, TB_Register src, TB_DataType dt); TB_API TB_Register tb_inst_local(TB_Function* f, uint32_t size, uint32_t alignment); TB_API TB_Register tb_inst_load(TB_Function* f, TB_DataType dt, TB_Register addr, uint32_t alignment); TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Register addr, TB_Register val, uint32_t alignment); -TB_API TB_Register tb_inst_iconst(TB_Function* f, TB_DataType dt, uint64_t imm); -TB_API TB_Register tb_inst_iconst128(TB_Function* f, TB_DataType dt, TB_Int128 imm); +TB_API TB_Register tb_inst_volatile_load(TB_Function* f, TB_DataType dt, TB_Register addr, uint32_t alignment); +TB_API void tb_inst_volatile_store(TB_Function* f, TB_DataType dt, TB_Register addr, TB_Register val, uint32_t alignment); +TB_API void tb_inst_initialize_mem(TB_Function* f, TB_Register addr, TB_InitializerID src); + +TB_API TB_Register tb_inst_iconst(TB_Function* f, TB_DataType dt, uint64_t imm); TB_API TB_Register tb_inst_fconst(TB_Function* f, TB_DataType dt, double imm); +// string is a UTF-8 null terminated string +TB_API TB_Register tb_inst_const_cstr(TB_Function* f, const char* str); + +// string is a slice of bytes +TB_API TB_Register tb_inst_const_string(TB_Function* f, const char* str, size_t len); + TB_API TB_Register tb_inst_array_access(TB_Function* f, TB_Register base, TB_Register index, uint32_t stride); TB_API TB_Register tb_inst_member_access(TB_Function* f, TB_Register base, int32_t offset); + +TB_API TB_Register tb_inst_get_func_address(TB_Function* f, const TB_Function* target); +TB_API TB_Register tb_inst_get_extern_address(TB_Function* f, TB_ExternalID target); +TB_API TB_Register tb_inst_get_global_address(TB_Function* f, TB_GlobalID target); + TB_API TB_Register tb_inst_call(TB_Function* f, TB_DataType dt, const TB_Function* target, size_t param_count, const TB_Register* params); +TB_API TB_Register tb_inst_vcall(TB_Function* f, TB_DataType dt, TB_Register target, size_t param_count, const TB_Register* params); TB_API TB_Register tb_inst_ecall(TB_Function* f, TB_DataType dt, const TB_ExternalID target, size_t param_count, const TB_Register* params); TB_API void tb_inst_memset(TB_Function* f, TB_Register dst, TB_Register val, TB_Register size, int align); @@ -299,32 +394,74 @@ TB_API TB_Register tb_inst_fdiv(TB_Function* f, TB_DataType dt, TB_Register a, T TB_API TB_Register tb_inst_cmp_eq(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); TB_API TB_Register tb_inst_cmp_ne(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_slt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_sle(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_sgt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_sge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); - -TB_API TB_Register tb_inst_cmp_ult(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_ule(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_ugt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Register tb_inst_cmp_uge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); +TB_API TB_Register tb_inst_cmp_ilt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, bool signedness); +TB_API TB_Register tb_inst_cmp_ile(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, bool signedness); +TB_API TB_Register tb_inst_cmp_igt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, bool signedness); +TB_API TB_Register tb_inst_cmp_ige(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b, bool signedness); TB_API TB_Register tb_inst_cmp_flt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); TB_API TB_Register tb_inst_cmp_fle(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); TB_API TB_Register tb_inst_cmp_fgt(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); TB_API TB_Register tb_inst_cmp_fge(TB_Function* f, TB_DataType dt, TB_Register a, TB_Register b); -TB_API TB_Label tb_inst_new_label_id(TB_Function* f); // Gives you a reference to a local label, doesn't place it anywhere. +TB_API TB_Label tb_inst_new_label_id(TB_Function* f); TB_API TB_Register tb_inst_phi2(TB_Function* f, TB_DataType dt, TB_Label a_label, TB_Register a, TB_Label b_label, TB_Register b); TB_API TB_Register tb_inst_label(TB_Function* f, TB_Label id); TB_API void tb_inst_goto(TB_Function* f, TB_Label id); TB_API TB_Register tb_inst_if(TB_Function* f, TB_Register cond, TB_Label if_true, TB_Label if_false); TB_API void tb_inst_switch(TB_Function* f, TB_DataType dt, TB_Register key, TB_Label default_label, size_t entry_count, const TB_SwitchEntry* entries); -TB_API void tb_inst_ret(TB_Function* f, TB_DataType dt, TB_Register value); +TB_API void tb_inst_ret(TB_Function* f, TB_Register value); -TB_API void tb_function_print(TB_Function* f, FILE* out); +//////////////////////////////// +// Optimizer +//////////////////////////////// +TB_API void tb_function_optimize(TB_Function* f, TB_OptLevel opt); + +TB_API bool tb_opt_mem2reg(TB_Function* f); +TB_API bool tb_opt_dead_expr_elim(TB_Function* f); +TB_API bool tb_opt_dead_block_elim(TB_Function* f); +TB_API bool tb_opt_fold(TB_Function* f); +TB_API bool tb_opt_load_elim(TB_Function* f); +TB_API bool tb_opt_inline(TB_Function* f); +TB_API bool tb_opt_hoist_locals(TB_Function* f); +TB_API bool tb_opt_canonicalize(TB_Function* f); +TB_API bool tb_opt_deshort_circuit(TB_Function* f); +TB_API bool tb_opt_remove_pass_node(TB_Function* f); +TB_API bool tb_opt_strength_reduction(TB_Function* f); +TB_API bool tb_opt_compact_dead_regs(TB_Function* f); +TB_API bool tb_opt_copy_elision(TB_Function* f); + +//////////////////////////////// +// IR access +//////////////////////////////// +TB_API TB_FunctionID tb_function_get_id(TB_Module* m, TB_Function* f); +TB_API TB_Function* tb_get_function_by_id(TB_Module* m, TB_FunctionID id); + +TB_API TB_Register tb_node_get_last_register(TB_Function* f); +TB_API TB_DataType tb_node_get_data_type(TB_Function* f, TB_Register r); + +// Returns the size and alignment of a LOCAL node, both must +// be valid addresses +TB_API void tb_get_function_get_local_info(TB_Function* f, TB_Register r, int* size, int* align); + +// is an IF node? +TB_API bool tb_node_is_conditional(TB_Function* f, TB_Register r); + +// is an IF, GOTO, RET, SWITCH, or LABEL node? +TB_API bool tb_node_is_terminator(TB_Function* f, TB_Register r); + +TB_API bool tb_node_is_label(TB_Function* f, TB_Register r); + +TB_API TB_Register tb_node_store_get_address(TB_Function* f, TB_Register r); +TB_API TB_Register tb_node_store_get_value(TB_Function* f, TB_Register r); + +TB_API TB_Register tb_node_load_get_address(TB_Function* f, TB_Register r); + +// These work for any floating point, comparison, or integer arithmatic ops +TB_API TB_Register tb_node_arith_get_left(TB_Function* f, TB_Register r); +TB_API TB_Register tb_node_arith_get_right(TB_Function* f, TB_Register r); #ifdef __cplusplus } diff --git a/test/test_suite/expressions/casts/cast_func_to_various.c3 b/test/test_suite/expressions/casts/cast_func_to_various.c3 index 9ef156925..6c747a215 100644 --- a/test/test_suite/expressions/casts/cast_func_to_various.c3 +++ b/test/test_suite/expressions/casts/cast_func_to_various.c3 @@ -21,17 +21,17 @@ fn void test1(Func arg) fn void test2(Func arg) { - ichar b = (ichar)(arg); // #error: 'Func' (func void(int)) to 'ichar' + ichar b = (ichar)(arg); // #error: 'Func' (fn void(int)) to 'ichar' } fn void test3(Func arg) { - uint c = (uint)(arg); // #error: 'Func' (func void(int)) to 'uint' + uint c = (uint)(arg); // #error: 'Func' (fn void(int)) to 'uint' } fn void test4(Func arg) { - float d = (float)(arg); // #error: 'Func' (func void(int)) to 'float' + float d = (float)(arg); // #error: 'Func' (fn void(int)) to 'float' } fn void test7(Func arg) @@ -39,7 +39,7 @@ fn void test7(Func arg) usize g = (usize)(arg); FuncOther k = (FuncOther)(arg); FuncSame l = (FuncSame)(arg); - FuncOther ke = arg; // #error: 'Func' (func void(int)) to 'FuncOther' (func bool(char*)) + FuncOther ke = arg; // #error: 'Func' (fn void(int)) to 'FuncOther' (fn bool(char*)) FuncSame fe = arg; - Enum j = (Enum)(arg); // #error: 'Func' (func void(int)) to 'Enum' + Enum j = (Enum)(arg); // #error: 'Func' (fn void(int)) to 'Enum' }