mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
224 lines
7.6 KiB
C
224 lines
7.6 KiB
C
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "llvm_codegen_internal.h"
|
|
|
|
|
|
|
|
static void gencontext_init(GenContext *context, Context *ast_context)
|
|
{
|
|
memset(context, 0, sizeof(GenContext));
|
|
context->context = LLVMContextCreate();
|
|
context->ast_context = ast_context;
|
|
}
|
|
|
|
static void gencontext_destroy(GenContext *context)
|
|
{
|
|
LLVMContextDispose(context->context);
|
|
}
|
|
|
|
|
|
static LLVMValueRef gencontext_emit_null_constant(GenContext *context, Type *type)
|
|
{
|
|
TODO
|
|
}
|
|
|
|
static LLVMValueRef gencontext_emit_initializer(GenContext *context, Expr *expr)
|
|
{
|
|
TODO
|
|
}
|
|
static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl, bool is_tentative)
|
|
{
|
|
assert(decl->var.kind == VARDECL_GLOBAL);
|
|
|
|
LLVMValueRef init = NULL;
|
|
|
|
if (!decl->var.init_expr)
|
|
{
|
|
// Tentative definition, initialized to zero, but only
|
|
// emitted at the end of the translation unit.
|
|
init = gencontext_emit_null_constant(context, decl->type);
|
|
}
|
|
else
|
|
{
|
|
init = gencontext_emit_initializer(context, decl->var.init_expr);
|
|
}
|
|
|
|
// TODO fix name
|
|
decl->var.backend_ref = LLVMAddGlobal(context->module, decl->type->backend_type, decl->name.string);
|
|
|
|
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
|
|
|
|
switch (decl->visibility)
|
|
{
|
|
case VISIBLE_MODULE:
|
|
LLVMSetVisibility(decl->var.backend_ref, LLVMProtectedVisibility);
|
|
break;
|
|
case VISIBLE_PUBLIC:
|
|
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
|
break;
|
|
case VISIBLE_EXTERN:
|
|
case VISIBLE_LOCAL:
|
|
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
|
break;
|
|
}
|
|
|
|
int alignment = 64; // TODO
|
|
// Should we set linkage here?
|
|
if (context->debug.builder)
|
|
{
|
|
decl->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression(context->debug.builder,
|
|
NULL /*scope*/,
|
|
decl->name.string,
|
|
decl->name.span.length,
|
|
"linkagename",
|
|
2,
|
|
context->debug.file,
|
|
12 /* lineno */,
|
|
decl->type->backend_debug_type,
|
|
decl->visibility ==
|
|
VISIBLE_LOCAL, /* expr */
|
|
NULL, /** declaration **/
|
|
NULL,
|
|
alignment);
|
|
}
|
|
}
|
|
static void gencontext_verify_ir(GenContext *context)
|
|
{
|
|
char *error = NULL;
|
|
assert(context->module);
|
|
LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error);
|
|
if (error)
|
|
{
|
|
if (*error)
|
|
{
|
|
error_exit("Could not verify IR: %s", error);
|
|
}
|
|
error_exit("Could not verify module IR.");
|
|
}
|
|
}
|
|
|
|
void gencontext_emit_object_file(GenContext *context)
|
|
{
|
|
char *err = "";
|
|
LLVMSetTarget(context->module, build_options.target);
|
|
char *layout = LLVMCopyStringRepOfTargetData(target_data_layout());
|
|
LLVMSetDataLayout(context->module, layout);
|
|
LLVMDisposeMessage(layout);
|
|
|
|
// Generate .o or .obj file
|
|
char *filename = strformat("%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name);
|
|
if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err) != 0)
|
|
{
|
|
error_exit("Could not emit object file: %s", err);
|
|
}
|
|
}
|
|
|
|
void gencontext_print_llvm_ir(GenContext *context)
|
|
{
|
|
char *err = NULL;
|
|
char *filename = strformat("%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name);
|
|
if (LLVMPrintModuleToFile(context->module, filename, &err) != 0)
|
|
{
|
|
error_exit("Could not emit ir to file: %s", err);
|
|
}
|
|
}
|
|
|
|
|
|
LLVMValueRef gencontext_emit_alloca(GenContext *context, LLVMTypeRef type, const char *name)
|
|
{
|
|
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
|
|
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
|
|
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, type, name);
|
|
LLVMPositionBuilderAtEnd(context->builder, current_block);
|
|
return alloca;
|
|
}
|
|
|
|
/**
|
|
* Values here taken from LLVM.
|
|
* @return return the inlining threshold given the build options.
|
|
*/
|
|
static int get_inlining_threshold(void)
|
|
{
|
|
if (build_options.optimization_level == OPTIMIZATION_AGGRESSIVE)
|
|
{
|
|
return 250;
|
|
}
|
|
switch (build_options.size_optimization_level)
|
|
{
|
|
case SIZE_OPTIMIZATION_TINY:
|
|
return 5;
|
|
case SIZE_OPTIMIZATION_SMALL:
|
|
return 50;
|
|
default:
|
|
return 250;
|
|
}
|
|
}
|
|
void llvm_codegen(Context *context)
|
|
{
|
|
GenContext gen_context;
|
|
gencontext_init(&gen_context, context);
|
|
gencontext_begin_module(&gen_context);
|
|
// EmitDeferred()
|
|
VECEACH(context->external_symbol_list, i)
|
|
{
|
|
gencontext_emit_extern_decl(&gen_context, context->external_symbol_list[i]);
|
|
}
|
|
VECEACH(context->functions, i)
|
|
{
|
|
gencontext_emit_function_decl(&gen_context, context->functions[i]);
|
|
}
|
|
|
|
VECEACH(gen_context.generated_types, i)
|
|
{
|
|
Type *type = gen_context.generated_types[i];
|
|
type->backend_debug_type = NULL;
|
|
type->backend_type = NULL;
|
|
}
|
|
|
|
gencontext_print_llvm_ir(&gen_context);
|
|
|
|
// Starting from here we could potentially thread this:
|
|
LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
|
|
LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level);
|
|
LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, build_options.size_optimization_level);
|
|
LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, build_options.optimization_level == OPTIMIZATION_NONE);
|
|
LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, get_inlining_threshold());
|
|
LLVMPassManagerRef pass_manager = LLVMCreatePassManager();
|
|
LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(gen_context.module);
|
|
LLVMAddAnalysisPasses(target_machine(), pass_manager);
|
|
LLVMAddAnalysisPasses(target_machine(), function_pass_manager);
|
|
LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager);
|
|
LLVMPassManagerBuilderPopulateFunctionPassManager(pass_manager_builder, function_pass_manager);
|
|
|
|
// IMPROVE
|
|
// In LLVM Opt, LoopVectorize and SLPVectorize settings are part of the PassManagerBuilder
|
|
// Anything else we need to manually add?
|
|
|
|
LLVMPassManagerBuilderDispose(pass_manager_builder);
|
|
|
|
// Run function passes
|
|
LLVMInitializeFunctionPassManager(function_pass_manager);
|
|
LLVMValueRef current_function = LLVMGetFirstFunction(gen_context.module);
|
|
while (current_function)
|
|
{
|
|
LLVMRunFunctionPassManager(function_pass_manager, current_function);
|
|
current_function = LLVMGetNextFunction(current_function);
|
|
}
|
|
LLVMFinalizeFunctionPassManager(function_pass_manager);
|
|
LLVMDisposePassManager(function_pass_manager);
|
|
|
|
// Run module pass
|
|
LLVMRunPassManager(pass_manager, gen_context.module);
|
|
LLVMDisposePassManager(pass_manager);
|
|
|
|
// Serialize the LLVM IR, if requested
|
|
if (build_options.emit_llvm) gencontext_print_llvm_ir(&gen_context);
|
|
|
|
if (build_options.emit_bitcode) gencontext_emit_object_file(&gen_context);
|
|
|
|
gencontext_end_module(&gen_context);
|
|
gencontext_destroy(&gen_context);
|
|
}
|