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",
" --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++) {

View File

@ -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;

View File

@ -4,6 +4,7 @@
#include <set/types.h>
#include <ast/ast.h>
#include <cfg/opt.h>
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

View File

@ -11,6 +11,8 @@
#include <io/files.h>
#include <assert.h>
#include <cfg/opt.h>
#include <codegen/backend.h>
#include <llvm/backend.h>
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);
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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:

View File

@ -3,11 +3,11 @@
#include <codegen/backend.h>
#include <llvm-c/Core.h>
#include <llvm-c/Types.h>
#include <llvm/func.h>
#include <llvm/parser.h>
#include <llvm/types.h>
#include <set/types.h>
#include <sys/log.h>
#include <llvm/func.h>
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;
}

View File

@ -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_

View File

@ -8,15 +8,15 @@
#include <llvm/parser.h>
#include <llvm/types.h>
#include <llvm/variables.h>
#include <llvm/func.h>
#include <set/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/log.h>
#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);

View File

@ -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_

View File

@ -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);
}

View File

@ -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_

View File

@ -5,6 +5,7 @@
#include <lex/util.h>
#include <cfg/opt.h>
#include <compiler.h>
#include <llvm/parser.h>
/**
* @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;