Initial add of tilde backend.

This commit is contained in:
Christoffer Lerno
2023-01-15 01:34:25 +01:00
committed by Christoffer Lerno
parent 6da6288ad8
commit e284d49dd9
29 changed files with 2454 additions and 4008 deletions

View File

@@ -196,7 +196,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
if [[ "${{matrix.llvm_version}}" == 15 ]]; then
if [[ "${{matrix.llvm_version}}" > 13 ]]; then
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
fi
- name: CMake

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "tilde-backend"]
path = tilde-backend
url = https://github.com/c3lang/tilde-backend

View File

@@ -24,9 +24,9 @@ endif()
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
option(C3_USE_TB "Enable TB" OFF)
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
option(C3_USE_TB "Use TB" OFF)
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
set(C3_USE_MIMALLOC OFF)
@@ -51,6 +51,21 @@ if (NOT C3_LLVM_VERSION STREQUAL "auto")
endif()
endif()
find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "14")
@@ -129,9 +144,6 @@ file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
if(C3_USE_TB)
find_library(TB_LIB NAMES tildebackend.a tildebackend.lib PATHS ${CMAKE_SOURCE_DIR}/tb/)
endif()
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
@@ -187,6 +199,7 @@ endif ()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
add_library(miniz STATIC dependencies/miniz/miniz.c)
@@ -249,11 +262,6 @@ add_executable(c3c
src/compiler/symtab.c
src/compiler/target.c
src/compiler/sema_asm.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
@@ -274,23 +282,57 @@ add_executable(c3c
src/utils/unzipper.c
src/compiler/decltable.c
src/compiler/mac_support.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
src/compiler/tilde_codegen_type.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/llvm_codegen_builtins.c
src/compiler/expr.c src/utils/time.c src/utils/http.c)
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
)
if (C3_USE_TB)
file(GLOB tilde-sources
tilde-backend/src/tb/*.c
tilde-backend/src/tb/codegen/*.c
tilde-backend/src/tb/bigint/*.c
tilde-backend/src/tb/objects/*.c
tilde-backend/src/tb/system/*.c
tilde-backend/src/tb/debug/cv/*.c
tilde-backend/src/tb/opt/*.c
tilde-backend/src/tb/x64/*.c
tilde-backend/src/tb/wasm/*.c
tilde-backend/src/tb/aarch64/*.c
)
target_sources(c3c PRIVATE
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/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c
src/compiler/tilde_codegen_abi.c
src/compiler/tilde_codegen_storeload.c)
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=1)
target_link_libraries(c3c tilde-backend)
add_library(tilde-backend STATIC ${tilde-sources})
target_include_directories(tilde-backend PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/src/" "${CMAKE_SOURCE_DIR}/tilde-backend/include")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
else()
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tb/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
@@ -300,12 +342,6 @@ target_include_directories(miniz PUBLIC
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
if(C3_USE_TB)
target_link_libraries(c3c c3c_wrappers ${TB_LIB})
target_compile_definitions(c3c PUBLIC TB_BACKEND=1)
else()
target_compile_definitions(c3c PUBLIC TB_BACKEND=0)
endif()
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
@@ -332,11 +368,18 @@ if(MSVC)
target_compile_options(c3c PUBLIC /MTd)
target_compile_options(c3c_wrappers PUBLIC /MTd)
target_compile_options(miniz PUBLIC /MTd)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MTd)
endif()
else()
target_compile_options(c3c PUBLIC /MT)
target_compile_options(c3c_wrappers PUBLIC /MT)
target_compile_options(miniz PUBLIC /MT)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)

View File

@@ -79,7 +79,7 @@ static void usage(void)
OUTPUT(" vendor-fetch <library> ... Fetches one or more libraries from the vendor collection.");
OUTPUT("");
OUTPUT("Options:");
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
OUTPUT(" --tb - Use Tilde Backend for compilation.");
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
OUTPUT(" --nostdlib - Do not include the standard library.");
OUTPUT(" --nolibc - Do not implicitly link libc nor any associated files.");
@@ -516,14 +516,10 @@ static void parse_option(BuildOptions *options)
options->compile_option = COMPILE_LEX_PARSE_CHECK_ONLY;
return;
case '-':
if (match_longopt("tinybackend"))
if (match_longopt("tb"))
{
#if TB_BACKEND
options->backend = BACKEND_TB;
return;
#else
error_exit("error: The TinyBackend is not supported on this platform.");
#endif
}
if (match_longopt("symtab"))
{

View File

@@ -14,8 +14,8 @@
typedef enum
{
BACKEND_LLVM,
BACKEND_TB
BACKEND_LLVM = 1,
BACKEND_TB = 2
} CompilerBackend;
typedef enum
@@ -341,7 +341,7 @@ typedef struct
const char **link_args;
const char *build_dir;
const char *object_file_dir;
const char *llvm_file_dir;
const char *ir_file_dir;
const char *asm_file_dir;
bool run_after_compile;
bool generate_test_runner;

View File

@@ -241,14 +241,14 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
{
target->build_dir = options->build_dir ? options->build_dir : NULL;
target->object_file_dir = options->obj_out ? options->obj_out : target->build_dir;
target->llvm_file_dir = options->llvm_out ? options->llvm_out : target->build_dir;
target->ir_file_dir = options->llvm_out ? options->llvm_out : target->build_dir;
target->asm_file_dir = options->asm_out ? options->asm_out : target->build_dir;
}
else
{
target->build_dir = options->build_dir ? options->build_dir : "build";
target->object_file_dir = options->obj_out ? options->obj_out : file_append_path(target->build_dir, "tmp");
target->llvm_file_dir = options->llvm_out ? options->llvm_out : file_append_path(target->build_dir, "llvm_ir");
target->ir_file_dir = options->llvm_out ? options->llvm_out : file_append_path(target->build_dir, "llvm_ir");
target->asm_file_dir = options->asm_out ? options->asm_out : file_append_path(target->build_dir, "asm");
}
switch (options->compile_option)

View File

@@ -1,5 +1,6 @@
#include "codegen_internal.h"
const char *test_fns_var_name = "__$C3_TEST_FN_LIST";
const char *test_count_var_name = "__$C3_TEST_COUNT";
const char *test_names_var_name = "__$C3_TEST_NAMES_LIST";
@@ -304,3 +305,17 @@ AlignSize type_alloca_alignment(Type *type)
return align;
}
void codegen_setup_object_names(Module *module, const char **ir_filename, const char **asm_filename, const char **object_filename)
{
const char *result = module_create_object_file_name(module);
*ir_filename = str_printf(active_target.backend == BACKEND_LLVM ? "%s.ll" : "%s.ir", result);
if (active_target.ir_file_dir) *ir_filename = file_append_path(active_target.ir_file_dir, *ir_filename);
*object_filename = str_printf("%s%s", result, get_object_extension());
if (active_target.emit_asm)
{
*asm_filename = str_printf("%s.s", result);
if (active_target.asm_file_dir) *asm_filename = file_append_path(active_target.asm_file_dir, *asm_filename);
}
if (active_target.object_file_dir) *object_filename = file_append_path(active_target.object_file_dir, *object_filename);
}

View File

@@ -56,6 +56,12 @@ static inline bool abi_type_is_promotable_integer_or_bool(AbiType type)
return false;
}
static inline bool expr_is_vector_index(Expr *expr)
{
return expr->expr_kind == EXPR_SUBSCRIPT
&& type_lowering(exprtype(expr->subscript_expr.expr))->type_kind == TYPE_VECTOR;
}
const char *codegen_create_asm(Ast *ast);
extern const char *test_fns_var_name;

View File

@@ -132,9 +132,20 @@ void thread_compile_task_llvm(void *compile_data)
void thread_compile_task_tb(void *compile_data)
{
CompileData *data = compile_data;
data->object_name = tinybackend_codegen(data->context);
data->object_name = tilde_codegen(data->context);
}
#if !TB_AVAILABLE
const char *tilde_codegen(void *context)
{
error_exit("TB backend not available.");
}
void **tilde_gen(Module** modules, unsigned module_count)
{
error_exit("TB backend not available.");
}
#endif
static const char *exe_name(void)
{
@@ -284,18 +295,18 @@ void compiler_compile(void)
void **gen_contexts = VECNEW(void*, module_count);
void (*task)(void *);
if (active_target.asm_file_dir || active_target.llvm_file_dir || active_target.emit_object_files)
if (active_target.asm_file_dir || active_target.ir_file_dir || active_target.emit_object_files)
{
if (active_target.build_dir && !file_exists(active_target.build_dir) && !dir_make(active_target.build_dir))
{
error_exit("Failed to create build directory '%s'.", active_target.build_dir);
}
}
if (active_target.llvm_file_dir && active_target.emit_llvm)
if (active_target.ir_file_dir && active_target.emit_llvm)
{
if (!file_exists(active_target.llvm_file_dir) && !dir_make(active_target.llvm_file_dir))
if (!file_exists(active_target.ir_file_dir) && !dir_make(active_target.ir_file_dir))
{
error_exit("Failed to create output directory '%s'.", active_target.llvm_file_dir);
error_exit("Failed to create output directory '%s'.", active_target.ir_file_dir);
}
}
if (active_target.asm_file_dir && active_target.emit_asm)
@@ -319,12 +330,7 @@ void compiler_compile(void)
task = &thread_compile_task_llvm;
break;
case BACKEND_TB:
tinybackend_codegen_setup();
for (unsigned i = 0; i < module_count; i++)
{
void *result = tinybackend_gen(modules[i]);
if (result) vec_add(gen_contexts, result);
}
gen_contexts = tilde_gen(modules, module_count);
task = &thread_compile_task_tb;
break;
default:

View File

@@ -317,7 +317,15 @@ struct Type_
CanonicalType *canonical;
const char *name;
Type **type_cache;
void *backend_type;
union
{
void *backend_type;
struct
{
uint16_t tb_set;
uint16_t tb_type;
};
};
void *backend_typeid;
void *backend_debug_type;
union
@@ -624,7 +632,11 @@ typedef struct
{
AstId defer;
bool next_target : 1;
void *break_target;
union
{
void *break_target;
int tb_break_target;
};
void *continue_target;
AstId scope_defer;
AstId parent;
@@ -666,6 +678,7 @@ typedef struct Decl_
void *backend_ref;
int tb_register;
void *backend_value;
void *tb_symbol;
};
AlignSize alignment;
const char *section;
@@ -1193,7 +1206,11 @@ typedef struct
};
struct
{
void *break_block;
union
{
void *break_block;
int tb_break_block;
};
} codegen;
};
} AstIfStmt;
@@ -2050,11 +2067,9 @@ bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type);
CastKind cast_to_bool_kind(Type *type);
const char *llvm_codegen(void *context);
const char *tilde_codegen(void *context);
void **llvm_gen(Module** modules, unsigned module_count);
const char *tinybackend_codegen(void *context);
void *tinybackend_gen(Module *module);
void tinybackend_codegen_setup();
void **tilde_gen(Module** modules, unsigned module_count);
void header_gen(Module *module);
@@ -2264,6 +2279,7 @@ const char *symtab_preset(const char *data, TokenType type);
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
void *llvm_target_machine_create(void);
void codegen_setup_object_names(Module *module, const char **ir_filename, const char **asm_filename, const char **object_filename);
void target_setup(BuildTarget *build_target);
int target_alloca_addr_space();
bool os_is_apple(OsType os_type);

View File

@@ -1112,7 +1112,7 @@ INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMC
void **llvm_gen(Module** modules, unsigned module_count)
{
if (!module_count) return NULL;
GenContext ** gen_contexts = NULL;
GenContext **gen_contexts = NULL;
llvm_codegen_setup();
if (active_target.single_module)
{

View File

@@ -169,7 +169,7 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue
if (rejump_block)
{
llvm_emit_block(c, rejump_block);
LLVMValueRef error = llvm_load_natural_alignment(c, type_anyerr, optional, "reload_err");
LLVMValueRef error = llvm_load_abi_alignment(c, type_anyerr, optional, "reload_err");
llvm_store_to_ptr_raw(c, c->opt_var, error, type_anyerr);
llvm_emit_br(c, c->catch_block);
}
@@ -3374,7 +3374,7 @@ void llvm_emit_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs,
llvm_emit_array_comp(c, result, lhs, rhs, binary_op);
return;
}
TODO
TODO // When updated, also update tilde_codegen_expr
}
static void llvm_emit_else(GenContext *c, BEValue *be_value, Expr *expr)
@@ -3969,11 +3969,6 @@ static inline void llvm_emit_force_unwrap_expr(GenContext *c, BEValue *be_value,
}
static bool expr_is_vector_index(Expr *expr)
{
return expr->expr_kind == EXPR_SUBSCRIPT
&& type_lowering(exprtype(expr->subscript_expr.expr))->type_kind == TYPE_VECTOR;
}
static void llvm_emit_vector_assign_expr(GenContext *c, BEValue *be_value, Expr *expr)
{
@@ -4756,8 +4751,8 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef *args, unsigned *arg_count_
return;
}
}
}
static void llvm_emit_splatted_variadic_arg(GenContext *c, Expr *expr, Type *vararg_type, BEValue *subarray)
{
BEValue value;

View File

@@ -371,7 +371,7 @@ INLINE bool llvm_is_const(LLVMValueRef 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_abi_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);

View File

@@ -40,16 +40,8 @@ void gencontext_begin_module(GenContext *c)
{
assert(!c->module && "Expected no module");
const char *result = module_create_object_file_name(c->code_module);
c->ir_filename = str_printf("%s.ll", result);
if (active_target.llvm_file_dir) c->ir_filename = file_append_path(active_target.llvm_file_dir, c->ir_filename);
c->object_filename = str_printf("%s%s", result, get_object_extension());
if (active_target.emit_asm)
{
c->asm_filename = str_printf("%s.s", result);
if (active_target.asm_file_dir) c->asm_filename = file_append_path(active_target.asm_file_dir, c->asm_filename);
}
if (active_target.object_file_dir) c->object_filename = file_append_path(active_target.object_file_dir, c->object_filename);
codegen_setup_object_names(c->code_module, &c->ir_filename, &c->asm_filename, &c->object_filename);
c->panic_var = global_context.panic_var;
c->module = LLVMModuleCreateWithNameInContext(c->code_module->name->module, c->context);
c->machine = llvm_target_machine_create();

View File

@@ -121,7 +121,7 @@ void llvm_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr)
}
}
void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr, bool bool_cast)
static void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr, bool bool_cast)
{
assert(expr->expr_kind == EXPR_COND);
ByteSize size = vec_size(expr->cond_expr);
@@ -285,7 +285,7 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
* 2. If the "else" branch is empty or missing, replace if with "exit".
* 3. If both "else" and "then" branches are empty, replace it with just the condition and remove the "exit"
*/
void llvm_emit_if(GenContext *c, Ast *ast)
static void llvm_emit_if_stmt(GenContext *c, Ast *ast)
{
// We need at least the exit block and the "then" block.
LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "if.exit");
@@ -1219,7 +1219,7 @@ static inline void llvm_emit_asm_block_stmt(GenContext *c, Ast *ast)
}
void gencontext_emit_expr_stmt(GenContext *c, Ast *ast)
static void llvm_emit_expr_stmt(GenContext *c, Ast *ast)
{
BEValue value;
if (IS_OPTIONAL(ast->expr_stmt))
@@ -1366,7 +1366,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_ASM_STMT:
UNREACHABLE
case AST_EXPR_STMT:
gencontext_emit_expr_stmt(c, ast);
llvm_emit_expr_stmt(c, ast);
break;
case AST_DECLARE_STMT:
{
@@ -1381,7 +1381,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
llvm_emit_continue(c, ast);
break;
case AST_IF_STMT:
llvm_emit_if(c, ast);
llvm_emit_if_stmt(c, ast);
break;
case AST_RETURN_STMT:
llvm_emit_return(c, ast);

View File

@@ -78,7 +78,7 @@ LLVMValueRef llvm_load(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, Al
return value;
}
LLVMValueRef llvm_load_natural_alignment(GenContext *c, Type *type, LLVMValueRef pointer, const char *name)
LLVMValueRef llvm_load_abi_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);
}

