mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Refactored some of the llvm lowering. Work on TB. Disabled mimalloc due to issues with LLVM.
This commit is contained in:
@@ -9,9 +9,7 @@ set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
option(C3_USE_MIMALLOC "Use built-in mimalloc" ON)
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
|
||||
if(WIN32)
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
endif()
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
|
||||
if(C3_USE_MIMALLOC)
|
||||
option(MI_BUILD_TESTS OFF)
|
||||
@@ -150,9 +148,11 @@ add_executable(c3c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/module.c
|
||||
src/compiler/number.c
|
||||
src/compiler/parse_expr.c
|
||||
@@ -173,6 +173,10 @@ add_executable(c3c
|
||||
src/compiler/symtab.c
|
||||
src/compiler/target.c
|
||||
src/compiler/tb_codegen.c
|
||||
src/compiler/tilde_codegen.c
|
||||
src/compiler/tilde_codegen_instr.c
|
||||
src/compiler/tilde_codegen_value.c
|
||||
src/compiler/tilde_codegen_storeload.c
|
||||
src/compiler_tests/benchmark.c
|
||||
src/compiler_tests/tests.c
|
||||
src/compiler/tokens.c
|
||||
@@ -188,7 +192,11 @@ add_executable(c3c
|
||||
src/utils/toml.c src/build/project.c
|
||||
src/utils/vmem.c
|
||||
src/utils/vmem.h
|
||||
src/utils/whereami.c)
|
||||
src/utils/whereami.c
|
||||
src/compiler/tilde_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/tilde_codegen_expr.c
|
||||
src/compiler/tilde_codegen_stmt.c)
|
||||
|
||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
|
||||
@@ -480,3 +480,17 @@ static Ast poison_ast = { .ast_kind = AST_POISONED };
|
||||
Ast *poisoned_ast = &poison_ast;
|
||||
|
||||
|
||||
bool ast_is_not_empty(Ast *ast)
|
||||
{
|
||||
if (!ast) return false;
|
||||
if (ast->ast_kind != AST_COMPOUND_STMT) return true;
|
||||
AstId first = ast->compound_stmt.first_stmt;
|
||||
if (first)
|
||||
{
|
||||
Ast *stmt = astptr(first);
|
||||
if (stmt->next) return true;
|
||||
if (ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end) return true;
|
||||
return ast_is_not_empty(stmt);
|
||||
}
|
||||
return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end;
|
||||
}
|
||||
@@ -32,4 +32,8 @@ static inline bool abi_type_is_valid(AbiType type)
|
||||
return type.int_bits_plus_1 != 0;
|
||||
}
|
||||
|
||||
static inline bool abi_info_should_flatten(ABIArgInfo *info)
|
||||
{
|
||||
return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten;
|
||||
}
|
||||
|
||||
|
||||
@@ -372,6 +372,7 @@ typedef struct VarDecl_
|
||||
{
|
||||
// Variable
|
||||
void *failable_ref;
|
||||
int tb_failable_reg;
|
||||
};
|
||||
};
|
||||
struct
|
||||
@@ -587,6 +588,7 @@ typedef struct Decl_
|
||||
union
|
||||
{
|
||||
void *backend_ref;
|
||||
int tb_register;
|
||||
void *backend_value;
|
||||
};
|
||||
const char *extname;
|
||||
@@ -1125,6 +1127,7 @@ typedef struct
|
||||
struct
|
||||
{
|
||||
void *exit_block;
|
||||
uint64_t exit_val;
|
||||
} codegen;
|
||||
} AstDeferStmt;
|
||||
|
||||
@@ -1562,6 +1565,7 @@ typedef struct FunctionPrototype_
|
||||
ABIArgInfo *ret_abi_info;
|
||||
ABIArgInfo *ret_by_ref_abi_info;
|
||||
ABIArgInfo **abi_args;
|
||||
void *tb_prototype;
|
||||
} FunctionPrototype;
|
||||
|
||||
extern GlobalContext global_context;
|
||||
@@ -1660,7 +1664,7 @@ ARENA_DEF(type_info, TypeInfo)
|
||||
|
||||
static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; }
|
||||
static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return false; }
|
||||
|
||||
bool ast_is_not_empty(Ast *ast);
|
||||
static inline Ast *new_ast(AstKind kind, SourceSpan range)
|
||||
{
|
||||
Ast *ast = ast_calloc();
|
||||
|
||||
@@ -55,6 +55,12 @@ static void gencontext_destroy(GenContext *context)
|
||||
free(context);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error_value)
|
||||
{
|
||||
return LLVMBuildICmp(c->builder, LLVMIntEQ, error_value, llvm_get_zero(c, type_anyerr), "not_err");
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, AlignSize align, bool bitcast)
|
||||
{
|
||||
|
||||
@@ -64,56 +70,6 @@ LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint
|
||||
|
||||
}
|
||||
|
||||
void llvm_emit_memclear(GenContext *c, BEValue *ref)
|
||||
{
|
||||
llvm_value_addr(c, ref);
|
||||
Type *type = ref->type;
|
||||
if (!type_is_abi_aggregate(type))
|
||||
{
|
||||
llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, type));
|
||||
return;
|
||||
}
|
||||
Type *single_type = type_abi_find_single_struct_element(type);
|
||||
|
||||
if (single_type && !type_is_abi_aggregate(single_type))
|
||||
{
|
||||
BEValue element;
|
||||
llvm_value_set_address_align(&element, llvm_emit_bitcast(c, ref->value, type_get_ptr(single_type)), single_type, (unsigned)ref->alignment);
|
||||
llvm_emit_memclear(c, &element);
|
||||
return;
|
||||
}
|
||||
if (type_size(type) <= 16)
|
||||
{
|
||||
if (type->type_kind == TYPE_STRUCT)
|
||||
{
|
||||
Decl *decl = type->decl;
|
||||
Decl **members = decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (!type_size(members[i]->type)) continue;
|
||||
BEValue member_ref;
|
||||
llvm_emit_struct_member_ref(c, ref, &member_ref, i);
|
||||
llvm_emit_memclear(c, &member_ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
LLVMTypeRef array_type = llvm_get_type(c, type);
|
||||
for (unsigned i = 0; i < type->array.len; i++)
|
||||
{
|
||||
AlignSize align;
|
||||
LLVMValueRef element_ptr = llvm_emit_array_gep_raw(c, ref->value, array_type, i, ref->alignment, &align);
|
||||
BEValue be_value;
|
||||
llvm_value_set_address_align(&be_value, element_ptr, type->array.base, align);
|
||||
llvm_emit_memclear(c, &be_value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified)
|
||||
{
|
||||
if (diff == 1) return LLVMConstNull(element_type);
|
||||
@@ -290,7 +246,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_
|
||||
{
|
||||
BEValue value;
|
||||
llvm_emit_expr(c, &value, const_init->init_value);
|
||||
return llvm_value_rvalue_store(c, &value);
|
||||
return llvm_load_value_store(c, &value);
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -337,7 +293,10 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value)
|
||||
BEValue member;
|
||||
llvm_emit_subarray_pointer(c, value, &member);
|
||||
llvm_value_rvalue(c, &member);
|
||||
llvm_value_set_address_align(value, member.value, type_get_ptr(value->type->array.base), type_abi_alignment(value->type->array.base));
|
||||
llvm_value_set_address(value,
|
||||
member.value,
|
||||
type_get_ptr(value->type->array.base),
|
||||
type_abi_alignment(value->type->array.base));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@@ -388,7 +347,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
|
||||
else
|
||||
{
|
||||
llvm_emit_expr(c, &value, decl->var.init_expr);
|
||||
init_value = llvm_value_rvalue_store(c, &value);
|
||||
init_value = llvm_load_value_store(c, &value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,7 +494,6 @@ void llvm_emit_local_var_alloca(GenContext *c, Decl *decl)
|
||||
{
|
||||
llvm_emit_debug_local_var(c, decl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -740,28 +698,13 @@ void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i)
|
||||
llvm_value_set(value, llvm_const_int(c, type, i), type);
|
||||
}
|
||||
|
||||
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(llvm_value || type == type_void);
|
||||
value->value = llvm_value;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = BE_VALUE;
|
||||
value->type = type;
|
||||
}
|
||||
|
||||
|
||||
bool llvm_value_is_const(BEValue *value)
|
||||
{
|
||||
return LLVMIsConstant(value->value);
|
||||
}
|
||||
|
||||
void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment)
|
||||
{
|
||||
value->value = llvm_value;
|
||||
value->alignment = alignment;
|
||||
value->kind = BE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
void llvm_value_set_decl(BEValue *value, Decl *decl)
|
||||
{
|
||||
@@ -774,73 +717,10 @@ void llvm_value_set_decl(BEValue *value, Decl *decl)
|
||||
llvm_value_set_decl_address(value, decl);
|
||||
}
|
||||
|
||||
void llvm_value_set_decl_address(BEValue *value, Decl *decl)
|
||||
{
|
||||
decl = decl_flatten(decl);
|
||||
llvm_value_set_address(value, decl_ref(decl), decl->type);
|
||||
value->alignment = decl->alignment;
|
||||
|
||||
if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl))
|
||||
{
|
||||
value->kind = BE_ADDRESS_FAILABLE;
|
||||
value->failable = decl->var.failable_ref;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type)
|
||||
{
|
||||
llvm_value_set_address_align(value, llvm_value, type_lowering(type), type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void llvm_value_fold_failable(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind == BE_ADDRESS_FAILABLE)
|
||||
{
|
||||
LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check");
|
||||
BEValue error_value;
|
||||
llvm_value_set_address(&error_value, value->failable, type_anyerr);
|
||||
BEValue comp;
|
||||
llvm_value_set_bool(&comp, llvm_emit_is_no_error_value(c, &error_value));
|
||||
if (c->error_var)
|
||||
{
|
||||
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error");
|
||||
llvm_emit_cond_br(c, &comp, after_block, error_block);
|
||||
llvm_emit_block(c, error_block);
|
||||
llvm_store_bevalue_dest_aligned(c, c->error_var, &error_value);
|
||||
llvm_emit_br(c, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(c->catch_block);
|
||||
llvm_emit_cond_br(c, &comp, after_block, c->catch_block);
|
||||
}
|
||||
llvm_emit_block(c, after_block);
|
||||
value->kind = BE_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value)
|
||||
{
|
||||
llvm_value_fold_failable(c, value);
|
||||
switch (value->kind)
|
||||
{
|
||||
case BE_VALUE:
|
||||
return value->value;
|
||||
case BE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case BE_ADDRESS:
|
||||
return llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type),
|
||||
"");
|
||||
case BE_BOOLEAN:
|
||||
if (LLVMIsConstant(value->value))
|
||||
{
|
||||
return LLVMConstZExt(value->value, c->byte_type);
|
||||
}
|
||||
return LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name)
|
||||
@@ -848,53 +728,7 @@ LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name)
|
||||
return LLVMCreateBasicBlockInContext(c->context, name);
|
||||
}
|
||||
|
||||
void llvm_value_addr(GenContext *c, BEValue *value)
|
||||
{
|
||||
llvm_value_fold_failable(c, value);
|
||||
if (value->kind == BE_ADDRESS) return;
|
||||
if (!c->builder)
|
||||
{
|
||||
LLVMValueRef val = llvm_value_rvalue_store(c, value);
|
||||
LLVMValueRef ref = LLVMAddGlobal(c->module, LLVMTypeOf(val), ".taddr");
|
||||
llvm_set_alignment(ref, llvm_abi_alignment(c, LLVMTypeOf(val)));
|
||||
llvm_set_private_linkage(ref);
|
||||
LLVMSetInitializer(ref, val);
|
||||
llvm_emit_bitcast(c, ref, type_get_ptr(value->type));
|
||||
llvm_value_set_address(value, ref, value->type);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr");
|
||||
llvm_store_bevalue_dest_aligned(c, temp, value);
|
||||
llvm_value_set_address(value, temp, value->type);
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_value_rvalue(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind != BE_ADDRESS && value->kind != BE_ADDRESS_FAILABLE)
|
||||
{
|
||||
if (value->type->type_kind == TYPE_BOOL && value->kind != BE_BOOLEAN)
|
||||
{
|
||||
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
value->kind = BE_BOOLEAN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
llvm_value_fold_failable(c, value);
|
||||
value->value = llvm_emit_load_aligned(c,
|
||||
llvm_get_type(c, value->type),
|
||||
value->value,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type),
|
||||
"");
|
||||
if (value->type->type_kind == TYPE_BOOL)
|
||||
{
|
||||
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
value->kind = BE_BOOLEAN;
|
||||
return;
|
||||
}
|
||||
value->kind = BE_VALUE;
|
||||
}
|
||||
|
||||
|
||||
static void llvm_emit_type_decls(GenContext *context, Decl *decl)
|
||||
@@ -1127,79 +961,8 @@ AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type)
|
||||
return (AlignSize)LLVMABIAlignmentOfType(c->target_data, type);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment)
|
||||
{
|
||||
// If we have an address but not an aggregate, do a load.
|
||||
llvm_value_fold_failable(c, value);
|
||||
if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value->value = llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, value->alignment, "");
|
||||
value->kind = BE_VALUE;
|
||||
}
|
||||
switch (value->kind)
|
||||
{
|
||||
case BE_BOOLEAN:
|
||||
value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
|
||||
value->kind = BE_VALUE;
|
||||
FALLTHROUGH;
|
||||
case BE_VALUE:
|
||||
return llvm_store_aligned(c, destination, value->value, alignment ? alignment : type_abi_alignment(value->type));
|
||||
case BE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case BE_ADDRESS:
|
||||
{
|
||||
// Here we do an optimized(?) memcopy.
|
||||
ByteSize size = type_size(value->type);
|
||||
LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size);
|
||||
destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), "");
|
||||
LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), "");
|
||||
LLVMValueRef copy = LLVMBuildMemCpy(c->builder,
|
||||
destination,
|
||||
alignment ? alignment : type_abi_alignment(value->type),
|
||||
source,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type),
|
||||
copy_size);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value)
|
||||
{
|
||||
llvm_store_bevalue_aligned(c, destination, value, LLVMGetAlignment(destination));
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value)
|
||||
{
|
||||
if (value->type == type_void) return NULL;
|
||||
assert(llvm_value_is_addr(destination));
|
||||
return llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment);
|
||||
}
|
||||
|
||||
void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value)
|
||||
{
|
||||
assert(llvm_value_is_addr(destination));
|
||||
llvm_store_aligned(c, destination->value, raw_value, destination->alignment);
|
||||
}
|
||||
|
||||
void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type)
|
||||
{
|
||||
llvm_store_aligned(context, pointer, value, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment)
|
||||
{
|
||||
LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer);
|
||||
if (alignment) llvm_set_alignment(ref, alignment);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value)
|
||||
{
|
||||
assert(!decl->is_value);
|
||||
llvm_store_aligned(context, decl->backend_ref, value, decl->alignment);
|
||||
}
|
||||
|
||||
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len)
|
||||
{
|
||||
@@ -1219,14 +982,7 @@ void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, un
|
||||
llvm_emit_memcpy(c, decl->backend_ref, decl->alignment, source, source_alignment, type_size(decl->type));
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name)
|
||||
{
|
||||
assert(c->builder);
|
||||
LLVMValueRef value = LLVMBuildLoad2(c->builder, type, pointer, name);
|
||||
assert(LLVMGetTypeContext(type) == c->context);
|
||||
llvm_set_alignment(value, alignment ? alignment : llvm_abi_alignment(c, type));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,18 +55,6 @@ void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block)
|
||||
{
|
||||
assert(context->current_block);
|
||||
assert(value->kind == BE_BOOLEAN);
|
||||
LLVMBuildCondBr(context->builder, value->value, then_block, else_block);
|
||||
LLVMClearInsertionPosition(context->builder);
|
||||
context->current_block = NULL;
|
||||
context->current_block_is_target = false;
|
||||
}
|
||||
|
||||
|
||||
void llvm_emit_block(GenContext *context, LLVMBasicBlockRef next_block)
|
||||
{
|
||||
assert(context->current_block == NULL);
|
||||
@@ -111,7 +99,7 @@ static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, u
|
||||
return;
|
||||
}
|
||||
default:
|
||||
llvm_store_aligned(c, ref, llvm_get_next_param(c, index), alignment);
|
||||
llvm_store(c, ref, llvm_get_next_param(c, index), alignment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -146,11 +134,11 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
AlignSize alignment = decl->alignment;
|
||||
AlignSize element_align;
|
||||
LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.lo_index, alignment, &element_align);
|
||||
llvm_store_aligned(c, gep_first, llvm_get_next_param(c, index), element_align);
|
||||
llvm_store(c, gep_first, llvm_get_next_param(c, index), element_align);
|
||||
if (abi_type_is_valid(info->coerce_expand.hi))
|
||||
{
|
||||
LLVMValueRef gep_second = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.hi_index, alignment, &element_align);
|
||||
llvm_store_aligned(c, gep_second, llvm_get_next_param(c, index), element_align);
|
||||
llvm_store(c, gep_second, llvm_get_next_param(c, index), element_align);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -169,11 +157,11 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
AlignSize element_align;
|
||||
LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, decl_alignment, &element_align);
|
||||
// Store it in the struct.
|
||||
llvm_store_aligned(c, lo_ptr, llvm_get_next_param(c, index), element_align);
|
||||
llvm_store(c, lo_ptr, llvm_get_next_param(c, index), element_align);
|
||||
// Point to the hi value.
|
||||
LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, decl_alignment, &element_align);
|
||||
// Store it in the struct.
|
||||
llvm_store_aligned(c, hi_ptr, llvm_get_next_param(c, index), element_align);
|
||||
llvm_store(c, hi_ptr, llvm_get_next_param(c, index), element_align);
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
@@ -185,7 +173,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
return;
|
||||
}
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
llvm_store_aligned_decl(c, decl, llvm_get_next_param(c, index));
|
||||
llvm_store_decl_raw(c, decl, llvm_get_next_param(c, index));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
@@ -218,7 +206,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
AlignSize align;
|
||||
LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, decl_alignment, &align);
|
||||
LLVMValueRef value = llvm_get_next_param(c, index);
|
||||
llvm_store_aligned(c, element_ptr, value, align);
|
||||
llvm_store(c, element_ptr, value, align);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -277,7 +265,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
{
|
||||
if (return_value && return_value->value)
|
||||
{
|
||||
llvm_store_bevalue_aligned(c, c->return_out, return_value, 0);
|
||||
llvm_store_value_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type));
|
||||
}
|
||||
return_out = c->failable_out;
|
||||
if (!failable)
|
||||
@@ -291,7 +279,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
llvm_store_bevalue_aligned(c, return_out, return_value, info->indirect.alignment);
|
||||
llvm_store_value_aligned(c, return_out, return_value, info->indirect.alignment);
|
||||
llvm_emit_return_value(c, NULL);
|
||||
return;
|
||||
case ABI_ARG_IGNORE:
|
||||
@@ -315,7 +303,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo);
|
||||
lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, "");
|
||||
lo_val = llvm_load(c, lo_type, lo, alignment, "");
|
||||
|
||||
// We're done if there's a single field.
|
||||
if (!abi_type_is_valid(info->coerce_expand.hi))
|
||||
@@ -328,7 +316,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi);
|
||||
LLVMValueRef hi_val = llvm_emit_load_aligned(c, hi_type, hi, alignment, "");
|
||||
LLVMValueRef hi_val = llvm_load(c, hi_type, hi, alignment, "");
|
||||
|
||||
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
|
||||
LLVMValueRef composite = LLVMGetUndef(unpadded_type);
|
||||
@@ -342,7 +330,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
// The normal return
|
||||
llvm_emit_return_value(c, llvm_value_rvalue_store(c, return_value));
|
||||
llvm_emit_return_value(c, llvm_load_value_store(c, return_value));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
@@ -351,7 +339,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
if (!coerce_type || coerce_type == llvm_get_type(c, call_return_type))
|
||||
{
|
||||
// The normal return
|
||||
llvm_emit_return_value(c, llvm_value_rvalue_store(c, return_value));
|
||||
llvm_emit_return_value(c, llvm_load_value_store(c, return_value));
|
||||
return;
|
||||
}
|
||||
assert(!abi_info_should_flatten(info));
|
||||
@@ -568,7 +556,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_id.noreturn, -1);
|
||||
}
|
||||
if (decl->alignment)
|
||||
if (decl->alignment != type_abi_alignment(decl->type))
|
||||
{
|
||||
llvm_set_alignment(function, decl->alignment);
|
||||
}
|
||||
|
||||
52
src/compiler/llvm_codegen_instr.c
Normal file
52
src/compiler/llvm_codegen_instr.c
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
void llvm_emit_cond_br_raw(GenContext *context, LLVMValueRef b, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block)
|
||||
{
|
||||
assert(context->current_block);
|
||||
LLVMBuildCondBr(context->builder, b, then_block, else_block);
|
||||
LLVMClearInsertionPosition(context->builder);
|
||||
context->current_block = NULL;
|
||||
context->current_block_is_target = false;
|
||||
}
|
||||
|
||||
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block)
|
||||
{
|
||||
assert(context->current_block);
|
||||
assert(value->kind == BE_BOOLEAN);
|
||||
LLVMBuildCondBr(context->builder, value->value, then_block, else_block);
|
||||
LLVMClearInsertionPosition(context->builder);
|
||||
context->current_block = NULL;
|
||||
context->current_block_is_target = false;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_lshr_fixed(GenContext *c, LLVMValueRef data, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return data;
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
BitSize bit_width = llvm_bitsize(c, type);
|
||||
if (shift >= bit_width) return LLVMConstNull(type);
|
||||
if (LLVMIsAConstant(data))
|
||||
{
|
||||
return LLVMConstLShr(data, LLVMConstInt(type, (unsigned)shift, false));
|
||||
}
|
||||
return LLVMBuildLShr(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), "");
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_shl_fixed(GenContext *c, LLVMValueRef data, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return data;
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
BitSize bit_width = llvm_bitsize(c, type);
|
||||
if (shift >= bit_width) return LLVMConstNull(type);
|
||||
if (LLVMIsAConstant(data))
|
||||
{
|
||||
return LLVMConstShl(data, LLVMConstInt(type, (unsigned)shift, false));
|
||||
}
|
||||
return LLVMBuildShl(c->builder, data, LLVMConstInt(type, (unsigned)shift, false), "");
|
||||
}
|
||||
@@ -210,14 +210,13 @@ void llvm_value_rvalue(GenContext *context, BEValue *value);
|
||||
void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value);
|
||||
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type);
|
||||
void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i);
|
||||
void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment);
|
||||
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type);
|
||||
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment);
|
||||
void llvm_value_set_address_abi_aligned(BEValue *value, LLVMValueRef llvm_value, Type *type);
|
||||
void llvm_value_set_decl_address(BEValue *value, Decl *decl);
|
||||
void llvm_value_set_decl(BEValue *value, Decl *decl);
|
||||
void llvm_value_fold_failable(GenContext *c, BEValue *value);
|
||||
void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index);
|
||||
|
||||
LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value);
|
||||
|
||||
LLVMTypeRef llvm_abi_type(GenContext *c, AbiType type);
|
||||
TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type);
|
||||
@@ -250,7 +249,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl);
|
||||
void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl);
|
||||
LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count);
|
||||
void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type);
|
||||
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
|
||||
|
||||
void llvm_emit_debug_function(GenContext *c, Decl *decl);
|
||||
void llvm_emit_debug_location(GenContext *context, SourceSpan location);
|
||||
void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index);
|
||||
@@ -269,14 +268,32 @@ void llvm_emit_int_comp_zero(GenContext *c, BEValue *result, BEValue *lhs, Binar
|
||||
void llvm_emit_int_comparison(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op);
|
||||
void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op);
|
||||
void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op);
|
||||
LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value);
|
||||
void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len);
|
||||
LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name);
|
||||
|
||||
// -- instr ---
|
||||
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
|
||||
void llvm_emit_cond_br_raw(GenContext *context, LLVMValueRef b, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
|
||||
|
||||
// -- general --
|
||||
LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error_value);
|
||||
|
||||
|
||||
// -- load ---
|
||||
LLVMValueRef llvm_load(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name);
|
||||
LLVMValueRef llvm_load_natural_alignment(GenContext *c, Type *type, LLVMValueRef pointer, const char *name);
|
||||
LLVMValueRef llvm_load_value(GenContext *c, BEValue *value);
|
||||
LLVMValueRef llvm_load_value_store(GenContext *c, BEValue *value);
|
||||
|
||||
// -- expr ---
|
||||
LLVMValueRef llvm_emit_shl_fixed(GenContext *c, LLVMValueRef data, int shift);
|
||||
LLVMValueRef llvm_emit_lshr_fixed(GenContext *c, LLVMValueRef data, int shift);
|
||||
|
||||
// -- general --
|
||||
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
|
||||
LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl);
|
||||
LLVMValueRef llvm_emit_aggregate_value(GenContext *c, Type *type, ...);
|
||||
LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, AlignSize align, bool bitcast);
|
||||
void llvm_emit_memclear(GenContext *c, BEValue *ref);
|
||||
void llvm_store_zero(GenContext *c, BEValue *ref);
|
||||
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len);
|
||||
void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment);
|
||||
void llvm_emit_stmt(GenContext *c, Ast *ast);
|
||||
@@ -318,13 +335,13 @@ bool llvm_emit_check_block_branch(GenContext *context);
|
||||
|
||||
|
||||
TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type);
|
||||
LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value);
|
||||
void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value);
|
||||
void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value);
|
||||
LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment);
|
||||
void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type);
|
||||
LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment);
|
||||
void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value);
|
||||
LLVMValueRef llvm_store_value(GenContext *c, BEValue *destination, BEValue *value);
|
||||
void llvm_store_value_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value);
|
||||
void llvm_store_value_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value);
|
||||
LLVMValueRef llvm_store_value_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment);
|
||||
void llvm_store_raw_abi_alignment(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type);
|
||||
LLVMValueRef llvm_store(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment);
|
||||
void llvm_store_decl_raw(GenContext *context, Decl *decl, LLVMValueRef value);
|
||||
|
||||
LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi);
|
||||
LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value, Type *original_type);
|
||||
@@ -467,10 +484,4 @@ void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMV
|
||||
c->error_var = _old_error_var
|
||||
|
||||
|
||||
static inline bool abi_info_should_flatten(ABIArgInfo *info);
|
||||
|
||||
static inline bool abi_info_should_flatten(ABIArgInfo *info)
|
||||
{
|
||||
return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,6 @@
|
||||
|
||||
static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast);
|
||||
|
||||
static bool ast_is_not_empty(Ast *ast)
|
||||
{
|
||||
if (!ast) return false;
|
||||
if (ast->ast_kind != AST_COMPOUND_STMT) return true;
|
||||
AstId first = ast->compound_stmt.first_stmt;
|
||||
if (first)
|
||||
{
|
||||
Ast *stmt = astptr(first);
|
||||
if (stmt->next) return true;
|
||||
if (ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end) return true;
|
||||
return ast_is_not_empty(stmt);
|
||||
}
|
||||
return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end;
|
||||
}
|
||||
|
||||
|
||||
void llvm_emit_compound_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
@@ -41,7 +26,7 @@ void llvm_emit_compound_stmt(GenContext *context, Ast *ast)
|
||||
}
|
||||
}
|
||||
|
||||
void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast)
|
||||
static void llvm_emit_ct_compound_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_CT_COMPOUND_STMT);
|
||||
AstId current = ast->compound_stmt.first_stmt;
|
||||
@@ -119,7 +104,7 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
|
||||
BEValue value;
|
||||
llvm_value_set_decl_address(&value, decl);
|
||||
value.kind = BE_ADDRESS;
|
||||
llvm_emit_memclear(c, &value);
|
||||
llvm_store_zero(c, &value);
|
||||
}
|
||||
}
|
||||
return decl->backend_ref;
|
||||
@@ -157,7 +142,7 @@ void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr
|
||||
llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false));
|
||||
return;
|
||||
}
|
||||
llvm_value_set_address(be_value, decl_value, type);
|
||||
llvm_value_set_address_abi_aligned(be_value, decl_value, type);
|
||||
}
|
||||
if (bool_cast)
|
||||
{
|
||||
@@ -216,7 +201,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast)
|
||||
{
|
||||
if (c->return_out)
|
||||
{
|
||||
llvm_store_bevalue_aligned(c, c->return_out, &return_value, 0);
|
||||
llvm_store_value_aligned(c, c->return_out, &return_value, type_alloca_alignment(return_value.type));
|
||||
}
|
||||
llvm_emit_jmp(c, c->block_return_exit);
|
||||
return;
|
||||
@@ -235,7 +220,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast)
|
||||
{
|
||||
llvm_emit_block(c, error_return_block);
|
||||
BEValue value;
|
||||
llvm_value_set_address(&value, error_out, type_anyerr);
|
||||
llvm_value_set_address_abi_aligned(&value, error_out, type_anyerr);
|
||||
llvm_emit_return_abi(c, NULL, &value);
|
||||
c->current_block = NULL;
|
||||
}
|
||||
@@ -765,9 +750,9 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, As
|
||||
|
||||
Type *switch_type = switch_ast->ast_kind == AST_IF_CATCH_SWITCH_STMT ? type_lowering(type_anyerr) : switch_ast->switch_stmt.cond->type;
|
||||
BEValue switch_var;
|
||||
llvm_value_set_address(&switch_var, llvm_emit_alloca_aligned(c, switch_type, "switch"), switch_type);
|
||||
llvm_value_set_address_abi_aligned(&switch_var, llvm_emit_alloca_aligned(c, switch_type, "switch"), switch_type);
|
||||
switch_ast->switch_stmt.codegen.retry_var = &switch_var;
|
||||
llvm_store_bevalue(c, &switch_var, switch_value);
|
||||
llvm_store_value(c, &switch_var, switch_value);
|
||||
|
||||
llvm_emit_br(c, switch_block);
|
||||
llvm_emit_block(c, switch_block);
|
||||
@@ -917,7 +902,7 @@ void gencontext_emit_next_stmt(GenContext *context, Ast *ast)
|
||||
}
|
||||
BEValue be_value;
|
||||
llvm_emit_expr(context, &be_value, ast->nextcase_stmt.switch_expr);
|
||||
llvm_store_bevalue(context, jump_target->switch_stmt.codegen.retry_var, &be_value);
|
||||
llvm_store_value(context, jump_target->switch_stmt.codegen.retry_var, &be_value);
|
||||
llvm_emit_defer(context, ast->nextcase_stmt.defers.start, ast->nextcase_stmt.defers.end);
|
||||
llvm_emit_jmp(context, jump_target->switch_stmt.codegen.retry_block);
|
||||
}
|
||||
@@ -1289,7 +1274,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
|
||||
llvm_emit_compound_stmt(c, ast);
|
||||
break;
|
||||
case AST_CT_COMPOUND_STMT:
|
||||
gencontext_emit_ct_compound_stmt(c, ast);
|
||||
llvm_emit_ct_compound_stmt(c, ast);
|
||||
break;
|
||||
case AST_FOR_STMT:
|
||||
llvm_emit_for_stmt(c, ast);
|
||||
|
||||
174
src/compiler/llvm_codegen_storeload.c
Normal file
174
src/compiler/llvm_codegen_storeload.c
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
LLVMValueRef llvm_store(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer);
|
||||
llvm_set_alignment(ref, alignment);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void llvm_store_raw_abi_alignment(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type)
|
||||
{
|
||||
llvm_store(context, pointer, value, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void llvm_store_value_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value)
|
||||
{
|
||||
assert(llvm_value_is_addr(destination));
|
||||
llvm_store(c, destination->value, raw_value, destination->alignment);
|
||||
}
|
||||
|
||||
void llvm_store_decl_raw(GenContext *context, Decl *decl, LLVMValueRef value)
|
||||
{
|
||||
assert(!decl->is_value);
|
||||
llvm_store(context, decl->backend_ref, value, decl->alignment);
|
||||
}
|
||||
|
||||
void llvm_store_value_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value)
|
||||
{
|
||||
llvm_store_value_aligned(c, destination, value, LLVMGetAlignment(destination));
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_store_value_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment)
|
||||
{
|
||||
// If we have an address but not an aggregate, do a load.
|
||||
assert(alignment);
|
||||
llvm_value_fold_failable(c, value);
|
||||
if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value->value = llvm_load_value_store(c, value);
|
||||
value->kind = BE_VALUE;
|
||||
}
|
||||
switch (value->kind)
|
||||
{
|
||||
case BE_BOOLEAN:
|
||||
value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
|
||||
value->kind = BE_VALUE;
|
||||
FALLTHROUGH;
|
||||
case BE_VALUE:
|
||||
return llvm_store(c, destination, value->value, alignment);
|
||||
case BE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case BE_ADDRESS:
|
||||
{
|
||||
// Here we do an optimized(?) memcopy.
|
||||
ByteSize size = type_size(value->type);
|
||||
LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size);
|
||||
destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), "");
|
||||
LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), "");
|
||||
LLVMValueRef copy = LLVMBuildMemCpy(c->builder,
|
||||
destination,
|
||||
alignment,
|
||||
source,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type),
|
||||
copy_size);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_store_value(GenContext *c, BEValue *destination, BEValue *value)
|
||||
{
|
||||
if (value->type == type_void) return NULL;
|
||||
assert(llvm_value_is_addr(destination));
|
||||
return llvm_store_value_aligned(c, destination->value, value, destination->alignment);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_load(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
assert(c->builder);
|
||||
assert(LLVMGetTypeContext(type) == c->context);
|
||||
LLVMValueRef value = LLVMBuildLoad2(c->builder, type, pointer, name);
|
||||
llvm_set_alignment(value, alignment ? alignment : llvm_abi_alignment(c, type));
|
||||
return value;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_load_natural_alignment(GenContext *c, Type *type, LLVMValueRef pointer, const char *name)
|
||||
{
|
||||
return llvm_load(c, llvm_get_type(c, type), pointer, type_abi_alignment(type), name);
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef llvm_load_value(GenContext *c, BEValue *value)
|
||||
{
|
||||
llvm_value_fold_failable(c, value);
|
||||
switch (value->kind)
|
||||
{
|
||||
case BE_BOOLEAN:
|
||||
case BE_VALUE:
|
||||
return value->value;
|
||||
case BE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case BE_ADDRESS:
|
||||
return llvm_load(c, llvm_get_type(c, value->type), value->value, value->alignment, "");
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_load_value_store(GenContext *c, BEValue *value)
|
||||
{
|
||||
LLVMValueRef val = llvm_load_value(c, value);
|
||||
if (value->kind != BE_BOOLEAN) return val;
|
||||
return LLVMIsConstant(val) ? LLVMConstZExt(val, c->byte_type) : LLVMBuildZExt(c->builder, val, c->byte_type, "");
|
||||
}
|
||||
|
||||
|
||||
void llvm_store_zero(GenContext *c, BEValue *ref)
|
||||
{
|
||||
llvm_value_addr(c, ref);
|
||||
Type *type = ref->type;
|
||||
if (!type_is_abi_aggregate(type))
|
||||
{
|
||||
llvm_store_value_raw(c, ref, llvm_get_zero(c, type));
|
||||
return;
|
||||
}
|
||||
Type *single_type = type_abi_find_single_struct_element(type);
|
||||
|
||||
if (single_type && !type_is_abi_aggregate(single_type))
|
||||
{
|
||||
BEValue element;
|
||||
llvm_value_set_address(&element,
|
||||
llvm_emit_bitcast(c, ref->value, type_get_ptr(single_type)),
|
||||
single_type,
|
||||
(unsigned)ref->alignment);
|
||||
llvm_store_zero(c, &element);
|
||||
return;
|
||||
}
|
||||
if (type_size(type) <= 16)
|
||||
{
|
||||
if (type->type_kind == TYPE_STRUCT)
|
||||
{
|
||||
Decl *decl = type->decl;
|
||||
Decl **members = decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (!type_size(members[i]->type)) continue;
|
||||
BEValue member_ref;
|
||||
llvm_emit_struct_member_ref(c, ref, &member_ref, i);
|
||||
llvm_store_zero(c, &member_ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
LLVMTypeRef array_type = llvm_get_type(c, type);
|
||||
for (unsigned i = 0; i < type->array.len; i++)
|
||||
{
|
||||
AlignSize align;
|
||||
LLVMValueRef element_ptr = llvm_emit_array_gep_raw(c, ref->value, array_type, i, ref->alignment, &align);
|
||||
BEValue be_value;
|
||||
llvm_value_set_address(&be_value, element_ptr, type->array.base, align);
|
||||
llvm_store_zero(c, &be_value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true);
|
||||
}
|
||||
110
src/compiler/llvm_codegen_value.c
Normal file
110
src/compiler/llvm_codegen_value.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(llvm_value || type == type_void);
|
||||
value->value = llvm_value;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = BE_VALUE;
|
||||
value->type = type;
|
||||
}
|
||||
|
||||
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
value->value = llvm_value;
|
||||
value->alignment = alignment;
|
||||
value->kind = BE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
void llvm_value_set_address_abi_aligned(BEValue *value, LLVMValueRef llvm_value, Type *type)
|
||||
{
|
||||
llvm_value_set_address(value, llvm_value, type_lowering(type), type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void llvm_value_addr(GenContext *c, BEValue *value)
|
||||
{
|
||||
llvm_value_fold_failable(c, value);
|
||||
if (value->kind == BE_ADDRESS) return;
|
||||
if (!c->builder)
|
||||
{
|
||||
LLVMValueRef val = llvm_load_value_store(c, value);
|
||||
LLVMValueRef ref = LLVMAddGlobal(c->module, LLVMTypeOf(val), ".taddr");
|
||||
llvm_set_alignment(ref, llvm_abi_alignment(c, LLVMTypeOf(val)));
|
||||
llvm_set_private_linkage(ref);
|
||||
LLVMSetInitializer(ref, val);
|
||||
llvm_emit_bitcast(c, ref, type_get_ptr(value->type));
|
||||
llvm_value_set_address_abi_aligned(value, ref, value->type);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr");
|
||||
llvm_store_value_dest_aligned(c, temp, value);
|
||||
llvm_value_set_address_abi_aligned(value, temp, value->type);
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_value_rvalue(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind != BE_ADDRESS && value->kind != BE_ADDRESS_FAILABLE)
|
||||
{
|
||||
if (value->type->type_kind == TYPE_BOOL && value->kind != BE_BOOLEAN)
|
||||
{
|
||||
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
value->kind = BE_BOOLEAN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
llvm_value_fold_failable(c, value);
|
||||
value->value = llvm_load(c,
|
||||
llvm_get_type(c, value->type),
|
||||
value->value,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type),
|
||||
"");
|
||||
if (value->type->type_kind == TYPE_BOOL)
|
||||
{
|
||||
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
value->kind = BE_BOOLEAN;
|
||||
return;
|
||||
}
|
||||
value->kind = BE_VALUE;
|
||||
}
|
||||
|
||||
void llvm_value_fold_failable(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind == BE_ADDRESS_FAILABLE)
|
||||
{
|
||||
LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check");
|
||||
LLVMValueRef err_value = llvm_load_natural_alignment(c, type_anyerr, value->failable, "");
|
||||
LLVMValueRef was_ok = llvm_emit_is_no_error(c, err_value);
|
||||
if (c->error_var)
|
||||
{
|
||||
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error");
|
||||
llvm_emit_cond_br_raw(c, was_ok, after_block, error_block);
|
||||
llvm_emit_block(c, error_block);
|
||||
llvm_store_raw_abi_alignment(c, c->error_var, err_value, type_anyerr);
|
||||
llvm_emit_br(c, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(c->catch_block);
|
||||
llvm_emit_cond_br_raw(c, was_ok, after_block, c->catch_block);
|
||||
}
|
||||
llvm_emit_block(c, after_block);
|
||||
value->kind = BE_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_value_set_decl_address(BEValue *value, Decl *decl)
|
||||
{
|
||||
decl = decl_flatten(decl);
|
||||
llvm_value_set_address(value, decl_ref(decl), decl->type, decl->alignment);
|
||||
|
||||
if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl))
|
||||
{
|
||||
value->kind = BE_ADDRESS_FAILABLE;
|
||||
value->failable = decl->var.failable_ref;
|
||||
}
|
||||
}
|
||||
@@ -1511,6 +1511,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
|
||||
decl_set_external_name(decl);
|
||||
}
|
||||
if (!sema_analyse_doc_header(decl->docs, decl->func_decl.function_signature.params, NULL)) return decl_poison(decl);
|
||||
decl->alignment = type_alloca_alignment(decl->type);
|
||||
DEBUG_LOG("Function analysis done.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1487,22 +1487,31 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
|
||||
{
|
||||
case VARDECL_PARAM_REF:
|
||||
// &foo
|
||||
{
|
||||
if (!sema_analyse_expr_lvalue(context, arg)) return false;
|
||||
}
|
||||
if (type && type->canonical != arg->type->canonical)
|
||||
{
|
||||
SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
if (param && !param->alignment)
|
||||
{
|
||||
if (arg->expr_kind == EXPR_IDENTIFIER || arg->expr_kind == EXPR_CONST)
|
||||
{
|
||||
param->alignment = arg->identifier_expr.decl->alignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
param->alignment = type_alloca_alignment(arg->type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM:
|
||||
// foo
|
||||
if (!sema_analyse_expr_rhs(context, type, arg, true)) return false;
|
||||
if (IS_FAILABLE(arg)) *failable = true;
|
||||
if (callee.macro)
|
||||
if (param && !param->alignment)
|
||||
{
|
||||
param->alignment = type_abi_alignment(type ? type : arg->type);
|
||||
param->alignment = type_alloca_alignment(arg->type);
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM_EXPR:
|
||||
@@ -1521,9 +1530,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
|
||||
break;
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
// $Foo
|
||||
{
|
||||
if (!sema_analyse_expr_lvalue(context, arg)) return false;
|
||||
}
|
||||
if (arg->expr_kind != EXPR_TYPEINFO)
|
||||
{
|
||||
SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name);
|
||||
|
||||
@@ -1,55 +1,36 @@
|
||||
#include "codegen_internal.h"
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
#include <tb.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Module *code_module;
|
||||
const char *object_filename;
|
||||
|
||||
TB_Module *module;
|
||||
TB_FeatureSet features;
|
||||
|
||||
Decl *current_func_decl;
|
||||
TB_Function *function;
|
||||
} GenContext;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BE_VALUE,
|
||||
BE_ADDRESS,
|
||||
BE_ADDRESS_FAILABLE,
|
||||
BE_BOOLEAN,
|
||||
} BackendValueKind;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BackendValueKind kind: 5;
|
||||
AlignSize alignment;
|
||||
|
||||
Type *type; // Should never be a distinct or canonical type.
|
||||
|
||||
TB_Register value;
|
||||
TB_Register failable; // TODO what even is a failable?
|
||||
} BEValue;
|
||||
|
||||
static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr);
|
||||
static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr);
|
||||
static inline void tilde_emit_block(TbContext *c, TB_Label label);
|
||||
|
||||
static TB_Register tilde_value_rvalue_get(TbContext *c, TBEValue *value);
|
||||
static void TBE_VALUE_set_reg(TBEValue *value, TB_Register reg, Type *type);
|
||||
// Per instance i think?
|
||||
void tinybackend_codegen_setup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline bool tinybackend_value_is_addr(BEValue *value)
|
||||
{ return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_FAILABLE; }
|
||||
static inline bool tinybackend_value_is_addr(TBEValue *value)
|
||||
{ return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_FAILABLE; }
|
||||
|
||||
static inline bool tinybackend_value_is_bool(BEValue *value)
|
||||
{ return value->kind == BE_BOOLEAN; }
|
||||
|
||||
static TB_DataType tbc3_get_type(Type *type)
|
||||
static TB_CallingConv tilde_call_convention(CallABI abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
case CALL_C:
|
||||
return TB_CDECL;
|
||||
case CALL_X86_STD:
|
||||
return TB_STDCALL;
|
||||
default:
|
||||
FATAL_ERROR("Unsupported call convention for TildeBE");
|
||||
}
|
||||
}
|
||||
|
||||
TB_DataType tbtype(Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
uint8_t elements = 1;
|
||||
@@ -124,63 +105,247 @@ static TB_DataType tbc3_get_type(Type *type)
|
||||
}
|
||||
}
|
||||
|
||||
static TB_DataType tilde_get_abi_type(AbiType type)
|
||||
{
|
||||
if (abi_type_is_type(type)) return tbtype(type.type);
|
||||
TODO
|
||||
}
|
||||
|
||||
static void param_expand(TB_DataType **params_ref, Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEDEF:
|
||||
UNREACHABLE
|
||||
case TYPE_ARRAY:
|
||||
for (ArraySize i = type->array.len; i > 0; i--)
|
||||
{
|
||||
param_expand(params_ref, type->array.base);
|
||||
}
|
||||
return;
|
||||
case TYPE_STRUCT:
|
||||
{
|
||||
Decl **members = type->decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
param_expand(params_ref, members[i]->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_ERRTYPE:
|
||||
param_expand(params_ref, type_lowering(type));
|
||||
return;
|
||||
case TYPE_UNION:
|
||||
{
|
||||
ByteSize largest = 0;
|
||||
Type *largest_type = NULL;
|
||||
Decl **members = type->decl->strukt.members;
|
||||
// Clang: Unions can be here only in degenerative cases - all the fields are same
|
||||
// after flattening. Thus we have to use the "largest" field.
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (type_size(type) > largest)
|
||||
{
|
||||
largest = type_size(type);
|
||||
type = type->canonical;
|
||||
}
|
||||
}
|
||||
if (!largest) return;
|
||||
param_expand(params_ref, largest_type);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
vec_add(*params_ref, tbtype(type));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void add_func_type_param(Type *param_type, ABIArgInfo *arg_info, TB_DataType **params)
|
||||
{
|
||||
arg_info->param_index_start = (MemberIndex)vec_size(*params);
|
||||
switch (arg_info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
break;
|
||||
case ABI_ARG_INDIRECT:
|
||||
vec_add(*params, TB_TYPE_PTR);
|
||||
break;
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
vec_add(*params, tilde_get_abi_type(arg_info->coerce_expand.lo));
|
||||
if (abi_type_is_valid(arg_info->coerce_expand.hi))
|
||||
{
|
||||
vec_add(*params, tilde_get_abi_type(arg_info->coerce_expand.hi));
|
||||
}
|
||||
break;
|
||||
case ABI_ARG_EXPAND:
|
||||
// Expanding a structs
|
||||
param_expand(params, param_type->canonical);
|
||||
// If we have padding, add it here.
|
||||
if (arg_info->expand.padding_type)
|
||||
{
|
||||
vec_add(*params, tbtype(arg_info->expand.padding_type));
|
||||
}
|
||||
break;
|
||||
case ABI_ARG_DIRECT:
|
||||
vec_add(*params, tbtype(param_type));
|
||||
break;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
// Normal direct.
|
||||
if (!abi_type_is_valid(arg_info->direct_coerce.type))
|
||||
{
|
||||
vec_add(*params, tbtype(param_type));
|
||||
break;
|
||||
}
|
||||
TB_DataType coerce_type = tilde_get_abi_type(arg_info->direct_coerce.type);
|
||||
if (!abi_info_should_flatten(arg_info))
|
||||
{
|
||||
vec_add(*params, coerce_type);
|
||||
break;
|
||||
}
|
||||
for (unsigned idx = 0; idx < arg_info->direct_coerce.elements; idx++)
|
||||
{
|
||||
vec_add(*params, coerce_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
// Pairs are passed by param.
|
||||
vec_add(*params, tilde_get_abi_type(arg_info->direct_pair.lo));
|
||||
vec_add(*params, tilde_get_abi_type(arg_info->direct_pair.hi));
|
||||
break;
|
||||
}
|
||||
arg_info->param_index_end = (MemberIndex)vec_size(*params);
|
||||
}
|
||||
|
||||
static TB_FunctionPrototype *tilde_get_function_type(TB_Module *module, FunctionPrototype *prototype)
|
||||
{
|
||||
if (prototype->tb_prototype) return prototype->tb_prototype;
|
||||
|
||||
TB_DataType *params = NULL;
|
||||
TB_DataType return_type = TB_TYPE_VOID;
|
||||
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
ABIArgInfo *ret_arg_info = prototype->ret_abi_info;
|
||||
|
||||
ret_arg_info->param_index_end = 0;
|
||||
ret_arg_info->param_index_start = 0;
|
||||
|
||||
switch (ret_arg_info->kind)
|
||||
{
|
||||
case ABI_ARG_EXPAND:
|
||||
UNREACHABLE;
|
||||
case ABI_ARG_INDIRECT:
|
||||
vec_add(params, TB_TYPE_PTR);
|
||||
return_type = TB_TYPE_VOID;
|
||||
break;
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
TB_DataType lo = tilde_get_abi_type(ret_arg_info->direct_pair.lo);
|
||||
if (!abi_type_is_valid(ret_arg_info->direct_pair.hi))
|
||||
{
|
||||
return_type = lo;
|
||||
break;
|
||||
}
|
||||
TB_DataType hi = tilde_get_abi_type(ret_arg_info->direct_pair.hi);
|
||||
TODO
|
||||
// return_type = llvm_get_twostruct(context, lo, hi);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_IGNORE:
|
||||
return_type = TB_TYPE_VOID;
|
||||
break;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
TB_DataType lo = tilde_get_abi_type(ret_arg_info->direct_pair.lo);
|
||||
TB_DataType hi = tilde_get_abi_type(ret_arg_info->direct_pair.hi);
|
||||
TODO // return_type = llvm_get_twostruct(context, lo, hi);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
return_type = tbtype(call_return_type);
|
||||
break;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
assert(!abi_info_should_flatten(ret_arg_info));
|
||||
TODO
|
||||
/*
|
||||
return_type = llvm_get_coerce_type(context, ret_arg_info);
|
||||
if (!return_type) return_type = llvm_get_type(context, call_return_type);*/
|
||||
break;
|
||||
}
|
||||
|
||||
// If it's failable and it's not void (meaning ret_abi_info will be NULL)
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
add_func_type_param(type_get_ptr(type_lowering(prototype->ret_by_ref_type)),
|
||||
prototype->ret_by_ref_abi_info,
|
||||
¶ms);
|
||||
}
|
||||
|
||||
// Add in all of the required arguments.
|
||||
VECEACH(prototype->params, i)
|
||||
{
|
||||
add_func_type_param(prototype->params[i], prototype->abi_args[i], ¶ms);
|
||||
}
|
||||
|
||||
unsigned param_count = vec_size(params);
|
||||
TB_FunctionPrototype *tb_proto = tb_prototype_create(module,
|
||||
tilde_call_convention(prototype->call_abi),
|
||||
return_type, param_count, prototype->variadic == VARIADIC_RAW);
|
||||
tb_prototype_add_params(tb_proto, param_count, params);
|
||||
prototype->tb_prototype = tb_proto;
|
||||
return tb_proto;
|
||||
}
|
||||
|
||||
static inline TB_Register decl_ref(Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_ref(decl->var.alias);
|
||||
|
||||
return (TB_Register)((uintptr_t)decl->backend_ref);
|
||||
return decl->tb_register;
|
||||
}
|
||||
|
||||
TB_Register tinybackend_emit_load_aligned(GenContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment)
|
||||
TB_Register tilde_load_aligned(TbContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment)
|
||||
{
|
||||
TB_Register value = tb_inst_load(c->function, dt, pointer, alignment);
|
||||
TB_Register value = tb_inst_load(c->f, dt, pointer, alignment);
|
||||
return value;
|
||||
}
|
||||
|
||||
void tinybackend_store_self_aligned(GenContext *c, TB_Register pointer, TB_Register value, Type *type)
|
||||
void tilde_store_self_aligned(TbContext *c, TB_Register pointer, TB_Register value, Type *type)
|
||||
{
|
||||
tb_inst_store(c->function, tbc3_get_type(type), pointer, value, type_abi_alignment(type));
|
||||
tb_inst_store(c->f, tbtype(type), pointer, value, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destination, BEValue *value)
|
||||
|
||||
|
||||
void tinybackend_store_value(TbContext *c, TBEValue *destination, TBEValue *value)
|
||||
{
|
||||
assert(tinybackend_value_is_addr(destination));
|
||||
if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
TB_DataType type = tbtype(destination->type);
|
||||
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value->value = tinybackend_emit_load_aligned(c,
|
||||
tbc3_get_type(value->type),
|
||||
value->value,
|
||||
value->alignment);
|
||||
value->kind = BE_VALUE;
|
||||
value->reg = tilde_load_aligned(c, type, value->reg, value->alignment);
|
||||
value->kind = TBE_VALUE;
|
||||
}
|
||||
|
||||
AlignSize alignment = destination->alignment;
|
||||
assert(alignment);
|
||||
switch (value->kind)
|
||||
{
|
||||
case BE_BOOLEAN:
|
||||
value->value = tb_inst_zxt(c->function, value->value, TB_TYPE_I8);
|
||||
value->kind = BE_VALUE;
|
||||
FALLTHROUGH;
|
||||
case BE_VALUE:
|
||||
tb_inst_store(c->function,
|
||||
tbc3_get_type(destination->type),
|
||||
destination->value,
|
||||
value->value,
|
||||
alignment ? alignment : type_abi_alignment(value->type));
|
||||
case TBE_VALUE:
|
||||
tb_inst_store(c->f, type, destination->reg, value->reg, alignment);
|
||||
return;
|
||||
case BE_ADDRESS:
|
||||
case TBE_ADDRESS:
|
||||
{
|
||||
// Here we do an optimized(?) memcopy.
|
||||
ByteSize size = type_size(value->type);
|
||||
TB_Register copy_size = tb_inst_iconst(c->function,
|
||||
TB_Register copy_size = tb_inst_iconst(c->f,
|
||||
size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64,
|
||||
size);
|
||||
|
||||
TB_Register source = value->value;
|
||||
tb_inst_memcpy(c->function,
|
||||
destination->value,
|
||||
value->value, copy_size,
|
||||
value->alignment ? (int)value->alignment : (int)type_abi_alignment(value->type));
|
||||
alignment = type_min_alignment(destination->alignment, value->alignment);
|
||||
tb_inst_memcpy(c->f, destination->reg, value->reg, copy_size, alignment);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@@ -188,43 +353,32 @@ void tinybackend_store_bevalue(GenContext *c, TB_DataType type, BEValue *destina
|
||||
}
|
||||
}
|
||||
|
||||
void tinybackend_value_set_address(BEValue *value, TB_Register tb_value, Type *type)
|
||||
{
|
||||
value->value = tb_value;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = BE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
void tinybackend_value_set_decl_address(BEValue *value, Decl *decl)
|
||||
{
|
||||
decl = decl_flatten(decl);
|
||||
tinybackend_value_set_address(value, decl_ref(decl), decl->type);
|
||||
value->alignment = decl->alignment;
|
||||
}
|
||||
|
||||
void tinybackend_value_set(BEValue *value, TB_Register reg, Type *type)
|
||||
void TBE_VALUE_set(TBEValue *value, TB_Register reg, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(reg || type == type_void);
|
||||
value->value = reg;
|
||||
value->reg = reg;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = BE_VALUE;
|
||||
value->kind = TBE_VALUE;
|
||||
value->type = type;
|
||||
}
|
||||
|
||||
static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
static void tinybackend_emit_const_expr(TbContext *c, TBEValue *value, Expr *expr)
|
||||
{
|
||||
Type *type = type_reduced_from_expr(expr)->canonical;
|
||||
|
||||
Type *type = type_flatten(expr->type);
|
||||
switch (expr->const_expr.const_kind)
|
||||
{
|
||||
case CONST_FLOAT:
|
||||
TBE_VALUE_set(value, tb_inst_fconst(c->f, tbtype(type), expr->const_expr.fxx.f), type);
|
||||
return;
|
||||
case CONST_INTEGER:
|
||||
{
|
||||
TB_Register reg;
|
||||
Int128 i = expr->const_expr.ixx.i;
|
||||
|
||||
TB_DataType dt = tbc3_get_type(type);
|
||||
TB_DataType dt = tbtype(type);
|
||||
printf("Const int type: %d\n", dt.type);
|
||||
|
||||
switch (expr->const_expr.ixx.type)
|
||||
@@ -232,15 +386,14 @@ static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *exp
|
||||
|
||||
case TYPE_I128:
|
||||
case TYPE_U128:
|
||||
TODO
|
||||
FATAL_ERROR("TB does not yet support int128");
|
||||
//reg = tb_inst_iconst128(c->function, dt, (TB_Int128){ .lo = i.low, .hi = i.high });
|
||||
break;
|
||||
default:
|
||||
reg = tb_inst_iconst(c->function, dt, i.low);
|
||||
reg = tb_inst_iconst(c->f, dt, i.low);
|
||||
break;
|
||||
}
|
||||
|
||||
tinybackend_value_set(value, reg, type);
|
||||
TBE_VALUE_set(value, reg, type);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@@ -248,17 +401,6 @@ static void tinybackend_emit_const_expr(GenContext *c, BEValue *value, Expr *exp
|
||||
}
|
||||
}
|
||||
|
||||
BEValue tinybackend_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, TB_Register failable)
|
||||
{
|
||||
assert(ref->kind == BE_ADDRESS || ref->kind == BE_ADDRESS_FAILABLE);
|
||||
|
||||
BEValue value;
|
||||
tinybackend_emit_expr(c, &value, expr);
|
||||
tinybackend_store_bevalue(c, tbc3_get_type(ref->type), ref, &value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int find_member_index(Decl *parent, Decl *member)
|
||||
{
|
||||
VECEACH(parent->strukt.members, i)
|
||||
@@ -276,7 +418,7 @@ static int find_member_index(Decl *parent, Decl *member)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member)
|
||||
static void tinybackend_emit_member_addr(TbContext *c, TBEValue *value, Decl *parent, Decl *member)
|
||||
{
|
||||
assert(member->resolve_status == RESOLVE_DONE);
|
||||
|
||||
@@ -298,9 +440,9 @@ static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *pa
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
{
|
||||
TB_Register ref = tb_inst_member_access(c->function, value->value, (int32_t)found->offset);
|
||||
TB_Register ref = tb_inst_member_access(c->f, value->reg, (int32_t)found->offset);
|
||||
|
||||
tinybackend_value_set_address(value, ref, member->type);
|
||||
value_set_address_abi_aligned(value, ref, member->type);
|
||||
value->alignment = found->alignment;
|
||||
}
|
||||
break;
|
||||
@@ -311,13 +453,13 @@ static void tinybackend_emit_member_addr(GenContext *c, BEValue *value, Decl *pa
|
||||
} while (found != member);
|
||||
}
|
||||
|
||||
void tinybackend_emit_access_addr(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
void tinybackend_emit_access_addr(TbContext *c, TBEValue *TBE_VALUE, Expr *expr)
|
||||
{
|
||||
Expr *parent = expr->access_expr.parent;
|
||||
tinybackend_emit_expr(c, be_value, parent);
|
||||
tinybackend_emit_expr(c, TBE_VALUE, parent);
|
||||
Decl *member = expr->access_expr.ref;
|
||||
|
||||
tinybackend_emit_member_addr(c, be_value, type_lowering(parent->type)->decl, member);
|
||||
tinybackend_emit_member_addr(c, TBE_VALUE, type_lowering(parent->type)->decl, member);
|
||||
}
|
||||
|
||||
static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type)
|
||||
@@ -330,44 +472,35 @@ static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type)
|
||||
return TB_CAN_WRAP;
|
||||
}
|
||||
|
||||
void tinybackend_value_rvalue(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind != BE_ADDRESS && value->kind != BE_ADDRESS_FAILABLE)
|
||||
{
|
||||
if (value->type->type_kind == TYPE_BOOL && value->kind != BE_BOOLEAN)
|
||||
{
|
||||
//value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
//value->kind = BE_BOOLEAN;
|
||||
TODO
|
||||
}
|
||||
return;
|
||||
}
|
||||
//llvm_value_fold_failable(c, value);
|
||||
value->value = tinybackend_emit_load_aligned(c,
|
||||
tbc3_get_type(value->type),
|
||||
value->value,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type));
|
||||
|
||||
if (value->type->type_kind == TYPE_BOOL)
|
||||
{
|
||||
//value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
|
||||
//value->kind = BE_BOOLEAN;
|
||||
//return;
|
||||
TODO
|
||||
}
|
||||
value->kind = BE_VALUE;
|
||||
|
||||
|
||||
static inline void tilde_emit_block(TbContext *c, TB_Label label)
|
||||
{
|
||||
tb_inst_label(c->f, label);
|
||||
}
|
||||
|
||||
void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op)
|
||||
TB_Register tilde_value_rvalue_get(TbContext *c, TBEValue *value)
|
||||
{
|
||||
if (value->kind == TBE_VALUE) return value->reg;
|
||||
//llvm_value_fold_failable(c, value);
|
||||
return tilde_load_aligned(c,
|
||||
tbtype(value->type),
|
||||
value->reg,
|
||||
value->alignment ? value->alignment : type_abi_alignment(value->type));
|
||||
|
||||
}
|
||||
|
||||
void tinybackend_emit_binary(TbContext *c, TBEValue *TBE_VALUE, Expr *expr, TBEValue *lhs_loaded, BinaryOp binary_op)
|
||||
{
|
||||
|
||||
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
|
||||
{
|
||||
//gencontext_emit_logical_and_or(c, be_value, expr, binary_op);
|
||||
//TbContext_emit_logical_and_or(c, TBE_VALUE, expr, binary_op);
|
||||
//return;
|
||||
TODO
|
||||
}
|
||||
BEValue lhs;
|
||||
TBEValue lhs;
|
||||
if (lhs_loaded)
|
||||
{
|
||||
lhs = *lhs_loaded;
|
||||
@@ -376,16 +509,16 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
{
|
||||
tinybackend_emit_expr(c, &lhs, expr->binary_expr.left);
|
||||
}
|
||||
tinybackend_value_rvalue(c, &lhs);
|
||||
value_rvalue(c, &lhs);
|
||||
|
||||
BEValue rhs;
|
||||
TBEValue rhs;
|
||||
tinybackend_emit_expr(c, &rhs, expr->binary_expr.right);
|
||||
tinybackend_value_rvalue(c, &rhs);
|
||||
value_rvalue(c, &rhs);
|
||||
|
||||
/*EMIT_LOC(c, expr);
|
||||
if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
|
||||
{
|
||||
llvm_emit_comparison(c, be_value, &lhs, &rhs, binary_op);
|
||||
llvm_emit_comparison(c, TBE_VALUE, &lhs, &rhs, binary_op);
|
||||
return;
|
||||
}*/
|
||||
|
||||
@@ -395,10 +528,10 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
bool is_float = type_is_float(lhs_type) || (vector_type && type_is_float(vector_type));
|
||||
|
||||
TB_Register val = TB_NULL_REG;
|
||||
TB_Register lhs_value = lhs.value;
|
||||
TB_Register rhs_value = rhs.value;
|
||||
TB_Register lhs_value = lhs.reg;
|
||||
TB_Register rhs_value = rhs.reg;
|
||||
|
||||
TB_DataType dt = tbc3_get_type(lhs_type);
|
||||
TB_DataType dt = tbtype(lhs_type);
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_ERROR:
|
||||
@@ -406,12 +539,12 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
case BINARYOP_MULT:
|
||||
if (is_float)
|
||||
{
|
||||
val = tb_inst_fmul(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_fmul(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(NeGate): review this later, maybe it shouldn't be NO_WRAP
|
||||
val = tb_inst_mul(c->function, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
|
||||
val = tb_inst_mul(c->f, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
|
||||
break;
|
||||
case BINARYOP_SUB:
|
||||
if (lhs_type->type_kind == TYPE_POINTER)
|
||||
@@ -431,10 +564,10 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
}
|
||||
if (is_float)
|
||||
{
|
||||
val = tb_inst_fsub(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_fsub(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
}
|
||||
val = tb_inst_mul(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
|
||||
val = tb_inst_mul(c->f, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
|
||||
break;
|
||||
case BINARYOP_ADD:
|
||||
if (lhs_type->type_kind == TYPE_POINTER)
|
||||
@@ -443,20 +576,20 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
}
|
||||
if (is_float)
|
||||
{
|
||||
val = tb_inst_fadd(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_fadd(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
}
|
||||
val = tb_inst_add(c->function, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
|
||||
val = tb_inst_add(c->f, dt, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
|
||||
break;
|
||||
case BINARYOP_DIV:
|
||||
//llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", TOKLOC(expr->span.loc));
|
||||
if (is_float)
|
||||
{
|
||||
val = tb_inst_fdiv(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_fdiv(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
}
|
||||
|
||||
val = tb_inst_div(c->function, dt, lhs_value, rhs_value, !type_is_unsigned(lhs_type));
|
||||
val = tb_inst_div(c->f, dt, lhs_value, rhs_value, !type_is_unsigned(lhs_type));
|
||||
break;
|
||||
case BINARYOP_MOD:
|
||||
{
|
||||
@@ -465,20 +598,20 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
case BINARYOP_SHR:
|
||||
if (type_is_unsigned(lhs_type))
|
||||
{
|
||||
val = tb_inst_shr(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_shr(c->f, dt, lhs_value, rhs_value);
|
||||
return;
|
||||
}
|
||||
|
||||
val = tb_inst_sar(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_sar(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
case BINARYOP_SHL:
|
||||
val = tb_inst_shl(c->function, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
|
||||
val = tb_inst_shl(c->f, dt, lhs_value, rhs_value, TB_ASSUME_NUW);
|
||||
break;
|
||||
case BINARYOP_BIT_AND:
|
||||
val = tb_inst_and(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_and(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
case BINARYOP_BIT_OR:
|
||||
val = tb_inst_or(c->function, dt, lhs_value, rhs_value);
|
||||
val = tb_inst_or(c->f, dt, lhs_value, rhs_value);
|
||||
break;
|
||||
case BINARYOP_BIT_XOR:
|
||||
TODO
|
||||
@@ -506,24 +639,16 @@ void tinybackend_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEVal
|
||||
UNREACHABLE
|
||||
}
|
||||
assert(val);
|
||||
tinybackend_value_set(be_value, val, expr->type);
|
||||
TBE_VALUE_set(TBE_VALUE, val, expr->type);
|
||||
}
|
||||
|
||||
void tinybackend_value_addr(GenContext *c, BEValue *value)
|
||||
{
|
||||
if (value->kind == BE_ADDRESS) return;
|
||||
|
||||
TB_Register temp = tb_inst_local(c->function, type_size(value->type), type_alloca_alignment(value->type));
|
||||
tinybackend_store_self_aligned(c, temp, value->value, value->type);
|
||||
tinybackend_value_set_address(value, temp, value->type);
|
||||
}
|
||||
|
||||
static void tinybackend_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
static void tinybackend_emit_binary_expr(TbContext *c, TBEValue *TBE_VALUE, Expr *expr)
|
||||
{
|
||||
BinaryOp binary_op = expr->binary_expr.operator;
|
||||
/*if (binary_op >= BINARYOP_ASSIGN && expr_is_vector_index(expr->binary_expr.left))
|
||||
{
|
||||
tinybackend_emit_vector_assign_expr(c, be_value, expr);
|
||||
tinybackend_emit_vector_assign_expr(c, TBE_VALUE, expr);
|
||||
return;
|
||||
}*/
|
||||
printf("A\n");
|
||||
@@ -531,28 +656,28 @@ static void tinybackend_emit_binary_expr(GenContext *c, BEValue *be_value, Expr
|
||||
{
|
||||
BinaryOp base_op = binaryop_assign_base_op(binary_op);
|
||||
assert(base_op != BINARYOP_ERROR);
|
||||
BEValue addr;
|
||||
TBEValue addr;
|
||||
tinybackend_emit_expr(c, &addr, expr->binary_expr.left);
|
||||
tinybackend_value_addr(c, &addr);
|
||||
tinybackend_emit_binary(c, be_value, expr, &addr, base_op);
|
||||
tinybackend_store_bevalue(c, tbc3_get_type(addr.type), &addr, be_value);
|
||||
value_rvalue(c, &addr);
|
||||
tinybackend_emit_binary(c, TBE_VALUE, expr, &addr, base_op);
|
||||
tinybackend_store_value(c, &addr, TBE_VALUE);
|
||||
return;
|
||||
}
|
||||
printf("B\n");
|
||||
if (binary_op == BINARYOP_ASSIGN)
|
||||
{
|
||||
Expr *left = expr->binary_expr.left;
|
||||
tinybackend_emit_expr(c, be_value, expr->binary_expr.left);
|
||||
assert(tinybackend_value_is_addr(be_value));
|
||||
tinybackend_emit_expr(c, TBE_VALUE, expr->binary_expr.left);
|
||||
assert(tinybackend_value_is_addr(TBE_VALUE));
|
||||
|
||||
*be_value = tinybackend_emit_assign_expr(c, be_value, expr->binary_expr.right, TB_NULL_REG /* failable_ref */);
|
||||
*TBE_VALUE = tilde_emit_assign_expr(c, TBE_VALUE, expr->binary_expr.right, TB_NULL_REG /* failable_ref */);
|
||||
return;
|
||||
}
|
||||
|
||||
tinybackend_emit_binary(c, be_value, expr, NULL, binary_op);
|
||||
tinybackend_emit_binary(c, TBE_VALUE, expr, NULL, binary_op);
|
||||
}
|
||||
|
||||
static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr)
|
||||
{
|
||||
printf("expr->expr_kind = %d\n", expr->expr_kind);
|
||||
|
||||
@@ -569,125 +694,134 @@ static void tinybackend_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
return;
|
||||
case EXPR_IDENTIFIER:
|
||||
case EXPR_CONST_IDENTIFIER:
|
||||
tinybackend_value_set_decl_address(value, expr->identifier_expr.decl);
|
||||
value_set_decl(value, expr->identifier_expr.decl);
|
||||
return;
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
static TB_Register tinybackend_emit_local_decl(GenContext *c, Decl *decl)
|
||||
|
||||
|
||||
static void TBE_VALUE_set_reg(TBEValue *value, TB_Register reg, Type *type)
|
||||
{
|
||||
value->reg = reg;
|
||||
value->kind = TBE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
|
||||
static TB_Register tilde_emit_local_decl(TbContext *c, Decl *decl)
|
||||
{
|
||||
// 1. Get the declaration and the LLVM type.
|
||||
Type *var_type = type_lowering(type_no_fail(decl->type));
|
||||
|
||||
// 2. In the case we have a static variable,
|
||||
// then we essentially treat this as a global.
|
||||
if (decl->var.is_static)
|
||||
{
|
||||
TODO
|
||||
if (IS_FAILABLE(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->external_name);
|
||||
scratch_buffer_append(".f");
|
||||
TB_InitializerID initializer = tb_initializer_create(c->module, type_size(type_anyerr), type_alloca_alignment(type_anyerr), 1);
|
||||
decl->var.tb_failable_reg = tb_global_create(c->module, scratch_buffer_to_string(), initializer);
|
||||
}
|
||||
TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1);
|
||||
decl->tb_register = tb_global_create(c->module, "tempglobal", static_initializer);
|
||||
tilde_emit_global_initializer(c, decl);
|
||||
return decl->tb_register;
|
||||
}
|
||||
|
||||
TB_DataType dt = tbc3_get_type(decl->type);
|
||||
TypeSize size = type_size(decl->type);
|
||||
AlignSize align = decl->alignment;
|
||||
|
||||
TB_Register reg = tb_inst_local(c->function, size, align);
|
||||
decl->backend_ref = (void *)((size_t)reg);
|
||||
|
||||
printf("Local declare: %s (size: %ld, align: %u)\n", decl->type->name, (long)size, align);
|
||||
|
||||
if (decl->var.failable_ref)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
tilde_emit_local_var_alloca(c, decl);
|
||||
TB_Register reg = decl->tb_register;
|
||||
Expr *init = decl->var.init_expr;
|
||||
if (IS_FAILABLE(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->name);
|
||||
scratch_buffer_append(".f");
|
||||
decl->var.tb_failable_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr));
|
||||
// Only clear out the result if the assignment isn't a failable.
|
||||
}
|
||||
|
||||
TBEValue value;
|
||||
value_set_decl(&value, decl);
|
||||
if (init)
|
||||
{
|
||||
// If we don't have undef, then make an assign.
|
||||
if (init->expr_kind != EXPR_UNDEF)
|
||||
{
|
||||
BEValue value;
|
||||
tinybackend_value_set_decl_address(&value, decl);
|
||||
tinybackend_emit_assign_expr(c,
|
||||
&value,
|
||||
decl->var.init_expr,
|
||||
(TB_Register)((uintptr_t)decl->var.failable_ref));
|
||||
tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg);
|
||||
}
|
||||
// TODO trap on undef in debug mode.
|
||||
}
|
||||
else
|
||||
{
|
||||
Type *type = type_lowering(decl->type);
|
||||
|
||||
// Normal case, zero init.
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
if (decl->var.tb_failable_reg)
|
||||
{
|
||||
tb_inst_store(c->function, dt, reg, tb_inst_iconst(c->function, dt, 0), align);
|
||||
}
|
||||
else
|
||||
{
|
||||
TODO
|
||||
tilde_store_self_aligned(c, tilde_get_zero(c, type_anyerr), decl->var.tb_failable_reg, type_anyerr);
|
||||
}
|
||||
tilde_store_value_zero(c, &value);
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void tinybackend_emit_stmt(GenContext *c, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_DECLARE_STMT:
|
||||
tinybackend_emit_local_decl(c, ast->declare_stmt);
|
||||
break;
|
||||
case AST_RETURN_STMT:
|
||||
//gencontext_emit_return(c, ast);
|
||||
tb_inst_ret(c->function, TB_NULL_REG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static TB_CallingConv tbc3_call_convention(CallABI abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
case CALL_C:
|
||||
return TB_CDECL;
|
||||
case CALL_X86_STD:
|
||||
return TB_STDCALL;
|
||||
default:
|
||||
FATAL_ERROR("Unsupported call convention for TildeBE");
|
||||
}
|
||||
}
|
||||
static void tbc3_emit_function_body(GenContext *c, Decl *decl)
|
||||
|
||||
|
||||
|
||||
static void tilde_emit_function_body(TbContext *c, Decl *decl)
|
||||
{
|
||||
printf("Function: %s\n", decl->external_name);
|
||||
|
||||
c->current_func_decl = decl;
|
||||
|
||||
FunctionSignature *func = &decl->func_decl.function_signature;
|
||||
unsigned params = vec_size(func->params);
|
||||
/*
|
||||
TB_FunctionPrototype *p = tb_prototype_create(c->module,
|
||||
tbc3_call_convention(func->call_abi),
|
||||
tbc3_get_type(abi_returntype(func->rtype->type)),
|
||||
params, func->variadic == VARIADIC_RAW);
|
||||
TB_FunctionPrototype *prototype = tilde_get_function_type(c->module, decl->type->func.prototype);
|
||||
TB_Function *function = tb_prototype_build(c->module, prototype, decl->external_name);
|
||||
c->f = function;
|
||||
|
||||
TB_Function *f = tb_prototype_add_param()*/
|
||||
/*
|
||||
VECEACH(decl->func_decl.body->compound_stmt.stmts, i)
|
||||
AstId current = decl->func_decl.body->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
tinybackend_emit_stmt(c, decl->func_decl.body->compound_stmt.stmts[i]);
|
||||
}*/
|
||||
tilde_emit_stmt(c, ast_next(¤t));
|
||||
}
|
||||
|
||||
tb_function_print(c->function, stdout);
|
||||
// Insert a return (and defer) if needed.
|
||||
/*
|
||||
if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block))
|
||||
{
|
||||
assert(!decl->func_decl.body->compound_stmt.defer_list.end);
|
||||
llvm_emit_defer(context, decl->func_decl.body->compound_stmt.defer_list.start, 0);
|
||||
llvm_emit_return_implicit(context);
|
||||
}
|
||||
|
||||
// erase alloca point
|
||||
if (LLVMGetInstructionParent(alloca_point))
|
||||
{
|
||||
context->alloca_point = NULL;
|
||||
LLVMInstructionEraseFromParent(alloca_point);
|
||||
}
|
||||
|
||||
LLVMDisposeBuilder(context->builder);
|
||||
context->builder = NULL;
|
||||
|
||||
if (llvm_use_debug(context))
|
||||
{
|
||||
llvm_debug_scope_pop(context);
|
||||
}
|
||||
|
||||
context->builder = prev_builder;
|
||||
context->function = prev_function;
|
||||
*/
|
||||
|
||||
tb_function_print(c->f, stdout);
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void tbc3_gen_context(GenContext *c)
|
||||
{
|
||||
const char *result = module_create_object_file_name(c->code_module);
|
||||
c->object_filename = strformat("%s%s", result, get_object_extension());
|
||||
}
|
||||
|
||||
static TB_Arch tbc3_get_arch(void)
|
||||
|
||||
static TB_Arch tilde_get_arch(void)
|
||||
{
|
||||
switch (platform_target.arch)
|
||||
{
|
||||
@@ -700,7 +834,7 @@ static TB_Arch tbc3_get_arch(void)
|
||||
}
|
||||
}
|
||||
|
||||
static TB_System tbc3_get_system(void)
|
||||
static TB_System tilde_get_system(void)
|
||||
{
|
||||
switch (platform_target.os)
|
||||
{
|
||||
@@ -723,12 +857,13 @@ static TB_System tbc3_get_system(void)
|
||||
void *tinybackend_gen(Module *module)
|
||||
{
|
||||
if (!vec_size(module->units)) return NULL;
|
||||
GenContext *c = ccalloc(sizeof(GenContext), 1);
|
||||
TbContext *c = ccalloc(sizeof(TbContext), 1);
|
||||
c->code_module = module;
|
||||
|
||||
c->module = tb_module_create(tbc3_get_arch(), tbc3_get_system(), &c->features);
|
||||
c->module = tb_module_create(tilde_get_arch(), tilde_get_system(), &c->features);
|
||||
|
||||
tbc3_gen_context(c);
|
||||
const char *result = module_create_object_file_name(module);
|
||||
c->object_filename = strformat("%s%s", result, get_object_extension());
|
||||
|
||||
printf("Module: %.*s\n", module->name->len, module->name->module);
|
||||
// Forward decls
|
||||
@@ -748,7 +883,7 @@ void *tinybackend_gen(Module *module)
|
||||
VECEACH(unit->functions, i)
|
||||
{
|
||||
Decl *decl = unit->functions[i];
|
||||
if (decl->func_decl.body) tbc3_emit_function_body(c, decl);
|
||||
if (decl->func_decl.body) tilde_emit_function_body(c, decl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +894,7 @@ void *tinybackend_gen(Module *module)
|
||||
// Compile module (multi threaded)
|
||||
const char *tinybackend_codegen(void *context)
|
||||
{
|
||||
GenContext *c = (GenContext *)context;
|
||||
TbContext *c = (TbContext *)context;
|
||||
|
||||
// Write out the object file
|
||||
tb_module_export(c->module, c->object_filename);
|
||||
|
||||
1219
src/compiler/tilde_codegen.c
Normal file
1219
src/compiler/tilde_codegen.c
Normal file
File diff suppressed because it is too large
Load Diff
758
src/compiler/tilde_codegen_expr.c
Normal file
758
src/compiler/tilde_codegen_expr.c
Normal file
@@ -0,0 +1,758 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
TB_Register tilde_get_zero(TbContext *c, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
TB_DataType data_type = tbtype(type);
|
||||
if (type_is_float(type))
|
||||
{
|
||||
return tb_inst_fconst(c->f, data_type, 0);
|
||||
}
|
||||
return tb_inst_iconst(c->f, data_type, 0);
|
||||
}
|
||||
|
||||
TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable)
|
||||
{
|
||||
assert(ref->kind == TBE_ADDRESS || ref->kind == TBE_ADDRESS_FAILABLE);
|
||||
TB_Label assign_block = 0;
|
||||
|
||||
PUSH_ERROR();
|
||||
|
||||
TBEValue result;
|
||||
if (failable)
|
||||
{
|
||||
if (IS_FAILABLE(expr))
|
||||
{
|
||||
if (expr->expr_kind == EXPR_FAILABLE)
|
||||
{
|
||||
c->error_var = 0;
|
||||
c->catch_block = 0;
|
||||
tilde_emit_expr(c, &result, expr->inner_expr);
|
||||
tilde_store_value_aligned(c, failable, &result, type_abi_alignment(type_anyerr));
|
||||
POP_ERROR();
|
||||
return (TBEValue) { .kind = TBE_VALUE };
|
||||
}
|
||||
assign_block = tb_inst_new_label_id(c->f);
|
||||
c->error_var = failable;
|
||||
c->catch_block = assign_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->error_var = 0;
|
||||
c->catch_block = 0;
|
||||
}
|
||||
}
|
||||
if (type_is_vector(expr->type))
|
||||
{
|
||||
tilde_emit_expr(c, &result, expr);
|
||||
tilde_store_value(c, ref, &result);
|
||||
}
|
||||
else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST)
|
||||
{
|
||||
TODO
|
||||
//llvm_emit_const_initialize_reference(c, ref, expr);
|
||||
//value = *ref;
|
||||
}
|
||||
else if (expr_is_init_list(expr))
|
||||
{
|
||||
TODO
|
||||
//llvm_emit_initialize_reference(c, ref, expr);
|
||||
//value = *ref;
|
||||
}
|
||||
else
|
||||
{
|
||||
tilde_emit_expr(c, &result, expr);
|
||||
tilde_store_value(c, ref, &result);
|
||||
}
|
||||
|
||||
if (failable)
|
||||
{
|
||||
tilde_store_zero(c, type_anyerr, failable, type_alloca_alignment(type_anyerr));
|
||||
}
|
||||
POP_ERROR();
|
||||
|
||||
if (assign_block)
|
||||
{
|
||||
tb_inst_goto(c->f, assign_block);
|
||||
tb_inst_label(c->f, assign_block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void tilde_emit_parameter(TbContext *c, TB_Reg **args, ABIArgInfo *info, TBEValue *be_value, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(be_value->type->canonical == type);
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
// Skip.
|
||||
return;
|
||||
case ABI_ARG_INDIRECT:
|
||||
{
|
||||
// If we want we could optimize for structs by doing it by reference here.
|
||||
assert(info->indirect.alignment == type_abi_alignment(type) || info->attributes.realign);
|
||||
TB_Reg indirect = tb_inst_local(c->f, type_size(type), info->indirect.alignment);
|
||||
tilde_store_value_aligned(c, indirect, be_value, info->indirect.alignment);
|
||||
vec_add(*args, indirect);
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
vec_add(*args, tilde_load_value(c, be_value));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
if (!coerce_type || coerce_type == llvm_get_type(c, type))
|
||||
{
|
||||
vec_add(*args, llvm_load_value_store(c, be_value));
|
||||
return;
|
||||
}
|
||||
if (!abi_info_should_flatten(info))
|
||||
{
|
||||
vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type));
|
||||
return;
|
||||
}
|
||||
AlignSize target_alignment = llvm_abi_alignment(c, coerce_type);
|
||||
|
||||
AlignSize alignment;
|
||||
LLVMValueRef cast = llvm_emit_coerce_alignment(c, be_value, coerce_type, target_alignment, &alignment);
|
||||
LLVMTypeRef element = llvm_abi_type(c, info->direct_coerce.type);
|
||||
for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++)
|
||||
{
|
||||
AlignSize load_align;
|
||||
LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, alignment, &load_align);
|
||||
vec_add(*args, llvm_load(c, element, element_ptr, load_align, ""));
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
/*
|
||||
llvm_value_addr(c, be_value);
|
||||
REMINDER("Handle invalid alignment");
|
||||
// Here we do the following transform:
|
||||
// struct -> { lo, hi } -> lo, hi
|
||||
LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo);
|
||||
LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi);
|
||||
LLVMTypeRef struct_type = llvm_get_coerce_type(c, info);
|
||||
|
||||
AlignSize struct_align;
|
||||
LLVMValueRef cast = llvm_emit_coerce_alignment(c,
|
||||
be_value,
|
||||
struct_type,
|
||||
llvm_abi_alignment(c, struct_type),
|
||||
&struct_align);
|
||||
// Get the lo value.
|
||||
|
||||
AlignSize alignment;
|
||||
LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, struct_align, &alignment);
|
||||
vec_add(*args, llvm_load(c, lo, lo_ptr, alignment, "lo"));
|
||||
// Get the hi value.
|
||||
LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, struct_align, &alignment);
|
||||
vec_add(*args, llvm_load(c, hi, hi_ptr, alignment, "hi"));
|
||||
return;*/
|
||||
}
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
// Move this to an address (if needed)
|
||||
value_addr(c, be_value);
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
AlignSize alignment;
|
||||
LLVMValueRef temp = llvm_emit_coerce_alignment(c,
|
||||
be_value,
|
||||
coerce_type,
|
||||
llvm_abi_alignment(c, coerce_type),
|
||||
&alignment);
|
||||
|
||||
AlignSize align;
|
||||
LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c,
|
||||
temp,
|
||||
coerce_type,
|
||||
info->coerce_expand.lo_index,
|
||||
alignment,
|
||||
&align);
|
||||
vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.lo), gep_first, align, ""));
|
||||
if (abi_type_is_valid(info->coerce_expand.hi))
|
||||
{
|
||||
LLVMValueRef gep_second = llvm_emit_struct_gep_raw(c,
|
||||
temp,
|
||||
coerce_type,
|
||||
info->coerce_expand.hi_index,
|
||||
alignment,
|
||||
&align);
|
||||
vec_add(*args, llvm_load(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, ""));
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_EXPAND:
|
||||
{
|
||||
// Move this to an address (if needed)
|
||||
value_addr(c, be_value);
|
||||
TODO
|
||||
/*
|
||||
llvm_expand_type_to_args(c, type, be_value->value, args, be_value->alignment);
|
||||
// Expand the padding here.
|
||||
if (info->expand.padding_type)
|
||||
{
|
||||
vec_add(*args, LLVMGetUndef(llvm_get_type(c, info->expand.padding_type)));
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr)
|
||||
{
|
||||
if (expr->call_expr.is_builtin)
|
||||
{
|
||||
TODO
|
||||
// llvm_emit_builtin_call(c, result_value, expr);
|
||||
return;
|
||||
}
|
||||
TB_DataType func_type;
|
||||
TB_Reg func;
|
||||
TBEValue temp_value;
|
||||
|
||||
bool always_inline = false;
|
||||
|
||||
FunctionPrototype *prototype;
|
||||
// 1. Call through a pointer.
|
||||
if (expr->call_expr.is_pointer_call)
|
||||
{
|
||||
Expr *function = expr->call_expr.function;
|
||||
|
||||
// 1a. Find the pointee type for the function pointer:
|
||||
Type *type = function->type->canonical->pointer;
|
||||
|
||||
// 1b. Find the type signature using the underlying pointer.
|
||||
prototype = type->func.prototype;
|
||||
|
||||
// 1c. Evaluate the pointer expression.
|
||||
TBEValue func_value;
|
||||
tilde_emit_expr(c, &func_value, expr->call_expr.function);
|
||||
|
||||
// 1d. Load it as a value
|
||||
func = tilde_load_value(c, &func_value);
|
||||
|
||||
// 1e. Calculate the function type
|
||||
func_type = tbtype(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2a. Get the function declaration
|
||||
|
||||
Decl *function_decl = expr->call_expr.func_ref;
|
||||
always_inline = function_decl->func_decl.attr_inline;
|
||||
|
||||
// 2b. Set signature, function and function type
|
||||
prototype = function_decl->type->func.prototype;
|
||||
func = function_decl->tb_register;
|
||||
assert(func);
|
||||
func_type = tbtype(function_decl->type);
|
||||
}
|
||||
|
||||
TB_Reg *values = NULL;
|
||||
Type **params = prototype->params;
|
||||
ABIArgInfo **abi_args = prototype->abi_args;
|
||||
unsigned param_count = vec_size(params);
|
||||
unsigned non_variadic_params = param_count;
|
||||
if (prototype->variadic == VARIADIC_TYPED) non_variadic_params--;
|
||||
ABIArgInfo *ret_info = prototype->ret_abi_info;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
|
||||
// 5. In the case of a failable, the error is replacing the regular return abi.
|
||||
TB_Reg error_var = 0;
|
||||
|
||||
*result_value = (TBEValue){ .kind = TBE_VALUE, .reg = 0 };
|
||||
// 6. Generate data for the return value.
|
||||
switch (ret_info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
// 6a. We can use the stored error var if there is no redirect.
|
||||
if (prototype->is_failable && c->error_var && !ret_info->attributes.realign)
|
||||
{
|
||||
error_var = c->error_var;
|
||||
vec_add(values, error_var);
|
||||
break;
|
||||
}
|
||||
// 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side.
|
||||
assert(ret_info->attributes.realign || ret_info->indirect.alignment == type_abi_alignment(call_return_type));
|
||||
AlignSize alignment = ret_info->indirect.alignment;
|
||||
value_set_address(result_value, tilde_emit_alloca(c, call_return_type), call_return_type, alignment);
|
||||
|
||||
// 6c. Add the pointer to the list of arguments.
|
||||
vec_add(values, result_value->reg);
|
||||
break;
|
||||
case ABI_ARG_EXPAND:
|
||||
UNREACHABLE
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
case ABI_ARG_IGNORE:
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
case ABI_ARG_DIRECT:
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 7. We might have a failable indirect return and a normal return.
|
||||
// In this case we need to add it by hand.
|
||||
TBEValue synthetic_return_param = { 0 };
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
// 7b. Create the address to hold the return.
|
||||
Type *actual_return_type = type_lowering(prototype->ret_by_ref_type);
|
||||
value_set(&synthetic_return_param, tilde_emit_alloca(c, actual_return_type), type_get_ptr(actual_return_type));
|
||||
// 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list)
|
||||
tilde_emit_parameter(c,
|
||||
&values,
|
||||
prototype->ret_by_ref_abi_info,
|
||||
&synthetic_return_param,
|
||||
synthetic_return_param.type);
|
||||
// 7d. Update the be_value to actually be an address.
|
||||
value_set_address_abi_aligned(&synthetic_return_param, synthetic_return_param.reg, actual_return_type);
|
||||
}
|
||||
|
||||
|
||||
// 8. Add all other arguments.
|
||||
unsigned arguments = vec_size(expr->call_expr.arguments);
|
||||
assert(arguments >= non_variadic_params);
|
||||
for (unsigned i = 0; i < non_variadic_params; i++)
|
||||
{
|
||||
// 8a. Evaluate the expression.
|
||||
Expr *arg_expr = expr->call_expr.arguments[i];
|
||||
tilde_emit_expr(c, &temp_value, arg_expr);
|
||||
|
||||
// 8b. Emit the parameter according to ABI rules.
|
||||
Type *param = params[i];
|
||||
ABIArgInfo *info = abi_args[i];
|
||||
tilde_emit_parameter(c, &values, info, &temp_value, param);
|
||||
}
|
||||
|
||||
// 9. Typed varargs
|
||||
if (prototype->variadic == VARIADIC_TYPED)
|
||||
{
|
||||
REMINDER("All varargs should be called with non-alias!");
|
||||
Type *vararg_param = params[non_variadic_params];
|
||||
ABIArgInfo *vararg_info = abi_args[non_variadic_params];
|
||||
|
||||
TBEValue subarray;
|
||||
|
||||
value_set_address_abi_aligned(&subarray, tilde_emit_alloca(c, vararg_param), vararg_param);
|
||||
|
||||
// 9a. Special case, empty argument
|
||||
if (arguments == non_variadic_params)
|
||||
{
|
||||
// Just set the size to zero.
|
||||
TBEValue len_addr;
|
||||
TODO
|
||||
// llvm_emit_subarray_len(c, &subarray, &len_addr);
|
||||
// llvm_store_value_raw(c, &len_addr, llvm_get_zero(c, type_usize));
|
||||
}
|
||||
else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last)
|
||||
{
|
||||
// 9b. We unpack the last type which is either a slice, an array or a dynamic array.
|
||||
TODO
|
||||
// llvm_emit_unpacked_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 9b. Otherwise we also need to allocate memory for the arguments:
|
||||
Type *pointee_type = vararg_param->array.base;
|
||||
Type *array = type_get_array(pointee_type, arguments - non_variadic_params);
|
||||
TB_DataType llvm_array_type = tbtype(array);
|
||||
AlignSize alignment = type_alloca_alignment(array);
|
||||
TB_Reg array_ref = tilde_emit_alloca(c, array);
|
||||
for (unsigned i = non_variadic_params; i < arguments; i++)
|
||||
{
|
||||
Expr *arg_expr = expr->call_expr.arguments[i];
|
||||
tilde_emit_expr(c, &temp_value, arg_expr);
|
||||
AlignSize store_alignment;
|
||||
TODO
|
||||
/*
|
||||
LLVMValueRef slot = llvm_emit_array_gep_raw(c, array_ref, llvm_array_type, i - non_variadic_params, alignment, &store_alignment);
|
||||
llvm_store_value_aligned(c, slot, &temp_value, store_alignment);*/
|
||||
}
|
||||
TODO
|
||||
/*
|
||||
BEValue len_addr;
|
||||
llvm_emit_subarray_len(c, &subarray, &len_addr);
|
||||
llvm_store_value_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params));
|
||||
BEValue pointer_addr;
|
||||
llvm_emit_subarray_pointer(c, &subarray, &pointer_addr);
|
||||
Type *array_as_pointer_type = type_get_ptr(pointee_type);
|
||||
llvm_store_value_raw(c, &pointer_addr, llvm_emit_bitcast(c, array_ref, array_as_pointer_type));*/
|
||||
}
|
||||
tilde_emit_parameter(c, &values, vararg_info, &subarray, vararg_param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 9. Emit varargs.
|
||||
for (unsigned i = param_count; i < arguments; i++)
|
||||
{
|
||||
Expr *arg_expr = expr->call_expr.arguments[i];
|
||||
tilde_emit_expr(c, &temp_value, arg_expr);
|
||||
REMINDER("Varargs should be expanded correctly");
|
||||
vec_add(values, tilde_load_value(c, &temp_value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params)
|
||||
//EMIT_LOC(c, expr);
|
||||
TODO
|
||||
TB_Reg call_value;
|
||||
// = tb_inst_call(c->f, func_type, func, vec_size(values), values);
|
||||
if (prototype->call_abi)
|
||||
{
|
||||
// LLVMSetInstructionCallConv(call_value, llvm_call_convention_from_call(prototype->call_abi, platform_target.arch, platform_target.os));
|
||||
}
|
||||
if (expr->call_expr.force_noinline)
|
||||
{
|
||||
// llvm_attribute_add_call(c, call_value, attribute_id.noinline, -1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expr->call_expr.force_inline || always_inline)
|
||||
{
|
||||
// llvm_attribute_add_call(c, call_value, attribute_id.alwaysinline, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for (unsigned i = 0; i < non_variadic_params; i++)
|
||||
{
|
||||
Type *param = params[i];
|
||||
ABIArgInfo *info = abi_args[i];
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
if (info->attributes.by_val)
|
||||
{
|
||||
// llvm_attribute_add_call_type(c, call_value, attribute_id.byval, (int)i + 1, llvm_get_type(c, info->indirect.type));
|
||||
}
|
||||
llvm_attribute_add_call(c, call_value, attribute_id.align, (int)i + 1, info->indirect.alignment);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Process the return value.
|
||||
switch (ret_info->kind)
|
||||
{
|
||||
case ABI_ARG_EXPAND:
|
||||
UNREACHABLE
|
||||
case ABI_ARG_IGNORE:
|
||||
// 12. Basically void returns or empty structs.
|
||||
// Here we know we don't have a failable or any return value that can be used.
|
||||
assert(!prototype->is_failable && "Failable should have produced a return value.");
|
||||
*result_value = (BEValue) { .type = type_void, .kind = BE_VALUE };
|
||||
return;
|
||||
case ABI_ARG_INDIRECT:
|
||||
llvm_attribute_add_call_type(c, call_value, attribute_id.sret, 1, llvm_get_type(c, ret_info->indirect.type));
|
||||
llvm_attribute_add_call(c, call_value, attribute_id.align, 1, ret_info->indirect.alignment);
|
||||
// 13. Indirect, that is passing the result through an out parameter.
|
||||
|
||||
// 13a. In the case of an already present error_var, we don't need to do a load here.
|
||||
if (error_var) break;
|
||||
|
||||
// 13b. Otherwise it will be contained in a be_value that is an address
|
||||
// so we don't need to do anything more.
|
||||
assert(result_value->kind == BE_ADDRESS);
|
||||
|
||||
break;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
// 14. A direct pair, in this case the data is stored like { lo, hi }
|
||||
// For example we might have { int, int, short, short, int },
|
||||
// this then gets bitcast to { long, long }, so we recover it by loading
|
||||
// { long, long } into memory, then performing a bitcast to { int, int, short, short, int }
|
||||
|
||||
// 14a. Generate the type.
|
||||
LLVMTypeRef lo = llvm_abi_type(c, ret_info->direct_pair.lo);
|
||||
LLVMTypeRef hi = llvm_abi_type(c, ret_info->direct_pair.hi);
|
||||
LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi);
|
||||
|
||||
// 14b. Use the coerce method to go from the struct to the actual type
|
||||
// by storing the { lo, hi } struct to memory, then loading it
|
||||
// again using a bitcast.
|
||||
llvm_emit_convert_value_from_coerced(c, result_value, struct_type, call_value, call_return_type);
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
// 15. Expand-coerce, this is similar to "direct pair", but looks like this:
|
||||
// { lo, hi } set into { pad, lo, pad, hi } -> original type.
|
||||
|
||||
// 15a. Create memory to hold the return type.
|
||||
LLVMValueRef ret = llvm_emit_alloca_aligned(c, call_return_type, "");
|
||||
llvm_value_set_address_abi_aligned(result_value, ret, call_return_type);
|
||||
|
||||
// 15b. "Convert" this return type pointer in memory to our coerce type which is { pad, lo, pad, hi }
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, ret_info);
|
||||
LLVMValueRef coerce = LLVMBuildBitCast(c->builder, ret, coerce_type, "");
|
||||
|
||||
// 15d. Find the address to the low value
|
||||
AlignSize alignment;
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.lo_index,
|
||||
type_abi_alignment(call_return_type), &alignment);
|
||||
|
||||
// 15e. If there is only a single field, we simply store the value,
|
||||
// so { lo } set into { pad, lo, pad } -> original type.
|
||||
if (!abi_type_is_valid(ret_info->coerce_expand.hi))
|
||||
{
|
||||
// Here we do a store to call -> lo (leaving the rest undefined)
|
||||
llvm_store(c, lo, call_value, alignment);
|
||||
break;
|
||||
}
|
||||
|
||||
// 15g. We can now extract { lo, hi } to lo_value and hi_value.
|
||||
LLVMValueRef lo_value = LLVMBuildExtractValue(c->builder, call_value, 0, "");
|
||||
LLVMValueRef hi_value = LLVMBuildExtractValue(c->builder, call_value, 1, "");
|
||||
|
||||
// 15h. Store lo_value into the { pad, lo, pad, hi } struct.
|
||||
llvm_store(c, lo, lo_value, alignment);
|
||||
|
||||
// 15i. Calculate the address to the high value (like for the low in 15d.
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.hi_index,
|
||||
type_abi_alignment(call_return_type), &alignment);
|
||||
|
||||
// 15h. Store the high value.
|
||||
llvm_store(c, hi, hi_value, alignment);
|
||||
|
||||
break;
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
llvm_value_set(result_value, call_value, call_return_type);
|
||||
break;
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
// 16. A direct coerce, this is basically "call result" bitcast return type.
|
||||
|
||||
// 16a. Get the type of the return.
|
||||
LLVMTypeRef coerce = llvm_get_coerce_type(c, ret_info);
|
||||
|
||||
// 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done.
|
||||
if (!coerce || coerce == llvm_get_type(c, call_return_type))
|
||||
{
|
||||
// 16c. We just set as a value in be_value.
|
||||
llvm_value_set(result_value, call_value, call_return_type);
|
||||
break;
|
||||
}
|
||||
// 16c. We use a normal bitcast coerce.
|
||||
assert(!abi_info_should_flatten(ret_info) && "Did not expect flattening on return types.");
|
||||
llvm_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Handle failables.
|
||||
if (prototype->is_failable)
|
||||
{
|
||||
BEValue no_err;
|
||||
|
||||
// 17a. If we used the error var as the indirect recipient, then that will hold the error.
|
||||
// otherwise it's whatever value in be_value.
|
||||
BEValue error_holder = *result_value;
|
||||
if (error_var)
|
||||
{
|
||||
llvm_value_set_address_abi_aligned(&error_holder, c->error_var, type_anyerr);
|
||||
}
|
||||
// 17b. Generate a boolean switch.
|
||||
llvm_value_set_bool(&no_err, llvm_emit_is_no_error(c, llvm_load_value(c, &error_holder)));
|
||||
|
||||
// 17c. If we have an error var, or we aren't interested in the error variable
|
||||
// - then it's straightforward. We just jump to the catch block.
|
||||
LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after.errcheck");
|
||||
if (error_var || !c->error_var)
|
||||
{
|
||||
llvm_emit_cond_br(c, &no_err, after_block, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 17d. If we have an error var we need to assign, then we need to
|
||||
// first jump to an error block, where we do the copy.
|
||||
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error");
|
||||
llvm_emit_cond_br(c, &no_err, after_block, error_block);
|
||||
llvm_emit_block(c, error_block);
|
||||
llvm_store_value_aligned(c, c->error_var, result_value, 0);
|
||||
// 17e. Then jump to the catch.
|
||||
llvm_emit_br(c, c->catch_block);
|
||||
}
|
||||
|
||||
// 17f. Emit the "after" block.
|
||||
llvm_emit_block(c, after_block);
|
||||
|
||||
// 17g. If void, be_value contents should be skipped.
|
||||
if (!prototype->ret_by_ref)
|
||||
{
|
||||
*result_value = (BEValue) { .type = type_void, .kind = BE_VALUE };
|
||||
return;
|
||||
}
|
||||
|
||||
// 17h. Assign the return param to be_value.
|
||||
*result_value = synthetic_return_param;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
// 17i. The simple case here is where there is a normal return.
|
||||
// In this case be_value already holds the result
|
||||
return;
|
||||
}
|
||||
|
||||
void tilde_emit_expr(TbContext *c, TBEValue *value, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_CALL:
|
||||
tilde_emit_call_expr(c, value, expr);
|
||||
return;
|
||||
default:
|
||||
TODO
|
||||
/*
|
||||
case EXPR_DESIGNATOR:
|
||||
case EXPR_POISONED:
|
||||
case EXPR_COND:
|
||||
case EXPR_TYPEINFO:
|
||||
case EXPR_MACRO_EXPANSION:
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_PLACEHOLDER:
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_VARIANTSWITCH:
|
||||
UNREACHABLE
|
||||
case EXPR_ARGV_TO_SUBARRAY:
|
||||
llvm_emit_argv_to_subarray(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY_UNWRAP_CHAIN:
|
||||
llvm_emit_try_unwrap_chain(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY_UNWRAP:
|
||||
llvm_emit_try_unwrap(c, value, expr);
|
||||
return;
|
||||
case EXPR_CATCH_UNWRAP:
|
||||
llvm_emit_catch_unwrap(c, value, expr);
|
||||
return;
|
||||
case EXPR_UNDEF:
|
||||
// Should never reach this.
|
||||
UNREACHABLE
|
||||
case EXPR_PTR:
|
||||
llvm_emit_ptr(c, value, expr);
|
||||
return;
|
||||
case EXPR_BUILTIN:
|
||||
UNREACHABLE;
|
||||
case EXPR_DECL:
|
||||
llvm_emit_local_decl(c, expr->decl_expr);
|
||||
llvm_value_set_decl_address(value, expr->decl_expr);
|
||||
return;
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
llvm_emit_slice_assign(c, value, expr);
|
||||
return;
|
||||
case EXPR_SLICE:
|
||||
gencontext_emit_slice(c, value, expr);
|
||||
return;
|
||||
case EXPR_LEN:
|
||||
llvm_emit_len(c, value, expr);
|
||||
return;
|
||||
case EXPR_FAILABLE:
|
||||
llvm_emit_failable(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY:
|
||||
llvm_emit_try_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_CATCH:
|
||||
llvm_emit_catch_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_NOP:
|
||||
llvm_value_set(value, NULL, type_void);
|
||||
return;
|
||||
case EXPR_OR_ERROR:
|
||||
gencontext_emit_or_error(c, value, expr);
|
||||
return;
|
||||
case EXPR_MACRO_BLOCK:
|
||||
llvm_emit_macro_block(c, value, expr);
|
||||
return;
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
UNREACHABLE
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
llvm_emit_initializer_list_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_EXPR_BLOCK:
|
||||
llvm_emit_expr_block(c, value, expr);
|
||||
return;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
gencontext_emit_scoped_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_UNARY:
|
||||
gencontext_emit_unary_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_CONST:
|
||||
llvm_emit_const_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
llvm_emit_macro_body_expansion(c, value, expr);
|
||||
return;
|
||||
case EXPR_BITASSIGN:
|
||||
llvm_emit_bitassign_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_BINARY:
|
||||
llvm_emit_binary_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_TERNARY:
|
||||
gencontext_emit_ternary_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_POST_UNARY:
|
||||
llvm_emit_post_unary_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
llvm_emit_force_unwrap_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_RETHROW:
|
||||
llvm_emit_rethrow_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_TYPEOFANY:
|
||||
llvm_emit_typeofany(c, value, expr);
|
||||
return;
|
||||
case EXPR_TYPEID:
|
||||
case EXPR_GROUP:
|
||||
// These are folded in the semantic analysis step.
|
||||
UNREACHABLE
|
||||
case EXPR_IDENTIFIER:
|
||||
case EXPR_CONST_IDENTIFIER:
|
||||
llvm_value_set_decl(value, expr->identifier_expr.decl);
|
||||
return;
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
gencontext_emit_subscript(c, value, expr);
|
||||
return;
|
||||
case EXPR_ACCESS:
|
||||
gencontext_emit_access_addr(c, value, expr);
|
||||
return;
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
gencontext_emit_expression_list_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_CAST:
|
||||
gencontext_emit_cast_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_BITACCESS:
|
||||
llvm_emit_bitaccess(c, value, expr);
|
||||
return;*/
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
#endif
|
||||
43
src/compiler/tilde_codegen_instr.c
Normal file
43
src/compiler/tilde_codegen_instr.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align)
|
||||
{
|
||||
ByteSize min = type_min_alignment(align, size);
|
||||
TB_Register zero = tb_inst_iconst(c->f, TB_TYPE_I8, 0);
|
||||
TB_Register elements = tb_inst_iconst(c->f, size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size);
|
||||
tb_inst_memset(c->f, ref, zero, elements, min);
|
||||
}
|
||||
|
||||
void tilde_emit_cond_br(TbContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block)
|
||||
{
|
||||
tb_inst_if(c->f, tilde_load_value(c, value), then_block, else_block);
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_shl_fixed(TbContext *c, Type *type, TB_Reg reg, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return reg;
|
||||
BitSize bit_width = type_kind_bitsize(type->type_kind);
|
||||
if (shift >= bit_width) return tilde_get_zero(c, type);
|
||||
TB_DataType int_type = tbtype(type);
|
||||
return tb_inst_shl(c->f, int_type, reg, tb_inst_iconst(c->f, int_type, (unsigned)shift), type_is_signed(type) ? TB_ASSUME_NSW : TB_ASSUME_NUW);
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift)
|
||||
{
|
||||
assert(shift >= 0);
|
||||
if (shift == 0) return reg;
|
||||
BitSize bit_width = type_kind_bitsize(type->type_kind);
|
||||
if (shift >= bit_width) return tilde_get_zero(c, type);
|
||||
TB_DataType int_type = tbtype(type);
|
||||
return tb_inst_shr(c->f, int_type, reg, tb_inst_iconst(c->f, int_type, (unsigned)shift));
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_alloca(TbContext *c, Type *type)
|
||||
{
|
||||
return tb_inst_local(c->f, type_size(type), type_alloca_alignment(type));
|
||||
}
|
||||
|
||||
#endif
|
||||
122
src/compiler/tilde_codegen_stmt.c
Normal file
122
src/compiler/tilde_codegen_stmt.c
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
void tilde_emit_compound_stmt(TbContext *context, Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_COMPOUND_STMT);
|
||||
AstId current = ast->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
tilde_emit_stmt(context, ast_next(¤t));
|
||||
}
|
||||
tilde_emit_defer(context, ast->compound_stmt.defer_list.start, ast->compound_stmt.defer_list.end);
|
||||
}
|
||||
|
||||
static void llvm_emit_ct_compound_stmt(TbContext *context, Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_CT_COMPOUND_STMT);
|
||||
AstId current = ast->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
tilde_emit_stmt(context, ast_next(¤t));
|
||||
}
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_local_decl(TbContext *c, Decl *decl)
|
||||
{
|
||||
// 1. Get the declaration and the TB type.
|
||||
Type *var_type = type_lowering(type_no_fail(decl->type));
|
||||
|
||||
// 2. In the case we have a static variable,
|
||||
// then we essentially treat this as a global.
|
||||
if (decl->var.is_static)
|
||||
{
|
||||
if (IS_FAILABLE(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->external_name);
|
||||
scratch_buffer_append(".f");
|
||||
TB_InitializerID initializer = tb_initializer_create(c->module, type_size(type_anyerr), type_alloca_alignment(type_anyerr), 1);
|
||||
decl->var.tb_failable_reg = tb_global_create(c->module, scratch_buffer_to_string(), initializer);
|
||||
}
|
||||
TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1);
|
||||
decl->tb_register = tb_global_create(c->module, "tempglobal", static_initializer);
|
||||
tilde_emit_global_initializer(c, decl);
|
||||
return decl->tb_register;
|
||||
}
|
||||
tilde_emit_local_var_alloca(c, decl);
|
||||
TB_Register reg = decl->tb_register;
|
||||
Expr *init = decl->var.init_expr;
|
||||
if (IS_FAILABLE(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->name);
|
||||
scratch_buffer_append(".f");
|
||||
decl->var.tb_failable_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr));
|
||||
// Only clear out the result if the assignment isn't a failable.
|
||||
}
|
||||
|
||||
TBEValue value;
|
||||
value_set_decl(&value, decl);
|
||||
if (init)
|
||||
{
|
||||
// If we don't have undef, then make an assign.
|
||||
if (init->expr_kind != EXPR_UNDEF)
|
||||
{
|
||||
tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg);
|
||||
}
|
||||
// TODO trap on undef in debug mode.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (decl->var.tb_failable_reg)
|
||||
{
|
||||
tilde_store_zero(c, type_anyerr, decl->var.tb_failable_reg, type_abi_alignment(type_anyerr));
|
||||
}
|
||||
tilde_store_value_zero(c, &value);
|
||||
}
|
||||
return reg;
|
||||
|
||||
}
|
||||
|
||||
void tilde_emit_defer(TbContext *c, AstId defer_start, AstId defer_end)
|
||||
{
|
||||
if (defer_start == defer_end) return;
|
||||
AstId defer = defer_start;
|
||||
while (defer && defer != defer_end)
|
||||
{
|
||||
Ast *def = astptr(defer);
|
||||
TB_Label exit = tb_inst_new_label_id(c->f);
|
||||
Ast *body = def->defer_stmt.body;
|
||||
def->defer_stmt.codegen.exit_val = exit;
|
||||
tilde_emit_stmt(c, body);
|
||||
tb_inst_goto(c->f, exit);
|
||||
tb_inst_label(c->f, exit);
|
||||
defer = def->defer_stmt.prev_defer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void tilde_emit_stmt(TbContext *c, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_DECLARE_STMT:
|
||||
tilde_emit_local_decl(c, ast->declare_stmt);
|
||||
break;
|
||||
case AST_RETURN_STMT:
|
||||
//TbContext_emit_return(c, ast);
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
105
src/compiler/tilde_codegen_storeload.c
Normal file
105
src/compiler/tilde_codegen_storeload.c
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
{
|
||||
tilde_store(c, tbtype(type), addr, value, alignment);
|
||||
}
|
||||
|
||||
void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value)
|
||||
{
|
||||
tilde_store(c, tbtype(type), addr, value, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value)
|
||||
{
|
||||
assert(value_is_addr(destination));
|
||||
tilde_store(c, tbtype(destination->type), destination->reg, value, destination->alignment);
|
||||
}
|
||||
|
||||
|
||||
void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value)
|
||||
{
|
||||
assert(!decl->is_value);
|
||||
tilde_store(c, tbtype(decl->type), decl->tb_register, value, decl->alignment);
|
||||
}
|
||||
|
||||
void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment);
|
||||
value_fold_failable(c, value);
|
||||
// If we have an address but not an aggregate, do a load.
|
||||
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
|
||||
{
|
||||
value_rvalue(c, value);
|
||||
}
|
||||
switch (value->kind)
|
||||
{
|
||||
case TBE_VALUE:
|
||||
tilde_store_value_raw(c, value, value->reg);
|
||||
return;
|
||||
case TBE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case TBE_ADDRESS:
|
||||
{
|
||||
ByteSize size = type_size(value->type);
|
||||
TB_Reg copy_size = tb_inst_iconst(c->f, size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64, size);
|
||||
tb_inst_memcpy(c->f, destination, value->reg, copy_size, type_min_alignment(alignment, value->alignment));
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void tilde_store_value(TbContext *c, TBEValue *dst, TBEValue *value)
|
||||
{
|
||||
assert(dst->kind == TBE_ADDRESS);
|
||||
tilde_store_value_aligned(c, dst->reg, value, dst->alignment);
|
||||
}
|
||||
|
||||
TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer)
|
||||
{
|
||||
return tilde_load(c, tbtype(type), pointer, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
TB_Reg tilde_load_value(TbContext *c, TBEValue *value)
|
||||
{
|
||||
value_fold_failable(c, value);
|
||||
switch (value->kind)
|
||||
{
|
||||
case TBE_VALUE:
|
||||
return value->reg;
|
||||
case TBE_ADDRESS_FAILABLE:
|
||||
UNREACHABLE
|
||||
case TBE_ADDRESS:
|
||||
return tilde_load(c, tbtype(value->type), value->reg, value->alignment);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
tilde_store_raw(c, type, addr, tilde_get_zero(c, type), alignment);
|
||||
return;
|
||||
}
|
||||
ByteSize size = type_size(type);
|
||||
ByteSize min = type_min_alignment(alignment, size);
|
||||
TB_Register zero = tb_inst_iconst(c->f, TB_TYPE_I8, 0);
|
||||
TB_Register elements = tb_inst_iconst(c->f, tbtype(type_usize), size);
|
||||
tb_inst_memset(c->f, addr, zero, elements, min);
|
||||
}
|
||||
|
||||
void tilde_store_value_zero(TbContext *c, TBEValue *to)
|
||||
{
|
||||
assert(to->kind == TBE_ADDRESS);
|
||||
tilde_store_zero(c, to->type, to->reg, to->alignment);
|
||||
}
|
||||
|
||||
#endif
|
||||
102
src/compiler/tilde_codegen_value.c
Normal file
102
src/compiler/tilde_codegen_value.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
void value_set(TBEValue *value, TB_Reg val, Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
assert(!val || type == type_void);
|
||||
value->reg = val;
|
||||
value->alignment = type_abi_alignment(type);
|
||||
value->kind = TBE_VALUE;
|
||||
value->type = type;
|
||||
}
|
||||
|
||||
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment)
|
||||
{
|
||||
value->reg = addr;
|
||||
value->alignment = alignment;
|
||||
value->kind = TBE_ADDRESS;
|
||||
value->type = type_lowering(type);
|
||||
}
|
||||
|
||||
void value_set_address_abi_aligned(TBEValue *value, TB_Reg val, Type *type)
|
||||
{
|
||||
value_set_address(value, val, type, type_abi_alignment(type));
|
||||
}
|
||||
|
||||
void value_addr(TbContext *c, TBEValue *value)
|
||||
{
|
||||
value_fold_failable(c, value);
|
||||
if (value->kind == TBE_ADDRESS) return;
|
||||
if (!c->f)
|
||||
{
|
||||
TODO
|
||||
// TB_Register val = value_rvalue_get(c, value);
|
||||
// TODO check whether new names must be added
|
||||
TB_InitializerID initializer_id = tb_initializer_create(c->module, type_size(value->type),
|
||||
type_alloca_alignment(value->type), 0);
|
||||
tb_global_create(c->module, ".taddr", initializer_id);
|
||||
// TODO set linkage
|
||||
/*
|
||||
llvm_set_private_linkage(ref);
|
||||
llvm_emit_bitcast(c, ref, type_get_ptr(value->type));
|
||||
llvm_value_set_address(value, ref, value->type);*/
|
||||
}
|
||||
else
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
*
|
||||
tilde_emit_alloca_aligned(c, value->type);
|
||||
TB_Register reg = llvm_emit_alloca_aligned(c, value->type, "taddr");
|
||||
tilde_store_bevalue_dest_aligned(c, temp, value);
|
||||
llvm_value_set_address(value, temp, value->type);*/
|
||||
}
|
||||
}
|
||||
|
||||
void value_rvalue(TbContext *c, TBEValue *value)
|
||||
{
|
||||
if (value->kind == TBE_VALUE) return;
|
||||
value_fold_failable(c, value);
|
||||
value->reg = tilde_load_value(c, value);
|
||||
value->kind = TBE_VALUE;
|
||||
}
|
||||
|
||||
void value_fold_failable(TbContext *c, TBEValue *value)
|
||||
{
|
||||
if (value->kind != TBE_ADDRESS_FAILABLE) return;
|
||||
TB_Label after_block = tb_inst_new_label_id(c->f);
|
||||
TB_Reg error_val = tilde_load_abi_alignment(c, type_anyerr, value->failable);
|
||||
TB_Reg comp = tilde_emit_is_no_error(c, error_val);
|
||||
assert(c->catch_block);
|
||||
if (c->error_var)
|
||||
{
|
||||
TB_Label err_block = tb_inst_new_label_id(c->f);
|
||||
tb_inst_if(c->f, comp, after_block, err_block);
|
||||
tb_inst_label(c->f, err_block);
|
||||
tilde_store_raw_abi_alignment(c, type_anyerr, c->error_var, error_val);
|
||||
tb_inst_goto(c->f, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_inst_if(c->f, comp, after_block, c->catch_block);
|
||||
}
|
||||
tb_inst_label(c->f, after_block);
|
||||
value->kind = TBE_ADDRESS;
|
||||
}
|
||||
|
||||
void value_set_decl(TBEValue *value, Decl *decl)
|
||||
{
|
||||
decl = decl_flatten(decl);
|
||||
value_set_address(value, decl_reg(decl), decl->type, decl->alignment);
|
||||
|
||||
if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl))
|
||||
{
|
||||
value->kind = TBE_ADDRESS_FAILABLE;
|
||||
value->failable = decl->var.tb_failable_reg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
125
src/compiler/tilde_internal.h
Normal file
125
src/compiler/tilde_internal.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include "codegen_internal.h"
|
||||
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
#include <tb.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Module *code_module;
|
||||
const char *object_filename;
|
||||
|
||||
TB_Module *module;
|
||||
TB_FeatureSet features;
|
||||
|
||||
Decl *current_func_decl;
|
||||
TB_Function *f;
|
||||
TB_Register error_var;
|
||||
TB_Label catch_block;
|
||||
} TbContext;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TBE_VALUE,
|
||||
TBE_ADDRESS,
|
||||
TBE_ADDRESS_FAILABLE,
|
||||
} TBBackendValueKind;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TBBackendValueKind kind: 5;
|
||||
AlignSize alignment;
|
||||
|
||||
Type *type; // Should never be a distinct or canonical type.
|
||||
|
||||
TB_Register reg;
|
||||
TB_Register failable; // TODO what even is a failable?
|
||||
} TBEValue;
|
||||
|
||||
TB_DataType tbtype(Type *type);
|
||||
|
||||
#define PUSH_ERROR() \
|
||||
TB_Label _old_catch = c->catch_block; \
|
||||
TB_Reg _old_error_var = c->error_var
|
||||
#define POP_ERROR() \
|
||||
c->catch_block = _old_catch; \
|
||||
c->error_var = _old_error_var
|
||||
|
||||
// -- store ---
|
||||
static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value);
|
||||
void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value);
|
||||
void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store_value(TbContext *c, TBEValue *dst, TBEValue *value);
|
||||
void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value);
|
||||
void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment);
|
||||
void tilde_store_value_zero(TbContext *c, TBEValue *to);
|
||||
|
||||
// -- load ---
|
||||
static inline TB_Reg tilde_load(TbContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment);
|
||||
TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value(TbContext *c, TBEValue *value);
|
||||
|
||||
// -- value --
|
||||
void value_set(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment);
|
||||
void value_set_address_abi_aligned(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_decl(TBEValue *value, Decl *decl);
|
||||
void value_addr(TbContext *c, TBEValue *value);
|
||||
|
||||
static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_FAILABLE; }
|
||||
void value_fold_failable(TbContext *c, TBEValue *value);
|
||||
void value_rvalue(TbContext *c, TBEValue *value);
|
||||
|
||||
|
||||
TB_Register tilde_get_zero(TbContext *c, Type *type);
|
||||
|
||||
// -- instructions --
|
||||
void tilde_emit_cond_br(TbContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block);
|
||||
TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift);
|
||||
TB_Reg tilde_emit_alloca(TbContext *c, Type *type);
|
||||
|
||||
// -- stmt ---
|
||||
void tilde_emit_stmt(TbContext *c, Ast *ast);
|
||||
void tilde_emit_defer(TbContext *c, AstId defer_start, AstId defer_end);
|
||||
|
||||
// -- general ---
|
||||
TB_Register tilde_emit_is_no_error(TbContext *c, TB_Reg reg);
|
||||
void tilde_emit_global_initializer(TbContext *c, Decl *decl);
|
||||
void tilde_emit_local_var_alloca(TbContext *c, Decl *decl);
|
||||
void tilde_emit_and_set_decl_alloca(TbContext *c, Decl *decl);
|
||||
|
||||
// -- expr --
|
||||
void tilde_emit_expr(TbContext *c, TBEValue *result, Expr *expr);
|
||||
TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable);
|
||||
|
||||
void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align);
|
||||
|
||||
|
||||
static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
tb_inst_store(c->f, type, addr, value, alignment);
|
||||
}
|
||||
|
||||
static inline TB_Reg tilde_load(TbContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
return tb_inst_load(c->f, type, addr, alignment);
|
||||
}
|
||||
|
||||
static inline TB_Reg decl_reg(Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_reg(decl->var.alias);
|
||||
assert(!decl->is_value);
|
||||
return decl->tb_register;
|
||||
}
|
||||
|
||||
#endif // TB_BACKEND
|
||||
Reference in New Issue
Block a user