diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ddaf654ca..93b821186 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..db2babda1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tilde-backend"] + path = tilde-backend + url = https://github.com/c3lang/tilde-backend diff --git a/CMakeLists.txt b/CMakeLists.txt index abcfcc7ea..54203b431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/src/build/build_options.c b/src/build/build_options.c index 84c5f2a38..b3008ca54 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -79,7 +79,7 @@ static void usage(void) OUTPUT(" vendor-fetch ... 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 - 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")) { diff --git a/src/build/build_options.h b/src/build/build_options.h index 2441c7e4f..ef9773eaf 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -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; diff --git a/src/build/builder.c b/src/build/builder.c index 5b44717f6..22af756a9 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -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) diff --git a/src/compiler/codegen_general.c b/src/compiler/codegen_general.c index c9b910349..d18a170d0 100644 --- a/src/compiler/codegen_general.c +++ b/src/compiler/codegen_general.c @@ -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); +} diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index d769c57ac..1bfadc2f5 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -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; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index c42bbda01..556d83fb0 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -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: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 565f79f1d..c8e422632 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 251cde58f..05a8d1dff 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -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) { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 875f6a0d0..9c3645218 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -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; diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 8ec85522d..538b6a629 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -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); diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 446c3c973..ed57b5a35 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -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(); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 441b23d97..6d1a26f8d 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -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); diff --git a/src/compiler/llvm_codegen_storeload.c b/src/compiler/llvm_codegen_storeload.c index 43ba8b692..a4b917185 100644 --- a/src/compiler/llvm_codegen_storeload.c +++ b/src/compiler/llvm_codegen_storeload.c @@ -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); } diff --git a/src/compiler/llvm_codegen_value.c b/src/compiler/llvm_codegen_value.c index 760f5909d..0f2dc3fe7 100644 --- a/src/compiler/llvm_codegen_value.c +++ b/src/compiler/llvm_codegen_value.c @@ -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; } } diff --git a/src/compiler/tb_codegen.c b/src/compiler/tb_codegen.c deleted file mode 100644 index dceff52a4..000000000 --- a/src/compiler/tb_codegen.c +++ /dev/null @@ -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, - ¶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 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(¤t)); - } - - // 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 - diff --git a/src/compiler/tilde_codegen.c b/src/compiler/tilde_codegen.c index 367513de9..8862aa430 100644 --- a/src/compiler/tilde_codegen.c +++ b/src/compiler/tilde_codegen.c @@ -1,1212 +1,225 @@ #include "tilde_internal.h" -#if TB_BACKEND -static void tbcontext_destroy(TbContext *context) -{ - free(context); -} - -TB_Register tilde_emit_is_no_error(TbContext *c, TB_Reg reg) -{ - return tb_inst_cmp_eq(c->f, reg, tilde_get_zero(c, type_anyerr)); -} - -void tilde_emit_global_initializer(TbContext *c, Decl *decl) -{ - TODO; -} -void tilde_emit_and_set_decl_alloca(TbContext *c, Decl *decl) +static TB_System os_to_tilde_system(OsType target) { - Type *type = type_lowering(decl->type); - if (type == type_void) return; - decl->tb_register = tb_inst_local(c->f, type_size(type), decl->alignment); -} - - -void tilde_emit_local_var_alloca(TbContext *c, Decl *decl) -{ - assert(!decl->var.is_static); - tilde_emit_and_set_decl_alloca(c, decl); - /* Emit debug here */ -} -#ifdef NEVER -void tilde_emit_memclear(TbContext *c, TBEValue *ref) -{ - assert(ref->kind == TBE_VALUE); - Type *type = ref->type; - if (!type_is_abi_aggregate(type)) + switch (target) { - tilde_store_bevalue_raw(c, ref, tilde_get_zero(c, type)); - return; + case OS_TYPE_LINUX: + return TB_SYSTEM_LINUX; + case OS_TYPE_MACOSX: + return TB_SYSTEM_MACOS; + case OS_TYPE_WIN32: + return TB_SYSTEM_WINDOWS; + case OS_TYPE_WASI: + case OS_TYPE_EMSCRIPTEN: + return TB_SYSTEM_WEB; + default: + error_exit("Unsupported system for TB compilation, use LLVM."); } - Type *single_type = type_abi_find_single_struct_element(type); - - if (single_type && !type_is_abi_aggregate(single_type)) +} +static TB_Arch arch_to_tilde_arch(ArchType target) +{ + switch (target) { - TBEValue element; - value_set_address_align(&element, ref->reg, single_type, ref->alignment); - tilde_emit_memclear(c, &element); - return; + case ARCH_TYPE_AARCH64: + return TB_ARCH_AARCH64; + case ARCH_TYPE_X86_64: + return TB_ARCH_X86_64; + case ARCH_TYPE_WASM32: + return TB_ARCH_WASM32; + default: + error_exit("Unsupported architecture for TB compilation, use LLVM."); } - if (type_size(type) <= 16) + UNREACHABLE +} + + +static void tilde_emit_function_body(TildeContext *c, Decl *decl) +{ + TB_Function *fn = tb_function_create(c->module, decl_get_extname(decl), tilde_linkage_for_decl(decl)); + decl->backend_value = fn; + tb_function_set_prototype(fn, (TB_FunctionPrototype*)tilde_get_func_prototype(c, decl->type->function.prototype)); + vec_add(c->functions, fn); + c->curr_func = decl; + c->cur_func.prototype = decl->type->function.prototype; + c->cur_func.rtype = decl->type->function.prototype->rtype; + c->cur_func.name = decl->name; + c->f = fn; + if (decl->func_decl.body) tilde_emit_stmt(c, astptr(decl->func_decl.body)); +} + +static TildeContext *tilde_gen_module(Module *module, TB_FeatureSet *feature_set) +{ + if (!vec_size(module->units)) return NULL; + TildeContext *context = CALLOCS(TildeContext); + TB_Module *tb_module = tb_module_create(arch_to_tilde_arch(platform_target.arch), + os_to_tilde_system(platform_target.os), + feature_set, false); + codegen_setup_object_names(module, &context->ir_filename, &context->asm_filename, &context->object_filename); + context->module = tb_module; + + FOREACH_BEGIN(CompilationUnit *unit, module->units) + + FOREACH_BEGIN(Decl *decl, unit->functions) + if (decl->func_decl.attr_test && !active_target.testing) continue; + if (decl->func_decl.body) tilde_emit_function_body(context, decl); + FOREACH_END(); + + FOREACH_END(); + + return context; +} + +void **tilde_gen(Module **modules, unsigned module_count) +{ + if (!module_count) return NULL; + TB_FeatureSet feature_set; + switch (platform_target.arch) { - if (type->type_kind == TYPE_STRUCT) + case ARCH_TYPE_AARCH64: + feature_set.aarch64.bf16 = false; + case ARCH_TYPE_X86_64: + feature_set.x64.avx = platform_target.x64.x86_vector_capability >= X86VECTOR_AVX; + default: + break; + } + TildeContext **contexts = NULL; + for (unsigned i = 0; i < module_count; i++) + { + TildeContext *c = tilde_gen_module(modules[i], &feature_set); + if (c) vec_add(contexts, c); + } + return (void**)contexts; +} + +void tinybackend_codegen_setup() +{ +} + + +static TB_DataType tilde_get_abi_type(AbiType type) +{ + if (abi_type_is_type(type)) return tildetype(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 *decl = type->decl; - Decl **members = decl->strukt.members; + Decl **members = type->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); + param_expand(params_ref, members[i]->type); } return; } - if (type->type_kind == TYPE_ARRAY) + case TYPE_ENUM: + case TYPE_ANYERR: + case TYPE_FAULTTYPE: + param_expand(params_ref, type_lowering(type)); + return; + case TYPE_UNION: { - LLVMTypeRef array_type = llvm_get_type(c, type); - for (unsigned i = 0; i < type->array.len; i++) + 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) { - 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); + if (type_size(type) > largest) + { + largest = type_size(type); + type = type->canonical; + } } + if (!largest) return; + param_expand(params_ref, largest_type); 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); - *modified = true; - LLVMTypeRef padding_type = LLVMArrayType(element_type, (unsigned)diff); - return LLVMConstNull(padding_type); -} - -LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init) -{ - switch (const_init->kind) - { - case CONST_INIT_ZERO: - return LLVMConstNull(llvm_get_type(c, const_init->type)); - case CONST_INIT_ARRAY_VALUE: - UNREACHABLE - case CONST_INIT_ARRAY_FULL: - { - bool was_modified = false; - Type *array_type = const_init->type; - Type *element_type = array_type->array.base; - LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); - ConstInitializer **elements = const_init->init_array_full; - assert(array_type->type_kind == TYPE_ARRAY || array_type->type_kind == TYPE_VECTOR); - ArraySize size = array_type->array.len; - assert(size > 0); - LLVMValueRef *parts = VECNEW(LLVMValueRef, size); - for (MemberIndex i = 0; i < size; i++) - { - LLVMValueRef element = llvm_emit_const_initializer(c, elements[i]); - if (element_type_llvm != LLVMTypeOf(element)) was_modified = true; - vec_add(parts, element); - } - if (array_type->type_kind == TYPE_VECTOR) - { - return LLVMConstVector(parts, vec_size(parts)); - } - if (was_modified) - { - return LLVMConstStructInContext(c->context, parts, vec_size(parts), true); - } - return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); - } - - case CONST_INIT_ARRAY: - { - bool was_modified = false; - Type *array_type = const_init->type; - Type *element_type = array_type->array.base; - LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); - AlignSize expected_align = llvm_abi_alignment(c, element_type_llvm); - ConstInitializer **elements = const_init->init_array.elements; - unsigned element_count = vec_size(elements); - assert(element_count > 0 && "Array should always have gotten at least one element."); - MemberIndex current_index = 0; - unsigned alignment = 0; - LLVMValueRef *parts = NULL; - bool pack = false; - VECEACH(elements, i) - { - ConstInitializer *element = elements[i]; - assert(element->kind == CONST_INIT_ARRAY_VALUE); - MemberIndex element_index = element->init_array_value.index; - IndexDiff diff = element_index - current_index; - if (alignment && expected_align != alignment) - { - pack = true; - } - alignment = expected_align; - // Add zeroes - if (diff > 0) - { - vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified)); - } - LLVMValueRef value = llvm_emit_const_initializer(c, element->init_array_value.element); - if (LLVMTypeOf(value) == element_type_llvm) was_modified = true; - vec_add(parts, value); - current_index = element_index + 1; - } - - IndexDiff end_diff = (MemberIndex)array_type->array.len - current_index; - if (end_diff > 0) - { - vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, end_diff, &was_modified)); - } - if (was_modified) - { - return LLVMConstStructInContext(c->context, parts, vec_size(parts), pack); - } - return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); - } - case CONST_INIT_UNION: - { - Decl *decl = const_init->type->decl; - - // Emit our value. - LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element); - LLVMTypeRef result_type = LLVMTypeOf(result); - - // Get the union value - LLVMTypeRef union_type_llvm = llvm_get_type(c, decl->type); - - // Take the first type in the union (note that there may be padding!) - LLVMTypeRef first_type = LLVMStructGetTypeAtIndex(union_type_llvm, 0); - - // We need to calculate some possible padding. - TypeSize union_size = type_size(const_init->type); - TypeSize member_size = llvm_abi_size(c, result_type); - - // Create the resulting values: - LLVMValueRef values[2] = { result, NULL }; - unsigned value_count = 1; - - // Add possible padding as and i8 array. - if (union_size > member_size) - { - values[1] = llvm_emit_const_padding(c, union_size - member_size); - value_count = 2; - } - - // Is this another type than usual for the union? - if (first_type != result_type) - { - // Yes, so the type needs to be modified. - return LLVMConstStructInContext(c->context, values, value_count, false); - } - - return LLVMConstNamedStruct(union_type_llvm, values, value_count); - } - case CONST_INIT_STRUCT: - { - if (const_init->type->type_kind == TYPE_BITSTRUCT) - { - return llvm_emit_const_bitstruct(c, const_init); - } - Decl *decl = const_init->type->decl; - Decl **members = decl->strukt.members; - uint32_t count = vec_size(members); - LLVMValueRef *entries = NULL; - bool was_modified = false; - for (MemberIndex i = 0; i < count; i++) - { - if (members[i]->padding) - { - vec_add(entries, llvm_emit_const_padding(c, members[i]->padding)); - } - LLVMTypeRef expected_type = llvm_get_type(c, const_init->init_struct[i]->type); - LLVMValueRef element = llvm_emit_const_initializer(c, const_init->init_struct[i]); - LLVMTypeRef element_type = LLVMTypeOf(element); - if (expected_type != element_type) - { - was_modified = true; - } - AlignSize new_align = llvm_abi_alignment(c, element_type); - AlignSize expected_align = llvm_abi_alignment(c, expected_type); - if (i != 0 && new_align < expected_align) - { - vec_add(entries, llvm_emit_const_padding(c, expected_align - new_align)); - } - vec_add(entries, element); - } - if (decl->strukt.padding) - { - vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding)); - } - if (was_modified) - { - LLVMValueRef value = LLVMConstStructInContext(c->context, entries, vec_size(entries), decl->is_packed); - return value; - } - return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, vec_size(entries)); - } - case CONST_INIT_VALUE: - { - BEValue value; - llvm_emit_expr(c, &value, const_init->init_value); - return llvm_load_value_store(c, &value); - } - } - UNREACHABLE -} - - -static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl) -{ - assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); - - // Skip real constants. - if (!decl->type) return; - - if (decl->type != type_void) - { - decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal"); - } - if (IS_FAILABLE(decl)) - { - scratch_buffer_clear(); - scratch_buffer_append(decl->external_name); - scratch_buffer_append(".f"); - decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string()); - } -} - - -void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) -{ - switch (value->type->type_kind) - { - case TYPE_POINTER: - llvm_value_rvalue(c, value); - value->kind = BE_ADDRESS; + default: + vec_add(*params_ref, tildetype(type)); return; - case TYPE_ARRAY: - case TYPE_VECTOR: - case TYPE_FLEXIBLE_ARRAY: - return; - case TYPE_SUBARRAY: - { - // TODO insert trap on overflow. - assert(value->kind == BE_ADDRESS); - 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)); - return; - } - default: - UNREACHABLE } + } -void llvm_set_internal_linkage(LLVMValueRef alloc) +static void callback(void* user_data, const char* fmt, ...) { - LLVMSetLinkage(alloc, LLVMInternalLinkage); - LLVMSetVisibility(alloc, LLVMDefaultVisibility); + va_list list; + va_start(list, fmt); + vprintf(fmt, list); + va_end(list); } -void llvm_set_private_linkage(LLVMValueRef alloc) + +// Compile module (multi threaded) +const char *tilde_codegen(void *context) { - LLVMSetLinkage(alloc, LLVMPrivateLinkage); - LLVMSetVisibility(alloc, LLVMDefaultVisibility); -} -void llvm_emit_global_variable_init(GenContext *c, Decl *decl) -{ - assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST || decl->var.is_static); + TildeContext *c = (TildeContext *)context; + bool is_win32 = platform_target.os == OS_TYPE_WIN32; + TB_DebugFormat debug_format = is_win32 ? TB_DEBUGFMT_CODEVIEW : TB_DEBUGFMT_DWARF; + if (active_target.debug_info == DEBUG_INFO_NONE) debug_format = TB_DEBUGFMT_NONE; - // Skip real constants. - if (!decl->type) return; - - LLVMValueRef init_value; - - Type *var_type = type_lowering(decl->type); - AlignSize alignment = type_alloca_alignment(var_type); - - Expr *init_expr = decl->var.init_expr; - if (init_expr) - { - if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_LIST) + FOREACH_BEGIN(TB_Function *function, c->functions) + if (!tb_module_compile_function(c->module, function, TB_ISEL_FAST)) { - ConstInitializer *list = init_expr->const_expr.list; - init_value = llvm_emit_const_initializer(c, list); + error_exit("Failed to compile function."); } - else - { - BEValue value; - if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_BYTES) - { - init_value = LLVMConstStringInContext(c->context, - init_expr->const_expr.bytes.ptr, - init_expr->const_expr.bytes.len, - 1); - } - else - { - llvm_emit_expr(c, &value, decl->var.init_expr); - init_value = llvm_load_value_store(c, &value); - } - } - } - else - { - init_value = LLVMConstNull(llvm_get_type(c, var_type)); - } - - - // TODO fix name - LLVMValueRef old = decl->backend_ref; - decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->extname ? decl->extname : decl->external_name); - LLVMSetThreadLocal(decl->backend_ref, decl->var.is_threadlocal); - if (decl->section) - { - LLVMSetSection(decl->backend_ref, decl->section); - } - llvm_set_alignment(decl->backend_ref, alignment); - - LLVMValueRef failable_ref = decl->var.failable_ref; - if (failable_ref) - { - llvm_set_alignment(failable_ref, type_alloca_alignment(type_anyerr)); - LLVMSetThreadLocal(failable_ref, decl->var.is_threadlocal); - } - if (init_expr && IS_FAILABLE(init_expr) && init_expr->expr_kind == EXPR_FAILABLE) - { - UNREACHABLE - } - if (decl->visibility != VISIBLE_EXTERN) - { - LLVMSetInitializer(decl->backend_ref, init_value); - if (failable_ref) - { - LLVMSetInitializer(failable_ref, LLVMConstNull(llvm_get_type(c, type_anyerr))); - } - } - - LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST); - - switch (decl->visibility) - { - case VISIBLE_MODULE: - LLVMSetVisibility(decl->backend_ref, LLVMProtectedVisibility); - if (failable_ref) LLVMSetVisibility(failable_ref, LLVMProtectedVisibility); - break; - case VISIBLE_PUBLIC: - LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); - if (failable_ref) LLVMSetVisibility(failable_ref, LLVMDefaultVisibility); - break; - case VISIBLE_EXTERN: - LLVMSetLinkage(decl->backend_ref, LLVMExternalLinkage); - if (failable_ref) LLVMSetLinkage(failable_ref, LLVMExternalLinkage); - //LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); - break; - case VISIBLE_LOCAL: - LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility); - if (failable_ref) LLVMSetVisibility(failable_ref, LLVMHiddenVisibility); - break; - } - - if (init_value && LLVMTypeOf(init_value) != llvm_get_type(c, var_type)) - { - decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, var_type)); - } - LLVMReplaceAllUsesWith(old, decl->backend_ref); - LLVMDeleteGlobal(old); - - // Should we set linkage here? - if (llvm_use_debug(c)) - { - llvm_emit_debug_global_var(c, decl); - } -} -static void gencontext_verify_ir(GenContext *context) -{ - char *error = NULL; - assert(context->module); - if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error)) - { - if (*error) - { - error_exit("Could not verify IR: %s", error); - } - error_exit("Could not verify module IR."); - } -} - -void gencontext_emit_object_file(GenContext *context) -{ - char *err = ""; - LLVMSetTarget(context->module, platform_target.target_triple); - char *layout = LLVMCopyStringRepOfTargetData(context->target_data); - LLVMSetDataLayout(context->module, layout); - LLVMDisposeMessage(layout); - - // Generate .o or .obj file - if (LLVMTargetMachineEmitToFile(context->machine, context->module, context->object_filename, LLVMObjectFile, &err)) - { - error_exit("Could not emit object file: %s", err); - } -} - -void gencontext_print_llvm_ir(GenContext *context) -{ - char *err = NULL; - if (LLVMPrintModuleToFile(context->module, context->ir_filename, &err)) - { - error_exit("Could not emit ir to file: %s", err); - } -} - - -LLVMValueRef llvm_emit_alloca(GenContext *c, LLVMTypeRef type, unsigned alignment, const char *name) -{ - assert(c->builder); - assert(alignment > 0); - LLVMBasicBlockRef current_block = LLVMGetInsertBlock(c->builder); - LLVMPositionBuilderBefore(c->builder, c->alloca_point); - assert(LLVMGetTypeContext(type) == c->context); - LLVMValueRef alloca = LLVMBuildAlloca(c->builder, type, name); - llvm_set_alignment(alloca, alignment); - LLVMPositionBuilderAtEnd(c->builder, current_block); - return alloca; -} - -LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name) -{ - return llvm_emit_alloca(c, llvm_get_type(c, type), type_alloca_alignment(type), name); -} - -void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl) -{ - Type *type = type_lowering(decl->type); - if (type == type_void) return; - decl->backend_ref = llvm_emit_alloca(c, llvm_get_type(c, type), decl->alignment, decl->name ? decl->name : ".anon"); -} - -void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) -{ - assert(!decl->var.is_static); - llvm_emit_and_set_decl_alloca(c, decl); - if (llvm_use_debug(c)) - { - llvm_emit_debug_local_var(c, decl); - } -} - -/** - * Values here taken from LLVM. - * @return return the inlining threshold given the build options. - */ -static int get_inlining_threshold() -{ - if (active_target.optimization_level == OPTIMIZATION_AGGRESSIVE) - { - return 250; - } - switch (active_target.size_optimization_level) - { - case SIZE_OPTIMIZATION_TINY: - return 5; - case SIZE_OPTIMIZATION_SMALL: - return 50; - default: - return 250; - } -} - - -static inline unsigned lookup_intrinsic(const char *name) -{ - return LLVMLookupIntrinsicID(name, strlen(name)); -} - -static inline unsigned lookup_attribute(const char *name) -{ - return LLVMGetEnumAttributeKindForName(name, strlen(name)); -} - -static bool intrinsics_setup = false; -LLVMAttributes attribute_id; -LLVMIntrinsics intrinsic_id; - -void llvm_codegen_setup() -{ - if (intrinsics_setup) return; - - intrinsic_id.trap = lookup_intrinsic("llvm.trap"); - intrinsic_id.assume = lookup_intrinsic("llvm.assume"); - - intrinsic_id.bswap = lookup_intrinsic("llvm.bswap"); - - intrinsic_id.lifetime_start = lookup_intrinsic("llvm.lifetime.start"); - intrinsic_id.lifetime_end = lookup_intrinsic("llvm.lifetime.end"); - - intrinsic_id.ssub_overflow = lookup_intrinsic("llvm.ssub.with.overflow"); - intrinsic_id.ssub_sat = lookup_intrinsic("llvm.ssub.sat"); - intrinsic_id.usub_overflow = lookup_intrinsic("llvm.usub.with.overflow"); - intrinsic_id.usub_sat = lookup_intrinsic("llvm.usub.sat"); - intrinsic_id.sadd_overflow = lookup_intrinsic("llvm.sadd.with.overflow"); - intrinsic_id.sadd_sat = lookup_intrinsic("llvm.sadd.sat"); - intrinsic_id.uadd_overflow = lookup_intrinsic("llvm.uadd.with.overflow"); - intrinsic_id.uadd_sat = lookup_intrinsic("llvm.uadd.sat"); - intrinsic_id.smul_overflow = lookup_intrinsic("llvm.smul.with.overflow"); - intrinsic_id.umul_overflow = lookup_intrinsic("llvm.umul.with.overflow"); - //intrinsic_id.sshl_sat = lookup_intrinsic("llvm.sshl.sat"); - //intrinsic_id.ushl_sat = lookup_intrinsic("llvm.ushl.sat"); - intrinsic_id.fshl = lookup_intrinsic("llvm.fshl"); - intrinsic_id.fshr = lookup_intrinsic("llvm.fshr"); - intrinsic_id.bitreverse = lookup_intrinsic("llvm.bitreverse"); - intrinsic_id.bswap = lookup_intrinsic("llvm.bswap"); - intrinsic_id.ctpop = lookup_intrinsic("llvm.ctpop"); - intrinsic_id.cttz = lookup_intrinsic("llvm.cttz"); - intrinsic_id.ctlz = lookup_intrinsic("llvm.ctlz"); - - intrinsic_id.rint = lookup_intrinsic("llvm.rint"); - intrinsic_id.trunc = lookup_intrinsic("llvm.trunc"); - intrinsic_id.ceil = lookup_intrinsic("llvm.ceil"); - intrinsic_id.floor = lookup_intrinsic("llvm.floor"); - intrinsic_id.sqrt = lookup_intrinsic("llvm.sqrt"); - intrinsic_id.powi = lookup_intrinsic("llvm.powi"); - intrinsic_id.pow = lookup_intrinsic("llvm.pow"); - intrinsic_id.sin = lookup_intrinsic("llvm.sin"); - intrinsic_id.cos = lookup_intrinsic("llvm.cos"); - intrinsic_id.exp = lookup_intrinsic("llvm.exp"); - intrinsic_id.exp2 = lookup_intrinsic("llvm.exp2"); - intrinsic_id.log = lookup_intrinsic("llvm.log"); - intrinsic_id.log2 = lookup_intrinsic("llvm.log2"); - intrinsic_id.log10 = lookup_intrinsic("llvm.log10"); - intrinsic_id.fabs = lookup_intrinsic("llvm.fabs"); - intrinsic_id.fma = lookup_intrinsic("llvm.fma"); - intrinsic_id.copysign = lookup_intrinsic("llvm.copysign"); - intrinsic_id.minnum = lookup_intrinsic("llvm.minnum"); - intrinsic_id.maxnum = lookup_intrinsic("llvm.maxnum"); - intrinsic_id.minimum = lookup_intrinsic("llvm.minimum"); - intrinsic_id.maximum = lookup_intrinsic("llvm.maximum"); - intrinsic_id.convert_to_fp16 = lookup_intrinsic("llvm.convert.to.fp16"); - intrinsic_id.convert_from_fp16 = lookup_intrinsic("llvm.convert.from.fp16"); - intrinsic_id.nearbyint = lookup_intrinsic("llvm.nearbyint"); - intrinsic_id.roundeven = lookup_intrinsic("llvm.roundeven"); - intrinsic_id.lround = lookup_intrinsic("llvm.lround"); - intrinsic_id.llround = lookup_intrinsic("llvm.llround"); - intrinsic_id.lrint = lookup_intrinsic("llvm.lrint"); - intrinsic_id.llrint = lookup_intrinsic("llvm.llrint"); - - //intrinsic_id.abs = lookup_intrinsic("llvm.abs"); - intrinsic_id.smax = lookup_intrinsic("llvm.smax"); - intrinsic_id.smin = lookup_intrinsic("llvm.smin"); - intrinsic_id.umax = lookup_intrinsic("llvm.umax"); - intrinsic_id.umin = lookup_intrinsic("llvm.umin"); - - attribute_id.noinline = lookup_attribute("noinline"); - attribute_id.optnone = lookup_attribute("optnone"); - attribute_id.alwaysinline = lookup_attribute("alwaysinline"); - attribute_id.inlinehint = lookup_attribute("inlinehint"); - attribute_id.noreturn = lookup_attribute("noreturn"); - attribute_id.nounwind = lookup_attribute("nounwind"); - attribute_id.writeonly = lookup_attribute("writeonly"); - attribute_id.readonly = lookup_attribute("readonly"); - attribute_id.optnone = lookup_attribute("optnone"); - attribute_id.sret = lookup_attribute("sret"); - attribute_id.noalias = lookup_attribute("noalias"); - attribute_id.zext = lookup_attribute("zeroext"); - attribute_id.sext = lookup_attribute("signext"); - attribute_id.align = lookup_attribute("align"); - attribute_id.byval = lookup_attribute("byval"); - attribute_id.inreg = lookup_attribute("inreg"); - attribute_id.naked = lookup_attribute("naked"); - intrinsics_setup = true; -} - -void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) -{ - if (decl->module != c->code_module) - { - LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); - LLVMSetVisibility(value, LLVMDefaultVisibility); - return; - } - switch (decl->visibility) - { - case VISIBLE_MODULE: - case VISIBLE_PUBLIC: - LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); - LLVMSetVisibility(value, LLVMDefaultVisibility); - break; - case VISIBLE_EXTERN: - case VISIBLE_LOCAL: - LLVMSetVisibility(value, LLVMHiddenVisibility); - LLVMSetLinkage(value, LLVMLinkerPrivateLinkage); - break; - } - -} -void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl) -{ - llvm_get_type(c, decl->type); - if (decl_is_struct_type(decl)) - { - Decl **decls = decl->strukt.members; - VECEACH(decls, i) - { - Decl *member_decl = decls[i]; - if (decl_is_struct_type(member_decl)) - { - llvm_emit_introspection_type_from_decl(c, member_decl); - } - } - } - if (decl_is_enum_kind(decl)) - { - unsigned elements = vec_size(decl->enums.values); - LLVMTypeRef element_type = llvm_get_type(c, type_voidptr); - LLVMTypeRef elements_type = LLVMArrayType(element_type, elements); - scratch_buffer_clear(); - scratch_buffer_append(decl->external_name); - scratch_buffer_append("$elements"); - LLVMValueRef enum_elements = LLVMAddGlobal(c->module, elements_type, scratch_buffer_to_string()); - LLVMSetGlobalConstant(enum_elements, 1); - llvm_set_linkage(c, decl, enum_elements); - LLVMSetInitializer(enum_elements, LLVMConstNull(elements_type)); - AlignSize alignment = type_alloca_alignment(type_voidptr); - for (unsigned i = 0; i < elements; i++) - { - AlignSize store_align; - decl->enums.values[i]->backend_ref = llvm_emit_array_gep_raw(c, enum_elements, elements_type, i, alignment, &store_align); - } - } - LLVMValueRef global_name = LLVMAddGlobal(c->module, llvm_get_type(c, type_char), decl->name ? decl->name : ".anon"); - LLVMSetGlobalConstant(global_name, 1); - LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); - decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); - llvm_set_linkage(c, decl, global_name); -} - - -void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value) -{ - value->value = llvm_value; - value->alignment = type_abi_alignment(type_bool); - value->kind = BE_BOOLEAN; - value->type = type_bool; -} - -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) -{ - decl = decl_flatten(decl); - if (decl->is_value) - { - llvm_value_set(value, decl->backend_value, decl->type); - return; - } - 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, "assign_optional"); - 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_load_value_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) -{ - 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 (llvm_is_global_builder(c)) - { - 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(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) -{ - switch (decl->decl_kind) - { - case DECL_POISONED: - UNREACHABLE; - case DECL_FUNC: - // TODO - break; - case DECL_VAR: - // TODO - break; - case DECL_TYPEDEF: - break; - case DECL_DISTINCT: - // TODO - break; - case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: - // TODO - break;; - case DECL_STRUCT: - case DECL_UNION: - case DECL_ENUM: - case DECL_FAULTTYPE: - case DECL_BITSTRUCT: - llvm_emit_introspection_type_from_decl(context, decl); - break; - case NON_TYPE_DECLS: - UNREACHABLE - } -} - -const char *llvm_codegen(void *context) -{ - GenContext *c = context; - LLVMModuleRef module = c->module; - // Starting from here we could potentially thread this: - LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate(); - LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, (unsigned)active_target.optimization_level); - LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, (unsigned)active_target.size_optimization_level); - LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, active_target.optimization_level == OPTIMIZATION_NONE); - if (active_target.optimization_level != OPTIMIZATION_NONE) - { - LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, (unsigned)get_inlining_threshold()); - } - LLVMPassManagerRef pass_manager = LLVMCreatePassManager(); - LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(module); - LLVMAddAnalysisPasses(c->machine, function_pass_manager); - LLVMAddAnalysisPasses(c->machine, pass_manager); - LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager); - LLVMPassManagerBuilderPopulateFunctionPassManager(pass_manager_builder, function_pass_manager); - - // IMPROVE - // In LLVM Opt, LoopVectorize and SLPVectorize settings are part of the PassManagerBuilder - // Anything else we need to manually add? - - LLVMPassManagerBuilderDispose(pass_manager_builder); - - // Run function passes - LLVMInitializeFunctionPassManager(function_pass_manager); - LLVMValueRef current_function = LLVMGetFirstFunction(module); - while (current_function) - { - LLVMRunFunctionPassManager(function_pass_manager, current_function); - current_function = LLVMGetNextFunction(current_function); - } - LLVMFinalizeFunctionPassManager(function_pass_manager); - LLVMDisposePassManager(function_pass_manager); - - // Run module pass - LLVMRunPassManager(pass_manager, module); - LLVMDisposePassManager(pass_manager); - - // Serialize the LLVM IR, if requested, also verify the IR in this case - if (active_target.emit_llvm) - { - gencontext_print_llvm_ir(c); - gencontext_verify_ir(c); - } + tb_function_print(function, &callback, NULL, true); + FOREACH_END(); const char *object_name = NULL; if (active_target.emit_object_files) { - gencontext_emit_object_file(c); + if (!tb_exporter_write_files(c->module, TB_FLAVOR_OBJECT, debug_format, 1, &c->object_filename)) + { + error_exit("Failed to create object file %s.", c->object_filename); + } object_name = c->object_filename; } - gencontext_end_module(c); - gencontext_destroy(c); + if (active_target.emit_asm && false) + { + if (!tb_exporter_write_files(c->module, TB_FLAVOR_ASSEMBLY, debug_format, 1, &c->asm_filename)) + { + error_exit("Failed to create asm %s.", c->asm_filename); + } + } + tb_module_destroy(c->module); return object_name; + + + return c->object_filename; } -void *llvm_gen(Module *module) -{ - if (!vec_size(module->units)) return NULL; - assert(intrinsics_setup); - GenContext *gen_context = cmalloc(sizeof(GenContext)); - gencontext_init(gen_context, module); - gencontext_begin_module(gen_context); - - VECEACH(module->units, j) - { - CompilationUnit *unit = module->units[j]; - gencontext_init_file_emit(gen_context, unit); - gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; - gen_context->debug.file = unit->llvm.debug_file; - - VECEACH(unit->methods, i) - { - llvm_emit_function_decl(gen_context, unit->methods[i]); - } - VECEACH(unit->types, i) - { - llvm_emit_type_decls(gen_context, unit->types[i]); - } - VECEACH(unit->functions, i) - { - llvm_emit_function_decl(gen_context, unit->functions[i]); - } - if (unit->main_function) llvm_emit_function_decl(gen_context, unit->main_function); - } - - VECEACH(module->units, j) - { - CompilationUnit *unit = module->units[j]; - gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; - gen_context->debug.file = unit->llvm.debug_file; - - VECEACH(unit->vars, i) - { - gencontext_emit_global_variable_definition(gen_context, unit->vars[i]); - } - VECEACH(unit->vars, i) - { - llvm_emit_global_variable_init(gen_context, unit->vars[i]); - } - VECEACH(unit->functions, i) - { - Decl *decl = unit->functions[i]; - if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); - } - if (unit->main_function) llvm_emit_function_body(gen_context, unit->main_function); - - VECEACH(unit->methods, i) - { - Decl *decl = unit->methods[i]; - if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); - } - - gencontext_end_file_emit(gen_context, unit); - } - // EmitDeferred() - - if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(gen_context->debug.builder); - - // If it's in test, then we want to serialize the IR before it is optimized. - if (active_target.test_output) - { - gencontext_print_llvm_ir(gen_context); - gencontext_verify_ir(gen_context); - } - return gen_context; -} - -void llvm_attribute_add_int(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, uint64_t val, int index) -{ - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute, val); - LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); -} - -void llvm_attribute_add_type(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute, LLVMTypeRef type, int index) -{ - LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type); - LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); -} - -void llvm_attribute_add(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index) -{ - llvm_attribute_add_int(context, value_to_add_attribute_to, attribute, 0, index); -} - -void llvm_attribute_add_call_type(GenContext *c, LLVMValueRef call, unsigned attribute, int index, LLVMTypeRef type) -{ - LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type); - LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr); -} - -void llvm_attribute_add_call(GenContext *context, LLVMValueRef call, unsigned attribute, int index, int64_t value) -{ - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute, (uint64_t)value); - LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr); -} - -void llvm_attribute_add_range(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index_start, int index_end) -{ - for (int i = index_start; i <= index_end; i++) - { - llvm_attribute_add_int(context, value_to_add_attribute_to, attribute, 0, i); - } -} - -void llvm_attribute_add_string(GenContext *context, LLVMValueRef value_to_add_attribute_to, const char *attribute, const char *value, int index) -{ - LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(context->context, attribute, (unsigned)strlen(attribute), value, (unsigned)strlen(value)); - LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr); -} - -BitSize llvm_bitsize(GenContext *c, LLVMTypeRef type) -{ - return LLVMSizeOfTypeInBits(c->target_data, type); -} -TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type) -{ - return (TypeSize)LLVMABISizeOfType(c->target_data, type); -} - -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) -{ - assert(dest_align && src_align); - if (len <= UINT32_MAX) - { - LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len)); - return; - } - LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len)); -} - -void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment) -{ - if (source_alignment == 0) source_alignment = type_abi_alignment(decl->type); - assert(!decl->is_value); - 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) -{ - return (TypeSize)LLVMStoreSizeOfType(c->target_data, type); -} - -void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block) -{ - c->catch_block = block; - c->error_var = NULL; -} - -void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef ref) -{ - c->catch_block = block; - c->error_var = ref; -} - -#endif - -#endif \ No newline at end of file diff --git a/src/compiler/tilde_codegen_abi.c b/src/compiler/tilde_codegen_abi.c new file mode 100644 index 000000000..d99a50054 --- /dev/null +++ b/src/compiler/tilde_codegen_abi.c @@ -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" diff --git a/src/compiler/tilde_codegen_expr.c b/src/compiler/tilde_codegen_expr.c index 1045712d8..251f912e8 100644 --- a/src/compiler/tilde_codegen_expr.c +++ b/src/compiler/tilde_codegen_expr.c @@ -4,89 +4,175 @@ #include "tilde_internal.h" -#if TB_BACKEND +TB_Reg tilde_get_const_int(TildeContext *c, Type *type, uint64_t i) +{ + return type_is_signed(type) + ? tb_inst_sint(c->f, tildetype(type), (int64_t)i) + : tb_inst_uint(c->f, tildetype(type), i); +} -TB_Register tilde_get_zero(TbContext *c, Type *type) +TB_Reg tilde_get_const_float(TildeContext *c, Type *type, double d) +{ + return type_size(type) == 4 ? tb_inst_float32(c->f, (float)d) : tb_inst_float64(c->f, d); +} + +TB_Register tilde_get_zero(TildeContext *c, Type *type) { type = type_lowering(type); - TB_DataType data_type = tbtype(type); - if (type_is_float(type)) + TB_DataType data_type = tildetype(type); + switch (data_type.type) { - return tb_inst_float(c->f, data_type, 0); + case TB_INT: + return type_is_signed(type) ? tb_inst_sint(c->f, data_type, 0) : tb_inst_uint(c->f, data_type, 0); + case TB_FLOAT: + return type->type_kind == TYPE_F32 ? tb_inst_float32(c->f, 0.0f) : tb_inst_float64(c->f, 0.0); + case TB_PTR: + return tb_inst_ptr(c->f, 0); + default: + UNREACHABLE; } - return type_is_signed(type) ? tb_inst_sint(c->f, data_type, 0) : tb_inst_uint(c->f, data_type, 0); } -TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable) +static void tilde_emit_const_expr(TildeContext *c, TBEValue *value, Expr *expr) { - assert(ref->kind == TBE_ADDRESS || ref->kind == TBE_ADDRESS_FAILABLE); - TB_Label assign_block = 0; - - PUSH_ERROR(); - - TBEValue result; - if (failable) + Type *type = type_reduced_from_expr(expr)->canonical; + switch (expr->const_expr.const_kind) { - if (IS_FAILABLE(expr)) + + case CONST_BYTES: + TODO + case CONST_INTEGER: { - if (expr->expr_kind == EXPR_FAILABLE) + Int128 i = expr->const_expr.ixx.i; + switch (expr->const_expr.ixx.type) { - 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 }; + case TYPE_I128: + case TYPE_U128: + { + uint64_t words[2] = { i.low, i.high }; + TODO + } + default: + value_set(value, tilde_get_const_int(c, type, i.low), type); + return; } - assign_block = tb_inst_new_label_id(c->f); - c->error_var = failable; - c->catch_block = assign_block; } - else + case CONST_FLOAT: + value_set(value, tilde_get_const_float(c, type, expr->const_expr.fxx.f), type); + return; + case CONST_POINTER: + if (!expr->const_expr.ptr) + { + value_set(value, tb_inst_ptr(c->f, 0), type); + } + else + { + value_set(value, tb_inst_ptr(c->f, expr->const_expr.ptr), type); + } + return; + case CONST_BOOL: + value_set(value, tb_inst_bool(c->f, expr->const_expr.b), type_bool); + return; + case CONST_STRING: { - c->error_var = 0; - c->catch_block = 0; + TODO + /* + Type *str_type = type_lowering(expr->type); + bool is_array = type_flat_is_char_array(str_type); + if (llvm_is_local_eval(c) || !is_array) + { + ArraySize strlen = expr->const_expr.string.len; + ArraySize size = expr->const_expr.string.len + 1; + if (type_flat_is_char_array(expr->type) && type->array.len > size) size = type->array.len; + LLVMValueRef global_name = llvm_add_global_raw(c, + ".str", + LLVMArrayType(llvm_get_type(c, type_char), size), + 1); + llvm_set_private_linkage(global_name); + LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(global_name, 1); + LLVMValueRef string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); + if (size > strlen + 1) + { + LLVMValueRef trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - strlen - 1)); + LLVMValueRef values[2] = { string, trailing_zeros }; + string = llvm_get_packed_struct(c, values, 2); + } + LLVMSetInitializer(global_name, string); + if (is_array) + { + global_name = llvm_emit_bitcast_ptr(c, global_name, type); + llvm_value_set_address(be_value, global_name, type, 1); + } + else + { + global_name = llvm_emit_bitcast(c, global_name, type); + llvm_value_set(be_value, global_name, type); + } + return; + } + ArraySize array_len = type->array.len; + ArraySize size = expr->const_expr.string.len + 1; + bool zero_terminate = array_len == size; + LLVMValueRef string; + if (array_len <= size) + { + if (zero_terminate) + { + string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); + } + else + { + string = llvm_get_bytes(c, expr->const_expr.string.chars, array_len); + } + } + else + { + char *buffer = ccalloc(1, array_len); + memcpy(buffer, expr->const_expr.string.chars, expr->const_expr.string.len); + string = llvm_get_bytes(c, buffer, array_len); + } + llvm_value_set(be_value, string, type);*/ + return; } - } - 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); - } + case CONST_TYPEID: + TODO + //llvm_emit_typeid(c, be_value, expr->const_expr.typeid); + return; + case CONST_ERR: + { + TODO + /* + Decl *decl = expr->const_expr.enum_err_val; - if (failable) - { - tilde_store_zero(c, type_anyerr, failable, type_alloca_alignment(type_anyerr)); + LLVMValueRef value; + if (decl) + { + value = LLVMBuildPtrToInt(c->builder, llvm_get_ref(c, decl), llvm_get_type(c, type_anyerr), ""); + } + else + { + value = llvm_get_zero(c, type_anyerr); + } + llvm_value_set(be_value, value, type_anyerr);*/ + return; + } + case CONST_ENUM: + value_set(value, tilde_get_const_int(c, type, expr->const_expr.enum_err_val->enum_constant.ordinal), type); + return; + case CONST_INITIALIZER: + TODO + //llvm_emit_const_initializer_list_expr(c, be_value, expr); + return; + case CONST_MEMBER: + case CONST_UNTYPED_LIST: + UNREACHABLE } - POP_ERROR(); + UNREACHABLE - 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) +void tilde_emit_parameter(TildeContext *c, TB_Reg *args, unsigned *arg_count_ref, ABIArgInfo *info, TBEValue *be_value, Type *type) { type = type_lowering(type); assert(be_value->type->canonical == type); @@ -95,10 +181,27 @@ static void tilde_emit_parameter(TbContext *c, TB_Reg **args, ABIArgInfo *info, 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); + if (info->attributes.by_val && value_is_addr(be_value) && info->indirect.alignment <= be_value->alignment) + { + value_fold_optional(c, be_value); + args[(*arg_count_ref)++] = be_value->reg; + return; + } + TB_Reg indirect = tilde_emit_alloca(c, type, info->indirect.alignment); + tilde_store_to_ptr_aligned(c, indirect, be_value, info->indirect.alignment); + args[(*arg_count_ref)++] = indirect; + return; + } + case ABI_ARG_DIRECT: + args[(*arg_count_ref)++] = tilde_load_value_store(c, be_value); + return; case ABI_ARG_DIRECT_SPLIT_STRUCT: { - TODO - /* + TODO; /*-- LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); assert(coerce_type && coerce_type != llvm_get_type(c, type)); AlignSize target_alignment = llvm_abi_alignment(c, coerce_type); @@ -110,124 +213,160 @@ static void tilde_emit_parameter(TbContext *c, TB_Reg **args, ABIArgInfo *info, { 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, "")); + args[(*arg_count_ref)++] = llvm_load(c, element, element_ptr, load_align, ""); } 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: { - TB_DataType coerce_type = tbtype(info->direct_coerce_type); - if (coerce_type.type == tbtype(type).type) + TODO; /* + LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type); + if (coerce_type == llvm_get_type(c, type)) { - vec_add(*args, tilde_load_value(c, be_value)); + args[(*arg_count_ref)++] = llvm_load_value_store(c, be_value); return; } - TODO - //vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type)); - //return; + args[(*arg_count_ref)++] = llvm_emit_coerce(c, coerce_type, be_value, type); + return;--*/ } case ABI_ARG_DIRECT_COERCE_INT: { - TB_DataType coerce_type = tilde_get_int_type_of_bytesize(type_size(type)); - if (coerce_type.type == tbtype(type).type) + TODO; /*-- + LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(type) * 8); + if (coerce_type == llvm_get_type(c, type)) { - vec_add(*args, tilde_load_value(c, be_value)); + args[(*arg_count_ref)++] = llvm_load_value_store(c, be_value); return; } - TODO - //vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type)); - //return; + args[(*arg_count_ref)++] = llvm_emit_coerce(c, coerce_type, be_value, type); + return; --*/ } case ABI_ARG_DIRECT_PAIR: { - TODO - /* + TODO; /*- + assert(type_flatten(be_value->type) == be_value->type); + LLVMTypeRef original_type = llvm_get_type(c, be_value->type); + LLVMTypeRef struct_type = llvm_get_coerce_type(c, info); + AlignSize alignment; + if (llvm_types_are_similar(original_type, struct_type)) + { + // Optimization + assert(LLVMGetTypeKind(original_type) == LLVMStructTypeKind && LLVMCountStructElementTypes(original_type) == 2); + if (llvm_value_is_addr(be_value)) + { + LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, be_value->value, original_type, 0, be_value->alignment, &alignment); + args[(*arg_count_ref)++] = llvm_load(c, LLVMStructGetTypeAtIndex(original_type, 0), ptr, alignment, "lo"); + ptr = llvm_emit_struct_gep_raw(c, be_value->value, original_type, 1, be_value->alignment, &alignment); + args[(*arg_count_ref)++] = llvm_load(c, LLVMStructGetTypeAtIndex(original_type, 1), ptr, alignment, "hi"); + return; + } + LLVMValueRef val = be_value->value; + // Maybe it's just created? Let's optimize codegen. + if (!LLVMGetFirstUse(val) && LLVMIsAInsertValueInst(val) && LLVMIsAInsertValueInst( + LLVMGetPreviousInstruction(val))) + { + LLVMValueRef prev = LLVMGetPreviousInstruction(val); + // Isn't this a second insert? + if (LLVMGetOperand(val, 0) != prev) goto NO_OPT; + // Is it used in between? + if (LLVMGetNextUse(LLVMGetFirstUse(prev))) goto NO_OPT; + // No, then we can replace the instructions with the values. + LLVMValueRef first_val = LLVMGetOperand(prev, 1); + LLVMValueRef second_val = LLVMGetOperand(val, 1); + LLVMInstructionEraseFromParent(val); + LLVMInstructionEraseFromParent(prev); + args[(*arg_count_ref)++] = first_val; + args[(*arg_count_ref)++] = second_val; + return; + } + NO_OPT: + args[(*arg_count_ref)++] = llvm_emit_extract_value(c, be_value->value, 0); + args[(*arg_count_ref)++] = llvm_emit_extract_value(c, be_value->value, 1); + return; + } 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")); + args[(*arg_count_ref)++] = 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;*/ + args[(*arg_count_ref)++] = llvm_load(c, hi, hi_ptr, alignment, "hi"); + return;--*/ } case ABI_ARG_EXPAND_COERCE: { - /* - // Create the expand type: - LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); - LLVMValueRef temp = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce"); - llvm_emit_and_set_decl_alloca(c, decl); + TODO - 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(c, gep_first, llvm_get_next_param(c, index), element_align); + /*-- + // Move this to an address (if needed) + llvm_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); + args[(*arg_count_ref)++] = 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, &element_align); - llvm_store(c, gep_second, llvm_get_next_param(c, index), element_align); + LLVMValueRef gep_second = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.hi_index, alignment, &align); + args[(*arg_count_ref)++] = llvm_load(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, ""); } - break;*/ - case ABI_ARG_EXPAND: + return;--*/ + } + case ABI_ARG_EXPAND: + { + // Move this to an address (if needed) + value_addr(c, be_value); + TODO /*--tilde_expand_type_to_args(c, type, be_value.reg, args, arg_count_ref, be_value->alignment); + // Expand the padding here. + if (info->expand.padding_type) { - TODO - /* - // Move this to an address (if needed) - llvm_value_addr(c, be_value); - 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;*/ + args[(*arg_count_ref)++] = TB_NULL_REG; } + return;--*/ } } - } -void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) +static void tilde_emit_call_expr(TildeContext *c, TBEValue *result_value, Expr *expr, TBEValue *target) { if (expr->call_expr.is_builtin) { - TODO - // llvm_emit_builtin_call(c, result_value, expr); + TODO // llvm_emit_builtin_call(c, result_value, expr); return; } - TB_DataType func_type; - TB_Reg func; + + REMINDER("Debug stack"); + /* + if (c->debug.stack_slot_row) + { + llvm_store_to_ptr_raw_aligned(c, + c->debug.stack_slot_row, + llvm_const_int(c, type_uint, expr->span.row), + type_abi_alignment(type_uint)); + }*/ + + TODO + /* + TB_FunctionPrototype *func_type; + TB_Function *func; TBEValue temp_value; bool always_inline = false; FunctionPrototype *prototype; // 1. Call through a pointer. - if (expr->call_expr.is_pointer_call) + if (!expr->call_expr.is_func_ref) { Expr *function = exprptr(expr->call_expr.function); @@ -235,7 +374,7 @@ void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) Type *type = function->type->canonical->pointer; // 1b. Find the type signature using the underlying pointer. - prototype = type->func.prototype; + prototype = type->function.prototype; // 1c. Evaluate the pointer expression. TBEValue func_value; @@ -245,7 +384,7 @@ void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) func = tilde_load_value(c, &func_value); // 1e. Calculate the function type - func_type = tbtype(type); + func_type = tilde_get_func_prototype(c, prototype); } else { @@ -255,505 +394,1066 @@ void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) 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; + prototype = function_decl->type->function.prototype; + func = tilde_get_function(c, function_decl); assert(func); - func_type = tbtype(function_decl->type); + func_type = tilde_get_func_prototype(c, prototype); } - (void)func_type; - TB_Reg *values = NULL; - Type **params = prototype->params; + TB_Reg arg_values[512]; + unsigned arg_count = 0; + Type **params = prototype->param_types; 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--; + Expr **args = expr->call_expr.arguments; + Expr **varargs = NULL; + Expr *vararg_splat = NULL; + if (prototype->variadic != VARIADIC_NONE) + { + if (expr->call_expr.splat_vararg) + { + vararg_splat = expr->call_expr.splat; + } + else + { + varargs = expr->call_expr.varargs; + } + } + FunctionPrototype copy; + if (prototype->variadic == VARIADIC_RAW) + { + if (varargs || vararg_splat) + { + assert(!vararg_splat); + copy = *prototype; + copy.varargs = NULL; + + foreach(Expr*, varargs) + { + vec_add(copy.varargs, type_flatten(val->type)); + } + copy.ret_abi_info = NULL; + copy.ret_by_ref_abi_info = NULL; + copy.abi_args = NULL; + c_abi_func_create(©); + prototype = © + TB_FunctionPrototype *params_type = NULL; + tilde_update_prototype_abi(c, prototype, ¶ms_type); + } + } ABIArgInfo *ret_info = prototype->ret_abi_info; Type *call_return_type = prototype->abi_ret_type; // 5. In the case of an optional, the error is replacing the regular return abi. - TB_Reg error_var = 0; + TB_Reg error_var = TB_NULL_REG; - *result_value = (TBEValue){ .kind = TBE_VALUE, .reg = 0 }; + *result_value = (TBEValue){ .kind = TBE_VALUE, .reg = TB_NULL_REG }; // 6. Generate data for the return value. + bool sret_return = false; 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) + if (prototype->is_optional && c->opt_var && !ret_info->attributes.realign) { - error_var = c->error_var; - vec_add(values, error_var); + error_var = c->opt_var; + arg_values[arg_count++] = 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); + // If we have a target, then use it. + if (target && alignment <= target->alignment) + { + assert(target->kind == TBE_ADDRESS); + arg_values[arg_count++] = target->reg; + sret_return = true; + break; + } + value_set_address(result_value, + tilde_emit_alloca(c, call_return_type, alignment), + call_return_type, alignment); // 6c. Add the pointer to the list of arguments. - vec_add(values, result_value->reg); + arg_values[arg_count++] = result_value->reg; break; case ABI_ARG_EXPAND: + case ABI_ARG_DIRECT_SPLIT_STRUCT: UNREACHABLE case ABI_ARG_DIRECT_PAIR: case ABI_ARG_IGNORE: + case ABI_ARG_DIRECT_COERCE_INT: case ABI_ARG_DIRECT_COERCE: case ABI_ARG_DIRECT: case ABI_ARG_EXPAND_COERCE: - case ABI_ARG_DIRECT_COERCE_INT: - case ABI_ARG_DIRECT_SPLIT_STRUCT: - TODO + break; } - // 7. We might have a failable indirect return and a normal return. + // 7. We might have an optional 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)); + value_set(&synthetic_return_param, tilde_emit_alloca(c, actual_return_type, 0), 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); + tilde_emit_parameter(c, arg_values, &arg_count, 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++) + for (unsigned i = 0; i < param_count; 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. + Expr *arg_expr = args[i]; 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) + if (arg_expr) { - // 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.splat_last) - { - // 9b. We splat the last type which is either a slice, an array or a dynamic array. - TODO - // llvm_emit_splatted_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); + tilde_emit_expr(c, &temp_value, arg_expr); } 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));*/ + assert(prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY); + TODO // tilde_emit_vararg_parameter(c, &temp_value, param, info, varargs, vararg_splat); } - tilde_emit_parameter(c, &values, vararg_info, &subarray, vararg_param); + + // 8b. Emit the parameter according to ABI rules. + tilde_emit_parameter(c, arg_values, &arg_count, info, &temp_value, param); } - else + + // 9. Typed varargs + + if (prototype->variadic == VARIADIC_RAW) { - // 9. Emit varargs. - for (unsigned i = param_count; i < arguments; i++) + if (prototype->abi_varargs) { - 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)); + // 9. Emit varargs. + unsigned index = 0; + ABIArgInfo **abi_varargs = prototype->abi_varargs; + foreach(Expr*, varargs) + { + tilde_emit_expr(c, &temp_value, val); + ABIArgInfo *info = abi_varargs[index]; + tilde_emit_parameter(c, arg_values, &arg_count, info, &temp_value, prototype->varargs[index]); + index++; + } + } + else + { + // 9. Emit varargs. + foreach(Expr*, varargs) + { + tilde_emit_expr(c, &temp_value, val); + REMINDER("Varargs should be expanded correctly"); + arg_values[arg_count++] = 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)); - } + EMIT_LOC(c, expr); + int inline_flag = 0; if (expr->call_expr.attr_force_noinline) { - // llvm_attribute_add_call(c, call_value, attribute_id.noinline, -1, 0); + inline_flag = -1; } else { - if (expr->call_expr.attr_force_inline || always_inline) - { - // llvm_attribute_add_call(c, call_value, attribute_id.alwaysinline, -1, 0); - } + inline_flag = expr->call_expr.attr_force_inline || always_inline ? 1 : 0; + } + TODO + // tilde_emit_raw_call(c, result_value, prototype, func_type, func, arg_values, arg_count, inline_flag, error_var, sret_return, &synthetic_return_param); + + // Emit the current stack into the thread local or things will get messed up. + if (c->debug.last_ptr) + { + tilde_store_to_ptr_raw_aligned(c, type_voidptr, c->debug.last_ptr, c->debug.stack_slot, + type_alloca_alignment(type_voidptr)); } - /* - for (unsigned i = 0; i < non_variadic_params; i++) + // 17i. The simple case here is where there is a normal return. + // In this case be_value already holds the result + return;*/ +} + +TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_Reg optional) +{ + assert(ref->kind == TBE_ADDRESS || ref->kind == TBE_ADDRESS_OPTIONAL); + + assert(optional || !IS_OPTIONAL(expr)); + // Special optimization of handling of optional + if (expr->expr_kind == EXPR_OPTIONAL) { - 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; - } + PUSH_OPT(); + + c->opt_var = TB_NULL_REG; + c->catch_block = TB_NULL_REG; + TBEValue result; + // Emit the fault type. + tilde_emit_expr(c, &result, expr->inner_expr); + TB_Reg err_val = result.reg; + // Store it in the optional + tilde_store_to_ptr(c, optional, &result); + // Set the result to an undef value + value_set(&result, TB_NULL_REG, ref->type); + + POP_OPT(); + + // If we had a catch block outside then we want to jump to that exit. + if (c->catch_block) tilde_emit_jump_to_optional_exit(c, err_val); + + // This return value will not be used. + return result; } - // 11. Process the return value. - switch (ret_info->kind) + PUSH_OPT(); + + + TB_Label assign_block = 0; + TB_Label rejump_block = 0; + + if (IS_OPTIONAL(expr)) { - 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) + assign_block = tb_basic_block_create(c->f); + assert(optional); + if (c->opt_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); + c->catch_block = rejump_block = tb_basic_block_create(c->f); } 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); + c->catch_block = assign_block; } + c->opt_var = optional; + } + else + { + c->opt_var = 0; + c->catch_block = 0; + } - // 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) + TBEValue value; + if (type_flat_is_vector(expr->type)) + { + tilde_emit_expr(c, &value, expr); + tilde_store(c, ref, &value); + } + else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + { + 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 + { + if (expr->expr_kind == EXPR_CALL) { - *result_value = (BEValue) { .type = type_void, .kind = BE_VALUE }; + tilde_emit_call_expr(c, &value, expr, ref); + } + else + { + tilde_emit_expr(c, &value, expr); + } + if (value.type != type_void) tilde_store(c, ref, &value); + } + + if (optional) + { + tilde_store_to_ptr_raw(c, optional, tilde_get_zero(c, type_anyerr), type_anyerr); + } + POP_OPT(); + + if (assign_block) + { + tb_inst_goto(c->f, assign_block); + if (rejump_block) + { + tilde_emit_block(c, rejump_block); + TB_Reg error = tilde_load_abi_alignment(c, type_anyerr, optional); + tilde_store_to_ptr_raw(c, c->opt_var, error, type_anyerr); + tb_inst_goto(c->f, c->catch_block); + } + tilde_emit_block(c, assign_block); + } + + return value; + +} + +static inline TB_Reg tilde_emit_add_int(TildeContext *c, Type *type, TB_Reg left, TB_Reg right, SourceSpan loc) +{ + if (active_target.feature.trap_on_wrap) + { + REMINDER("Unable to trap on wrap"); + TODO /*-- + LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); + LLVMValueRef args[2] = { left, right }; + assert(type->canonical == type); + LLVMValueRef add_res; + if (type_is_unsigned(type)) + { + add_res = llvm_emit_call_intrinsic(c, intrinsic_id.uadd_overflow, &type_to_use, 1, args, 2); + } + else + { + add_res = llvm_emit_call_intrinsic(c, intrinsic_id.sadd_overflow, &type_to_use, 1, args, 2); + } + LLVMValueRef result = llvm_emit_extract_value(c, add_res, 0); + LLVMValueRef ok = llvm_emit_extract_value(c, add_res, 1); + llvm_emit_panic_on_true(c, ok, "Addition overflow", loc); + return result; --*/ + } + + return tb_inst_add(c->f, left, right, (TB_ArithmaticBehavior)0); +} + +static inline TB_Reg tilde_emit_mult_int(TildeContext *c, Type *type, TB_Reg left, TB_Reg right, SourceSpan loc) +{ + if (active_target.feature.trap_on_wrap) + { + TODO /* + LLVMTypeRef type_to_use = llvm_get_type(c, type); + LLVMValueRef args[2] = { left, right }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + unsigned operation = type_is_integer_unsigned(type) ? intrinsic_id.umul_overflow + : intrinsic_id.smul_overflow; + LLVMValueRef call_res = llvm_emit_call_intrinsic(c, + operation, + types, + 1, + args, + 2); + LLVMValueRef val = llvm_emit_extract_value(c, call_res, 0); + LLVMValueRef ok = llvm_emit_extract_value(c, call_res, 1); + llvm_emit_panic_on_true(c, ok, "Integer multiplication overflow", loc); + return val;*/ + } + return tb_inst_mul(c->f, left, right, (TB_ArithmaticBehavior)0); +} + +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) +{ + bool lhs_signed, rhs_signed; + Type *vector_type = type_vector_type(lhs_type); + if (vector_type) + { + lhs_signed = type_is_signed(vector_type); + rhs_signed = type_is_signed(type_vector_type(rhs_type)); + } + else + { + assert(type_is_integer_or_bool_kind(lhs_type)); + lhs_signed = type_is_signed(lhs_type); + rhs_signed = type_is_signed(rhs_type); + } + if (lhs_signed != rhs_signed) + { + // Swap sides if needed. + if (!lhs_signed) + { + Type *temp = lhs_type; + lhs_type = rhs_type; + rhs_type = temp; + lhs_signed = true; + rhs_signed = false; + TB_Reg temp_val = lhs_value; + lhs_value = rhs_value; + rhs_value = temp_val; + switch (binary_op) + { + case BINARYOP_GE: + binary_op = BINARYOP_LE; + break; + case BINARYOP_GT: + binary_op = BINARYOP_LT; + break; + case BINARYOP_LE: + binary_op = BINARYOP_GE; + break; + case BINARYOP_LT: + binary_op = BINARYOP_GT; + break; + default: + break; + } + } + } + + if (!lhs_signed) + { + assert(lhs_signed == rhs_signed); + // Right and left side are both unsigned. + TB_Reg value; + switch (binary_op) + { + case BINARYOP_EQ: + value = tb_inst_cmp_eq(c->f, lhs_value, rhs_value); + break; + case BINARYOP_NE: + value = tb_inst_cmp_ne(c->f, lhs_value, rhs_value); + break; + case BINARYOP_GE: + value = tb_inst_cmp_ige(c->f, lhs_value, rhs_value, false); + break; + case BINARYOP_GT: + value = tb_inst_cmp_igt(c->f, lhs_value, rhs_value, false); + break; + case BINARYOP_LE: + value = tb_inst_cmp_ile(c->f, lhs_value, rhs_value, false); + break; + case BINARYOP_LT: + value = tb_inst_cmp_ilt(c->f, lhs_value, rhs_value, false); + break; + default: + UNREACHABLE + } + if (vector_type) + { + TODO // llvm_convert_vector_comparison(c, result, value, lhs_type, binary_op == BINARYOP_EQ); return; } - - // 17h. Assign the return param to be_value. - *result_value = synthetic_return_param; + value_set(result, value, type_bool); return; } -*/ - // 17i. The simple case here is where there is a normal return. - // In this case be_value already holds the result - return; + + + // Left side is signed. + TB_Reg comp_value; + TB_Reg check_value; + + switch (binary_op) + { + case BINARYOP_EQ: + comp_value = tb_inst_cmp_eq(c->f, lhs_value, rhs_value); + break; + case BINARYOP_NE: + comp_value = tb_inst_cmp_ne(c->f, lhs_value, rhs_value); + break; + case BINARYOP_GE: + comp_value = tb_inst_cmp_ige(c->f, lhs_value, rhs_value, true); + break; + case BINARYOP_GT: + comp_value = tb_inst_cmp_igt(c->f, lhs_value, rhs_value, true); + break; + case BINARYOP_LE: + comp_value = tb_inst_cmp_ile(c->f, lhs_value, rhs_value, true); + break; + case BINARYOP_LT: + comp_value = tb_inst_cmp_ilt(c->f, lhs_value, rhs_value, true); + break; + default: + UNREACHABLE + } + + // If right side is also signed then this is fine. + if (rhs_signed) + { + if (vector_type) + { + TODO // tilde_convert_vector_comparison(c, result, comp_value, lhs_type, binary_op == BINARYOP_EQ); + return; + } + value_set(result, comp_value, type_bool); + return; + } + + // Otherwise, special handling for left side signed, right side unsigned. + TB_Reg zero = tilde_get_zero(c, lhs_type); + switch (binary_op) + { + case BINARYOP_EQ: + // Only true if lhs >= 0 + check_value = tb_inst_cmp_ige(c->f, lhs_value, zero, true); + comp_value = tb_inst_and(c->f, check_value, comp_value); + break; + case BINARYOP_NE: + // Always true if lhs < 0 + check_value = tb_inst_cmp_ilt(c->f, lhs_value, zero, true); + comp_value = tb_inst_and(c->f, check_value, comp_value); + break; + case BINARYOP_GE: + // Only true if rhs >= 0 when regarded as a signed integer + check_value = tb_inst_cmp_ige(c->f, rhs_value, zero, true); + comp_value = tb_inst_and(c->f, check_value, comp_value); + break; + case BINARYOP_GT: + // Only true if rhs >= 0 when regarded as a signed integer + check_value = tb_inst_cmp_ige(c->f, rhs_value, zero, true); + comp_value = tb_inst_and(c->f, check_value, comp_value); + break; + case BINARYOP_LE: + // Always true if rhs < 0 when regarded as a signed integer + check_value = tb_inst_cmp_ilt(c->f, rhs_value, zero, true); + comp_value = tb_inst_or(c->f, check_value, comp_value); + break; + case BINARYOP_LT: + // Always true if rhs < 0 when regarded as a signed integer + check_value = tb_inst_cmp_ilt(c->f, rhs_value, zero, true); + comp_value = tb_inst_or(c->f, check_value, comp_value); + break; + default: + UNREACHABLE + } + if (vector_type) + { + TODO // tilde_convert_vector_comparison(c, result, comp_value, lhs_type, BINARYOP_EQ == binary_op); + return; + } + value_set(result, comp_value, type_bool); } -void tilde_emit_expr(TbContext *c, TBEValue *value, Expr *expr) + +void tilde_emit_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op) { + assert(binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ); + value_rvalue(c, lhs); + value_rvalue(c, rhs); + if (type_is_integer_or_bool_kind(lhs->type)) + { + tilde_emit_int_comp_raw(c, result, lhs->type, rhs->type, lhs->reg, rhs->reg, binary_op); + return; + } + if (type_is_pointer(lhs->type)) + { + TODO // tilde_emit_ptr_comparison(c, result, lhs, rhs, binary_op); + return; + } + if (type_is_float(lhs->type)) + { + TODO // tilde_emit_float_comp(c, result, lhs, rhs, binary_op, NULL); + return; + } + if (lhs->type->type_kind == TYPE_SUBARRAY) + { + TODO // tilde_emit_subarray_comp(c, result, lhs, rhs, binary_op); + return; + } + if (lhs->type->type_kind == TYPE_VECTOR) + { + Type *type = type_vector_type(lhs->type); + if (type_is_float(type)) + { + TODO // tilde_emit_float_comp(c, result, lhs, rhs, binary_op, lhs->type); + } + else + { + TODO // tilde_emit_int_comp_raw(c, result, lhs->type, rhs->type, lhs->reg, rhs->reg, binary_op); + } + return; + } + if (lhs->type->type_kind == TYPE_ARRAY) + { + TODO // tilde_emit_array_comp(c, result, lhs, rhs, binary_op); + return; + } + TODO +} + + +void tilde_emit_binary(TildeContext *c, TBEValue *be_value, Expr *expr, TBEValue *lhs_loaded, BinaryOp binary_op) +{ + // foo ?? bar + if (binary_op == BINARYOP_ELSE) + { + TODO // llvm_emit_else(c, be_value, expr); + return; + } + + // foo || bar and foo && bar + if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR) + { + TODO // llvm_emit_logical_and_or(c, be_value, expr, binary_op); + return; + } + + // Load if needed, otherwise use the already loaded. + TBEValue lhs; + if (lhs_loaded) + { + lhs = *lhs_loaded; + } + else + { + if (type_is_float(type_flatten(expr->type)) && (binary_op == BINARYOP_ADD || binary_op == BINARYOP_SUB)) + { + TODO // if (tilde_emit_fmuladd_maybe(c, be_value, expr, binary_op)) return; + } + tilde_emit_expr(c, &lhs, exprptr(expr->binary_expr.left)); + } + // We need the rvalue. + value_rvalue(c, &lhs); + + // Evaluate rhs + TBEValue rhs; + tilde_emit_expr(c, &rhs, exprptr(expr->binary_expr.right)); + value_rvalue(c, &rhs); + + EMIT_LOC(c, expr); + // Comparison <=> + if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ) + { + tilde_emit_comp(c, be_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_Reg val = TB_NULL_REG; + TB_Reg lhs_value = lhs.reg; + TB_Reg rhs_value = rhs.reg; + switch (binary_op) + { + case BINARYOP_ERROR: + UNREACHABLE + case BINARYOP_MULT: + if (is_float) + { + val = tb_inst_fmul(c->f, lhs_value, rhs_value); + break; + } + val = tilde_emit_mult_int(c, lhs_type, lhs_value, rhs_value, expr->span); + break; + case BINARYOP_SUB: + if (lhs_type->type_kind == TYPE_POINTER) + { + TODO /*--- + 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 = LLVMBuildNeg(c->builder, rhs_value, ""); + val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value); + */ + break; + } + if (is_float) + { + val = tb_inst_fsub(c->f, lhs_value, rhs_value); + break; + } + TODO // val = tilde_emit_sub_int(c, lhs_type, lhs_value, rhs_value, expr->span); + break; + case BINARYOP_ADD: + if (lhs_type->type_kind == TYPE_POINTER) + { + assert(type_is_integer(rhs_type)); + TODO // val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value); + break; + } + if (is_float) + { + val = tb_inst_fadd(c->f, lhs_value, rhs_value); + break; + } + val = tilde_emit_add_int(c, lhs_type, lhs_value, rhs_value, expr->span); + break; + case BINARYOP_DIV: + TODO // tilde_emit_trap_zero(c, rhs_type, rhs_value, "Division by zero.", expr->span); + 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_signed(lhs_type)); + break; + case BINARYOP_MOD: + TODO // tilde_emit_trap_zero(c, rhs_type, rhs_value, "% by zero.", expr->span); + if (type_is_float(lhs_type)) + { + TODO // val = LLVMBuildFRem(c->builder, lhs_value, rhs_value, "fmod"); + break; + } + val = tb_inst_mod(c->f, lhs_value, rhs_value, type_is_signed(lhs_type)); + break; + case BINARYOP_SHR: + TODO /*--- + rhs_value = llvm_zext_trunc(c, rhs_value, LLVMTypeOf(lhs_value)); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); + val = type_is_unsigned(lhs_type) + ? LLVMBuildLShr(c->builder, lhs_value, rhs_value, "lshr") + : LLVMBuildAShr(c->builder, lhs_value, rhs_value, "ashr"); + val = LLVMBuildFreeze(c->builder, val, ""); --*/ + break; + case BINARYOP_SHL: + /*--- + rhs_value = llvm_zext_trunc(c, rhs_value, LLVMTypeOf(lhs_value)); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); + val = LLVMBuildShl(c->builder, lhs_value, rhs_value, "shl"); + val = LLVMBuildFreeze(c->builder, val, ""); --*/ + 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: + val = tb_inst_xor(c->f, lhs_value, rhs_value); + break; + case BINARYOP_ELSE: + case BINARYOP_EQ: + case BINARYOP_NE: + case BINARYOP_GE: + case BINARYOP_GT: + case BINARYOP_LE: + case BINARYOP_LT: + 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: + // Handled elsewhere. + UNREACHABLE + } + assert(val); + value_set(be_value, val, expr->type); +} + +static void tilde_emit_binary_expr(TildeContext *c, TBEValue *be_value, Expr *expr) +{ + BinaryOp binary_op = expr->binary_expr.operator; + if (binary_op >= BINARYOP_ASSIGN && expr_is_vector_index(exprptr(expr->binary_expr.left))) + { + TODO // llvm_emit_vector_assign_expr(c, be_value, expr); + return; + } + if (binary_op > BINARYOP_ASSIGN) + { + BinaryOp base_op = binaryop_assign_base_op(binary_op); + assert(base_op != BINARYOP_ERROR); + TBEValue addr; + tilde_emit_expr(c, &addr, exprptr(expr->binary_expr.left)); + value_addr(c, &addr); + tilde_emit_binary(c, be_value, expr, &addr, base_op); + tilde_store(c, &addr, be_value); + return; + } + if (binary_op == BINARYOP_ASSIGN) + { + Expr *left = exprptr(expr->binary_expr.left); + tilde_emit_expr(c, be_value, left); + assert(value_is_addr(be_value)); + TB_Reg optional_ref = TB_NULL_REG; + if (left->expr_kind == EXPR_IDENTIFIER) + { + TODO + /*--- + optional_ref = decl_optional_ref(left->identifier_expr.decl); + be_value->kind = BE_ADDRESS;*/ + } + *be_value = tilde_emit_assign_expr(c, be_value, exprptr(expr->binary_expr.right), optional_ref); + return; + } + + tilde_emit_binary(c, be_value, expr, NULL, binary_op); +} + +void tilde_emit_expr(TildeContext *c, TBEValue *value, Expr *expr) +{ + EMIT_LOC(c, 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 NON_RUNTIME_EXPR: 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_CT_ARG: + case EXPR_ASM: + case EXPR_VASPLAT: + case EXPR_CT_CHECKS: 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); + tilde_emit_const_expr(c, value, expr); return; case EXPR_BINARY: - llvm_emit_binary_expr(c, value, expr); + tilde_emit_binary_expr(c, value, expr); return; - case EXPR_TERNARY: - gencontext_emit_ternary_expr(c, value, expr); + case EXPR_NOP: + value_set(value, TB_NULL_REG, type_void); 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); + value_set_decl(c, 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;*/ + default: + break; } - UNREACHABLE + TODO } -#endif \ No newline at end of file +void tilde_emit_cast(TildeContext *c, CastKind cast_kind, Expr *expr, TBEValue *value, Type *to_type, Type *from_type) +{ + Type *to_type_original = to_type; + to_type = type_flatten(to_type); + from_type = type_flatten(from_type); + + switch (cast_kind) + { + case CAST_BOOLVECINT: + TODO // llvm_emit_bool_to_intvec_cast(c, value, to_type, from_type); + return; + case CAST_ARRVEC: + TODO // llvm_emit_array_to_vector_cast(c, value, to_type, from_type); + return; + case CAST_PTRANY: + { + value_rvalue(c, value); + TODO /*-- + LLVMValueRef pointer = llvm_emit_bitcast(c, value->value, type_voidptr); + BEValue typeid; + llvm_emit_typeid(c, &typeid, from_type->pointer); + llvm_value_aggregate_two(c, value, to_type, pointer, typeid.value); + return;--*/ + } + case CAST_BSARRY: + TODO + /*-- + llvm_value_addr(c, value); + llvm_value_bitcast(c, value, to_type); + llvm_value_rvalue(c, value);--*/ + return; + /* + case CAST_BSINT: + llvm_value_addr(c, value); + llvm_value_bitcast(c, value, to_type); + llvm_value_rvalue(c, value); + return; + case CAST_EUINT: + case CAST_ERINT: + to_type = type_lowering(to_type); + from_type = type_lowering(from_type); + llvm_value_rvalue(c, value); + if (type_convert_will_trunc(to_type, from_type)) + { + value->value = LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "errinttrunc"); + } + else + { + value->value = type_is_signed(to_type) + ? LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "errsiext") + : LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "erruiext"); + + } + break; + case CAST_ANYPTR: + llvm_emit_any_pointer(c, value, value); + if (llvm_value_is_addr(value)) + { + value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), ""); + } + else + { + value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_type(c, to_type), ""); + } + break; + case CAST_XIERR: + to_type = type_lowering(to_type); + llvm_value_rvalue(c, value); + value->value = llvm_zext_trunc(c, value->value, llvm_get_type(c, to_type)); + break; + case CAST_ERROR: + UNREACHABLE + case CAST_STRPTR: + case CAST_PTRPTR: + llvm_value_rvalue(c, value); + value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr"); + break; + case CAST_PTRXI: + llvm_value_rvalue(c, value); + value->value = LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi"); + break; + case CAST_APTSA: + llvm_emit_arr_to_subarray_cast(c, value, to_type); + break; + case CAST_SASA: + assert(type_is_pointer(value->type->array.base)); + llvm_value_addr(c, value); + llvm_value_bitcast(c, value, to_type); + break; + case CAST_SAPTR: + llvm_emit_subarray_pointer(c, value, value); + if (value->type != to_type) + { + if (llvm_value_is_addr(value)) + { + value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), "saptr"); + } + else + { + value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), "saptr"); + } + } + break; + case CAST_EREU: + // This is a no op. + assert(type_lowering(to_type) == type_lowering(from_type)); + break; + case CAST_VECARR: + llvm_emit_vector_to_array_cast(c, value, to_type, from_type); + break; + case CAST_EUER: + TODO // gencontext_emit_value_bitcast(c, value->value, to_type, from_type); + case CAST_ERBOOL: + case CAST_EUBOOL: + { + BEValue zero; + llvm_value_set_int(c, &zero, type_anyerr, 0); + llvm_emit_int_comp(c, value, value, &zero, BINARYOP_NE); + break; + } + case CAST_PTRBOOL: + llvm_value_rvalue(c, value); + value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool"); + value->kind = BE_BOOLEAN; + break; + case CAST_BOOLINT: + llvm_value_rvalue(c, value); + value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "boolsi"); + value->kind = BE_VALUE; + break; + case CAST_FPBOOL: + llvm_value_rvalue(c, value); + value->value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, from_type), "fpbool"); + value->kind = BE_BOOLEAN; + break; + case CAST_BOOLBOOL: + value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "boolbool"); + value->kind = BE_BOOLEAN; + break; + case CAST_BOOLFP: + llvm_value_rvalue(c, value); + value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "boolfp"); + value->kind = BE_VALUE; + break; + case CAST_INTBOOL: + llvm_value_rvalue(c, value); + value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, from_type), "intbool"); + value->kind = type_kind_is_any_vector(value->type->type_kind) ? BE_BOOLVECTOR : BE_BOOLEAN; + break; + case CAST_FPFP: + llvm_value_rvalue(c, value); + value->value = type_convert_will_trunc(to_type, from_type) + ? LLVMBuildFPTrunc(c->builder, value->value, llvm_get_type(c, to_type), "fpfptrunc") + : LLVMBuildFPExt(c->builder, value->value, llvm_get_type(c, to_type), "fpfpext"); + break; + case CAST_FPSI: + llvm_value_rvalue(c, value); + value->value = LLVMBuildFPToSI(c->builder, value->value, llvm_get_type(c, to_type), "fpsi"); + break; + case CAST_FPUI: + llvm_value_rvalue(c, value); + value->value = LLVMBuildFPToUI(c->builder, value->value, llvm_get_type(c, to_type), "fpui"); + break; + case CAST_SISI: + llvm_value_rvalue(c, value); + value->value = type_convert_will_trunc(to_type, from_type) + ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "sisitrunc") + : LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "sisiext"); + break; + case CAST_SIUI: + llvm_value_rvalue(c, value); + value->value = type_convert_will_trunc(to_type, from_type) + ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "siuitrunc") + : LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "siuiext"); + break; + case CAST_SIFP: + llvm_value_rvalue(c, value); + value->value = LLVMBuildSIToFP(c->builder, value->value, llvm_get_type(c, to_type), "sifp"); + break; + case CAST_XIPTR: + llvm_value_rvalue(c, value); + value->value = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_type(c, to_type), "xiptr"); + break; + case CAST_UISI: + llvm_value_rvalue(c, value); + value->value = type_convert_will_trunc(to_type, from_type) + ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "uisitrunc") + : LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "uisiext"); + break; + case CAST_UIUI: + llvm_value_rvalue(c, value); + value->value = llvm_zext_trunc(c, value->value, llvm_get_type(c, to_type)); + break; + case CAST_UIFP: + llvm_value_rvalue(c, value); + value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "uifp"); + break; + case CAST_ENUMLOW: + llvm_value_rvalue(c, value); + break; + case CAST_STST: + llvm_value_addr(c, value); + value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), ""); + value->type = to_type; + return; + case CAST_INTENUM: + if (active_target.feature.safe_mode && c->builder != c->global_builder) + { + llvm_value_rvalue(c, value); + BEValue check; + Decl *decl = to_type_original->canonical->decl; + unsigned max = vec_size(decl->enums.values); + if (type_is_signed(value->type)) + { + scratch_buffer_clear(); + scratch_buffer_printf("Conversion to enum '%s' failed - tried to convert a negative value.", decl->name); + llvm_emit_int_comp_zero(c, &check, value, BINARYOP_LT); + llvm_emit_panic_on_true(c, check.value, scratch_buffer_to_string(), expr->span); + } + scratch_buffer_clear(); + scratch_buffer_printf("Conversion to enum '%s' failed - the value was greater than %u.", decl->name, max - 1); + LLVMValueRef val = llvm_const_int(c, value->type, max); + llvm_emit_int_comp_raw(c, &check, value->type, value->type, value->value, val, BINARYOP_GE); + llvm_emit_panic_on_true(c, check.value,scratch_buffer_to_string(), expr->span); + } + return; + case CAST_SABOOL: + llvm_value_fold_optional(c, value); + if (llvm_value_is_addr(value)) + { + value->value = llvm_emit_struct_gep_raw(c, + value->value, + llvm_get_type(c, value->type), + 1, + value->alignment, + &value->alignment); + } + else + { + value->value = llvm_emit_extract_value(c, value->value, 1); + } + value->type = type_usize->canonical; + llvm_value_rvalue(c, value); + llvm_emit_int_comp_zero(c, value, value, BINARYOP_NE); + break;*/ + default: + TODO + } + value->type = to_type; +} diff --git a/src/compiler/tilde_codegen_instr.c b/src/compiler/tilde_codegen_instr.c index 6ed61aa39..a28d94e9c 100644 --- a/src/compiler/tilde_codegen_instr.c +++ b/src/compiler/tilde_codegen_instr.c @@ -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 \ No newline at end of file diff --git a/src/compiler/tilde_codegen_stmt.c b/src/compiler/tilde_codegen_stmt.c index 9336d6adb..dbdb6d115 100644 --- a/src/compiler/tilde_codegen_stmt.c +++ b/src/compiler/tilde_codegen_stmt.c @@ -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(¤t)); + tilde_emit_stmt(c, ast_next(¤t)); } } -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(¤t)); + 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 \ No newline at end of file +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); + +} diff --git a/src/compiler/tilde_codegen_storeload.c b/src/compiler/tilde_codegen_storeload.c index 3dfa0ec86..500163eaa 100644 --- a/src/compiler/tilde_codegen_storeload.c +++ b/src/compiler/tilde_codegen_storeload.c @@ -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 diff --git a/src/compiler/tilde_codegen_type.c b/src/compiler/tilde_codegen_type.c index c6a844e54..a89a47ac2 100644 --- a/src/compiler/tilde_codegen_type.c +++ b/src/compiler/tilde_codegen_type.c @@ -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 \ No newline at end of file +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; +} diff --git a/src/compiler/tilde_codegen_value.c b/src/compiler/tilde_codegen_value.c index 74a24d488..f46a92e37 100644 --- a/src/compiler/tilde_codegen_value.c +++ b/src/compiler/tilde_codegen_value.c @@ -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 \ No newline at end of file diff --git a/src/compiler/tilde_internal.h b/src/compiler/tilde_internal.h index d38bd6c17..9b3205a25 100644 --- a/src/compiler/tilde_internal.h +++ b/src/compiler/tilde_internal.h @@ -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 -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 \ No newline at end of file +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); +} diff --git a/tb/tb.h b/tb/tb.h deleted file mode 100644 index 54a58cddb..000000000 --- a/tb/tb.h +++ /dev/null @@ -1,1077 +0,0 @@ -// -// _______ _ _ _ ____ ______ -// |__ __(_) | | | | _ \| ____| -// | | _| | __| | ___| |_) | |__ -// | | | | |/ _` |/ _ \ _ <| __| -// | | | | | (_| | __/ |_) | |____ -// |_| |_|_|\__,_|\___|____/|______| -// -// It's a relatively small compiler backend all -// behind a simple C API! -// -#ifndef _TILDEBACKEND_H_ -#define _TILDEBACKEND_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -// Windows likes it's secure functions, i kinda do too -// but only sometimes and this isn't one of them -#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#include -#include - -// https://semver.org/ -#define TB_VERSION_MAJOR 0 -#define TB_VERSION_MINOR 2 -#define TB_VERSION_PATCH 0 - -#ifndef TB_MAX_THREADS -#define TB_MAX_THREADS 16 -#endif - -// Per-thread -#ifndef TB_TEMPORARY_STORAGE_SIZE -#define TB_TEMPORARY_STORAGE_SIZE (1 << 20) -#endif - -#ifndef TB_MAX_FUNCTIONS -#define TB_MAX_FUNCTIONS (1 << 22) -#endif - -#define TB_API extern - -#define TB_HOST_UNKNOWN 0 -#define TB_HOST_X86_64 1 - -// If on, the labels aren't marked in the object file -// might save on performance at the cost of some assembly -// readability. -#define TB_STRIP_LABELS 1 - -#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) -#define TB_HOST_ARCH TB_HOST_X86_64 -#else -#define TB_HOST_ARCH TB_HOST_UNKNOWN -#endif - -typedef enum { - // No overflow will assume the value does not - // overflow and if it does this can be considered - // undefined behavior with unknown consequences. - // no signed wrap - TB_ASSUME_NSW, - // no unsigned wrap - TB_ASSUME_NUW, - - // Wrapping will allow the integer to safely wrap. - TB_CAN_WRAP, - - // Overflow check will throw an error if the result - // cannot be represented in the resulting type. - TB_SIGNED_TRAP_ON_WRAP, - TB_UNSIGNED_TRAP_ON_WRAP, - - // Saturated arithmatic will clamp the results in the - // event of overflow/underflow. - TB_SATURATED_UNSIGNED, - TB_SATURATED_SIGNED -} TB_ArithmaticBehavior; - -typedef enum { - TB_DEBUGFMT_NONE, - - TB_DEBUGFMT_DWARF, - TB_DEBUGFMT_CODEVIEW, - - TB_DEBUGFMT_COLINPILLED -} TB_DebugFormat; - -typedef enum { - TB_ARCH_UNKNOWN, - - TB_ARCH_X86_64, - // unsupported but planned - TB_ARCH_AARCH64 -} TB_Arch; - -typedef enum { - TB_SYSTEM_WINDOWS, - TB_SYSTEM_LINUX, - - // TODO(NeGate): Actually implement these lol - TB_SYSTEM_MACOS, - TB_SYSTEM_ANDROID -} TB_System; - -typedef enum { - TB_CDECL, - TB_STDCALL -} TB_CallingConv; - -typedef struct TB_FeatureSet { - struct { - bool sse3 : 1; - - bool popcnt : 1; - bool lzcnt : 1; - bool sse41 : 1; - bool sse42 : 1; - - bool clmul : 1; - bool f16c : 1; - - bool bmi1 : 1; - bool bmi2 : 1; - - bool avx : 1; - bool avx2 : 1; - } x64; - struct { - bool bf16 : 1; - } aarch64; -} TB_FeatureSet; - -typedef enum -{ - TB_BRANCH_HINT_NONE, - TB_BRANCH_HINT_LIKELY, - TB_BRANCH_HINT_UNLIKELY -} TB_BranchHint; - -typedef enum -{ - TB_LINKAGE_PUBLIC, - TB_LINKAGE_PRIVATE -} TB_Linkage; - -typedef enum -{ - // data is the default way to handle storage, this is usually - // passed onto the .data section - TB_STORAGE_DATA, - - // Thread local storage will have the values defined per thread - // and only accessible within that thread - TB_STORAGE_TLS -} TB_StorageClass; - -typedef enum -{ - TB_MEM_ORDER_RELAXED, - TB_MEM_ORDER_CONSUME, - TB_MEM_ORDER_ACQUIRE, - TB_MEM_ORDER_RELEASE, - TB_MEM_ORDER_ACQ_REL, - TB_MEM_ORDER_SEQ_CST, -} TB_MemoryOrder; - -typedef enum -{ - // no optimizer run - TB_OPT_O0, - - // run optimizer with all optimizations - TB_OPT_O1, - - // same as O1 but favors size, will aggresively deduplicate - // (at least that's the plan :P) - TB_OPT_SIZE, - - // same as O1 but favors speed, will aggresively unroll - // sometimes (at least that's the plan :P) - TB_OPT_SPEED, -} TB_OptLevel; - -typedef enum -{ - // FastISel - TB_ISEL_FAST, - TB_ISEL_COMPLEX -} TB_ISelMode; - -typedef enum -{ - TB_VOID, - // Boolean - TB_BOOL, - // Integers - TB_I8, - TB_I16, - TB_I32, - TB_I64, - // IEEE 754 Floating point - TB_F32, - TB_F64, - // Pointers - TB_PTR, - - TB_MAX_TYPES -} TB_DataTypeEnum; - -typedef enum -{ - TB_NULL = 0, - - /* metadata */ - TB_LINE_INFO, - TB_KEEPALIVE, - TB_POISON, - - TB_ICALL, /* internal use only, inline call */ - TB_CALL, /* standard function call */ - TB_VCALL, /* virtual call */ - TB_ECALL, /* extern call */ - - /* Memory operations */ - TB_STORE, - - TB_MEMCLR, - TB_MEMCPY, - TB_MEMSET, - TB_MEMCMP, - TB_INITIALIZE, - - /* Atomics */ - TB_ATOMIC_TEST_AND_SET, - TB_ATOMIC_CLEAR, - - TB_ATOMIC_XCHG, - TB_ATOMIC_ADD, - TB_ATOMIC_SUB, - TB_ATOMIC_AND, - TB_ATOMIC_XOR, - TB_ATOMIC_OR, - - TB_ATOMIC_CMPXCHG, /* These are always bundled together */ - TB_ATOMIC_CMPXCHG2, - TB_DEBUGBREAK, - - /* Terminators */ - TB_LABEL, - TB_GOTO, - TB_SWITCH, - TB_IF, - TB_RET, - TB_TRAP, - TB_UNREACHABLE, - - /* Load */ - TB_LOAD, - - /* Pointers */ - TB_RESTRICT, - TB_LOCAL, - TB_PARAM_ADDR, - - TB_PARAM, - TB_FUNC_ADDRESS, - TB_EXTERN_ADDRESS, - TB_GLOBAL_ADDRESS, - - TB_MEMBER_ACCESS, - TB_ARRAY_ACCESS, - - /* Immediates */ - TB_UNSIGNED_CONST, - TB_SIGNED_CONST, - TB_FLOAT_CONST, - TB_STRING_CONST, - - /* Conversions */ - TB_TRUNCATE, - TB_FLOAT_EXT, - TB_SIGN_EXT, - TB_ZERO_EXT, - TB_INT2PTR, - TB_PTR2INT, - TB_INT2FLOAT, - TB_FLOAT2INT, - TB_BITCAST, - - /* Select */ - TB_SELECT, - - /* Unary operations */ - TB_NOT, - TB_NEG, - - /* Integer arithmatic */ - TB_AND, - TB_OR, - TB_XOR, - TB_ADD, - TB_SUB, - TB_MUL, - - TB_SHL, - TB_SHR, - TB_SAR, - TB_UDIV, - TB_SDIV, - TB_UMOD, - TB_SMOD, - - /* Float arithmatic */ - TB_FADD, - TB_FSUB, - TB_FMUL, - TB_FDIV, - - /* Comparisons */ - TB_CMP_EQ, - TB_CMP_NE, - TB_CMP_SLT, - TB_CMP_SLE, - TB_CMP_ULT, - TB_CMP_ULE, - TB_CMP_FLT, - TB_CMP_FLE, - - /* PHI */ - // NOTE(NeGate): phi1 and phi2 are just to avoid - // using extra space for the common cases - TB_PHI1, - TB_PHI2, - TB_PHIN, - - // NOTE(NeGate): only used internally, if you - // see one in normal IR things went wrong in - // an optimization pass - TB_PASS, - - // variadic - TB_VA_START, - - // x86 intrinsics - TB_X86INTRIN_SQRT, - TB_X86INTRIN_RSQRT, -} TB_NodeTypeEnum; -typedef uint8_t TB_NodeType; - -#define TB_IS_NODE_SIDE_EFFECT(type) ((type) >= TB_LINE_INFO && (type) <= TB_DEBUGBREAK) -#define TB_IS_NODE_TERMINATOR(type) ((type) >= TB_LABEL && (type) <= TB_RET) - -#define TB_IS_INTEGER_TYPE(x) ((x) >= TB_I8 && (x) <= TB_I64) -#define TB_IS_FLOAT_TYPE(x) ((x) >= TB_F32 && (x) <= TB_F64) -#define TB_IS_POINTER_TYPE(x) ((x) == TB_PTR) - -typedef struct { - uint8_t type; - - // 2^N where N is the width value. - // Only integers and floats can be wide. - uint8_t width; -} TB_DataType; - -typedef int TB_Label; - -typedef struct { - int32_t key; - TB_Label value; -} TB_SwitchEntry; - -// just represents some region of bytes, usually in file parsing crap -typedef struct { - size_t length; - uint8_t* data; -} TB_Slice; - -// represents byte counts -typedef uint32_t TB_CharUnits; - -typedef unsigned int TB_AttributeID; -typedef unsigned int TB_FileID; -typedef unsigned int TB_FunctionID; -typedef unsigned int TB_ExternalID; // 0 means NULL -typedef unsigned int TB_GlobalID; -typedef unsigned int TB_InitializerID; - -typedef struct TB_Module TB_Module; -typedef struct TB_Function TB_Function; -typedef struct TB_AttribList TB_AttribList; -typedef struct TB_FunctionPrototype TB_FunctionPrototype; - -// references to a node within a TB_Function -// these are virtual registers so they don't necessarily -// map to any hardware but instead represent some operation -typedef int TB_Reg, TB_Register; - -#define TB_NULL_REG ((TB_Reg)0) -#define TB_REG_MAX ((TB_Reg)INT_MAX) - -typedef struct TB_Node { - TB_NodeType type; - TB_DataType dt; - TB_Reg next; - TB_AttribList* first_attrib; - - union { - struct TB_NodeUint { - uint64_t value; - } uint; - struct TB_NodeSint { - int64_t value; - } sint; - struct TB_NodeFloat { - double value; - } flt; - struct TB_NodeString { - size_t length; - const char* data; - } string; - struct TB_NodeFunction { - const TB_Function* value; - } func; - struct TB_NodeExtern { - TB_ExternalID value; - } external; - struct TB_NodeGlobal { - TB_GlobalID value; - } global; - struct TB_NodeLine { - TB_FileID file; - int line; - } line_info; - struct TB_NodeMemberAccess { - TB_Reg base; - int32_t offset; - } member_access; - struct TB_NodeArrayAccess { - TB_Reg base; - TB_Reg index; - TB_CharUnits stride; - } array_access; - struct TB_NodePtrdiff { - TB_Reg a; - TB_Reg b; - TB_CharUnits stride; - } ptrdiff; - struct TB_NodeParam { - uint32_t id; - TB_CharUnits size; - } param; - struct TB_NodeParamAddr { - TB_Reg param; - - TB_CharUnits size; - TB_CharUnits alignment; - } param_addr; - struct TB_NodeLocal { - TB_CharUnits size; - TB_CharUnits alignment; - } local; - struct TB_NodeUnary { - TB_Reg src; - } unary; - struct TB_NodeIArith { - TB_Reg a; - TB_Reg b; - TB_ArithmaticBehavior arith_behavior; - } i_arith; - struct TB_NodeFArith { - TB_Reg a; - TB_Reg b; - } f_arith; - struct TB_NodeCompare { - TB_Reg a; - TB_Reg b; - TB_DataType dt; - } cmp; - struct TB_NodeSelect { - TB_Reg a; - TB_Reg b; - TB_Reg cond; - } select; - struct TB_NodeLoad { - TB_Reg address; - // this is only here to make load and store - // payloads match in data layout... just because - TB_Reg _; - TB_CharUnits alignment; - bool is_volatile; - } load; - struct TB_NodeStore { - TB_Reg address; - TB_Reg value; - TB_CharUnits alignment; - bool is_volatile; - } store; - struct TB_NodeAtomicRMW { - TB_Reg addr; - TB_Reg src; - TB_MemoryOrder order; - - // NOTE(NeGate): this is used for fail - TB_MemoryOrder order2; - } atomic; - struct TB_NodeReturn { - TB_Reg value; - } ret; - struct TB_NodePass { - TB_Reg value; - } pass; - struct TB_NodePhi1 { - TB_Reg a_label; - TB_Reg a; - } phi1; - struct TB_NodePhi2 { - TB_Reg a_label; - TB_Reg a; - TB_Reg b_label; - TB_Reg b; - } phi2; - struct TB_NodePhi { - int param_start, param_end; - } phin; - struct TB_NodeLabel { - TB_Label id; - TB_Reg terminator; - } label; - struct TB_NodeIf { - TB_Reg cond; - TB_Label if_true; - TB_Label if_false; - } if_; - struct TB_NodeGoto { - TB_Label label; - } goto_; - struct TB_NodeExternCall { - int param_start, param_end; - TB_ExternalID target; - } ecall; - struct TB_NodeDynamicCall { - int param_start, param_end; - TB_Reg target; - } vcall; - struct TB_NodeFunctionCall { - int param_start, param_end; - const TB_Function* target; - } call; - struct TB_NodeSwitch { - TB_Reg key; - TB_Label default_label; - int entries_start, entries_end; - } switch_; - struct TB_NodeMemoryOp { - TB_Reg dst; - TB_Reg src; - TB_Reg size; - TB_CharUnits align; - } mem_op; - struct TB_NodeMemoryClear { - TB_Reg dst; - TB_CharUnits size; - TB_CharUnits align; - } clear; - struct TB_NodeInitialize { - TB_Reg addr; - TB_InitializerID id; - } init; - }; -} TB_Node; -static_assert(sizeof(TB_Node) <= 32, "sizeof(TB_Node) <= 32"); - -// represents the atomic cmpxchg result since it's two values -typedef struct { - TB_Reg success; - TB_Reg old_value; -} TB_CmpXchgResult; - -typedef struct { - size_t count; - struct TB_Loop { - int parent_loop; - - // the terminator of the header will exit - TB_Register header; - - // this is where the contents of the loop begin - TB_Register body; - - // this is not part of the loop but instead where - // the loop goes on exit - TB_Register exit; - } loops[]; -} TB_LoopInfo; - -typedef enum { - TB_OBJECT_RELOC_NONE, // how? - - // Target independent - TB_OBJECT_RELOC_ADDR32, - TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms - TB_OBJECT_RELOC_SECREL, - TB_OBJECT_RELOC_SECTION, - - // x64 only - TB_OBJECT_RELOC_REL32, // relative 32bit displacement - TB_OBJECT_RELOC_REL32_1, // plus 1 - TB_OBJECT_RELOC_REL32_2, // plus 2 - TB_OBJECT_RELOC_REL32_3, // and so on - TB_OBJECT_RELOC_REL32_4, // ... - TB_OBJECT_RELOC_REL32_5, - - // Aarch64 only - TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions - TB_OBJECT_RELOC_REL21, // for ADR instructions - - // TODO(NeGate): fill in the rest of this later -} TB_ObjectRelocType; - -typedef struct { - TB_ObjectRelocType type; - uint32_t symbol_index; - size_t virtual_address; -} TB_ObjectReloc; - -typedef struct { - TB_Slice name; - - size_t virtual_address; - size_t virtual_size; - - // You can have a virtual size without having a raw - // data size, that's how the BSS section works - TB_Slice raw_data; - - size_t relocation_count; - TB_ObjectReloc* relocations; -} TB_ObjectSection; - -typedef enum { - TB_OBJECT_SYMBOL_SECTION -} TB_ObjectSymbolType; - -typedef struct { - TB_Slice name; -} TB_ObjectSymbol; - -typedef enum { - TB_OBJECT_FILE_UNKNOWN, - - TB_OBJECT_FILE_COFF, - TB_OBJECT_FILE_ELF64 -} TB_ObjectFileType; - -typedef struct { - TB_ObjectFileType type; - TB_Arch arch; - - size_t symbol_count; - TB_ObjectSymbol* symbols; - - size_t section_count; - TB_ObjectSection sections[]; -} TB_ObjectFile; - -typedef struct { - size_t object_file_count; - - // Name table maps to the object files directly - char** object_file_names; - TB_Slice object_files[]; -} TB_ArchiveFile; - -// ******************************* -// Public macros -// ******************************* -#ifdef __cplusplus -#define TB_TYPE_VOID TB_DataType{ TB_VOID } - -#define TB_TYPE_I8 TB_DataType{ TB_I8 } -#define TB_TYPE_I16 TB_DataType{ TB_I16 } -#define TB_TYPE_I32 TB_DataType{ TB_I32 } -#define TB_TYPE_I64 TB_DataType{ TB_I64 } - -#define TB_TYPE_F32 TB_DataType{ TB_F32 } -#define TB_TYPE_F64 TB_DataType{ TB_F64 } - -#define TB_TYPE_BOOL TB_DataType{ TB_BOOL } -#define TB_TYPE_PTR TB_DataType{ TB_PTR } - -#else - -#define TB_TYPE_VOID (TB_DataType){ TB_VOID, 0 } -#define TB_TYPE_I8 (TB_DataType){ TB_I8, 0 } -#define TB_TYPE_I16 (TB_DataType){ TB_I16, 0 } -#define TB_TYPE_I32 (TB_DataType){ TB_I32, 0 } -#define TB_TYPE_I64 (TB_DataType){ TB_I64, 0 } - -#define TB_TYPE_F32 (TB_DataType){ TB_F32, 0 } -#define TB_TYPE_F64 (TB_DataType){ TB_F64, 0 } -#define TB_TYPE_BOOL (TB_DataType){ TB_BOOL, 0 } -#define TB_TYPE_PTR (TB_DataType){ TB_PTR, 0 } - -#endif - -typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...); - -//////////////////////////////// -// Module management -//////////////////////////////// -// Creates a module with the correct target and settings -TB_API TB_Module* tb_module_create(TB_Arch target_arch, TB_System target_system, TB_DebugFormat debug_fmt, const TB_FeatureSet* features); - -// Validates IR & compiles the function into machine code. -// For isel_mode, TB_ISEL_FAST will compile faster but worse codegen -// TB_ISEL_COMPLEX will compile slower but better codegen -// -// returns false if it fails. -TB_API bool tb_module_compile_func(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode); - -// Frees all resources for the TB_Module and it's functions, globals and -// compiled code. -TB_API void tb_module_destroy(TB_Module* m); - -// When targetting windows & thread local storage, you'll need to bind a tls index -// which is usually just a global that the runtime support has initialized, if you -// dont and the tls_index is used, it'll crash -TB_API void tb_module_set_tls_index(TB_Module* m, TB_ExternalID e); - -// Exports an object file with all the machine code and symbols generated. -TB_API bool tb_module_export(TB_Module* m, const char* path); - -// For isel_mode, TB_ISEL_FAST will compile faster but worse codegen -// TB_ISEL_COMPLEX will compile slower but better codegen -TB_API void tb_module_export_jit(TB_Module* m, TB_ISelMode isel_mode); - -TB_API void* tb_module_get_jit_func_by_name(TB_Module* m, const char* name); -TB_API void* tb_module_get_jit_func_by_id(TB_Module* m, size_t i); -TB_API void* tb_module_get_jit_func(TB_Module* m, TB_Function* f); - -// Binds an external to an address -TB_API bool tb_jit_import(TB_Module* m, const char* name, void* address); - -TB_API TB_ExternalID tb_extern_create(TB_Module* m, const char* name); -TB_API TB_FileID tb_file_create(TB_Module* m, const char* path); - -//////////////////////////////// -// Function Prototypes -//////////////////////////////// -// creates a function prototype used to define a function's parameters and -// return type. -// -// function prototypes do not get freed individually and last for the entire run -// of the backend, they can also be reused for multiple functions which have -// matching signatures. -TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv conv, TB_DataType return_dt, int num_params, bool has_varargs); - -// adds a parameter to the function prototype, TB doesn't support struct -// parameters so the frontend must lower them to pointers or any other type -// depending on their preferred ABI. -TB_API void tb_prototype_add_param(TB_FunctionPrototype* p, TB_DataType dt); - -// same as tb_prototype_add_param(...) but takes an array -TB_API void tb_prototype_add_params(TB_FunctionPrototype* p, size_t count, const TB_DataType* dt); - -// adds a parameter to the function prototype, TB doesn't support struct -// parameters so the frontend must lower them to pointers or any other type -// depending on their preferred ABI. -TB_API TB_Function* tb_prototype_build(TB_Module* m, TB_FunctionPrototype* p, const char* name, TB_Linkage linkage); - -//////////////////////////////// -// Constant Initializers -//////////////////////////////// -// NOTE: the max objects is a cap and thus it can be bigger than the actual -// number used. -TB_API TB_InitializerID tb_initializer_create(TB_Module* m, size_t size, size_t align, size_t max_objects); - -// returns a buffer which the user can fill to then have represented in the -// initializer -TB_API void* tb_initializer_add_region(TB_Module* m, TB_InitializerID id, size_t offset, size_t size); - -// places a relocation for a global at offset, the size of the relocation -// depends on the pointer size -TB_API void tb_initializer_add_global(TB_Module* m, TB_InitializerID id, size_t offset, TB_GlobalID global); -TB_API void tb_initializer_add_function(TB_Module* m, TB_InitializerID id, size_t offset, TB_FunctionID func); -TB_API void tb_initializer_add_extern(TB_Module* m, TB_InitializerID id, size_t offset, TB_ExternalID external); - -//////////////////////////////// -// Constant Initializers -//////////////////////////////// -TB_API TB_GlobalID tb_global_create(TB_Module* m, const char* name, TB_StorageClass storage, TB_Linkage linkage); -TB_API void tb_global_set_initializer(TB_Module* m, TB_GlobalID global, TB_InitializerID initializer); - -//////////////////////////////// -// Function Attributes -//////////////////////////////// -// These are parts of a function that describe metadata for instructions - -// restrict is applied onto loads and store operations meaning that the pointer -// being accessed doesn't alias with any of the other pointers within the scope -TB_API TB_AttributeID tb_function_attrib_restrict(TB_Function* f, TB_AttributeID scope); - -// This defines a some scope which can have a set of restrict pointers defined -// in it -TB_API TB_AttributeID tb_function_attrib_scope(TB_Function* f, TB_AttributeID parent_scope); - -// places an attribute on a function, note that there's no limit to how many -// registers can share an attribute -TB_API void tb_function_append_attrib(TB_Function* f, TB_Reg r, TB_AttributeID a); - -//////////////////////////////// -// Function IR Generation -//////////////////////////////// -// the user_data is expected to be a valid FILE* -TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...); - -// this only allows for power of two vector types -TB_API TB_DataType tb_vector_type(TB_DataTypeEnum type, int width); - -TB_API TB_Function* tb_function_clone(TB_Module* m, TB_Function* f, const char* name); -TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data); - -// Prints out the IR in the GraphViz format -TB_API void tb_function_print_cfg(TB_Function* f, TB_PrintCallback callback, void* user_data); -TB_API void tb_function_free(TB_Function* f); - -TB_API TB_Label tb_inst_get_current_label(TB_Function* f); -TB_API void tb_inst_loc(TB_Function* f, TB_FileID file, int line); - -TB_API void tb_inst_set_scope(TB_Function* f, TB_AttributeID scope); -TB_API TB_AttributeID tb_inst_get_scope(TB_Function* f); - -TB_API void tb_inst_debugbreak(TB_Function* f); - -TB_API TB_Reg tb_inst_param(TB_Function* f, int param_id); -TB_API TB_Reg tb_inst_param_addr(TB_Function* f, int param_id); - -TB_API TB_Reg tb_inst_fpxt(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_sxt(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_zxt(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_trunc(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_int2ptr(TB_Function* f, TB_Reg src); -TB_API TB_Reg tb_inst_ptr2int(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_int2float(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_float2int(TB_Function* f, TB_Reg src, TB_DataType dt); -TB_API TB_Reg tb_inst_bitcast(TB_Function* f, TB_Reg src, TB_DataType dt); - -TB_API TB_Reg tb_inst_local(TB_Function* f, uint32_t size, TB_CharUnits align); -TB_API TB_Reg tb_inst_load(TB_Function* f, TB_DataType dt, TB_Reg addr, TB_CharUnits align); -TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Reg addr, TB_Reg val, TB_CharUnits align); - -TB_API TB_Reg tb_inst_volatile_load(TB_Function* f, TB_DataType dt, TB_Reg addr, TB_CharUnits alignment); -TB_API void tb_inst_volatile_store(TB_Function* f, TB_DataType dt, TB_Reg addr, TB_Reg val, TB_CharUnits alignment); - -TB_API TB_Reg tb_inst_bool(TB_Function* f, bool imm); -TB_API TB_Reg tb_inst_ptr(TB_Function* f, uint64_t imm); -TB_API TB_Reg tb_inst_sint(TB_Function* f, TB_DataType dt, int64_t imm); -TB_API TB_Reg tb_inst_uint(TB_Function* f, TB_DataType dt, uint64_t imm); -TB_API TB_Reg tb_inst_float(TB_Function* f, TB_DataType dt, double imm); -TB_API TB_Reg tb_inst_cstring(TB_Function* f, const char* str); -TB_API TB_Reg tb_inst_string(TB_Function* f, size_t len, const char* str); - -// Applies an initializer to a memory region -TB_API void tb_inst_initialize_mem(TB_Function* f, TB_Reg addr, TB_InitializerID src); - -// Broadcasts 'val' across 'count' elements starting 'dst' -TB_API void tb_inst_memset(TB_Function* f, TB_Reg dst, TB_Reg val, TB_Reg count, TB_CharUnits align); - -// performs a copy of 'count' elements from one memory location to another -// both locations cannot overlap. -TB_API void tb_inst_memcpy(TB_Function* f, TB_Reg dst, TB_Reg src, TB_Reg count, TB_CharUnits align); - -// Clears a memory region to zeroes -TB_API void tb_inst_memclr(TB_Function* f, TB_Reg addr, TB_CharUnits size, TB_CharUnits align); - -// result = base + (index * stride) -TB_API TB_Reg tb_inst_array_access(TB_Function* f, TB_Reg base, TB_Reg index, uint32_t stride); - -// result = base + offset -// where base is a pointer -TB_API TB_Reg tb_inst_member_access(TB_Function* f, TB_Reg base, int32_t offset); - -TB_API TB_Reg tb_inst_get_func_address(TB_Function* f, const TB_Function* target); -TB_API TB_Reg tb_inst_get_extern_address(TB_Function* f, TB_ExternalID target); -TB_API TB_Reg tb_inst_get_global_address(TB_Function* f, TB_GlobalID target); - -// Performs a conditional select between two values, if the operation is -// performed wide then the cond is expected to be the same type as a and b where -// the condition is resolved as true if the MSB (per component) is 1. -// -// result = cond ? a : b -// a, b must match in type -TB_API TB_Reg tb_inst_select(TB_Function* f, TB_Reg cond, TB_Reg a, TB_Reg b); - -// Integer arithmatic -TB_API TB_Reg tb_inst_add(TB_Function* f, TB_Reg a, TB_Reg b, TB_ArithmaticBehavior arith_behavior); -TB_API TB_Reg tb_inst_sub(TB_Function* f, TB_Reg a, TB_Reg b, TB_ArithmaticBehavior arith_behavior); -TB_API TB_Reg tb_inst_mul(TB_Function* f, TB_Reg a, TB_Reg b, TB_ArithmaticBehavior arith_behavior); -TB_API TB_Reg tb_inst_div(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); -TB_API TB_Reg tb_inst_mod(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); - -// Bitwise operations -TB_API TB_Reg tb_inst_not(TB_Function* f, TB_Reg n); -TB_API TB_Reg tb_inst_neg(TB_Function* f, TB_Reg n); -TB_API TB_Reg tb_inst_and(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_or(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_xor(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_sar(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_shl(TB_Function* f, TB_Reg a, TB_Reg b, TB_ArithmaticBehavior arith_behavior); -TB_API TB_Reg tb_inst_shr(TB_Function* f, TB_Reg a, TB_Reg b); - -// Atomics -// By default you can use TB_MEM_ORDER_SEQ_CST for the memory order to get -// correct but possibly slower results on certain platforms (those with relaxed -// memory models). -TB_API TB_Reg tb_inst_atomic_test_and_set(TB_Function* f, TB_Reg addr, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_clear(TB_Function* f, TB_Reg addr, TB_MemoryOrder order); - -// All atomic operations here return the old value and the operations are -// performed in the same data type as 'src' with alignment of 'addr' being -// the natural alignment of 'src' -TB_API TB_Reg tb_inst_atomic_xchg(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_add(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_sub(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_and(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_xor(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); -TB_API TB_Reg tb_inst_atomic_or(TB_Function* f, TB_Reg addr, TB_Reg src, TB_MemoryOrder order); - -// if (*addr == expected) { -// old_value = atomic_xchg(addr, desired); -// return { true, old_value }; -// } else { -// return { false }; -// } -TB_API TB_CmpXchgResult tb_inst_atomic_cmpxchg(TB_Function* f, TB_Reg addr, TB_Reg expected, TB_Reg desired, TB_MemoryOrder succ, TB_MemoryOrder fail); - -// Float math -TB_API TB_Reg tb_inst_fadd(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_fsub(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_fmul(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_fdiv(TB_Function* f, TB_Reg a, TB_Reg b); - -// Comparisons -TB_API TB_Reg tb_inst_cmp_eq(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_cmp_ne(TB_Function* f, TB_Reg a, TB_Reg b); - -TB_API TB_Reg tb_inst_cmp_ilt(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); -TB_API TB_Reg tb_inst_cmp_ile(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); -TB_API TB_Reg tb_inst_cmp_igt(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); -TB_API TB_Reg tb_inst_cmp_ige(TB_Function* f, TB_Reg a, TB_Reg b, bool signedness); - -TB_API TB_Reg tb_inst_cmp_flt(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_cmp_fle(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_cmp_fgt(TB_Function* f, TB_Reg a, TB_Reg b); -TB_API TB_Reg tb_inst_cmp_fge(TB_Function* f, TB_Reg a, TB_Reg b); - -TB_API TB_Reg tb_inst_restrict(TB_Function* f, TB_Reg value); - -// General intrinsics -TB_API TB_Reg tb_inst_va_start(TB_Function* f, TB_Reg a); - -// x86 Intrinsics -TB_API TB_Reg tb_inst_x86_sqrt(TB_Function* f, TB_Reg a); -TB_API TB_Reg tb_inst_x86_rsqrt(TB_Function* f, TB_Reg a); - -// Control flow -TB_API TB_Reg tb_inst_call(TB_Function* f, TB_DataType dt, const TB_Function* target, size_t param_count, const TB_Reg* params); -TB_API TB_Reg tb_inst_vcall(TB_Function* f, TB_DataType dt, TB_Reg target, size_t param_count, const TB_Reg* params); -TB_API TB_Reg tb_inst_ecall(TB_Function* f, TB_DataType dt, const TB_ExternalID target, size_t param_count, const TB_Reg* params); - -TB_API TB_Label tb_inst_new_label_id(TB_Function* f); -TB_API TB_Reg tb_inst_phi2(TB_Function* f, TB_Label a_label, TB_Reg a, TB_Label b_label, TB_Reg b); -TB_API TB_Reg tb_inst_label(TB_Function* f, TB_Label id); -TB_API void tb_inst_goto(TB_Function* f, TB_Label id); -TB_API TB_Reg tb_inst_if(TB_Function* f, TB_Reg cond, TB_Label if_true, TB_Label if_false); -TB_API void tb_inst_switch(TB_Function* f, TB_DataType dt, TB_Reg key, TB_Label default_label, size_t entry_count, const TB_SwitchEntry* entries); -TB_API void tb_inst_ret(TB_Function* f, TB_Reg value); - -//////////////////////////////// -// Optimizer -//////////////////////////////// -// Applies single function optimizations until it runs out -TB_API bool tb_function_optimize(TB_Function* f); - -// Applies whole program optimizations until it runs out -TB_API bool tb_module_optimize(TB_Module* m); - -// analysis -TB_API TB_LoopInfo* tb_function_get_loop_info(TB_Function* f); - -// passes -TB_API bool tb_opt_mem2reg(TB_Function* f); -TB_API bool tb_opt_subexpr_elim(TB_Function* f); -TB_API bool tb_opt_dead_expr_elim(TB_Function* f); -TB_API bool tb_opt_dead_block_elim(TB_Function* f); -TB_API bool tb_opt_fold(TB_Function* f); -TB_API bool tb_opt_load_elim(TB_Function* f); -TB_API bool tb_opt_hoist_invariants(TB_Function* f); -TB_API bool tb_opt_inline(TB_Function* f); -TB_API bool tb_opt_hoist_locals(TB_Function* f); -TB_API bool tb_opt_canonicalize(TB_Function* f); -TB_API bool tb_opt_deshort_circuit(TB_Function* f); -TB_API bool tb_opt_remove_pass_node(TB_Function* f); -TB_API bool tb_opt_strength_reduction(TB_Function* f); -TB_API bool tb_opt_compact_dead_regs(TB_Function* f); -TB_API bool tb_opt_copy_elision(TB_Function* f); - -// analysis -TB_API TB_LoopInfo* tb_function_get_loop_info(TB_Function* f); - -//////////////////////////////// -// IR access -//////////////////////////////// -TB_API TB_FunctionID tb_function_get_id(TB_Module* m, TB_Function* f); -TB_API TB_Function* tb_function_from_id(TB_Module* m, TB_FunctionID id); - -TB_API TB_Reg tb_node_get_last_register(TB_Function* f); - -TB_API TB_Node* tb_function_get_node(TB_Function* f, TB_Reg r); - -// either an unsigned or signed constant -TB_API bool tb_node_is_constant_int(TB_Function* f, TB_Reg r, uint64_t imm); - -// returns true if it's a signed or unsigned constant -// in which case *imm is the raw bits and *is_signed is -// signedness -// -// notes: -// imm cannot be NULL -// is_signed can be NULL -TB_API bool tb_node_get_constant_int(TB_Function* f, TB_Reg r, uint64_t* imm, bool* is_signed); - -// Returns the size and alignment of a LOCAL node, both must -// be valid addresses -TB_API void tb_get_function_get_local_info(TB_Function* f, TB_Reg r, int* size, int* align); - -// is an IF node? -TB_API bool tb_node_is_conditional(TB_Function* f, TB_Reg r); - -// is an IF, GOTO, RET, SWITCH, or LABEL node? -TB_API bool tb_node_is_terminator(TB_Function* f, TB_Reg r); - -TB_API bool tb_node_is_label(TB_Function* f, TB_Reg r); - -TB_API TB_Reg tb_node_store_get_address(TB_Function* f, TB_Reg r); -TB_API TB_Reg tb_node_store_get_value(TB_Function* f, TB_Reg r); - -TB_API TB_Reg tb_node_load_get_address(TB_Function* f, TB_Reg r); - -// These work for any floating point, comparison, or integer arithmatic ops -TB_API TB_Reg tb_node_arith_get_left(TB_Function* f, TB_Reg r); -TB_API TB_Reg tb_node_arith_get_right(TB_Function* f, TB_Reg r); - -//////////////////////////////// -// Objects -//////////////////////////////// -TB_ArchiveFile* tb_archive_parse_lib(const TB_Slice file); -void tb_archive_free(TB_ArchiveFile* archive); - -TB_ObjectFile* tb_object_parse_coff(const TB_Slice file); -void tb_object_free(TB_ObjectFile* obj); - -#ifdef __cplusplus -} -#endif -#endif /* _TINYBACKEND_H_ */ diff --git a/tilde-backend b/tilde-backend new file mode 160000 index 000000000..802936760 --- /dev/null +++ b/tilde-backend @@ -0,0 +1 @@ +Subproject commit 80293676077f474cb76b8bc918c71590f7412dc6