gemstone/src/llvm/parser.c

300 lines
8.9 KiB
C

#include <codegen/backend.h>
#include <llvm-c/Core.h>
#include <llvm-c/Target.h>
#include <llvm-c/TargetMachine.h>
#include <llvm-c/Types.h>
#include <llvm-c/Analysis.h>
#include <llvm/backend.h>
#include <llvm/parser.h>
#include <llvm/llvm-ir/types.h>
#include <llvm/llvm-ir/variables.h>
#include <llvm/llvm-ir/func.h>
#include <set/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/log.h>
#include <llvm/link/lld.h>
BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
const TargetConfig* config) {
DEBUG("exporting module to LLVM-IR...");
BackendError err = SUCCESS;
// convert module to LLVM-IR
char* ir = LLVMPrintModuleToString(unit->module);
char* basename = g_strjoin(".", target->name.str, "ll", NULL);
// construct file name
const char* filename = g_build_filename(config->archive_directory, basename, NULL);
INFO("Writing LLVM-IR to %s", filename);
DEBUG("opening file...");
FILE* output = fopen(filename, "w");
if (output == NULL) {
ERROR("unable to open file: %s", filename);
err = new_backend_impl_error(Implementation, NULL,
"unable to open file for writing");
LLVMDisposeMessage(ir);
}
DEBUG("printing LLVM-IR to file...");
size_t bytes = fprintf(output, "%s", ir);
// flush and close output file
fflush(output);
fclose(output);
INFO("%ld bytes written to %s", bytes, filename);
g_free((char*)filename);
g_free(basename);
// clean up LLVM-IR string
LLVMDisposeMessage(ir);
return err;
}
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* basename;
const char* filename;
switch (file_type) {
case LLVMAssemblyFile:
basename = g_strjoin(".", config->name, "s", NULL);
filename = g_build_filename(config->archive_directory, basename, NULL);
break;
case LLVMObjectFile:
basename = g_strjoin(".", config->name, "o", NULL);
filename = g_build_filename(config->archive_directory, basename, NULL);
break;
default:
return new_backend_impl_error(Implementation, NULL,
"invalid codegen file");
}
INFO("export to file: %s", filename);
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);
} else {
print_message(Info, "Generated code was written to: %s", filename);
}
g_free((void*) filename);
g_free((void*) basename);
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,
target->triple.str, target->features.str);
LLVMTargetRef llvm_target = NULL;
char* error = NULL;
LLVMInitializeAllTargets();
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
// NOTE: for code generation (assmebly or binary) we need the following:
LLVMInitializeAllAsmParsers();
LLVMInitializeAllAsmPrinters();
DEBUG("creating target...");
if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error) !=
0) {
ERROR("failed to create target machine: %s", error);
err = new_backend_impl_error(Implementation, NULL,
"unable to create target machine");
LLVMDisposeMessage(error);
return err;
}
DEBUG("Creating target machine...");
LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine(
llvm_target, target->triple.str, target->cpu.str, target->features.str,
target->opt, target->reloc, target->model);
print_message(Info, "Generating code for: %s", target->triple.str);
if (config->print_asm) {
err = emit_module_to_file(unit, target_machine, LLVMAssemblyFile, error,
config);
}
if (err.kind != Success) {
return err;
}
err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error,
config);
return err;
}
void list_available_targets() {
DEBUG("initializing all available targets...");
LLVMInitializeAllTargetInfos();
printf("Available targets:\n");
LLVMTargetRef target = LLVMGetFirstTarget();
while (target) {
const char* name = LLVMGetTargetName(target);
const char* desc = LLVMGetTargetDescription(target);
printf(" - %s: (%s)\n", name, desc);
target = LLVMGetNextTarget(target);
}
char* default_triple = LLVMGetDefaultTargetTriple();
printf("Default: %s\n", default_triple);
LLVMDisposeMessage(default_triple);
}
BackendError export_module(LLVMBackendCompileUnit* unit, const Target* target,
const TargetConfig* config) {
DEBUG("exporting module...");
BackendError err = SUCCESS;
export_object(unit, target, config);
if (config->print_ir) {
export_IR(unit, target, config);
}
return err;
}
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);
if (err.kind != Success) {
return err;
}
// NOTE: functions of boxes are not stored in the box itself,
// thus for a box we only implement the type
err = impl_types(unit, global_scope, module->boxes);
if (err.kind != Success) {
return err;
}
err = impl_global_variables(unit, global_scope, module->variables);
if (err.kind != Success) {
return err;
}
err = impl_function_types(unit, global_scope, module->functions);
if (err.kind != Success) {
return err;
}
err = impl_functions(unit, global_scope, module->functions);
if (err.kind != Success) {
return err;
}
char* error = NULL;
if (LLVMVerifyModule(unit->module, LLVMReturnStatusAction, &error)) {
print_message(Error, "Unable to compile due to: %s", error);
LLVMDisposeMessage(error);
err = new_backend_impl_error(Implementation, NULL, "LLVM backend verification error, see stdout");
}
return err;
}
BackendError parse_module(const Module* module, const TargetConfig* config) {
DEBUG("generating code for module %p", module);
if (module == NULL) {
ERROR("no module for codegen");
return new_backend_impl_error(Implementation, NULL, "no module");
}
LLVMBackendCompileUnit* unit = malloc(sizeof(LLVMBackendCompileUnit));
// we start with a LLVM module
DEBUG("creating LLVM context and module");
unit->context = LLVMContextCreate();
unit->module =
LLVMModuleCreateWithNameInContext(config->root_module, unit->context);
LLVMGlobalScope* global_scope = new_global_scope(module);
DEBUG("generating code...");
BackendError err = build_module(unit, global_scope, module);
if (err.kind == Success) {
INFO("Module build successfully...");
Target target = create_target_from_config(config);
err = export_module(unit, &target, config);
if (err.kind == Success) {
if (config->mode == Application) {
TargetLinkConfig* link_config = lld_create_link_config(&target, config, module);
lld_link_target(link_config);
lld_delete_link_config(link_config);
}
}
delete_target(target);
}
delete_global_scope(global_scope);
LLVMDisposeModule(unit->module);
LLVMContextDispose(unit->context);
free(unit);
return err;
}
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);
return scope;
}
void delete_global_scope(LLVMGlobalScope* scope) {
DEBUG("deleting global scope...");
g_hash_table_unref(scope->functions);
g_hash_table_unref(scope->types);
g_hash_table_unref(scope->variables);
free(scope);
}