build bridge between compiler config and backend config

This commit is contained in:
Sven Vogel 2024-06-04 14:22:12 +02:00
parent 05634db44a
commit 54c7103df1
14 changed files with 203 additions and 57 deletions

View File

@ -181,7 +181,8 @@ void print_help(void) {
" --verbose print logs with level information or higher", " --verbose print logs with level information or higher",
" --debug print debug logs (if not disabled at compile time)", " --debug print debug logs (if not disabled at compile time)",
" --version print the version", " --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++) { for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) {

View File

@ -68,7 +68,7 @@ BackendError set_backend(const codegen_init init_func, const codegen_deinit dein
return new_backend_error(Success); 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); DEBUG("generating code with backend: %s", CodegenBackend.name);
if (CodegenBackend.codegen_func == NULL) { if (CodegenBackend.codegen_func == NULL) {
@ -76,7 +76,7 @@ BackendError generate_code(const Module* root, void** output) {
return new_backend_error(NoBackend); return new_backend_error(NoBackend);
} }
BackendError code = CodegenBackend.codegen_func(root, output); BackendError code = CodegenBackend.codegen_func(root, target);
if (code.kind) { if (code.kind) {
ERROR("code generation of backend: %s failed with code: %ld", CodegenBackend.name, code); ERROR("code generation of backend: %s failed with code: %ld", CodegenBackend.name, code);
return code; return code;

View File

@ -4,6 +4,7 @@
#include <set/types.h> #include <set/types.h>
#include <ast/ast.h> #include <ast/ast.h>
#include <cfg/opt.h>
typedef struct BackendImplError_t { typedef struct BackendImplError_t {
// faulty AST node // faulty AST node
@ -30,7 +31,7 @@ typedef struct BackendError_t {
* @brief Function called by the compiler backend to generate an intermediate format * @brief Function called by the compiler backend to generate an intermediate format
* from AST. Returns a custom container for its intermediate language. * 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. * @brief Initialize the code generation backend.
@ -78,7 +79,7 @@ BackendError deinit_backend(void);
* @return BackendError * @return BackendError
*/ */
[[nodiscard]] [[nodiscard]]
BackendError generate_code(const Module* root, void** code); BackendError generate_code(const Module* root, const TargetConfig* target);
/** /**
* @brief Create a new backend error * @brief Create a new backend error

View File

@ -11,6 +11,8 @@
#include <io/files.h> #include <io/files.h>
#include <assert.h> #include <assert.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <codegen/backend.h>
#include <llvm/backend.h>
extern void yyrestart(FILE *); 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); 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 * @brief Build the given target
* @param unit * @param unit
@ -142,7 +163,9 @@ static void build_target(ModuleFileStack *unit, const TargetConfig *target) {
print_ast_to_file(ast, target); print_ast_to_file(ast, target);
// TODO: parse AST to semantic values // TODO: parse AST to semantic values
// TODO: backend codegen Module* module = NULL;
run_backend_codegen(module, target);
} }
} }

View File

@ -34,7 +34,35 @@ Target create_native_target() {
return 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) { static void delete_string(String string) {
DEBUG("deleting string..."); DEBUG("deleting string...");
@ -59,8 +87,9 @@ void delete_target(Target target) {
typedef enum LLVMBackendError_t { UnresolvedImport } LLVMBackendError; typedef enum LLVMBackendError_t { UnresolvedImport } LLVMBackendError;
static BackendError llvm_backend_codegen(const Module* unit, void** output) { static BackendError llvm_backend_codegen(const Module* unit,
return parse_module(unit, output); const TargetConfig* target) {
return parse_module(unit, target);
} }
static BackendError llvm_backend_codegen_init(void) { static BackendError llvm_backend_codegen_init(void) {

View File

@ -27,7 +27,7 @@ typedef struct Target_t {
Target create_native_target(); Target create_native_target();
Target create_target_from_config(); Target create_target_from_config(const TargetConfig* config);
void delete_target(Target target); void delete_target(Target target);

View File

@ -300,7 +300,7 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
switch (expr->kind) { switch (expr->kind) {
case ExpressionKindConstant: 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); &expr->impl.constant, llvm_result);
break; break;
case ExpressionKindTransmute: case ExpressionKindTransmute:

View File

@ -3,11 +3,11 @@
#include <codegen/backend.h> #include <codegen/backend.h>
#include <llvm-c/Core.h> #include <llvm-c/Core.h>
#include <llvm-c/Types.h> #include <llvm-c/Types.h>
#include <llvm/func.h>
#include <llvm/parser.h> #include <llvm/parser.h>
#include <llvm/types.h> #include <llvm/types.h>
#include <set/types.h> #include <set/types.h>
#include <sys/log.h> #include <sys/log.h>
#include <llvm/func.h>
LLVMLocalScope* new_local_scope(LLVMLocalScope* parent) { LLVMLocalScope* new_local_scope(LLVMLocalScope* parent) {
LLVMLocalScope* scope = malloc(sizeof(LLVMLocalScope)); LLVMLocalScope* scope = malloc(sizeof(LLVMLocalScope));
@ -24,7 +24,8 @@ void delete_local_scope(LLVMLocalScope* scope) {
free(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)) { if (g_hash_table_contains(scope->params, name)) {
return g_hash_table_lookup(scope->params, name); return g_hash_table_lookup(scope->params, name);
} }
@ -108,7 +109,8 @@ BackendError impl_func_decl(LLVMBackendCompileUnit* unit,
return err; return err;
} }
BackendError impl_func(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_scope, BackendError impl_func(LLVMBackendCompileUnit* unit,
LLVMGlobalScope* global_scope,
FunctionDefinition* fundef, const char* name) { FunctionDefinition* fundef, const char* name) {
BackendError err = SUCCESS; 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); func_scope->params = g_hash_table_new(g_str_hash, g_str_equal);
// store function type in global scope // 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 // 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); LLVMBuilderRef builder = LLVMCreateBuilderInContext(unit->context);
LLVMPositionBuilderAtEnd(builder, body); LLVMPositionBuilderAtEnd(builder, body);
// create value references for parameter // create value references for parameter
const size_t params = fundef->parameter->len; const size_t params = fundef->parameter->len;
for (size_t i = 0; i < params; i++) { for (size_t i = 0; i < params; i++) {
const Paramer* param = ((Paramer*) fundef->parameter) + i; const Paramer* param = ((Paramer*)fundef->parameter) + i;
g_hash_table_insert(func_scope->params, (gpointer) param->name, LLVMGetParam(llvm_func, i)); g_hash_table_insert(func_scope->params, (gpointer)param->name,
LLVMGetParam(llvm_func, i));
} }
// TODO: parse function body // TODO: parse function body
@ -149,3 +153,29 @@ BackendError impl_func(LLVMBackendCompileUnit* unit, LLVMGlobalScope* global_sco
return err; 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;
}

View File

@ -28,4 +28,8 @@ void delete_local_scope(LLVMLocalScope*);
LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name); LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name);
BackendError impl_functions(LLVMBackendCompileUnit* unit,
LLVMGlobalScope* scope,
GHashTable* variables);
#endif // LLVM_BACKEND_FUNC_H_ #endif // LLVM_BACKEND_FUNC_H_

View File

@ -8,15 +8,15 @@
#include <llvm/parser.h> #include <llvm/parser.h>
#include <llvm/types.h> #include <llvm/types.h>
#include <llvm/variables.h> #include <llvm/variables.h>
#include <llvm/func.h>
#include <set/types.h> #include <set/types.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/log.h> #include <sys/log.h>
#define EXPORT_IR 1 BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
const TargetConfig* config) {
BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target) {
DEBUG("exporting module to LLVM-IR..."); DEBUG("exporting module to LLVM-IR...");
BackendError err = SUCCESS; BackendError err = SUCCESS;
@ -25,8 +25,8 @@ BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target) {
char* ir = LLVMPrintModuleToString(unit->module); char* ir = LLVMPrintModuleToString(unit->module);
// construct file name // construct file name
char* filename = alloca(strlen(target->name.str) + 4); const char* filename =
sprintf(filename, "%s.ll", target->name.str); make_file_path(target->name.str, ".ll", 1, config->archive_directory);
INFO("Writing LLVM-IR to %s", filename); 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); INFO("%ld bytes written to %s", bytes, filename);
free((char*)filename);
// clean up LLVM-IR string // clean up LLVM-IR string
LLVMDisposeMessage(ir); LLVMDisposeMessage(ir);
return err; 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..."); DEBUG("exporting object file...");
INFO("Using target (%s): %s with features: %s", target->name.str, 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) != if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error) !=
0) { 0) {
ERROR("failed to create target machine: %s", error); ERROR("failed to create target machine: %s", error);
BackendError err = new_backend_impl_error( err = new_backend_impl_error(Implementation, NULL,
Implementation, NULL, "unable to create target machine"); "unable to create target machine");
LLVMDisposeMessage(error); LLVMDisposeMessage(error);
return err; 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, llvm_target, target->triple.str, target->cpu.str, target->features.str,
target->opt, target->reloc, target->model); 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 (err.kind != Success) {
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);
return err; return err;
} }
return SUCCESS; err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error,
config);
return err;
} }
void list_available_targets() { void list_available_targets() {
@ -120,23 +159,25 @@ void list_available_targets() {
LLVMDisposeMessage(default_triple); 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..."); DEBUG("exporting module...");
BackendError err = SUCCESS; BackendError err = SUCCESS;
export_object(unit, target); export_object(unit, target, config);
if (EXPORT_IR) { if (config->print_ir) {
export_IR(unit, target); export_IR(unit, target, config);
} else {
DEBUG("not exporting LLVM-IR");
} }
return err; 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; BackendError err = SUCCESS;
err = impl_types(unit, global_scope, module->types); err = impl_types(unit, global_scope, module->types);
@ -156,10 +197,13 @@ static BackendError build_module(LLVMBackendCompileUnit* unit, LLVMGlobalScope*
return err; return err;
} }
// TODO: implement functions
err = impl_functions(unit, global_scope, module->functions);
return err; 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); DEBUG("generating code for module %p", module);
if (module == NULL) { if (module == NULL) {
ERROR("no module for codegen"); ERROR("no module for codegen");
@ -171,20 +215,19 @@ BackendError parse_module(const Module* module, void**) {
// we start with a LLVM module // we start with a LLVM module
DEBUG("creating LLVM context and module"); DEBUG("creating LLVM context and module");
unit->context = LLVMContextCreate(); unit->context = LLVMContextCreate();
unit->module = LLVMModuleCreateWithNameInContext("gemstone application", unit->module =
unit->context); LLVMModuleCreateWithNameInContext(config->name, unit->context);
LLVMGlobalScope* global_scope = new_global_scope(); LLVMGlobalScope* global_scope = new_global_scope(module);
BackendError err = new_backend_error(Success);
DEBUG("generating code..."); DEBUG("generating code...");
err = build_module(unit, global_scope, module); BackendError err = build_module(unit, global_scope, module);
if (err.kind == Success) { 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); delete_target(target);
} }
@ -199,10 +242,11 @@ BackendError parse_module(const Module* module, void**) {
return err; return err;
} }
LLVMGlobalScope* new_global_scope() { LLVMGlobalScope* new_global_scope(const Module* module) {
DEBUG("creating global scope..."); DEBUG("creating global scope...");
LLVMGlobalScope* scope = malloc(sizeof(LLVMGlobalScope)); LLVMGlobalScope* scope = malloc(sizeof(LLVMGlobalScope));
scope->module = (Module*) module;
scope->functions = g_hash_table_new(g_str_hash, g_str_equal); 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->variables = g_hash_table_new(g_str_hash, g_str_equal);
scope->types = g_hash_table_new(g_str_hash, g_str_equal); scope->types = g_hash_table_new(g_str_hash, g_str_equal);

View File

@ -18,12 +18,16 @@ typedef struct LLVMGlobalScope_t {
GHashTable* variables; GHashTable* variables;
// of type LLVMTypeRef // of type LLVMTypeRef
GHashTable* functions; GHashTable* functions;
// module definition
Module* module;
} LLVMGlobalScope; } LLVMGlobalScope;
LLVMGlobalScope* new_global_scope(); LLVMGlobalScope* new_global_scope(const Module* module);
void list_available_targets();
void delete_global_scope(LLVMGlobalScope* scope); 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_ #endif // LLVM_BACKEND_PARSE_H_

View File

@ -103,6 +103,9 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit,
break; 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); g_array_append_vals(arguments, &llvm_arg, 1);
} }

View File

@ -18,8 +18,9 @@ BackendError get_type_default_value(LLVMBackendCompileUnit* unit,
LLVMGlobalScope* scope, Type* gemstone_type, LLVMGlobalScope* scope, Type* gemstone_type,
LLVMValueRef* llvm_value); LLVMValueRef* llvm_value);
BackendError get_type_value(LLVMBackendCompileUnit* unit, BackendError get_const_type_value(LLVMBackendCompileUnit* unit,
LLVMGlobalScope* scope, TypeValue* gemstone_value, LLVMGlobalScope* scope,
LLVMValueRef* llvm_value); TypeValue* gemstone_value,
LLVMValueRef* llvm_value);
#endif // LLVM_BACKEND_TYPES_H_ #endif // LLVM_BACKEND_TYPES_H_

View File

@ -5,6 +5,7 @@
#include <lex/util.h> #include <lex/util.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <compiler.h> #include <compiler.h>
#include <llvm/parser.h>
/** /**
* @brief Log a debug message to inform about beginning exit procedures * @brief Log a debug message to inform about beginning exit procedures
@ -55,6 +56,11 @@ int main(int argc, char *argv[]) {
exit(0); exit(0);
} }
if (is_option_set("list-targets")) {
list_available_targets();
exit(0);
}
run_compiler(); run_compiler();
return 0; return 0;