build bridge between compiler config and backend config
This commit is contained in:
parent
05634db44a
commit
54c7103df1
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue