Refactored function pointer.

This commit is contained in:
Christoffer Lerno
2022-01-07 19:13:07 +01:00
parent 8a840746f6
commit a176ae353b
30 changed files with 997 additions and 697 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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)
{

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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(&regs, prototype->abi_ret_type, true, is_vector_call, is_reg_call);
if (prototype->ret_by_ref)
{
signature->failable_abi_info = win64_classify(&regs, type_anyerr, true, is_vector_call, is_reg_call);
if (rtype->type_kind != TYPE_VOID)
{
signature->ret_abi_info = win64_classify(&regs, type_get_ptr(type_lowering(rtype)), false, is_vector_call, is_reg_call);
}
}
else
{
signature->ret_abi_info = win64_classify(&regs, rtype, true, is_vector_call, is_reg_call);
prototype->ret_by_ref_abi_info = win64_classify(&regs, 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(&regs, signature, is_vector_call, is_reg_call);
win64_vector_call_args(&regs, 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(&regs, 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(&regs, params[i], false, is_vector_call, is_reg_call);
}
prototype->abi_args = args;
}
}

View File

@@ -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;
}
}

View File

@@ -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, &regs, prototype->abi_ret_type);
if (prototype->ret_by_ref)
{
signature->failable_abi_info = x86_classify_return(signature->call_abi, &regs, type_anyerr);
if (rtype->type_kind != TYPE_VOID)
{
signature->ret_abi_info = x86_classify_argument(signature->call_abi, &regs, type_get_ptr(type_lowering(rtype)));
}
}
else
{
signature->ret_abi_info = x86_classify_return(signature->call_abi, &regs, rtype);
prototype->ret_by_ref_abi_info = x86_classify_argument(prototype->call_abi, &regs, 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, &regs, 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, &regs, params[i]);
}
prototype->abi_args = args;
}
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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());

View File

@@ -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");

View File

@@ -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, &params);
add_func_type_param(context, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), prototype->ret_by_ref_abi_info, &params);
}
// Add in all of the required arguments.
VECEACH(signature->params, i)
VECEACH(prototype->params, i)
{
add_func_type_param(context, signature->params[i]->type, signature->params[i]->var.abi_info, &params);
add_func_type_param(context, prototype->params[i], prototype->abi_args[i], &params);
}
return LLVMFunctionType(return_type, params, vec_size(params), signature->variadic == VARIADIC_RAW);
return LLVMFunctionType(return_type, params, vec_size(params), prototype->variadic == VARIADIC_RAW);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 <type>[], so we get the <type>
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
};

View File

@@ -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);
void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable);

View File

@@ -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.

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -1 +1 @@
#define COMPILER_VERSION "PRE.11"
#define COMPILER_VERSION "PRE.12"

321
tb/tb.h
View File

@@ -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 <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <inttypes.h>
// 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
}

View File

@@ -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'
}