mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Initial add of tilde backend.
This commit is contained in:
committed by
Christoffer Lerno
parent
6da6288ad8
commit
e284d49dd9
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -196,7 +196,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
if [[ "${{matrix.llvm_version}}" == 15 ]]; then
|
||||
if [[ "${{matrix.llvm_version}}" > 13 ]]; then
|
||||
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
|
||||
fi
|
||||
- name: CMake
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "tilde-backend"]
|
||||
path = tilde-backend
|
||||
url = https://github.com/c3lang/tilde-backend
|
||||
@@ -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)
|
||||
|
||||
@@ -79,7 +79,7 @@ static void usage(void)
|
||||
OUTPUT(" vendor-fetch <library> ... Fetches one or more libraries from the vendor collection.");
|
||||
OUTPUT("");
|
||||
OUTPUT("Options:");
|
||||
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
|
||||
OUTPUT(" --tb - Use Tilde Backend for compilation.");
|
||||
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
|
||||
OUTPUT(" --nostdlib - Do not include the standard library.");
|
||||
OUTPUT(" --nolibc - Do not implicitly link libc nor any associated files.");
|
||||
@@ -516,14 +516,10 @@ static void parse_option(BuildOptions *options)
|
||||
options->compile_option = COMPILE_LEX_PARSE_CHECK_ONLY;
|
||||
return;
|
||||
case '-':
|
||||
if (match_longopt("tinybackend"))
|
||||
if (match_longopt("tb"))
|
||||
{
|
||||
#if TB_BACKEND
|
||||
options->backend = BACKEND_TB;
|
||||
return;
|
||||
#else
|
||||
error_exit("error: The TinyBackend is not supported on this platform.");
|
||||
#endif
|
||||
}
|
||||
if (match_longopt("symtab"))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -317,7 +317,15 @@ struct Type_
|
||||
CanonicalType *canonical;
|
||||
const char *name;
|
||||
Type **type_cache;
|
||||
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;
|
||||
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;
|
||||
@@ -1192,8 +1205,12 @@ typedef struct
|
||||
AstId else_body;
|
||||
};
|
||||
struct
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
5
src/compiler/tilde_codegen_abi.c
Normal file
5
src/compiler/tilde_codegen_abi.c
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tilde_internal.h"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
tilde_emit_stmt(context, ast_next(¤t));
|
||||
FunctionPrototype *prototype = c->cur_func.prototype;
|
||||
|
||||
// If there is no prototype, this is a static initializer, so bail.
|
||||
if (!prototype)
|
||||
{
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
}
|
||||
|
||||
ABIArgInfo *info = prototype->ret_abi_info;
|
||||
|
||||
// If we have an optional it's always the return argument, so we need to copy
|
||||
// the return value into the return value holder.
|
||||
TB_Reg return_out = c->return_out;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
|
||||
TBEValue no_fail;
|
||||
|
||||
// In this case we use the optional as the actual return.
|
||||
if (prototype->is_optional)
|
||||
{
|
||||
if (return_value && return_value->type != type_void)
|
||||
{
|
||||
assert(return_value->type);
|
||||
tilde_store_to_ptr_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type));
|
||||
}
|
||||
return_out = c->optional_out;
|
||||
if (!optional)
|
||||
{
|
||||
value_set(&no_fail, tilde_get_zero(c, type_anyerr), type_anyerr);
|
||||
optional = &no_fail;
|
||||
}
|
||||
return_value = optional;
|
||||
}
|
||||
assert(return_value || info->kind == ABI_ARG_IGNORE);
|
||||
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
assert(return_value);
|
||||
tilde_store_to_ptr_aligned(c, return_out, return_value, info->indirect.alignment);
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
case ABI_ARG_IGNORE:
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
return;
|
||||
case ABI_ARG_DIRECT_SPLIT_STRUCT:
|
||||
case ABI_ARG_EXPAND:
|
||||
// Expands to multiple slots -
|
||||
// Not applicable to return values.
|
||||
UNREACHABLE
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
// Pick the return as an address.
|
||||
value_addr(c, return_value);
|
||||
// Get the coerce type.
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
// Create the new pointer
|
||||
assert(return_value);
|
||||
LLVMValueRef coerce = LLVMBuildBitCast(c->builder, return_value->value, coerce_type, "");
|
||||
// We might have only one value, in that case, build a GEP to that one.
|
||||
LLVMValueRef lo_val;
|
||||
AlignSize alignment;
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo);
|
||||
lo_val = llvm_load(c, lo_type, lo, alignment, "");
|
||||
|
||||
// We're done if there's a single field.
|
||||
if (!abi_type_is_valid(info->coerce_expand.hi))
|
||||
{
|
||||
llvm_emit_return_value(c, lo_val);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's make a first class aggregate
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
||||
return_value->alignment, &alignment);
|
||||
LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi);
|
||||
LLVMValueRef hi_val = llvm_load(c, hi_type, hi, alignment, "");
|
||||
|
||||
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
|
||||
LLVMValueRef composite = llvm_get_undef_raw(unpadded_type);
|
||||
|
||||
composite = llvm_emit_insert_value(c, composite, lo_val, 0);
|
||||
composite = llvm_emit_insert_value(c, composite, hi_val, 1);
|
||||
|
||||
// And return that unpadded result
|
||||
llvm_emit_return_value(c, composite);
|
||||
break;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT:
|
||||
DIRECT_RETURN:
|
||||
// The normal return
|
||||
tb_inst_ret(c->f, tilde_load_value_store(c, return_value));
|
||||
return;
|
||||
case ABI_ARG_DIRECT_PAIR:
|
||||
{
|
||||
TODO
|
||||
goto DIRECT_RETURN;
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));
|
||||
return;*/
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE_INT:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(call_return_type) * 8);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_DIRECT_COERCE:
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type);
|
||||
if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN;
|
||||
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type));*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TB_Reg tilde_emit_local_decl(TbContext *c, Decl *decl)
|
||||
static void tilde_emit_return_implicit(TildeContext *c)
|
||||
{
|
||||
// 1. Get the declaration and the TB type.
|
||||
Type *var_type = type_lowering(type_no_fail(decl->type));
|
||||
Type *rtype_real = c->cur_func.prototype ? c->cur_func.prototype->rtype : type_void;
|
||||
if (type_lowering(type_no_optional(rtype_real)) != type_void)
|
||||
{
|
||||
tb_inst_unreachable(c->f);
|
||||
return;
|
||||
}
|
||||
if (type_is_optional(rtype_real))
|
||||
{
|
||||
tilde_emit_return_abi(c, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
TBEValue value;
|
||||
value_set(&value, tb_inst_ptr(c->f, 0), type_anyerr);
|
||||
tilde_emit_return_abi(c, NULL, &value);
|
||||
}
|
||||
|
||||
static void tilde_emit_decl_expr_list(TildeContext *c, TBEValue *be_value, Expr *expr, bool bool_cast)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_COND);
|
||||
ByteSize size = vec_size(expr->cond_expr);
|
||||
ByteSize last_index = size - 1;
|
||||
for (ByteSize i = 0; i < last_index; i++)
|
||||
{
|
||||
TBEValue value;
|
||||
tilde_emit_expr(c, &value, expr->cond_expr[i]);
|
||||
}
|
||||
Expr *last = expr->cond_expr[last_index];
|
||||
Type *type = last->type;
|
||||
tilde_emit_expr(c, be_value, last);
|
||||
if (last->expr_kind == EXPR_DECL)
|
||||
{
|
||||
type = last->decl_expr->var.type_info->type;
|
||||
|
||||
TB_Reg decl_value = tilde_get_ref(c, last->decl_expr);
|
||||
if (bool_cast && last->decl_expr->var.unwrap)
|
||||
{
|
||||
value_set(be_value, tb_inst_bool(c->f, true), type_bool);
|
||||
return;
|
||||
}
|
||||
value_set_address_abi_aligned(be_value, decl_value, type);
|
||||
}
|
||||
if (bool_cast)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (type->type_kind != TYPE_BOOL)
|
||||
{
|
||||
CastKind cast = cast_to_bool_kind(type);
|
||||
tilde_emit_cast(c, cast, last, be_value, type, type_bool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_return_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
PUSH_OPT();
|
||||
|
||||
Expr *expr = ast->return_stmt.expr;
|
||||
if (expr && expr->expr_kind == EXPR_OPTIONAL)
|
||||
{
|
||||
TBEValue be_value;
|
||||
tilde_emit_expr(c, &be_value, expr->inner_expr);
|
||||
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
|
||||
tilde_emit_return_abi(c, NULL, &be_value);
|
||||
return;
|
||||
}
|
||||
|
||||
TB_Label error_return_block = 0;
|
||||
TB_Reg error_out = TB_NULL_REG;
|
||||
if (c->cur_func.prototype && type_is_optional(c->cur_func.prototype->rtype))
|
||||
{
|
||||
error_return_block = tb_basic_block_create(c->f);
|
||||
error_out = tilde_emit_alloca(c, type_anyerr, 0);
|
||||
c->opt_var = error_out;
|
||||
c->catch_block = error_return_block;
|
||||
}
|
||||
|
||||
bool has_return_value = ast->return_stmt.expr != NULL;
|
||||
TBEValue return_value = { 0 };
|
||||
if (has_return_value)
|
||||
{
|
||||
tilde_emit_expr(c, &return_value, ast->return_stmt.expr);
|
||||
value_fold_optional(c, &return_value);
|
||||
c->retval = return_value;
|
||||
}
|
||||
|
||||
POP_OPT();
|
||||
|
||||
tilde_emit_statement_chain(c, ast->return_stmt.cleanup);
|
||||
|
||||
// Are we in an expression block?
|
||||
if (!has_return_value)
|
||||
{
|
||||
tilde_emit_return_implicit(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
tilde_emit_return_abi(c, &return_value, NULL);
|
||||
}
|
||||
if (error_return_block)
|
||||
{
|
||||
tilde_emit_block(c, error_return_block);
|
||||
TBEValue value;
|
||||
value_set_address_abi_aligned(&value, error_out, type_anyerr);
|
||||
tilde_emit_return_abi(c, NULL, &value);
|
||||
}
|
||||
}
|
||||
|
||||
void tilde_emit_local_decl(TildeContext *c, Decl *decl, TBEValue *value)
|
||||
{
|
||||
// 1. Get the declaration and the LLVM type.
|
||||
Type *var_type = type_lowering(type_no_optional(decl->type));
|
||||
|
||||
// 2. In the case we have a static variable,
|
||||
// then we essentially treat this as a global.
|
||||
if (decl->var.is_static)
|
||||
{
|
||||
if (IS_FAILABLE(decl))
|
||||
TODO
|
||||
/*
|
||||
// In defers we might already have generated this variable.
|
||||
if (decl->backend_ref)
|
||||
{
|
||||
llvm_value_set_decl(c, value, decl);
|
||||
return;
|
||||
}
|
||||
void *builder = c->builder;
|
||||
c->builder = c->global_builder;
|
||||
decl->backend_ref = llvm_add_global(c, "tempglobal", var_type, decl->alignment);
|
||||
if (IS_OPTIONAL(decl))
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl_get_extname(decl));
|
||||
scratch_buffer_append(".f");
|
||||
TB_InitializerID initializer = tb_initializer_create(c->module, type_size(type_anyerr), type_alloca_alignment(type_anyerr), 1);
|
||||
decl->var.tb_failable_reg = tb_global_create(c->module, scratch_buffer_to_string(), TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
|
||||
tb_global_set_initializer(c->module, decl->var.tb_failable_reg, initializer);
|
||||
scratch_buffer_append(decl->extname);
|
||||
scratch_buffer_append("$f");
|
||||
decl->var.optional_ref = llvm_add_global(c, scratch_buffer_to_string(), type_anyerr, 0);
|
||||
}
|
||||
decl->tb_register = tb_global_create(c->module, decl_get_extname(decl), TB_STORAGE_DATA, TB_LINKAGE_PRIVATE);
|
||||
TB_InitializerID static_initializer = tb_initializer_create(c->module, type_size(var_type), type_alloca_alignment(var_type), 1);
|
||||
tb_global_set_initializer(c->module, decl->tb_register, static_initializer);
|
||||
tilde_emit_global_initializer(c, decl);
|
||||
return decl->tb_register;
|
||||
llvm_emit_global_variable_init(c, decl);
|
||||
c->builder = builder;
|
||||
llvm_value_set_decl(c, value, decl);
|
||||
return;*/
|
||||
}
|
||||
tilde_emit_local_var_alloca(c, decl);
|
||||
TB_Register reg = decl->tb_register;
|
||||
assert(!decl->backend_ref);
|
||||
decl->tb_register = tb_inst_local(c->f, type_size(var_type), type_alloca_alignment(var_type));
|
||||
Expr *init = decl->var.init_expr;
|
||||
if (IS_FAILABLE(decl))
|
||||
bool is_optional = IS_OPTIONAL(decl);
|
||||
if (is_optional)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->name);
|
||||
scratch_buffer_append(".f");
|
||||
decl->var.tb_failable_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr));
|
||||
// Only clear out the result if the assignment isn't a failable.
|
||||
decl->var.tb_optional_reg = tb_inst_local(c->f, type_size(type_anyerr), type_alloca_alignment(type_anyerr));
|
||||
// Only clear out the result if the assignment isn't an optional.
|
||||
}
|
||||
|
||||
TBEValue value;
|
||||
value_set_decl(&value, decl);
|
||||
if (init)
|
||||
{
|
||||
tilde_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.tb_failable_reg);
|
||||
value_set_decl_address(c, value, decl);
|
||||
value->kind = TBE_ADDRESS;
|
||||
TBEValue val = tilde_emit_assign_expr(c, value, decl->var.init_expr, decl->var.tb_optional_reg);
|
||||
if (!is_optional) *value = val;
|
||||
}
|
||||
else if (!decl->var.no_init)
|
||||
else if (decl->var.no_init)
|
||||
{
|
||||
if (decl->var.tb_failable_reg)
|
||||
value_set(value, tb_inst_poison(c->f), decl->type);
|
||||
if (decl->var.tb_optional_reg)
|
||||
{
|
||||
tilde_store_zero(c, type_anyerr, decl->var.tb_failable_reg, type_abi_alignment(type_anyerr));
|
||||
tilde_store_to_ptr_raw(c, decl->var.tb_optional_reg, tb_inst_poison(c->f), type_anyerr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (decl->var.tb_optional_reg)
|
||||
{
|
||||
tilde_store_zero(c, type_anyerr, decl->var.tb_optional_reg, 0);
|
||||
}
|
||||
|
||||
Type *type = type_lowering(decl->type);
|
||||
// Normal case, zero init.
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
tilde_store_zero(c, type_anyerr, decl->tb_register, decl->alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_inst_memclr(c->f, decl->tb_register, type_bit_size(type), decl->alignment);
|
||||
}
|
||||
tilde_store_value_zero(c, &value);
|
||||
}
|
||||
return reg;
|
||||
|
||||
}
|
||||
|
||||
static void tilde_emit_expr_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
TBEValue value;
|
||||
if (IS_OPTIONAL(ast->expr_stmt))
|
||||
{
|
||||
PUSH_OPT();
|
||||
TB_Label discard_fail = tb_basic_block_create(c->f);
|
||||
c->catch_block = discard_fail;
|
||||
c->opt_var = TB_NULL_REG;
|
||||
tilde_emit_expr(c, &value, ast->expr_stmt);
|
||||
value_fold_optional(c, &value);
|
||||
EMIT_LOC(c, ast);
|
||||
tilde_emit_goto_if_needed(c, discard_fail);
|
||||
tilde_emit_block(c, discard_fail);
|
||||
POP_OPT();
|
||||
return;
|
||||
}
|
||||
|
||||
void tilde_emit_stmt(TbContext *c, Ast *ast)
|
||||
tilde_emit_expr(c, &value, ast->expr_stmt);
|
||||
}
|
||||
|
||||
// See llvm_emit_if_stmt
|
||||
static void tilde_emit_if_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
// We need at least the exit block and the "then" block.
|
||||
TB_Label exit_block = tb_basic_block_create(c->f);
|
||||
TB_Label then_block = exit_block;
|
||||
TB_Label else_block = exit_block;
|
||||
|
||||
Ast *then_body = astptr(ast->if_stmt.then_body);
|
||||
// Only generate a target if
|
||||
if (ast_is_not_empty(then_body))
|
||||
{
|
||||
then_block = tb_basic_block_create(c->f);
|
||||
}
|
||||
|
||||
// We have an optional else block.
|
||||
AstId else_id = ast->if_stmt.else_body;
|
||||
Ast *else_body = else_id ? astptr(else_id) : NULL;
|
||||
if (ast_is_not_empty(else_body))
|
||||
{
|
||||
else_block = tb_basic_block_create(c->f);
|
||||
}
|
||||
|
||||
Expr *cond = exprptr(ast->if_stmt.cond);
|
||||
ast->if_stmt.codegen.tb_break_block = exit_block;
|
||||
|
||||
// Output boolean value and switch.
|
||||
|
||||
Decl *label = ast->if_stmt.flow.label;
|
||||
if (label)
|
||||
{
|
||||
label->label.tb_break_target = exit_block;
|
||||
}
|
||||
|
||||
TBEValue be_value = { 0 };
|
||||
|
||||
bool exit_in_use = true;
|
||||
|
||||
if (then_body->ast_kind == AST_IF_CATCH_SWITCH_STMT)
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
tilde_emit_decl_expr_list(c, &be_value, cond, false);
|
||||
value_rvalue(c, &be_value);
|
||||
TBEValue comp;
|
||||
tilde_emit_int_comp_zero(c, &comp, &be_value, BINARYOP_NE);
|
||||
tb_inst_if(c->f, comp.reg, then_block, else_block);
|
||||
tb_inst_set_label(c->f, then_block);
|
||||
tilde_emit_switch_body(c, &be_value, then_body);
|
||||
tb_inst_goto(c->f, exit_block);
|
||||
goto EMIT_ELSE;*/
|
||||
}
|
||||
|
||||
tilde_emit_decl_expr_list(c, &be_value, cond, true);
|
||||
|
||||
value_rvalue(c, &be_value);
|
||||
|
||||
if (then_block != else_block)
|
||||
{
|
||||
tb_inst_if(c->f, be_value.reg, then_block, else_block);
|
||||
}
|
||||
|
||||
// Emit the 'then' code.
|
||||
if (then_block != exit_block)
|
||||
{
|
||||
tilde_emit_block(c, then_block);
|
||||
tilde_emit_stmt(c, then_body);
|
||||
|
||||
// Jump to exit.
|
||||
tilde_emit_goto_if_needed(c, exit_block);
|
||||
}
|
||||
|
||||
// Emit the 'else' branch if present.
|
||||
if (else_block != exit_block)
|
||||
{
|
||||
tilde_emit_block(c, else_block);
|
||||
tilde_emit_stmt(c, else_body);
|
||||
tilde_emit_goto_if_needed(c, exit_block);
|
||||
}
|
||||
|
||||
tilde_emit_block(c, exit_block);
|
||||
}
|
||||
|
||||
void tilde_emit_stmt(TildeContext *c, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_DECLARE_STMT:
|
||||
tilde_emit_local_decl(c, ast->declare_stmt);
|
||||
break;
|
||||
case AST_COMPOUND_STMT:
|
||||
tilde_emit_compound_stmt(c, ast);
|
||||
return;
|
||||
case AST_RETURN_STMT:
|
||||
//TbContext_emit_return(c, ast);
|
||||
tb_inst_ret(c->f, TB_NULL_REG);
|
||||
break;
|
||||
tilde_emit_return_stmt(c, ast);
|
||||
return;
|
||||
case AST_IF_STMT:
|
||||
tilde_emit_if_stmt(c, ast);
|
||||
return;
|
||||
case AST_DECLARE_STMT:
|
||||
{
|
||||
TBEValue value;
|
||||
tilde_emit_local_decl(c, ast->declare_stmt, &value);
|
||||
return;
|
||||
}
|
||||
case AST_EXPR_STMT:
|
||||
tilde_emit_expr_stmt(c, ast);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value)
|
||||
{
|
||||
assert(c->catch_block && "unexpected emit");
|
||||
bool is_constant_opt_zero = tb_node_is_constant_zero(c->f, opt_value);
|
||||
|
||||
// Maybe we don't need to emit anything?
|
||||
if (is_constant_opt_zero) return;
|
||||
|
||||
bool is_constant_opt = false;
|
||||
|
||||
TB_Label after_block = tb_basic_block_create(c->f);
|
||||
|
||||
// No error variable
|
||||
if (!c->opt_var)
|
||||
{
|
||||
// No error var and a constant error means jumping to the "catch" block
|
||||
if (is_constant_opt)
|
||||
{
|
||||
tb_inst_goto(c->f, c->catch_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_inst_if(c->f, tilde_emit_is_no_opt(c, opt_value), after_block, c->catch_block);
|
||||
}
|
||||
tilde_emit_block(c, after_block);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's not a constant, then jump conditionally
|
||||
if (!is_constant_opt)
|
||||
{
|
||||
TB_Reg was_ok = tilde_emit_is_no_opt(c, opt_value);
|
||||
TB_Label error_block = tb_basic_block_create(c->f);
|
||||
tb_inst_if(c->f, was_ok, after_block, error_block);
|
||||
tilde_emit_block(c, error_block);
|
||||
}
|
||||
|
||||
tilde_store_to_ptr_raw(c, c->opt_var, opt_value, type_anyerr);
|
||||
tb_inst_goto(c->f, c->catch_block);
|
||||
tilde_emit_block(c, after_block);
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,8 +4,102 @@
|
||||
|
||||
#include "tilde_internal.h"
|
||||
|
||||
#if TB_BACKEND
|
||||
TB_CallingConv calling_conv_from(CallABI abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
case CALL_X86_STD:
|
||||
return TB_STDCALL;
|
||||
case CALL_C:
|
||||
return TB_CDECL;
|
||||
case CALL_X86_FAST:
|
||||
case CALL_X86_THIS:
|
||||
case CALL_X86_VECTOR:
|
||||
case CALL_X86_REG:
|
||||
case CALL_AAPCS:
|
||||
case CALL_AAPCS_VFP:
|
||||
REMINDER("Using C decl even though actual calling convention is different.");
|
||||
return TB_CDECL;
|
||||
}
|
||||
}
|
||||
|
||||
TB_DebugType *tilde_get_debug_type(Type *type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype)
|
||||
{
|
||||
if (prototype->tb_prototype) return prototype->tb_prototype;
|
||||
TB_FunctionPrototype *proto =
|
||||
tb_prototype_create(c->module,
|
||||
calling_conv_from(prototype->call_abi),
|
||||
tildetype(prototype->abi_ret_type),
|
||||
tilde_get_debug_type(prototype->rtype),
|
||||
vec_size(prototype->param_types),
|
||||
prototype->variadic == VARIADIC_RAW);
|
||||
FOREACH_BEGIN(AbiType *param, prototype->abi_args)
|
||||
tb_prototype_add_param_named(proto, tildetype(param->type), "foek", NULL);
|
||||
FOREACH_END();
|
||||
prototype->tb_prototype = proto;
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
TB_DataType tildetype(Type *type)
|
||||
{
|
||||
type = type_lowering(type);
|
||||
if (type->tb_set) return (TB_DataType) { .raw = type->tb_type };
|
||||
TB_DataType tb_type;
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEID:
|
||||
case TYPE_ANYERR:
|
||||
UNREACHABLE;
|
||||
case TYPE_FUNC:
|
||||
TODO
|
||||
case TYPE_VECTOR:
|
||||
tb_type = tildetype(type->array.base);
|
||||
tb_type.width = next_highest_power_of_2(type->array.len);
|
||||
break;
|
||||
case TYPE_F32:
|
||||
tb_type = TB_TYPE_F32;
|
||||
break;
|
||||
case TYPE_F64:
|
||||
tb_type = TB_TYPE_F64;
|
||||
break;
|
||||
case TYPE_VOID:
|
||||
tb_type = TB_TYPE_VOID;
|
||||
break;
|
||||
case TYPE_I8:
|
||||
case TYPE_U8:
|
||||
tb_type = TB_TYPE_I8;
|
||||
break;
|
||||
case TYPE_I16:
|
||||
case TYPE_U16:
|
||||
tb_type = TB_TYPE_I16;
|
||||
break;
|
||||
case TYPE_I32:
|
||||
case TYPE_U32:
|
||||
tb_type = TB_TYPE_I32;
|
||||
break;
|
||||
case TYPE_I64:
|
||||
case TYPE_U64:
|
||||
tb_type = TB_TYPE_I64;
|
||||
break;
|
||||
case TYPE_I128:
|
||||
case TYPE_U128:
|
||||
tb_type = (TB_DataType) { { TB_INT, 0, 128 } };
|
||||
break;
|
||||
case TYPE_POINTER:
|
||||
tb_type = TB_TYPE_PTR;
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
tb_type = TB_TYPE_BOOL;
|
||||
break;
|
||||
case TYPE_ANY:
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
type->tb_set = 1;
|
||||
type->tb_type = tb_type.raw;
|
||||
return tb_type;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -6,29 +6,15 @@
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#if TB_BACKEND
|
||||
|
||||
#undef TB_Reg
|
||||
#include <tb.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Module *code_module;
|
||||
const char *object_filename;
|
||||
|
||||
TB_Module *module;
|
||||
TB_FeatureSet features;
|
||||
|
||||
Decl *current_func_decl;
|
||||
TB_Function *f;
|
||||
TB_Register error_var;
|
||||
TB_Label catch_block;
|
||||
} TbContext;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TBE_VALUE,
|
||||
TBE_ADDRESS,
|
||||
TBE_ADDRESS_FAILABLE,
|
||||
TBE_ADDRESS_OPTIONAL,
|
||||
} TBBackendValueKind;
|
||||
|
||||
typedef struct
|
||||
@@ -38,79 +24,137 @@ typedef struct
|
||||
|
||||
Type *type; // Should never be a distinct or canonical type.
|
||||
|
||||
TB_Register reg;
|
||||
TB_Register failable; // TODO what even is a failable?
|
||||
TB_Reg reg;
|
||||
TB_Reg failable;
|
||||
} TBEValue;
|
||||
|
||||
TB_DataType tbtype(Type *type);
|
||||
typedef struct
|
||||
{
|
||||
Module *code_module;
|
||||
const char *object_filename;
|
||||
const char *ir_filename;
|
||||
const char *asm_filename;
|
||||
|
||||
TB_Function **functions;
|
||||
TB_Module *module;
|
||||
TB_FeatureSet features;
|
||||
|
||||
struct
|
||||
{
|
||||
const char *name;
|
||||
FunctionPrototype *prototype;
|
||||
Type *rtype;
|
||||
} cur_func;
|
||||
Decl *curr_func;
|
||||
TB_Function *f;
|
||||
TB_Reg opt_var;
|
||||
TB_Label catch_block;
|
||||
TBEValue retval;
|
||||
TB_Reg return_out;
|
||||
TB_Reg optional_out;
|
||||
struct
|
||||
{
|
||||
TB_Reg last_ptr;
|
||||
TB_Reg stack_slot;
|
||||
} debug;
|
||||
} TildeContext;
|
||||
|
||||
|
||||
TB_DataType tildetype(Type *type);
|
||||
|
||||
#define PUSH_OPT() TB_Label _old_catch = c->catch_block; TB_Reg _old_opt_var = c->opt_var
|
||||
#define POP_OPT() c->catch_block = _old_catch; c->opt_var = _old_opt_var
|
||||
|
||||
#define PUSH_ERROR() \
|
||||
TB_Label _old_catch = c->catch_block; \
|
||||
TB_Reg _old_error_var = c->error_var
|
||||
#define POP_ERROR() \
|
||||
c->catch_block = _old_catch; \
|
||||
c->error_var = _old_error_var
|
||||
|
||||
// -- store ---
|
||||
static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_raw(TbContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_raw_abi_alignment(TbContext *c, Type *type, TB_Reg addr, TB_Reg value);
|
||||
void tilde_store_value_raw(TbContext *c, TBEValue *destination, TB_Reg value);
|
||||
void tilde_store_value_aligned(TbContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store_value(TbContext *c, TBEValue *dst, TBEValue *value);
|
||||
void tilde_store_decl_raw(TbContext *c, Decl *decl, TB_Reg value);
|
||||
void tilde_store_zero(TbContext *c, Type *type, TB_Reg addr, AlignSize alignment);
|
||||
void tilde_store_value_zero(TbContext *c, TBEValue *to);
|
||||
static inline void tilde_store_internal(TildeContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_to_ptr_raw_aligned(TildeContext *c, Type *type, TB_Reg addr, TB_Reg value, AlignSize alignment);
|
||||
void tilde_store_to_ptr_raw(TildeContext *c, TB_Reg addr, TB_Reg value, Type *type);
|
||||
void tilde_store_value_raw(TildeContext *c, TBEValue *destination, TB_Reg value);
|
||||
void tilde_store_to_ptr_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store_value_aligned(TildeContext *c, TB_Reg destination, TBEValue *value, AlignSize alignment);
|
||||
void tilde_store(TildeContext *c, TBEValue *dst, TBEValue *value);
|
||||
void tilde_store_decl_raw(TildeContext *c, Decl *decl, TB_Reg value);
|
||||
TB_Reg tilde_load_natural_alignment(TildeContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value_store(TildeContext *c, TBEValue *value);
|
||||
void tilde_store_zero(TildeContext *c, Type *type, TB_Reg addr, AlignSize alignment);
|
||||
void tilde_store_value_zero(TildeContext *c, TBEValue *to);
|
||||
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value);
|
||||
|
||||
// -- load ---
|
||||
static inline TB_Reg tilde_load(TbContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment);
|
||||
TB_Reg tilde_load_abi_alignment(TbContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value(TbContext *c, TBEValue *value);
|
||||
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment);
|
||||
TB_Reg tilde_load_abi_alignment(TildeContext *c, Type *type, TB_Reg pointer);
|
||||
TB_Reg tilde_load_value(TildeContext *c, TBEValue *value);
|
||||
|
||||
// -- value --
|
||||
void value_set(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_address(TBEValue *value, TB_Reg addr, Type *type, AlignSize alignment);
|
||||
void value_set_address_abi_aligned(TBEValue *value, TB_Reg val, Type *type);
|
||||
void value_set_decl(TBEValue *value, Decl *decl);
|
||||
void value_addr(TbContext *c, TBEValue *value);
|
||||
void value_set_decl_address(TildeContext *c, TBEValue *value, Decl *decl);
|
||||
void value_set_decl(TildeContext *c, TBEValue *value, Decl *decl);
|
||||
void value_addr(TildeContext *c, TBEValue *value);
|
||||
|
||||
static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_FAILABLE; }
|
||||
void value_fold_failable(TbContext *c, TBEValue *value);
|
||||
void value_rvalue(TbContext *c, TBEValue *value);
|
||||
static inline bool value_is_addr(TBEValue *value) { return value->kind == TBE_ADDRESS || value->kind == TBE_ADDRESS_OPTIONAL; }
|
||||
void value_fold_optional(TildeContext *c, TBEValue *value);
|
||||
void value_rvalue(TildeContext *c, TBEValue *value);
|
||||
|
||||
|
||||
TB_Register tilde_get_zero(TbContext *c, Type *type);
|
||||
TB_Reg tilde_get_ref(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_get_opt_ref(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_get_const_int(TildeContext *c, Type *type, uint64_t i);
|
||||
TB_Reg tilde_get_const_float(TildeContext *c, Type *type, double d);
|
||||
TB_Register tilde_get_zero(TildeContext *c, Type *type);
|
||||
|
||||
// -- type ---
|
||||
|
||||
// -- instructions --
|
||||
void tilde_emit_cond_br(TbContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block);
|
||||
TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift);
|
||||
TB_Reg tilde_emit_alloca(TbContext *c, Type *type);
|
||||
void tilde_emit_cond_br(TildeContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block);
|
||||
TB_Reg tilde_emit_lshr_fixed(TildeContext *c, Type *type, TB_Reg reg, int shift);
|
||||
|
||||
// -- stmt ---
|
||||
void tilde_emit_stmt(TbContext *c, Ast *ast);
|
||||
void tilde_emit_stmt(TildeContext *c, Ast *ast);
|
||||
|
||||
// -- general ---
|
||||
TB_Register tilde_emit_is_no_error(TbContext *c, TB_Reg reg);
|
||||
void tilde_emit_global_initializer(TbContext *c, Decl *decl);
|
||||
void tilde_emit_local_var_alloca(TbContext *c, Decl *decl);
|
||||
void tilde_emit_and_set_decl_alloca(TbContext *c, Decl *decl);
|
||||
TB_Register tilde_emit_is_no_error(TildeContext *c, TB_Reg reg);
|
||||
void tilde_emit_global_initializer(TildeContext *c, Decl *decl);
|
||||
TB_Reg tilde_emit_alloca(TildeContext *c, Type *type, AlignSize alignment);
|
||||
void tilde_emit_local_var_alloca(TildeContext *c, Decl *decl);
|
||||
void tilde_emit_and_set_decl_alloca(TildeContext *c, Decl *decl);
|
||||
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl);
|
||||
void tilde_emit_parameter(TildeContext *c, TB_Reg *args, unsigned *arg_count_ref, ABIArgInfo *info, TBEValue *be_value, Type *type);
|
||||
|
||||
|
||||
// -- expr --
|
||||
void tilde_emit_expr(TbContext *c, TBEValue *result, Expr *expr);
|
||||
TBEValue tilde_emit_assign_expr(TbContext *c, TBEValue *ref, Expr *expr, TB_Reg failable);
|
||||
void tilde_emit_expr(TildeContext *c, TBEValue *result, Expr *expr);
|
||||
TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_Reg optional);
|
||||
void tilde_emit_cast(TildeContext *c, CastKind cast_kind, Expr *expr, TBEValue *value, Type *to_type, Type *from_type);
|
||||
|
||||
void tilde_emit_memclear_size_align(TbContext *c, TB_Register ref, uint64_t size, AlignSize align);
|
||||
// -- comparisons --
|
||||
void tilde_emit_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp_zero(TildeContext *c, TBEValue *result, TBEValue *lhs, BinaryOp binary_op);
|
||||
void tilde_emit_int_comp_raw(TildeContext *c, TBEValue *result, Type *lhs_type, Type *rhs_type, TB_Reg lhs_value, TB_Reg rhs_value, BinaryOp binary_op);
|
||||
|
||||
// -- optional --
|
||||
void tilde_emit_jump_to_optional_exit(TildeContext *c, TB_Reg opt_value);
|
||||
TB_Reg tilde_emit_is_no_opt(TildeContext *c, TB_Reg error_value);
|
||||
|
||||
|
||||
static inline void tilde_store(TbContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
// -- jumps ---
|
||||
bool tilde_emit_goto_if_needed(TildeContext *c, TB_Label jump);
|
||||
INLINE void tilde_emit_block(TildeContext *c, TB_Label block);
|
||||
|
||||
void tilde_emit_memclear_size_align(TildeContext *c, TB_Register ref, uint64_t size, AlignSize align);
|
||||
|
||||
#define EMIT_LOC(x, y) do { } while(0)
|
||||
|
||||
|
||||
static inline void tilde_store_internal(TildeContext *c, TB_DataType type, TB_Reg addr, TB_Reg value, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
tb_inst_store(c->f, type, addr, value, alignment);
|
||||
}
|
||||
|
||||
static inline TB_Reg tilde_load(TbContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment)
|
||||
static inline TB_Reg tilde_load(TildeContext *c, TB_DataType type, TB_Reg addr, AlignSize alignment)
|
||||
{
|
||||
assert(alignment > 0);
|
||||
return tb_inst_load(c->f, type, addr, alignment);
|
||||
@@ -124,5 +168,19 @@ static inline TB_Reg decl_reg(Decl *decl)
|
||||
}
|
||||
|
||||
TB_DataType tilde_get_int_type_of_bytesize(int byte_size);
|
||||
TB_FunctionPrototype *tilde_get_func_prototype(TildeContext *c, FunctionPrototype *prototype);
|
||||
INLINE TB_Linkage tilde_linkage_for_decl(Decl *decl)
|
||||
{
|
||||
if (!decl->is_external_visible && decl->visibility == VISIBLE_LOCAL) return TB_LINKAGE_PRIVATE;
|
||||
return TB_LINKAGE_PUBLIC;
|
||||
}
|
||||
|
||||
#endif // TB_BACKEND
|
||||
INLINE void tilde_store_to_ptr(TildeContext *c, TB_Reg destination, TBEValue *value)
|
||||
{
|
||||
return tilde_store_to_ptr_aligned(c, destination, value, type_alloca_alignment(value->type));
|
||||
}
|
||||
|
||||
INLINE void tilde_emit_block(TildeContext *c, TB_Label block)
|
||||
{
|
||||
tb_inst_set_label(c->f, block);
|
||||
}
|
||||
|
||||
1
tilde-backend
Submodule
1
tilde-backend
Submodule
Submodule tilde-backend added at 8029367607
Reference in New Issue
Block a user