View File

@@ -144,7 +144,7 @@ void llvm_value_fold_optional(GenContext *c, BEValue *value)
{
if (value->kind == BE_ADDRESS_OPTIONAL)
{
llvm_emit_jump_to_optional_exit(c, llvm_load_natural_alignment(c, type_anyerr, value->optional, "optval"));
llvm_emit_jump_to_optional_exit(c, llvm_load_abi_alignment(c, type_anyerr, value->optional, "optval"));
value->kind = BE_ADDRESS;
}
}

View File

@@ -1,949 +0,0 @@
#include "tilde_internal.h"
#if TB_BACKEND
static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr);
static inline void tilde_emit_block(TbContext *c, TB_Label label);
static inline void tinybackend_emit_exprid(TbContext *c, TBEValue *value, ExprId id)
{
tinybackend_emit_expr(c, value, exprptr(id));
}
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(TBEValue *value)
{ return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_OPTIONAL; }
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;
if (type->type_kind == TYPE_VECTOR)
{
elements = (uint8_t)type->array.len;
switch (type->array.base->type_kind)
{
case TYPE_U8:
case TYPE_I8:
return tb_vector_type(TB_I8, elements);
case TYPE_U16:
case TYPE_I16:
return tb_vector_type(TB_I16, elements);
case TYPE_U32:
case TYPE_I32:
return tb_vector_type(TB_I32, elements);
case TYPE_U64:
case TYPE_I64:
return tb_vector_type(TB_I64, elements);
case TYPE_I128:
case TYPE_U128:
FATAL_ERROR("Unsupported int128");
case TYPE_F32:
return tb_vector_type(TB_F32, elements);
case TYPE_F64:
return tb_vector_type(TB_F64, elements);
case TYPE_F16:
FATAL_ERROR("Unsupported f16");
case TYPE_F128:
FATAL_ERROR("Unsupported f128");
default:
UNREACHABLE
}
}
switch (type->type_kind)
{
case TYPE_U8:
case TYPE_I8:
return TB_TYPE_I8;
case TYPE_U16:
case TYPE_I16:
return TB_TYPE_I16;
case TYPE_U32:
case TYPE_I32:
return TB_TYPE_I32;
case TYPE_U64:
case TYPE_I64:
return TB_TYPE_I64;
case TYPE_POINTER:
return TB_TYPE_PTR;
case TYPE_I128:
case TYPE_U128:
FATAL_ERROR("Unsupported int128");
case TYPE_BOOL:
return TB_TYPE_BOOL;
case TYPE_F64:
return TB_TYPE_F64;
case TYPE_F32:
return TB_TYPE_F32;
case TYPE_VOID:
return TB_TYPE_VOID;
case TYPE_F16:
FATAL_ERROR("Unsupported f16");
case TYPE_F128:
FATAL_ERROR("Unsupported f128");
case TYPE_VECTOR:
UNREACHABLE
default:
// Structs? Unions?
TODO
}
}
static TB_DataType tilde_get_abi_type(AbiType type)
{
if (abi_type_is_type(type)) return tbtype(type.type);
TODO
}
TB_DataType tilde_get_int_type_of_bytesize(int byte_size)
{
switch (byte_size)
{
case 1:
return TB_TYPE_I8;
case 2:
return TB_TYPE_I16;
case 4:
return TB_TYPE_I32;
case 8:
return TB_TYPE_I64;
default:
FATAL_ERROR("Unsupported size");
}
}
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_FAULTTYPE:
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_SPLIT_STRUCT:
{
// Normal direct.
TB_DataType coerce_type = tbtype(arg_info->direct_struct_expand.type);
for (unsigned idx = 0; idx < arg_info->direct_struct_expand.elements; idx++)
{
vec_add(*params, coerce_type);
}
break;
}
case ABI_ARG_DIRECT_COERCE_INT:
vec_add(*params, tilde_get_int_type_of_bytesize(type_size(param_type)));
break;
case ABI_ARG_DIRECT_COERCE:
{
// Normal direct.
TB_DataType coerce_type = tbtype(arg_info->direct_coerce_type);
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_SPLIT_STRUCT:
UNREACHABLE
case ABI_ARG_DIRECT_COERCE_INT:
return_type = tilde_get_int_type_of_bytesize(type_size(call_return_type));
break;
case ABI_ARG_DIRECT_COERCE:
return_type = tbtype(ret_arg_info->direct_coerce_type);
break;
}
// If it's optional 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,
&params);
}
// Add in all of the required arguments.
VECEACH(prototype->params, i)
{
add_func_type_param(prototype->params[i], prototype->abi_args[i], &params);
}
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 decl->tb_register;
}
TB_Register tilde_load_aligned(TbContext *c, TB_DataType dt, TB_Register pointer, AlignSize alignment)
{
TB_Register value = tb_inst_load(c->f, dt, pointer, alignment);
return value;
}
void tilde_store_self_aligned(TbContext *c, TB_Register pointer, TB_Register value, Type *type)
{
tb_inst_store(c->f, tbtype(type), pointer, value, type_abi_alignment(type));
}
void tinybackend_store_value(TbContext *c, TBEValue *destination, TBEValue *value)
{
assert(tinybackend_value_is_addr(destination));
TB_DataType type = tbtype(destination->type);
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
{
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 TBE_VALUE:
tb_inst_store(c->f, type, destination->reg, value->reg, alignment);
return;
case TBE_ADDRESS:
{
// Here we do an optimized(?) memcopy.
ByteSize size = type_size(value->type);
TB_Register copy_size = tb_inst_uint(c->f,
size <= UINT32_MAX ? TB_TYPE_I32 : TB_TYPE_I64,
size);
alignment = type_min_alignment(destination->alignment, value->alignment);
tb_inst_memcpy(c->f, destination->reg, value->reg, copy_size, alignment);
return;
}
default:
UNREACHABLE
}
}
void TBE_VALUE_set(TBEValue *value, TB_Register reg, Type *type)
{
type = type_lowering(type);
assert(reg || type == type_void);
value->reg = reg;
value->alignment = type_abi_alignment(type);
value->kind = TBE_VALUE;
value->type = type;
}
static void tinybackend_emit_const_expr(TbContext *c, TBEValue *value, Expr *expr)
{
Type *type = type_flatten(expr->type);
switch (expr->const_expr.const_kind)
{
case CONST_FLOAT:
TBE_VALUE_set(value, tb_inst_float(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 = tbtype(type);
printf("Const int type: %d\n", dt.type);
switch (expr->const_expr.ixx.type)
{
case TYPE_I128:
case TYPE_U128:
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 = type_kind_is_signed(expr->const_expr.ixx.type) ? tb_inst_sint(c->f, dt, i.low) : tb_inst_uint(c->f, dt, i.low);
break;
}
TBE_VALUE_set(value, reg, type);
return;
}
default:
TODO
}
}
static int find_member_index(Decl *parent, Decl *member)
{
VECEACH(parent->strukt.members, i)
{
Decl *maybe_member = parent->strukt.members[i];
if (member == maybe_member)
{
return (int)i;
}
if (!maybe_member->name)
{
if (find_member_index(maybe_member, member) != -1) return (int)i;
}
}
return -1;
}
static void tinybackend_emit_member_addr(TbContext *c, TBEValue *value, Decl *parent, Decl *member)
{
assert(member->resolve_status == RESOLVE_DONE);
Decl *found = NULL;
do
{
int index = find_member_index(parent, member);
assert(index > -1);
found = parent->strukt.members[index];
// Unlike LLVM, TinyBackend doesn't handle aggregates for you
// so we need offsetof to perform member accesses
switch (parent->type->canonical->type_kind)
{
case TYPE_UNION:
{
TODO
}
break;
case TYPE_STRUCT:
{
TB_Register ref = tb_inst_member_access(c->f, value->reg, (int32_t)found->offset);
value_set_address_abi_aligned(value, ref, member->type);
value->alignment = found->alignment;
}
break;
default:
UNREACHABLE
}
parent = found;
} while (found != member);
}
void tinybackend_emit_access_addr(TbContext *c, TBEValue *TBE_VALUE, Expr *expr)
{
Expr *parent = expr->access_expr.parent;
tinybackend_emit_expr(c, TBE_VALUE, parent);
Decl *member = expr->access_expr.ref;
tinybackend_emit_member_addr(c, TBE_VALUE, type_lowering(parent->type)->decl, member);
}
static inline TB_ArithmaticBehavior tinybackend_get_arith_behavior(Type *type)
{
if (active_target.feature.trap_on_wrap)
{
return type_is_unsigned(type) ? TB_UNSIGNED_TRAP_ON_WRAP : TB_SIGNED_TRAP_ON_WRAP;
}
return TB_CAN_WRAP;
}
static inline void tilde_emit_block(TbContext *c, TB_Label label)
{
tb_inst_label(c->f, label);
}
TB_Register tilde_value_rvalue_get(TbContext *c, TBEValue *value)
{
if (value->kind == TBE_VALUE) return value->reg;
//llvm_value_fold_optional(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)
{
//TbContext_emit_logical_and_or(c, TBE_VALUE, expr, binary_op);
//return;
TODO
}
TBEValue lhs;
if (lhs_loaded)
{
lhs = *lhs_loaded;
}
else
{
tinybackend_emit_exprid(c, &lhs, expr->binary_expr.left);
}
value_rvalue(c, &lhs);
TBEValue rhs;
tinybackend_emit_exprid(c, &rhs, expr->binary_expr.right);
value_rvalue(c, &rhs);
/*EMIT_LOC(c, expr);
if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
{
llvm_emit_comparison(c, TBE_VALUE, &lhs, &rhs, binary_op);
return;
}*/
Type *lhs_type = lhs.type;
Type *rhs_type = rhs.type;
Type *vector_type = lhs_type->type_kind == TYPE_VECTOR ? lhs_type->array.base : NULL;
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.reg;
TB_Register rhs_value = rhs.reg;
TB_DataType dt = tbtype(lhs_type);
switch (binary_op)
{
case BINARYOP_ERROR:
UNREACHABLE
case BINARYOP_MULT:
if (is_float)
{
val = tb_inst_fmul(c->f, lhs_value, rhs_value);
break;
}
// TODO(NeGate): review this later, maybe it shouldn't be NO_WRAP
val = tb_inst_mul(c->f, lhs_value, rhs_value, TB_ASSUME_NUW);
break;
case BINARYOP_SUB:
if (lhs_type->type_kind == TYPE_POINTER)
{
/*bool is_constant = LLVMIsConstant(lhs_value) && LLVMIsConstant(rhs_value);
if (lhs_type == rhs_type)
{
LLVMTypeRef int_type = llvm_get_type(c, type_isz);
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_type, ""),
LLVMBuildPtrToInt(c->builder, rhs_value, int_type, ""), "");
val = LLVMBuildExactSDiv(c->builder, val, llvm_const_int(c, type_isz, type_abi_alignment(lhs_type->pointer)), "");
break;
}
rhs_value = LLVMConstNeg(rhs_value);
val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value);*/
TODO
}
if (is_float)
{
val = tb_inst_fsub(c->f, lhs_value, rhs_value);
break;
}
val = tb_inst_mul(c->f, lhs_value, rhs_value, tinybackend_get_arith_behavior(lhs_type));
break;
case BINARYOP_ADD:
if (lhs_type->type_kind == TYPE_POINTER)
{
TODO
}
if (is_float)
{
val = tb_inst_fadd(c->f, lhs_value, rhs_value);
break;
}
val = tb_inst_add(c->f, 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->f, lhs_value, rhs_value);
break;
}
val = tb_inst_div(c->f, lhs_value, rhs_value, !type_is_unsigned(lhs_type));
break;
case BINARYOP_MOD:
{
TODO
}
case BINARYOP_SHR:
if (type_is_unsigned(lhs_type))
{
val = tb_inst_shr(c->f, lhs_value, rhs_value);
return;
}
val = tb_inst_sar(c->f, lhs_value, rhs_value);
break;
case BINARYOP_SHL:
val = tb_inst_shl(c->f, lhs_value, rhs_value, TB_ASSUME_NUW);
break;
case BINARYOP_BIT_AND:
val = tb_inst_and(c->f, lhs_value, rhs_value);
break;
case BINARYOP_BIT_OR:
val = tb_inst_or(c->f, lhs_value, rhs_value);
break;
case BINARYOP_BIT_XOR:
TODO
break;
case BINARYOP_EQ:
case BINARYOP_NE:
case BINARYOP_GE:
case BINARYOP_GT:
case BINARYOP_LE:
case BINARYOP_LT:
UNREACHABLE
case BINARYOP_AND:
case BINARYOP_OR:
case BINARYOP_ASSIGN:
case BINARYOP_MULT_ASSIGN:
case BINARYOP_ADD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
case BINARYOP_DIV_ASSIGN:
case BINARYOP_MOD_ASSIGN:
case BINARYOP_BIT_AND_ASSIGN:
case BINARYOP_BIT_OR_ASSIGN:
case BINARYOP_BIT_XOR_ASSIGN:
case BINARYOP_SHR_ASSIGN:
case BINARYOP_SHL_ASSIGN:
UNREACHABLE
case BINARYOP_OR_ERR:
TODO
break;
}
assert(val);
TBE_VALUE_set(TBE_VALUE, val, expr->type);
}
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, TBE_VALUE, expr);
return;
}*/
printf("A\n");
if (binary_op > BINARYOP_ASSIGN)
{
BinaryOp base_op = binaryop_assign_base_op(binary_op);
assert(base_op != BINARYOP_ERROR);
TBEValue addr;
tinybackend_emit_exprid(c, &addr, expr->binary_expr.left);
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)
{
tinybackend_emit_exprid(c, TBE_VALUE, expr->binary_expr.left);
assert(tinybackend_value_is_addr(TBE_VALUE));
*TBE_VALUE = tilde_emit_assign_expr(c, TBE_VALUE, exprptr(expr->binary_expr.right), TB_NULL_REG /* optional_ref */);
return;
}
tinybackend_emit_binary(c, TBE_VALUE, expr, NULL, binary_op);
}
static void tinybackend_emit_expr(TbContext *c, TBEValue *value, Expr *expr)
{
printf("expr->expr_kind = %d\n", expr->expr_kind);
switch (expr->expr_kind)
{
case EXPR_CONST:
tinybackend_emit_const_expr(c, value, expr);
return;
case EXPR_ACCESS:
tinybackend_emit_access_addr(c, value, expr);
return;
case EXPR_BINARY:
tinybackend_emit_binary_expr(c, value, expr);
return;
case EXPR_IDENTIFIER:
value_set_decl(value, expr->identifier_expr.decl);
return;
default:
TODO
}
}
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)
{
if (IS_FAILABLE(decl))
{
scratch_buffer_clear();
scratch_buffer_append(decl_get_extname(decl));
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(), TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
tb_global_set_initializer(c->module, decl->var.tb_failable_reg, 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", TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
tb_global_set_initializer(c->module, decl->tb_register, 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 an optional.
}
TBEValue value;
value_set_decl(&value, decl);
if (init)
{
tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg);
}
else if (!decl->var.no_init)
{
if (decl->var.tb_failable_reg)
{
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 print_callback(void *ptr, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static void tilde_emit_function_body(TbContext *c, Decl *decl)
{
printf("Function: %s\n", decl_get_extname(decl));
c->current_func_decl = decl;
TB_FunctionPrototype *prototype = tilde_get_function_type(c->module, decl->type->func.prototype);
TB_Function *function = tb_prototype_build(c->module, prototype, decl_get_extname(decl), TB_LINKAGE_PUBLIC);
c->f = function;
AstId current = astptr(decl->func_decl.body)->compound_stmt.first_stmt;
while (current)
{
tilde_emit_stmt(c, ast_next(&current));
}
// 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, &print_callback, NULL);
}
static TB_Arch tilde_get_arch(void)
{
switch (platform_target.arch)
{
case ARCH_TYPE_AARCH64:
return TB_ARCH_AARCH64;
case ARCH_TYPE_X86_64:
return TB_ARCH_X86_64;
default:
FATAL_ERROR("Unsupported architecture for TB backend.");
}
}
static TB_System tilde_get_system(void)
{
switch (platform_target.os)
{
case OS_TYPE_LINUX:
return TB_SYSTEM_LINUX;
case OS_TYPE_WIN32:
return TB_SYSTEM_WINDOWS;
case OS_TYPE_MACOSX:
// TODO add when support exists.
// return TB_SYSTEM_MACOS;
return TB_SYSTEM_LINUX;
case OS_TYPE_UNKNOWN:
return TB_SYSTEM_LINUX;
default:
FATAL_ERROR("Unsupported OS");
}
}
// Generate context per module (single threaded)
void *tinybackend_gen(Module *module)
{
if (!vec_size(module->units)) return NULL;
TbContext *c = ccalloc(sizeof(TbContext), 1);
c->code_module = module;
c->module = tb_module_create(tilde_get_arch(), tilde_get_system(), TB_DEBUGFMT_NONE, &c->features);
const char *result = module_create_object_file_name(module);
scratch_buffer_clear();
scratch_buffer_printf("%s%s", result, get_object_extension());
c->object_filename = scratch_buffer_copy();
printf("Module: %.*s\n", module->name->len, module->name->module);
// Forward decls
VECEACH(module->units, j)
{
CompilationUnit *unit = module->units[j];
printf(" Functions: %d\n", vec_size(unit->functions));
}
VECEACH(module->units, j)
{
CompilationUnit *unit = module->units[j];
VECEACH(unit->functions, i)
{
Decl *decl = unit->functions[i];
if (decl->func_decl.body) tilde_emit_function_body(c, decl);
}
}
// tb_module_compile(c->module);
TODO
return c;
}
// Compile module (multi threaded)
const char *tinybackend_codegen(void *context)
{
TbContext *c = (TbContext *)context;
// Write out the object file
tb_module_export(c->module, c->object_filename);
return c->object_filename;
}
#else
const char *tinybackend_codegen(void *context)
{
FATAL_ERROR("Not enabled");
}
void *tinybackend_gen(Module *module)
{
FATAL_ERROR("Not enabled");
}
void tinybackend_codegen_setup()
{
FATAL_ERROR("TB not enabled");
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
// Copyright (c) 2023 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"

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,7 @@
#include "tilde_internal.h"
#if TB_BACKEND
void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align)
void tilde_emit_memclear_size_align(TildeContext *c, TB_Register ref, uint64_t size, AlignSize align)
{
ByteSize min = type_min_alignment(align, size);
TB_Register zero = tb_inst_uint(c->f, TB_TYPE_I8, 0);
@@ -10,34 +9,38 @@ void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t 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)
void tilde_emit_cond_br(TildeContext *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)
TB_Reg tilde_emit_shl_fixed(TildeContext *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, reg, tb_inst_uint(c->f, int_type, (unsigned)shift), type_is_signed(type) ? TB_ASSUME_NSW : TB_ASSUME_NUW);
TB_DataType int_type = tildetype(type);
return tb_inst_shl(c->f, reg, tb_inst_uint(c->f, int_type, (unsigned)shift), type_is_signed(type) ? TB_ARITHMATIC_NSW : TB_ARITHMATIC_NUW);
}
TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift)
TB_Reg tilde_emit_lshr_fixed(TildeContext *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);
TB_DataType int_type = tildetype(type);
return tb_inst_shr(c->f, reg, tb_inst_uint(c->f, int_type, (unsigned)shift));
}
TB_Reg tilde_emit_alloca(TbContext *c, Type *type)
TB_Reg tilde_emit_alloca(TildeContext *c, Type *type, AlignSize alignment)
{
return tb_inst_local(c->f, type_size(type), type_alloca_alignment(type));
return tb_inst_local(c->f, type_size(type), alignment ? alignment : type_alloca_alignment(type));
}
TB_Reg tilde_emit_is_no_opt(TildeContext *c, TB_Reg error_value)
{
return tb_inst_cmp_eq(c->f, error_value, tilde_get_zero(c, type_anyerr));
}
#endif

View File

@@ -4,97 +4,520 @@
#include "tilde_internal.h"
#if TB_BACKEND
void tilde_emit_compound_stmt(TbContext *context, Ast *ast)
bool tilde_emit_goto_if_needed(TildeContext *c, TB_Label jump)
{
if (tb_basic_block_is_complete(c->f, tb_inst_get_label(c->f))) return false;
tb_inst_goto(c->f, jump);
return true;
}
INLINE void tilde_emit_statement_chain(TildeContext *c, AstId current)
{
assert(ast->ast_kind == AST_COMPOUND_STMT);
AstId current = ast->compound_stmt.first_stmt;
while (current)
{
tilde_emit_stmt(context, ast_next(&current));
tilde_emit_stmt(c, ast_next(&current));
}
}
static void llvm_emit_ct_compound_stmt(TbContext *context, Ast *ast)
INLINE void tilde_emit_compound_stmt(TildeContext *c, Ast *ast)
{
assert(ast->ast_kind == AST_COMPOUND_STMT);
AstId current = ast->compound_stmt.first_stmt;
while (current)
// Push debug scope
tilde_emit_statement_chain(c, ast->compound_stmt.first_stmt);
// Pop debug scope
}
static void tilde_emit_return_abi(TildeContext *c, TBEValue *return_value, TBEValue *optional)
{
FunctionPrototype *prototype = c->cur_func.prototype;
// If there is no prototype, this is a static initializer, so bail.
if (!prototype)
{
tilde_emit_stmt(context, ast_next(&current));
tb_inst_ret(c->f, TB_NULL_REG);
return;
}
ABIArgInfo *info = prototype->ret_abi_info;
// If we have an optional it's always the return argument, so we need to copy
// the return value into the return value holder.
TB_Reg return_out = c->return_out;
Type *call_return_type = prototype->abi_ret_type;
TBEValue no_fail;
// In this case we use the optional as the actual return.
if (prototype->is_optional)
{
if (return_value && return_value->type != type_void)
{
assert(return_value->type);
tilde_store_to_ptr_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type));
}
return_out = c->optional_out;
if (!optional)
{
value_set(&no_fail, tilde_get_zero(c, type_anyerr), type_anyerr);
optional = &no_fail;
}
return_value = optional;
}
assert(return_value || info->kind == ABI_ARG_IGNORE);
switch (info->kind)
{
case ABI_ARG_INDIRECT:
assert(return_value);
tilde_store_to_ptr_aligned(c, return_out, return_value, info->indirect.alignment);
tb_inst_ret(c->f, TB_NULL_REG);
return;
case ABI_ARG_IGNORE:
tb_inst_ret(c->f, TB_NULL_REG);
return;
case ABI_ARG_DIRECT_SPLIT_STRUCT:
case ABI_ARG_EXPAND:
// Expands to multiple slots -
// Not applicable to return values.
UNREACHABLE
case ABI_ARG_EXPAND_COERCE:
{
TODO
/*
// Pick the return as an address.
value_addr(c, return_value);
// Get the coerce type.
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
// Create the new pointer
assert(return_value);
LLVMValueRef coerce = LLVMBuildBitCast(c->builder, return_value->value, coerce_type, "");
// We might have only one value, in that case, build a GEP to that one.
LLVMValueRef lo_val;
AlignSize alignment;
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_load(c, lo_type, lo, alignment, "");
// We're done if there's a single field.
if (!abi_type_is_valid(info->coerce_expand.hi))
{
llvm_emit_return_value(c, lo_val);
return;
}
// Let's make a first class aggregate
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_load(c, hi_type, hi, alignment, "");
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
LLVMValueRef composite = llvm_get_undef_raw(unpadded_type);
composite = llvm_emit_insert_value(c, composite, lo_val, 0);
composite = llvm_emit_insert_value(c, composite, hi_val, 1);
// And return that unpadded result
llvm_emit_return_value(c, composite);
break;*/
}
case ABI_ARG_DIRECT:
DIRECT_RETURN:
// The normal return
tb_inst_ret(c->f, tilde_load_value_store(c, return_value));
return;
case ABI_ARG_DIRECT_PAIR:
{
TODO
goto DIRECT_RETURN;
/*
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));
return;*/
}
case ABI_ARG_DIRECT_COERCE_INT:
{
TODO
/*
LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(call_return_type) * 8);
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
return;
}
case ABI_ARG_DIRECT_COERCE:
{
TODO
/*
LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type);
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
return;
}
}
}
TB_Reg tilde_emit_local_decl(TbContext *c, Decl *decl)
static void tilde_emit_return_implicit(TildeContext *c)
{
// 1. Get the declaration and the TB type.
Type *var_type = type_lowering(type_no_fail(decl->type));
Type *rtype_real = c->cur_func.prototype ? c->cur_func.prototype->rtype : type_void;
if (type_lowering(type_no_optional(rtype_real)) != type_void)
{
tb_inst_unreachable(c->f);
return;
}
if (type_is_optional(rtype_real))
{
tilde_emit_return_abi(c, NULL, NULL);
return;
}
TBEValue value;
value_set(&value, tb_inst_ptr(c->f, 0), type_anyerr);
tilde_emit_return_abi(c, NULL, &value);
}
static void tilde_emit_decl_expr_list(TildeContext *c, TBEValue *be_value, Expr *expr, bool bool_cast)
{
assert(expr->expr_kind == EXPR_COND);
ByteSize size = vec_size(expr->cond_expr);
ByteSize last_index = size - 1;
for (ByteSize i = 0; i < last_index; i++)
{
TBEValue value;
tilde_emit_expr(c, &value, expr->cond_expr[i]);
}
Expr *last = expr->cond_expr[last_index];
Type *type = last->type;
tilde_emit_expr(c, be_value, last);
if (last->expr_kind == EXPR_DECL)
{
type = last->decl_expr->var.type_info->type;
TB_Reg decl_value = tilde_get_ref(c, last->decl_expr);
if (bool_cast && last->decl_expr->var.unwrap)
{
value_set(be_value, tb_inst_bool(c->f, true), type_bool);
return;
}
value_set_address_abi_aligned(be_value, decl_value, type);
}
if (bool_cast)
{
type = type_lowering(type);
if (type->type_kind != TYPE_BOOL)
{
CastKind cast = cast_to_bool_kind(type);
tilde_emit_cast(c, cast, last, be_value, type, type_bool);
}
}
}
INLINE void tilde_emit_return_stmt(TildeContext *c, Ast *ast)
{
PUSH_OPT();
Expr *expr = ast->return_stmt.expr;
if (expr && expr->expr_kind == EXPR_OPTIONAL)
{
TBEValue be_value;
tilde_emit_expr(c, &be_value, expr->inner_expr);
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
tilde_emit_return_abi(c, NULL, &be_value);
return;
}
TB_Label error_return_block = 0;
TB_Reg error_out = TB_NULL_REG;
if (c->cur_func.prototype && type_is_optional(c->cur_func.prototype->rtype))
{
error_return_block = tb_basic_block_create(c->f);
error_out = tilde_emit_alloca(c, type_anyerr, 0);
c->opt_var = error_out;
c->catch_block = error_return_block;
}
bool has_return_value = ast->return_stmt.expr != NULL;
TBEValue return_value = { 0 };
if (has_return_value)
{
tilde_emit_expr(c, &return_value, ast->return_stmt.expr);
value_fold_optional(c, &return_value);
c->retval = return_value;
}
POP_OPT();
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
// Are we in an expression block?
if (!has_return_value)
{
tilde_emit_return_implicit(c);
}
else
{
tilde_emit_return_abi(c, &return_value, NULL);
}
if (error_return_block)
{
tilde_emit_block(c, error_return_block);
TBEValue value;
value_set_address_abi_aligned(&value, error_out, type_anyerr);
tilde_emit_return_abi(c, NULL, &value);
}
}
void tilde_emit_local_decl(TildeContext *c, Decl *decl, TBEValue *value)
{
// 1. Get the declaration and the LLVM type.
Type *var_type = type_lowering(type_no_optional(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))
TODO
/*
// In defers we might already have generated this variable.
if (decl->backend_ref)
{
llvm_value_set_decl(c, value, decl);
return;
}
void *builder = c->builder;
c->builder = c->global_builder;
decl->backend_ref = llvm_add_global(c, "tempglobal", var_type, decl->alignment);
if (IS_OPTIONAL(decl))
{
scratch_buffer_clear();
scratch_buffer_append(decl_get_extname(decl));
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(), TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
tb_global_set_initializer(c->module, decl->var.tb_failable_reg, initializer);
scratch_buffer_append(decl->extname);
scratch_buffer_append("$f");
decl->var.optional_ref = llvm_add_global(c, scratch_buffer_to_string(), type_anyerr, 0);
}
decl->tb_register = tb_global_create(c->module, decl_get_extname(decl), TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1);
tb_global_set_initializer(c->module, decl->tb_register, static_initializer);
tilde_emit_global_initializer(c, decl);
return decl->tb_register;
llvm_emit_global_variable_init(c, decl);
c->builder = builder;
llvm_value_set_decl(c, value, decl);
return;*/
}
tilde_emit_local_var_alloca(c, decl);
TB_Register reg = decl->tb_register;
assert(!decl->backend_ref);
decl->tb_register = tb_inst_local(c->f, type_size(var_type), type_alloca_alignment(var_type));
Expr *init = decl->var.init_expr;
if (IS_FAILABLE(decl))
bool is_optional = IS_OPTIONAL(decl);
if (is_optional)
{
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.
decl->var.tb_optional_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 an optional.
}
TBEValue value;
value_set_decl(&value, decl);
if (init)
{
tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg);
value_set_decl_address(c, value, decl);
value->kind = TBE_ADDRESS;
TBEValue val = tilde_emit_assign_expr(c, value, decl->var.init_expr, decl->var.tb_optional_reg);
if (!is_optional) *value = val;
}
else if (!decl->var.no_init)
else if (decl->var.no_init)
{
if (decl->var.tb_failable_reg)
value_set(value, tb_inst_poison(c->f), decl->type);
if (decl->var.tb_optional_reg)
{
tilde_store_zero(c, type_anyerr, decl->var.tb_failable_reg, type_abi_alignment(type_anyerr));
tilde_store_to_ptr_raw(c, decl->var.tb_optional_reg, tb_inst_poison(c->f), type_anyerr);
}
}
else
{
if (decl->var.tb_optional_reg)
{
tilde_store_zero(c, type_anyerr, decl->var.tb_optional_reg, 0);
}
Type *type = type_lowering(decl->type);
// Normal case, zero init.
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
{
tilde_store_zero(c, type_anyerr, decl->tb_register, decl->alignment);
}
else
{
tb_inst_memclr(c->f, decl->tb_register, type_bit_size(type), decl->alignment);
}
tilde_store_value_zero(c, &value);
}
return reg;
}
static void tilde_emit_expr_stmt(TildeContext *c, Ast *ast)
{
TBEValue value;
if (IS_OPTIONAL(ast->expr_stmt))
{
PUSH_OPT();
TB_Label discard_fail = tb_basic_block_create(c->f);
c->catch_block = discard_fail;
c->opt_var = TB_NULL_REG;
tilde_emit_expr(c, &value, ast->expr_stmt);
value_fold_optional(c, &value);
EMIT_LOC(c, ast);
tilde_emit_goto_if_needed(c, discard_fail);
tilde_emit_block(c, discard_fail);
POP_OPT();
return;
}
void tilde_emit_stmt(TbContext *c, Ast *ast)
tilde_emit_expr(c, &value, ast->expr_stmt);
}
// See llvm_emit_if_stmt
static void tilde_emit_if_stmt(TildeContext *c, Ast *ast)
{
// We need at least the exit block and the "then" block.
TB_Label exit_block = tb_basic_block_create(c->f);
TB_Label then_block = exit_block;
TB_Label else_block = exit_block;
Ast *then_body = astptr(ast->if_stmt.then_body);
// Only generate a target if
if (ast_is_not_empty(then_body))
{
then_block = tb_basic_block_create(c->f);
}
// We have an optional else block.
AstId else_id = ast->if_stmt.else_body;
Ast *else_body = else_id ? astptr(else_id) : NULL;
if (ast_is_not_empty(else_body))
{
else_block = tb_basic_block_create(c->f);
}
Expr *cond = exprptr(ast->if_stmt.cond);
ast->if_stmt.codegen.tb_break_block = exit_block;
// Output boolean value and switch.
Decl *label = ast->if_stmt.flow.label;
if (label)
{
label->label.tb_break_target = exit_block;
}
TBEValue be_value = { 0 };
bool exit_in_use = true;
if (then_body->ast_kind == AST_IF_CATCH_SWITCH_STMT)
{
TODO
/*
tilde_emit_decl_expr_list(c, &be_value, cond, false);
value_rvalue(c, &be_value);
TBEValue comp;
tilde_emit_int_comp_zero(c, &comp, &be_value, BINARYOP_NE);
tb_inst_if(c->f, comp.reg, then_block, else_block);
tb_inst_set_label(c->f, then_block);
tilde_emit_switch_body(c, &be_value, then_body);
tb_inst_goto(c->f, exit_block);
goto EMIT_ELSE;*/
}
tilde_emit_decl_expr_list(c, &be_value, cond, true);
value_rvalue(c, &be_value);
if (then_block != else_block)
{
tb_inst_if(c->f, be_value.reg, then_block, else_block);
}
// Emit the 'then' code.
if (then_block != exit_block)
{
tilde_emit_block(c, then_block);
tilde_emit_stmt(c, then_body);
// Jump to exit.
tilde_emit_goto_if_needed(c, exit_block);
}
// Emit the 'else' branch if present.
if (else_block != exit_block)
{
tilde_emit_block(c, else_block);
tilde_emit_stmt(c, else_body);
tilde_emit_goto_if_needed(c, exit_block);
}
tilde_emit_block(c, exit_block);
}
void tilde_emit_stmt(TildeContext *c, Ast *ast)
{
switch (ast->ast_kind)
{
case AST_DECLARE_STMT:
tilde_emit_local_decl(c, ast->declare_stmt);
break;
case AST_COMPOUND_STMT:
tilde_emit_compound_stmt(c, ast);
return;
case AST_RETURN_STMT:
//TbContext_emit_return(c, ast);
tb_inst_ret(c->f, TB_NULL_REG);
break;
tilde_emit_return_stmt(c, ast);
return;
case AST_IF_STMT:
tilde_emit_if_stmt(c, ast);
return;
case AST_DECLARE_STMT:
{
TBEValue value;
tilde_emit_local_decl(c, ast->declare_stmt, &value);
return;
}
case AST_EXPR_STMT:
tilde_emit_expr_stmt(c, ast);
return;
default:
break;
TODO
}
}
#endif
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value)
{
assert(c->catch_block && "unexpected emit");
bool is_constant_opt_zero = tb_node_is_constant_zero(c->f, opt_value);
// Maybe we don't need to emit anything?
if (is_constant_opt_zero) return;
bool is_constant_opt = false;
TB_Label after_block = tb_basic_block_create(c->f);
// No error variable
if (!c->opt_var)
{
// No error var and a constant error means jumping to the "catch" block
if (is_constant_opt)
{
tb_inst_goto(c->f, c->catch_block);
}
else
{
tb_inst_if(c->f, tilde_emit_is_no_opt(c, opt_value), after_block, c->catch_block);
}
tilde_emit_block(c, after_block);
return;
}
// If it's not a constant, then jump conditionally
if (!is_constant_opt)
{
TB_Reg was_ok = tilde_emit_is_no_opt(c, opt_value);
TB_Label error_block = tb_basic_block_create(c->f);
tb_inst_if(c->f, was_ok, after_block, error_block);
tilde_emit_block(c, error_block);
}
tilde_store_to_ptr_raw(c, c->opt_var, opt_value, type_anyerr);
tb_inst_goto(c->f, c->catch_block);
tilde_emit_block(c, after_block);
}

