From 54c7103df1f1106d679f87d3096ccad0a59b1d25 Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 4 Jun 2024 14:22:12 +0200 Subject: [PATCH] build bridge between compiler config and backend config --- src/cfg/opt.c | 3 +- src/codegen/backend.c | 4 +- src/codegen/backend.h | 5 +- src/compiler.c | 25 +++++++++- src/llvm/backend.c | 35 +++++++++++-- src/llvm/backend.h | 2 +- src/llvm/expr.c | 2 +- src/llvm/func.c | 44 ++++++++++++++--- src/llvm/func.h | 4 ++ src/llvm/parser.c | 112 +++++++++++++++++++++++++++++------------- src/llvm/parser.h | 8 ++- src/llvm/stmt.c | 3 ++ src/llvm/types.h | 7 +-- src/main.c | 6 +++ 14 files changed, 203 insertions(+), 57 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 6de694e..256e2eb 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -181,7 +181,8 @@ void print_help(void) { " --verbose print logs with level information or higher", " --debug print debug logs (if not disabled at compile time)", " --version print the version", - " --help print this hel dialog", + " --list-targets print a list of all available targets supported" + " --help print this help dialog" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { diff --git a/src/codegen/backend.c b/src/codegen/backend.c index 8e6f5ca..9258284 100644 --- a/src/codegen/backend.c +++ b/src/codegen/backend.c @@ -68,7 +68,7 @@ BackendError set_backend(const codegen_init init_func, const codegen_deinit dein return new_backend_error(Success); } -BackendError generate_code(const Module* root, void** output) { +BackendError generate_code(const Module* root, const TargetConfig* target) { DEBUG("generating code with backend: %s", CodegenBackend.name); if (CodegenBackend.codegen_func == NULL) { @@ -76,7 +76,7 @@ BackendError generate_code(const Module* root, void** output) { return new_backend_error(NoBackend); } - BackendError code = CodegenBackend.codegen_func(root, output); + BackendError code = CodegenBackend.codegen_func(root, target); if (code.kind) { ERROR("code generation of backend: %s failed with code: %ld", CodegenBackend.name, code); return code; diff --git a/src/codegen/backend.h b/src/codegen/backend.h index ad3613d..f25e69d 100644 --- a/src/codegen/backend.h +++ b/src/codegen/backend.h @@ -4,6 +4,7 @@ #include #include +#include typedef struct BackendImplError_t { // faulty AST node @@ -30,7 +31,7 @@ typedef struct BackendError_t { * @brief Function called by the compiler backend to generate an intermediate format * from AST. Returns a custom container for its intermediate language. */ -typedef BackendError (*codegen)(const Module*, void**); +typedef BackendError (*codegen)(const Module*, const TargetConfig* target); /** * @brief Initialize the code generation backend. @@ -78,7 +79,7 @@ BackendError deinit_backend(void); * @return BackendError */ [[nodiscard]] -BackendError generate_code(const Module* root, void** code); +BackendError generate_code(const Module* root, const TargetConfig* target); /** * @brief Create a new backend error diff --git a/src/compiler.c b/src/compiler.c index 709592e..a11d556 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include extern void yyrestart(FILE *); @@ -125,6 +127,25 @@ static void print_ast_to_file(const AST_NODE_PTR ast, const TargetConfig *target free((void *) path); } +static void run_backend_codegen(const Module* module, const TargetConfig* target) { + DEBUG("initializing LLVM codegen backend..."); + llvm_backend_init(); + + DEBUG("initiializing backend for codegen..."); + BackendError err = init_backend(); + if (err.kind != Success) { + return; + } + + DEBUG("generating code..."); + err = generate_code(module, target); + if (err.kind != Success) { + return; + } + + err = deinit_backend(); +} + /** * @brief Build the given target * @param unit @@ -142,7 +163,9 @@ static void build_target(ModuleFileStack *unit, const TargetConfig *target) { print_ast_to_file(ast, target); // TODO: parse AST to semantic values - // TODO: backend codegen + Module* module = NULL; + + run_backend_codegen(module, target); } } diff --git a/src/llvm/backend.c b/src/llvm/backend.c index 6bd5997..e2b2646 100644 --- a/src/llvm/backend.c +++ b/src/llvm/backend.c @@ -34,7 +34,35 @@ Target create_native_target() { return target; } -Target create_target_from_config() { PANIC("NOT IMPLEMENTED"); } +LLVMCodeGenOptLevel llvm_opt_from_int(int level) { + switch (level) { + case 1: + return LLVMCodeGenLevelLess; + case 2: + return LLVMCodeGenLevelDefault; + case 3: + return LLVMCodeGenLevelAggressive; + default: + break; + } + PANIC("invalid code generation optimization level: %d", level); +} + +Target create_target_from_config(const TargetConfig* config) { + DEBUG("Building target from configuration"); + + Target target = create_native_target(); + + target.name.str = config->name; + target.name.allocation = NONE; // freed later by compiler + + target.opt = llvm_opt_from_int(config->optimization_level); + + INFO("Configured target: %s/%d: (%s) on %s { %s }", target.name, target.opt, + target.triple, target.cpu, target.features); + + return target; +} static void delete_string(String string) { DEBUG("deleting string..."); @@ -59,8 +87,9 @@ void delete_target(Target target) { typedef enum LLVMBackendError_t { UnresolvedImport } LLVMBackendError; -static BackendError llvm_backend_codegen(const Module* unit, void** output) { - return parse_module(unit, output); +static BackendError llvm_backend_codegen(const Module* unit, + const TargetConfig* target) { + return parse_module(unit, target); } static BackendError llvm_backend_codegen_init(void) { diff --git a/src/llvm/backend.h b/src/llvm/backend.h index 4625aa7..59c2bd1 100644 --- a/src/llvm/backend.h +++ b/src/llvm/backend.h @@ -27,7 +27,7 @@ typedef struct Target_t { Target create_native_target(); -Target create_target_from_config(); +Target create_target_from_config(const TargetConfig* config); void delete_target(Target target); diff --git a/src/llvm/expr.c b/src/llvm/expr.c index c43c72c..e95ffa9 100644 --- a/src/llvm/expr.c +++ b/src/llvm/expr.c @@ -300,7 +300,7 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope, switch (expr->kind) { case ExpressionKindConstant: - err = get_type_value(unit, scope->func_scope->global_scope, + err = get_const_type_value(unit, scope->func_scope->global_scope, &expr->impl.constant, llvm_result); break; case ExpressionKindTransmute: diff --git a/src/llvm/func.c b/src/llvm/func.c index dc6c8da..fc21f27 100644 --- a/src/llvm/func.c +++ b/src/llvm/func.c @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include LLVMLocalScope* new_local_scope(LLVMLocalScope* parent) { LLVMLocalScope* scope = malloc(sizeof(LLVMLocalScope)); @@ -24,7 +24,8 @@ void delete_local_scope(LLVMLocalScope* scope) { free(scope); } -static LLVMValueRef get_parameter(const LLVMFuncScope* scope, const char* name) { +static LLVMValueRef get_parameter(const LLVMFuncScope* scope, + const char* name) { if (g_hash_table_contains(scope->params, name)) { return g_hash_table_lookup(scope->params, name); } @@ -108,7 +109,8 @@ BackendError impl_func_decl(LLVMBackendCompileUnit* unit, return err; } -BackendError impl_func(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_scope, +BackendError impl_func(LLVMBackendCompileUnit* unit, + LLVMGlobalScope* global_scope, FunctionDefinition* fundef, const char* name) { BackendError err = SUCCESS; @@ -125,18 +127,20 @@ BackendError impl_func(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_sco func_scope->params = g_hash_table_new(g_str_hash, g_str_equal); // store function type in global scope - g_hash_table_insert(global_scope->functions, (gpointer) name, llvm_func); + g_hash_table_insert(global_scope->functions, (gpointer)name, llvm_func); // create function body builder - LLVMBasicBlockRef body = LLVMAppendBasicBlockInContext(unit->context, llvm_func, "entry"); + LLVMBasicBlockRef body = + LLVMAppendBasicBlockInContext(unit->context, llvm_func, "entry"); LLVMBuilderRef builder = LLVMCreateBuilderInContext(unit->context); LLVMPositionBuilderAtEnd(builder, body); // create value references for parameter const size_t params = fundef->parameter->len; for (size_t i = 0; i < params; i++) { - const Paramer* param = ((Paramer*) fundef->parameter) + i; - g_hash_table_insert(func_scope->params, (gpointer) param->name, LLVMGetParam(llvm_func, i)); + const Paramer* param = ((Paramer*)fundef->parameter) + i; + g_hash_table_insert(func_scope->params, (gpointer)param->name, + LLVMGetParam(llvm_func, i)); } // TODO: parse function body @@ -149,3 +153,29 @@ BackendError impl_func(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_sco return err; } + +BackendError impl_functions(LLVMBackendCompileUnit* unit, + LLVMGlobalScope* scope, GHashTable* functions) { + DEBUG("implementing functions..."); + GHashTableIter iterator; + g_hash_table_iter_init(&iterator, functions); + + gpointer key = NULL; + gpointer val = NULL; + + BackendError err = SUCCESS; + + size_t variable_count = 0; + while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) { + err = + impl_func(unit, scope, (FunctionDefinition*)val, (const char*)key); + + if (err.kind != Success) { + break; + } + + variable_count++; + } + + return err; +} diff --git a/src/llvm/func.h b/src/llvm/func.h index e93eca4..acae8a6 100644 --- a/src/llvm/func.h +++ b/src/llvm/func.h @@ -28,4 +28,8 @@ void delete_local_scope(LLVMLocalScope*); LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name); +BackendError impl_functions(LLVMBackendCompileUnit* unit, + LLVMGlobalScope* scope, + GHashTable* variables); + #endif // LLVM_BACKEND_FUNC_H_ diff --git a/src/llvm/parser.c b/src/llvm/parser.c index 4d00ac0..3a9bfa9 100644 --- a/src/llvm/parser.c +++ b/src/llvm/parser.c @@ -8,15 +8,15 @@ #include #include #include +#include #include #include #include #include #include -#define EXPORT_IR 1 - -BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target) { +BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target, + const TargetConfig* config) { DEBUG("exporting module to LLVM-IR..."); BackendError err = SUCCESS; @@ -25,8 +25,8 @@ BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target) { char* ir = LLVMPrintModuleToString(unit->module); // construct file name - char* filename = alloca(strlen(target->name.str) + 4); - sprintf(filename, "%s.ll", target->name.str); + const char* filename = + make_file_path(target->name.str, ".ll", 1, config->archive_directory); INFO("Writing LLVM-IR to %s", filename); @@ -49,13 +49,52 @@ BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target) { INFO("%ld bytes written to %s", bytes, filename); + free((char*)filename); + // clean up LLVM-IR string LLVMDisposeMessage(ir); return err; } -BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target) { +BackendError emit_module_to_file(LLVMBackendCompileUnit* unit, + LLVMTargetMachineRef target_machine, + LLVMCodeGenFileType file_type, char* error, + const TargetConfig* config) { + BackendError err = SUCCESS; + DEBUG("Generating code..."); + + const char* filename; + switch (file_type) { + case LLVMAssemblyFile: + filename = make_file_path(config->name, ".s", 1, + config->archive_directory); + break; + case LLVMObjectFile: + filename = make_file_path(config->name, ".o", 1, + config->archive_directory); + break; + default: + return new_backend_impl_error(Implementation, NULL, + "invalid codegen file"); + } + + if (LLVMTargetMachineEmitToFile(target_machine, unit->module, filename, + file_type, &error) != 0) { + ERROR("failed to emit code: %s", error); + err = + new_backend_impl_error(Implementation, NULL, "failed to emit code"); + LLVMDisposeMessage(error); + } + + free((void*)filename); + + return err; +} + +BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target, + const TargetConfig* config) { + BackendError err = SUCCESS; DEBUG("exporting object file..."); INFO("Using target (%s): %s with features: %s", target->name.str, @@ -72,8 +111,8 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target) { if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error) != 0) { ERROR("failed to create target machine: %s", error); - BackendError err = new_backend_impl_error( - Implementation, NULL, "unable to create target machine"); + err = new_backend_impl_error(Implementation, NULL, + "unable to create target machine"); LLVMDisposeMessage(error); return err; } @@ -83,19 +122,19 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target) { llvm_target, target->triple.str, target->cpu.str, target->features.str, target->opt, target->reloc, target->model); - LLVMCodeGenFileType file_type = LLVMObjectFile; + if (config->print_asm) { + err = emit_module_to_file(unit, target_machine, LLVMAssemblyFile, error, + config); + } - DEBUG("Generating code..."); - if (LLVMTargetMachineEmitToFile(target_machine, unit->module, "output", - file_type, &error) != 0) { - ERROR("failed to emit code: %s", error); - BackendError err = - new_backend_impl_error(Implementation, NULL, "failed to emit code"); - LLVMDisposeMessage(error); + if (err.kind != Success) { return err; } - return SUCCESS; + err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error, + config); + + return err; } void list_available_targets() { @@ -120,23 +159,25 @@ void list_available_targets() { LLVMDisposeMessage(default_triple); } -BackendError export_module(LLVMBackendCompileUnit* unit, const Target* target) { +BackendError export_module(LLVMBackendCompileUnit* unit, const Target* target, + const TargetConfig* config) { DEBUG("exporting module..."); BackendError err = SUCCESS; - export_object(unit, target); + export_object(unit, target, config); - if (EXPORT_IR) { - export_IR(unit, target); - } else { - DEBUG("not exporting LLVM-IR"); + if (config->print_ir) { + export_IR(unit, target, config); } return err; } -static BackendError build_module(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_scope, const Module* module) { +static BackendError build_module(LLVMBackendCompileUnit* unit, + LLVMGlobalScope* global_scope, + const Module* module) { + DEBUG("building module..."); BackendError err = SUCCESS; err = impl_types(unit, global_scope, module->types); @@ -156,10 +197,13 @@ static BackendError build_module(LLVMBackendCompileUnit* unit, LLVMGlobalScope* return err; } + // TODO: implement functions + err = impl_functions(unit, global_scope, module->functions); + return err; } -BackendError parse_module(const Module* module, void**) { +BackendError parse_module(const Module* module, const TargetConfig* config) { DEBUG("generating code for module %p", module); if (module == NULL) { ERROR("no module for codegen"); @@ -171,20 +215,19 @@ BackendError parse_module(const Module* module, void**) { // we start with a LLVM module DEBUG("creating LLVM context and module"); unit->context = LLVMContextCreate(); - unit->module = LLVMModuleCreateWithNameInContext("gemstone application", - unit->context); + unit->module = + LLVMModuleCreateWithNameInContext(config->name, unit->context); - LLVMGlobalScope* global_scope = new_global_scope(); - - BackendError err = new_backend_error(Success); + LLVMGlobalScope* global_scope = new_global_scope(module); DEBUG("generating code..."); - err = build_module(unit, global_scope, module); + BackendError err = build_module(unit, global_scope, module); if (err.kind == Success) { - Target target = create_native_target(); + INFO("Module build successfully..."); + Target target = create_target_from_config(config); - export_module(unit, &target); + export_module(unit, &target, config); delete_target(target); } @@ -199,10 +242,11 @@ BackendError parse_module(const Module* module, void**) { return err; } -LLVMGlobalScope* new_global_scope() { +LLVMGlobalScope* new_global_scope(const Module* module) { DEBUG("creating global scope..."); LLVMGlobalScope* scope = malloc(sizeof(LLVMGlobalScope)); + scope->module = (Module*) module; scope->functions = g_hash_table_new(g_str_hash, g_str_equal); scope->variables = g_hash_table_new(g_str_hash, g_str_equal); scope->types = g_hash_table_new(g_str_hash, g_str_equal); diff --git a/src/llvm/parser.h b/src/llvm/parser.h index f045f43..4b740ce 100644 --- a/src/llvm/parser.h +++ b/src/llvm/parser.h @@ -18,12 +18,16 @@ typedef struct LLVMGlobalScope_t { GHashTable* variables; // of type LLVMTypeRef GHashTable* functions; + // module definition + Module* module; } LLVMGlobalScope; -LLVMGlobalScope* new_global_scope(); +LLVMGlobalScope* new_global_scope(const Module* module); + +void list_available_targets(); void delete_global_scope(LLVMGlobalScope* scope); -BackendError parse_module(const Module* module, void**); +BackendError parse_module(const Module* module, const TargetConfig* config); #endif // LLVM_BACKEND_PARSE_H_ diff --git a/src/llvm/stmt.c b/src/llvm/stmt.c index 9d23c07..c712ae5 100644 --- a/src/llvm/stmt.c +++ b/src/llvm/stmt.c @@ -103,6 +103,9 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit, break; } + Paramer* parameter = (Paramer*) call->function->parameter->data + i; + // TODO: create a pointer to LLVMValueRef in case parameter is `out` + g_array_append_vals(arguments, &llvm_arg, 1); } diff --git a/src/llvm/types.h b/src/llvm/types.h index edd8b66..405aa91 100644 --- a/src/llvm/types.h +++ b/src/llvm/types.h @@ -18,8 +18,9 @@ BackendError get_type_default_value(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope, Type* gemstone_type, LLVMValueRef* llvm_value); -BackendError get_type_value(LLVMBackendCompileUnit* unit, - LLVMGlobalScope* scope, TypeValue* gemstone_value, - LLVMValueRef* llvm_value); +BackendError get_const_type_value(LLVMBackendCompileUnit* unit, + LLVMGlobalScope* scope, + TypeValue* gemstone_value, + LLVMValueRef* llvm_value); #endif // LLVM_BACKEND_TYPES_H_ diff --git a/src/main.c b/src/main.c index 3ad368c..b69b613 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include #include #include +#include /** * @brief Log a debug message to inform about beginning exit procedures @@ -55,6 +56,11 @@ int main(int argc, char *argv[]) { exit(0); } + if (is_option_set("list-targets")) { + list_available_targets(); + exit(0); + } + run_compiler(); return 0;