mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Refactored function pointer.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -106,37 +106,41 @@ ABIArgInfo *win64_reclassify_hva_arg(Regs *regs, Type *type, ABIArgInfo *info)
|
||||
return info;
|
||||
}
|
||||
|
||||
void win64_vector_call_args(Regs *regs, FunctionSignature *signature, bool is_vector, bool is_reg)
|
||||
void win64_vector_call_args(Regs *regs, FunctionPrototype *prototype, bool is_vector, bool is_reg)
|
||||
{
|
||||
static const unsigned max_param_vector_calls_as_reg = 6;
|
||||
unsigned count = 0;
|
||||
Decl **params = signature->params;
|
||||
VECEACH(params, i)
|
||||
Type **params = prototype->params;
|
||||
unsigned param_count = vec_size(prototype->params);
|
||||
if (param_count)
|
||||
{
|
||||
Decl *param = params[i];
|
||||
if (count < max_param_vector_calls_as_reg)
|
||||
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
|
||||
for (unsigned i = 0; i < param_count; i++)
|
||||
{
|
||||
param->var.abi_info = win64_classify(regs, param->type, false, is_vector, is_reg);
|
||||
Type *type = params[i];
|
||||
if (count < max_param_vector_calls_as_reg)
|
||||
{
|
||||
args[i] = win64_classify(regs, type, false, is_vector, is_reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot be passed in registers pretend no registers.
|
||||
unsigned float_regs = regs->float_regs;
|
||||
regs->float_regs = 0;
|
||||
args[i] = win64_classify(regs, type, false, is_vector, is_reg);
|
||||
regs->float_regs = float_regs;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
else
|
||||
for (unsigned i = 0; i < param_count; i++)
|
||||
{
|
||||
// Cannot be passed in registers pretend no registers.
|
||||
unsigned float_regs = regs->float_regs;
|
||||
regs->float_regs = 0;
|
||||
param->var.abi_info = win64_classify(regs, param->type, false, is_vector, is_reg);
|
||||
regs->float_regs = float_regs;
|
||||
args[i] = win64_reclassify_hva_arg(regs, params[i], args[i]);
|
||||
}
|
||||
count++;
|
||||
prototype->abi_args = args;
|
||||
}
|
||||
VECEACH(params, i)
|
||||
{
|
||||
Decl *param = params[i];
|
||||
param->var.abi_info = win64_reclassify_hva_arg(regs, param->type, param->var.abi_info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void c_abi_func_create_win64(FunctionSignature *signature)
|
||||
void c_abi_func_create_win64(FunctionPrototype *prototype)
|
||||
{
|
||||
// allow calling sysv?
|
||||
|
||||
@@ -144,7 +148,7 @@ void c_abi_func_create_win64(FunctionSignature *signature)
|
||||
Regs regs = { 0, 0 };
|
||||
bool is_reg_call = false;
|
||||
bool is_vector_call = false;
|
||||
switch (signature->call_abi)
|
||||
switch (prototype->call_abi)
|
||||
{
|
||||
case CALL_X86_VECTOR:
|
||||
regs.float_regs = 4;
|
||||
@@ -159,22 +163,14 @@ void c_abi_func_create_win64(FunctionSignature *signature)
|
||||
break;
|
||||
}
|
||||
|
||||
Type *rtype = abi_rtype(signature);
|
||||
if (IS_FAILABLE(signature->rtype))
|
||||
prototype->ret_abi_info = win64_classify(®s, prototype->abi_ret_type, true, is_vector_call, is_reg_call);
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
signature->failable_abi_info = win64_classify(®s, type_anyerr, true, is_vector_call, is_reg_call);
|
||||
if (rtype->type_kind != TYPE_VOID)
|
||||
{
|
||||
signature->ret_abi_info = win64_classify(®s, type_get_ptr(type_lowering(rtype)), false, is_vector_call, is_reg_call);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
signature->ret_abi_info = win64_classify(®s, rtype, true, is_vector_call, is_reg_call);
|
||||
prototype->ret_by_ref_abi_info = win64_classify(®s, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), false, is_vector_call, is_reg_call);
|
||||
}
|
||||
|
||||
// Set up parameter registers.
|
||||
switch (signature->call_abi)
|
||||
switch (prototype->call_abi)
|
||||
{
|
||||
case CALL_X86_VECTOR:
|
||||
regs.float_regs = 6;
|
||||
@@ -190,12 +186,19 @@ void c_abi_func_create_win64(FunctionSignature *signature)
|
||||
}
|
||||
if (is_vector_call)
|
||||
{
|
||||
win64_vector_call_args(®s, signature, is_vector_call, is_reg_call);
|
||||
win64_vector_call_args(®s, prototype, is_vector_call, is_reg_call);
|
||||
return;
|
||||
}
|
||||
Decl **params = signature->params;
|
||||
VECEACH(params, i)
|
||||
|
||||
Type **params = prototype->params;
|
||||
unsigned param_count = vec_size(prototype->params);
|
||||
if (param_count)
|
||||
{
|
||||
params[i]->var.abi_info = win64_classify(®s, params[i]->type, false, is_vector_call, is_reg_call);
|
||||
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
|
||||
for (unsigned i = 0; i < param_count; i++)
|
||||
{
|
||||
args[i] = win64_classify(®s, params[i], false, is_vector_call, is_reg_call);
|
||||
}
|
||||
prototype->abi_args = args;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,7 +600,7 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void c_abi_func_create_x86(FunctionSignature *signature)
|
||||
void c_abi_func_create_x86(FunctionPrototype *prototype)
|
||||
{
|
||||
// 1. Calculate the registers we have available
|
||||
// Normal: 0 / 0 (3 on win32 struct ABI)
|
||||
@@ -608,7 +608,7 @@ void c_abi_func_create_x86(FunctionSignature *signature)
|
||||
// Vector: 2 / 6
|
||||
// Fast: 2 / 3
|
||||
Regs regs = { 0, 0 };
|
||||
switch (signature->call_abi)
|
||||
switch (prototype->call_abi)
|
||||
{
|
||||
case CALL_C:
|
||||
if (platform_target.x86.is_win32_float_struct_abi)
|
||||
@@ -641,18 +641,10 @@ void c_abi_func_create_x86(FunctionSignature *signature)
|
||||
|
||||
// 4. Classify the return type. In the case of failable, we need to classify the failable itself as the
|
||||
// return type.
|
||||
Type *rtype = abi_rtype(signature);
|
||||
if (IS_FAILABLE(signature->rtype))
|
||||
prototype->ret_abi_info = x86_classify_return(prototype->call_abi, ®s, prototype->abi_ret_type);
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
signature->failable_abi_info = x86_classify_return(signature->call_abi, ®s, type_anyerr);
|
||||
if (rtype->type_kind != TYPE_VOID)
|
||||
{
|
||||
signature->ret_abi_info = x86_classify_argument(signature->call_abi, ®s, type_get_ptr(type_lowering(rtype)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
signature->ret_abi_info = x86_classify_return(signature->call_abi, ®s, rtype);
|
||||
prototype->ret_by_ref_abi_info = x86_classify_argument(prototype->call_abi, ®s, type_get_ptr(type_lowering(prototype->ret_by_ref_type)));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -666,16 +658,22 @@ void c_abi_func_create_x86(FunctionSignature *signature)
|
||||
runVectorCallFirstPass(FI, State);
|
||||
*/
|
||||
|
||||
if (signature->call_abi == CALL_X86_VECTOR)
|
||||
if (prototype->call_abi == CALL_X86_VECTOR)
|
||||
{
|
||||
FATAL_ERROR("X86 vector call not supported");
|
||||
}
|
||||
else
|
||||
{
|
||||
Decl **params = signature->params;
|
||||
VECEACH(params, i)
|
||||
Type **params = prototype->params;
|
||||
unsigned param_count = vec_size(prototype->params);
|
||||
if (param_count)
|
||||
{
|
||||
params[i]->var.abi_info = x86_classify_argument(signature->call_abi, ®s, params[i]->type);
|
||||
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
|
||||
for (unsigned i = 0; i < param_count; i++)
|
||||
{
|
||||
args[i] = x86_classify_argument(prototype->call_abi, ®s, params[i]);
|
||||
}
|
||||
prototype->abi_args = args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl)
|
||||
{
|
||||
static LLVMTypeRef params[MAX_PARAMS];
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_VAR:
|
||||
@@ -19,18 +18,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl)
|
||||
case DECL_BITSTRUCT:
|
||||
return llvm_get_type(c, decl->bitstruct.base_type->type);
|
||||
case DECL_FUNC:
|
||||
{
|
||||
VECEACH(decl->func_decl.function_signature.params, i)
|
||||
{
|
||||
params[i] = llvm_get_type(c, decl->func_decl.function_signature.params[i]->type);
|
||||
}
|
||||
unsigned param_size = vec_size(decl->func_decl.function_signature.params);
|
||||
return LLVMFunctionType(llvm_get_type(c, decl->func_decl.function_signature.rtype->type),
|
||||
params,
|
||||
param_size,
|
||||
decl->func_decl.function_signature.variadic == VARIADIC_RAW);
|
||||
|
||||
}
|
||||
UNREACHABLE
|
||||
case DECL_TYPEDEF:
|
||||
return llvm_get_type(c, decl->typedef_decl.type_info->type);
|
||||
case DECL_DISTINCT:
|
||||
@@ -232,15 +220,12 @@ static inline void add_func_type_param(GenContext *context, Type *param_type, AB
|
||||
LLVMTypeRef llvm_func_type(GenContext *context, Type *type)
|
||||
{
|
||||
LLVMTypeRef *params = NULL;
|
||||
FunctionSignature *signature = type->func.signature;
|
||||
FunctionPrototype *prototype = type->func.prototype;
|
||||
|
||||
LLVMTypeRef return_type = NULL;
|
||||
|
||||
Type *rtype = signature->rtype->type;
|
||||
bool is_failable = type_is_failable(rtype);
|
||||
if (is_failable) rtype = rtype->failable;
|
||||
Type *real_return_type = is_failable ? type_anyerr : rtype;
|
||||
ABIArgInfo *ret_arg_info = is_failable ? signature->failable_abi_info : signature->ret_abi_info;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
ABIArgInfo *ret_arg_info = prototype->ret_abi_info;
|
||||
|
||||
ret_arg_info->param_index_end = 0;
|
||||
ret_arg_info->param_index_start = 0;
|
||||
@@ -250,7 +235,7 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type)
|
||||
case ABI_ARG_EXPAND:
|
||||
UNREACHABLE;
|
||||
case ABI_ARG_INDIRECT:
|
||||
vec_add(params, llvm_get_ptr_type(context, real_return_type));
|
||||
vec_add(params, llvm_get_ptr_type(context, call_return_type));
|
||||
return_type = llvm_get_type(context, type_void);
|
||||
break;
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
@@ -276,28 +261,28 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type)
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
return_type = llvm_get_type(context, real_return_type);
|
||||
return_type = llvm_get_type(context, call_return_type);
|
||||
break;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
assert(!abi_info_should_flatten(ret_arg_info));
|
||||
return_type = llvm_get_coerce_type(context, ret_arg_info);
|
||||
if (!return_type) return_type = llvm_get_type(context, real_return_type);
|
||||
if (!return_type) return_type = llvm_get_type(context, call_return_type);
|
||||
break;
|
||||
}
|
||||
|
||||
// If it's failable and it's not void (meaning ret_abi_info will be NULL)
|
||||
if (is_failable && signature->ret_abi_info)
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
add_func_type_param(context, type_get_ptr(rtype), signature->ret_abi_info, ¶ms);
|
||||
add_func_type_param(context, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), prototype->ret_by_ref_abi_info, ¶ms);
|
||||
}
|
||||
|
||||
// Add in all of the required arguments.
|
||||
VECEACH(signature->params, i)
|
||||
VECEACH(prototype->params, i)
|
||||
{
|
||||
add_func_type_param(context, signature->params[i]->type, signature->params[i]->var.abi_info, ¶ms);
|
||||
add_func_type_param(context, prototype->params[i], prototype->abi_args[i], ¶ms);
|
||||
}
|
||||
|
||||
return LLVMFunctionType(return_type, params, vec_size(params), signature->variadic == VARIADIC_RAW);
|
||||
return LLVMFunctionType(return_type, params, vec_size(params), prototype->variadic == VARIADIC_RAW);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "PRE.11"
|
||||
#define COMPILER_VERSION "PRE.12"
|
||||
321
tb/tb.h
321
tb/tb.h
@@ -1,17 +1,16 @@
|
||||
// _______ _ ____ _ _
|
||||
// |__ __(_) | _ \ | | | |
|
||||
// | | _ _ __ _ _| |_) | __ _ ___| | _____ _ __ __| |
|
||||
// | | | | '_ \| | | | _ < / _` |/ __| |/ / _ \ '_ \ / _` |
|
||||
// | | | | | | | |_| | |_) | (_| | (__| < __/ | | | (_| |
|
||||
// |_| |_|_| |_|\__, |____/ \__,_|\___|_|\_\___|_| |_|\__,_|
|
||||
// __/ |
|
||||
// |___/
|
||||
//
|
||||
// It's a relatively small compiler backend all behind a
|
||||
// simple C API! To get started: TODO
|
||||
// _______ _ _ _ ____ ______
|
||||
// |__ __(_) | | | | _ \| ____|
|
||||
// | | _| | __| | ___| |_) | |__
|
||||
// | | | | |/ _` |/ _ \ _ <| __|
|
||||
// | | | | | (_| | __/ |_) | |____
|
||||
// |_| |_|_|\__,_|\___|____/|______|
|
||||
//
|
||||
#ifndef _TINYBACKEND_H_
|
||||
#define _TINYBACKEND_H_
|
||||
// It's a relatively small compiler backend all
|
||||
// behind a simple C API!
|
||||
//
|
||||
#ifndef _TILDEBACKEND_H_
|
||||
#define _TILDEBACKEND_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -27,12 +26,15 @@ extern "C" {
|
||||
#include <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
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user