View File

@@ -4,35 +4,82 @@
#include "tilde_internal.h"
#if TB_BACKEND
void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment)
void tilde_store_to_ptr_raw_aligned(TildeContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment)
{
tilde_store(c, tbtype(type), addr, value, alignment);
tilde_store_internal(c, tildetype(type), addr, value, alignment);
}
void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value)
void tilde_store_to_ptr_raw(TildeContext *c, TB_Reg addr, TB_Reg value, Type *type)
{
tilde_store(c, tbtype(type), addr, value, type_abi_alignment(type));
tilde_store_internal(c, tildetype(type), addr, value, type_abi_alignment(type));
}
void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value)
void tilde_store_value_raw(TildeContext *c, TBEValue *destination, TB_Reg value)
{
assert(value_is_addr(destination));
tilde_store(c, tbtype(destination->type), destination->reg, value, destination->alignment);
tilde_store_internal(c, tildetype(destination->type), destination->reg, value, destination->alignment);
}
void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value)
void tilde_store_decl_raw(TildeContext *c, Decl *decl, TB_Reg value)
{
assert(!decl->is_value);
tilde_store(c, tbtype(decl->type), decl->tb_register, value, decl->alignment);
tilde_store_internal(c, tildetype(decl->type), decl->tb_register, value, decl->alignment);
}
void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
TB_Reg tilde_load_value_store(TildeContext *c, TBEValue *value)
{
TB_Reg val = tilde_load_value(c, value);
return val;
/*
if (value->kind == BE_BOOLVECTOR)
{
return LLVMBuildSExt(c->builder, val, llvm_get_type(c, type_get_vector_bool(value->type)), "");
}
if (value->kind != BE_BOOLEAN) return val;
return LLVMBuildZExt(c->builder, val, c->byte_type, "");*/
}
void tilde_store_to_ptr_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
{
// If we have an address but not an aggregate, do a load.
assert(alignment);
value_fold_optional(c, value);
if (value->kind == TBE_ADDRESS && !type_is_abi_aggregate(value->type))
{
value->reg = tilde_load_value_store(c, value);
value->kind = TBE_VALUE;
}
switch (value->kind)
{
/*
case BE_BOOLVECTOR:
value->value = LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, value->type), "");
value->kind = BE_VALUE;
return llvm_store_to_ptr_raw_aligned(c, destination, value->value, alignment);
case BE_BOOLEAN:
value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
value->kind = BE_VALUE;
FALLTHROUGH;*/
case TBE_VALUE:
tilde_store_to_ptr_raw_aligned(c, value->type, destination, value->reg, alignment);
return;
case TBE_ADDRESS_OPTIONAL:
UNREACHABLE
case TBE_ADDRESS:
tb_inst_memcpy(c->f, destination, value->reg, tb_inst_uint(c->f, TB_TYPE_I32, type_size(value->type)), alignment);
return;
}
UNREACHABLE
}
void tilde_store_value_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment)
{
assert(alignment);
value_fold_failable(c, value);
value_fold_optional(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))
{
@@ -43,7 +90,7 @@ void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value
case TBE_VALUE:
tilde_store_value_raw(c, value, value->reg);
return;
case TBE_ADDRESS_FAILABLE:
case TBE_ADDRESS_OPTIONAL:
UNREACHABLE
case TBE_ADDRESS:
{
@@ -55,51 +102,52 @@ void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value
UNREACHABLE
}
void tilde_store_value(TbContext *c, TBEValue *dst, TBEValue *value)
void tilde_store(TildeContext *c, TBEValue *dst, TBEValue *value)
{
assert(dst->kind == TBE_ADDRESS);
tilde_store_value_aligned(c, dst->reg, value, dst->alignment);
if (value->type == type_void) return;
assert(value_is_addr(dst));
tilde_store_to_ptr_aligned(c, dst->reg, value, dst->alignment);
}
TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer)
TB_Reg tilde_load_abi_alignment(TildeContext *c, Type *type, TB_Reg pointer)
{
return tilde_load(c, tbtype(type), pointer, type_abi_alignment(type));
return tilde_load(c, tildetype(type), pointer, type_abi_alignment(type));
}
TB_Reg tilde_load_value(TbContext *c, TBEValue *value)
TB_Reg tilde_load_value(TildeContext *c, TBEValue *value)
{
value_fold_failable(c, value);
value_fold_optional(c, value);
switch (value->kind)
{
case TBE_VALUE:
return value->reg;
case TBE_ADDRESS_FAILABLE:
case TBE_ADDRESS_OPTIONAL:
UNREACHABLE
case TBE_ADDRESS:
return tilde_load(c, tbtype(value->type), value->reg, value->alignment);
return tilde_load(c, tildetype(value->type), value->reg, value->alignment);
}
UNREACHABLE
}
void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment)
void tilde_store_zero(TildeContext *c, Type *type, TB_Reg addr, AlignSize alignment)
{
type = type_lowering(type);
if (alignment == 0) alignment = type_alloca_alignment(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);
tilde_store_to_ptr_raw_aligned(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_uint(c->f, TB_TYPE_I8, 0);
TB_Register elements = tb_inst_uint(c->f, tbtype(type_usize), size);
TB_Register elements = tb_inst_uint(c->f, tildetype(type_usize), size);
tb_inst_memset(c->f, addr, zero, elements, min);
}
void tilde_store_value_zero(TbContext *c, TBEValue *to)
void tilde_store_value_zero(TildeContext *c, TBEValue *to)
{
assert(to->kind == TBE_ADDRESS);
tilde_store_zero(c, to->type, to->reg, to->alignment);
}
#endif

View File

@@ -4,8 +4,102 @@
#include "tilde_internal.h"
#if TB_BACKEND
TB_CallingConv calling_conv_from(CallABI abi)
{
switch (abi)
{
case CALL_X86_STD:
return TB_STDCALL;
case CALL_C:
return TB_CDECL;
case CALL_X86_FAST:
case CALL_X86_THIS:
case CALL_X86_VECTOR:
case CALL_X86_REG:
case CALL_AAPCS:
case CALL_AAPCS_VFP:
REMINDER("Using C decl even though actual calling convention is different.");
return TB_CDECL;
}
}
TB_DebugType *tilde_get_debug_type(Type *type)
{
return NULL;
}
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype)
{
if (prototype->tb_prototype) return prototype->tb_prototype;
TB_FunctionPrototype *proto =
tb_prototype_create(c->module,
calling_conv_from(prototype->call_abi),
tildetype(prototype->abi_ret_type),
tilde_get_debug_type(prototype->rtype),
vec_size(prototype->param_types),
prototype->variadic == VARIADIC_RAW);
FOREACH_BEGIN(AbiType *param, prototype->abi_args)
tb_prototype_add_param_named(proto, tildetype(param->type), "foek", NULL);
FOREACH_END();
prototype->tb_prototype = proto;
return proto;
}
#endif
TB_DataType tildetype(Type *type)
{
type = type_lowering(type);
if (type->tb_set) return (TB_DataType) { .raw = type->tb_type };
TB_DataType tb_type;
switch (type->type_kind)
{
case TYPE_TYPEID:
case TYPE_ANYERR:
UNREACHABLE;
case TYPE_FUNC:
TODO
case TYPE_VECTOR:
tb_type = tildetype(type->array.base);
tb_type.width = next_highest_power_of_2(type->array.len);
break;
case TYPE_F32:
tb_type = TB_TYPE_F32;
break;
case TYPE_F64:
tb_type = TB_TYPE_F64;
break;
case TYPE_VOID:
tb_type = TB_TYPE_VOID;
break;
case TYPE_I8:
case TYPE_U8:
tb_type = TB_TYPE_I8;
break;
case TYPE_I16:
case TYPE_U16:
tb_type = TB_TYPE_I16;
break;
case TYPE_I32:
case TYPE_U32:
tb_type = TB_TYPE_I32;
break;
case TYPE_I64:
case TYPE_U64:
tb_type = TB_TYPE_I64;
break;
case TYPE_I128:
case TYPE_U128:
tb_type = (TB_DataType) { { TB_INT, 0, 128 } };
break;
case TYPE_POINTER:
tb_type = TB_TYPE_PTR;
break;
case TYPE_BOOL:
tb_type = TB_TYPE_BOOL;
break;
case TYPE_ANY:
default:
TODO
}
type->tb_set = 1;
type->tb_type = tb_type.raw;
return tb_type;
}

View File

@@ -1,17 +1,28 @@
#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);
assert(val || type == type_void);
value->reg = val;
value->alignment = type_abi_alignment(type);
value->kind = TBE_VALUE;
value->type = type;
}
void value_set_decl(TildeContext *c, TBEValue *value, Decl *decl)
{
decl = decl_flatten(decl);
if (decl->is_value)
{
value_set(value, decl->tb_register, decl->type);
return;
}
value_set_decl_address(c, value, decl);
}
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment)
{
value->reg = addr;
@@ -25,80 +36,124 @@ 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)
void value_addr(TildeContext *c, TBEValue *value)
{
value_fold_failable(c, value);
value_fold_optional(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_Register val = tb_global_create(c->module, ".taddr", TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
TB_InitializerID initializer_id = tb_initializer_create(c->module, type_size(value->type),
type_alloca_alignment(value->type), 0);
tb_global_set_initializer(c->module, val, initializer_id);
TODO
// 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)
TB_Reg tilde_get_opt_ref(TildeContext *c, Decl *decl)
{
tilde_get_ref(c, decl);
decl = decl_flatten(decl);
if (decl->decl_kind != DECL_VAR) return TB_NULL_REG;
return decl->var.tb_optional_reg;
}
TB_Reg tilde_get_ref(TildeContext *c, Decl *decl)
{
switch (decl->decl_kind)
{
case DECL_VAR:
if (decl_is_local(decl))
{
return decl->tb_register;
}
if (decl->var.kind == VARDECL_UNWRAPPED) return tilde_get_ref(c, decl->var.alias);
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
{
TB_Symbol *symbol = decl->backend_value;
if (symbol->module != c->module || !symbol)
{
TODO
}
return tb_inst_get_symbol_address(c->f, decl->backend_value);
}
case DECL_FUNC:
/*
backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type));
if (decl->unit->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility))
{
llvm_set_internal_linkage(backend_ref);
}
return backend_ref;*/
case DECL_DEFINE:
if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) return tilde_get_ref(c, decl->define_decl.alias);
UNREACHABLE
case DECL_FAULTVALUE:
/*
if (!decl->backend_ref)
{
llvm_get_typeid(c, declptr(decl->enum_constant.parent)->type);
}
assert(decl->backend_ref);
return decl->backend_ref;*/
TODO
case DECL_POISONED:
case DECL_ATTRIBUTE:
case DECL_BITSTRUCT:
case DECL_CT_CASE:
case DECL_CT_ELIF:
case DECL_CT_ELSE:
case DECL_CT_IF:
case DECL_CT_SWITCH:
case DECL_CT_ASSERT:
case DECL_DISTINCT:
case DECL_ENUM:
case DECL_ENUM_CONSTANT:
case DECL_FAULT:
case DECL_GENERIC:
case DECL_IMPORT:
case DECL_LABEL:
case DECL_MACRO:
case DECL_STRUCT:
case DECL_TYPEDEF:
case DECL_UNION:
case DECL_DECLARRAY:
case DECL_INITIALIZE:
case DECL_FINALIZE:
case DECL_BODYPARAM:
case DECL_CT_ECHO:
case DECL_CT_INCLUDE:
UNREACHABLE;
}
UNREACHABLE
}
void value_set_decl_address(TildeContext *c, TBEValue *value, Decl *decl)
{
TB_Reg backend_ref = tilde_get_ref(c, decl);
value_set_address(value, backend_ref, decl->type, decl->alignment);
if ((value->failable = tilde_get_opt_ref(c, decl)))
{
value->kind = TBE_ADDRESS_OPTIONAL;
}
}
void value_fold_optional(TildeContext *c, TBEValue *value)
{
if (value->kind == TBE_ADDRESS_OPTIONAL)
{
TODO
//tilde_emit_jump_to_optional_exit(c, tilde_load_natural_alignment(c, type_anyerr, value->optional, "optval"));
value->kind = TBE_ADDRESS;
}
}
void value_rvalue(TildeContext *c, TBEValue *value)
{
if (value->kind == TBE_VALUE) return;
value_fold_failable(c, value);
value_fold_optional(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

View File

@@ -6,29 +6,15 @@
// 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
#undef TB_Reg
#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,
TBE_ADDRESS_OPTIONAL,
} TBBackendValueKind;
typedef struct
@@ -38,79 +24,137 @@ typedef struct
Type *type; // Should never be a distinct or canonical type.
TB_Register reg;
TB_Register failable; // TODO what even is a failable?
TB_Reg reg;
TB_Reg failable;
} TBEValue;
TB_DataType tbtype(Type *type);
typedef struct
{
Module *code_module;
const char *object_filename;
const char *ir_filename;
const char *asm_filename;
TB_Function **functions;
TB_Module *module;
TB_FeatureSet features;
struct
{
const char *name;
FunctionPrototype *prototype;
Type *rtype;
} cur_func;
Decl *curr_func;
TB_Function *f;
TB_Reg opt_var;
TB_Label catch_block;
TBEValue retval;
TB_Reg return_out;
TB_Reg optional_out;
struct
{
TB_Reg last_ptr;
TB_Reg stack_slot;
} debug;
} TildeContext;
TB_DataType tildetype(Type *type);
#define PUSH_OPT() TB_Label _old_catch = c->catch_block; TB_Reg _old_opt_var = c->opt_var
#define POP_OPT() c->catch_block = _old_catch; c->opt_var = _old_opt_var
#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);
static inline void tilde_store_internal(TildeContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment);
void tilde_store_to_ptr_raw_aligned(TildeContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment);
void tilde_store_to_ptr_raw(TildeContext *c, TB_Reg addr, TB_Reg value, Type *type);
void tilde_store_value_raw(TildeContext *c, TBEValue *destination, TB_Reg value);
void tilde_store_to_ptr_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
void tilde_store_value_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
void tilde_store(TildeContext *c, TBEValue *dst, TBEValue *value);
void tilde_store_decl_raw(TildeContext *c, Decl *decl, TB_Reg value);
TB_Reg tilde_load_natural_alignment(TildeContext *c, Type *type, TB_Reg pointer);
TB_Reg tilde_load_value_store(TildeContext *c, TBEValue *value);
void tilde_store_zero(TildeContext *c, Type *type, TB_Reg addr, AlignSize alignment);
void tilde_store_value_zero(TildeContext *c, TBEValue *to);
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value);
// -- 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);
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment);
TB_Reg tilde_load_abi_alignment(TildeContext *c, Type *type, TB_Reg pointer);
TB_Reg tilde_load_value(TildeContext *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);
void value_set_decl_address(TildeContext *c, TBEValue *value, Decl *decl);
void value_set_decl(TildeContext *c, TBEValue *value, Decl *decl);
void value_addr(TildeContext *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);
static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_OPTIONAL; }
void value_fold_optional(TildeContext *c, TBEValue *value);
void value_rvalue(TildeContext *c, TBEValue *value);
TB_Register tilde_get_zero(TbContext *c, Type *type);
TB_Reg tilde_get_ref(TildeContext *c, Decl *decl);
TB_Reg tilde_get_opt_ref(TildeContext *c, Decl *decl);
TB_Reg tilde_get_const_int(TildeContext *c, Type *type, uint64_t i);
TB_Reg tilde_get_const_float(TildeContext *c, Type *type, double d);
TB_Register tilde_get_zero(TildeContext *c, Type *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);
void tilde_emit_cond_br(TildeContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block);
TB_Reg tilde_emit_lshr_fixed(TildeContext *c, Type *type, TB_Reg reg, int shift);
// -- stmt ---
void tilde_emit_stmt(TbContext *c, Ast *ast);
void tilde_emit_stmt(TildeContext *c, Ast *ast);
// -- 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);
TB_Register tilde_emit_is_no_error(TildeContext *c, TB_Reg reg);
void tilde_emit_global_initializer(TildeContext *c, Decl *decl);
TB_Reg tilde_emit_alloca(TildeContext *c, Type *type, AlignSize alignment);
void tilde_emit_local_var_alloca(TildeContext *c, Decl *decl);
void tilde_emit_and_set_decl_alloca(TildeContext *c, Decl *decl);
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl);
void tilde_emit_parameter(TildeContext *c, TB_Reg *args, unsigned *arg_count_ref, ABIArgInfo *info, TBEValue *be_value, Type *type);
// -- 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_expr(TildeContext *c, TBEValue *result, Expr *expr);
TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_Reg optional);
void tilde_emit_cast(TildeContext *c, CastKind cast_kind, Expr *expr, TBEValue *value, Type *to_type, Type *from_type);
void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align);
// -- comparisons --
void tilde_emit_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
void tilde_emit_int_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
void tilde_emit_int_comp_zero(TildeContext *c, TBEValue *result, TBEValue *lhs, BinaryOp binary_op);
void tilde_emit_int_comp_raw(TildeContext *c, TBEValue *result, Type *lhs_type, Type *rhs_type, TB_Reg lhs_value, TB_Reg rhs_value, BinaryOp binary_op);
// -- optional --
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value);
TB_Reg tilde_emit_is_no_opt(TildeContext *c, TB_Reg error_value);
static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment)
// -- jumps ---
bool tilde_emit_goto_if_needed(TildeContext *c, TB_Label jump);
INLINE void tilde_emit_block(TildeContext *c, TB_Label block);
void tilde_emit_memclear_size_align(TildeContext *c, TB_Register ref, uint64_t size, AlignSize align);
#define EMIT_LOC(x, y) do { } while(0)
static inline void tilde_store_internal(TildeContext *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)
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment)
{
assert(alignment > 0);
return tb_inst_load(c->f, type, addr, alignment);
@@ -124,5 +168,19 @@ static inline TB_Reg decl_reg(Decl *decl)
}
TB_DataType tilde_get_int_type_of_bytesize(int byte_size);
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype);
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl)
{
if (!decl->is_external_visible && decl->visibility == VISIBLE_LOCAL) return TB_LINKAGE_PRIVATE;
return TB_LINKAGE_PUBLIC;
}
#endif // TB_BACKEND
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value)
{
return tilde_store_to_ptr_aligned(c, destination, value, type_alloca_alignment(value->type));
}
INLINE void tilde_emit_block(TildeContext *c, TB_Label block)
{
tb_inst_set_label(c->f, block);
}

1077
tb/tb.h

File diff suppressed because it is too large Load Diff

1
tilde-backend Submodule

Submodule tilde-backend added at 8029367607