Files
c3c/src/compiler/llvm_codegen.c

1033 lines
33 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 inline void llvm_set_alignment(LLVMValueRef alloca, LLVMTypeRef type, AlignSize alignment)
{
LLVMSetAlignment(alloca, alignment ?: llvm_abi_alignment(type));
}
static int get_inlining_threshold(void);
static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context)
{
char *message = LLVMGetDiagInfoDescription(ref);
LLVMDiagnosticSeverity severity = LLVMGetDiagInfoSeverity(ref);
const char *severerity_name = "unknown";
switch (severity)
{
case LLVMDSError:
error_exit("LLVM error generating code for %s: %s", ((GenContext *)context)->ast_context->module->name, message);
break;
case LLVMDSWarning:
severerity_name = "warning";
break;
case LLVMDSRemark:
severerity_name = "remark";
break;
case LLVMDSNote:
severerity_name = "note";
break;
}
DEBUG_LOG("LLVM message [%s]: %s ", severerity_name, message);
LLVMDisposeMessage(message);
}
static void gencontext_init(GenContext *context, Context *ast_context)
{
memset(context, 0, sizeof(GenContext));
context->context = LLVMContextCreate();
context->bool_type = LLVMInt1TypeInContext(context->context);
context->byte_type = LLVMInt8TypeInContext(context->context);
LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context);
context->ast_context = ast_context;
}
static void gencontext_destroy(GenContext *context)
{
free(context->ir_filename);
free(context->object_filename);
LLVMContextDispose(context->context);
free(context);
}
LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, unsigned int align, bool bitcast)
{
LLVMValueRef target = bitcast ? LLVMBuildBitCast(c->builder, ref, llvm_get_type(c, type_get_ptr(type_byte)), "") : ref;
return LLVMBuildMemSet(c->builder, target, LLVMConstInt(llvm_get_type(c, type_byte), 0, false),
LLVMConstInt(llvm_get_type(c, type_ulong), size, false), align);
}
LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref)
{
// TODO avoid bitcast on those that do not need them.
llvm_value_addr(c, ref);
return llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true);
}
LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified)
{
if (diff == 1) return LLVMConstNull(element_type);
*modified = true;
LLVMTypeRef padding_type = LLVMArrayType(element_type, (unsigned)diff);
return LLVMConstNull(padding_type);
}
LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init, bool *modified)
{
switch (const_init->kind)
{
case CONST_INIT_ZERO:
return LLVMConstNull(llvm_get_type(c, const_init->type));
case CONST_INIT_ARRAY_VALUE:
UNREACHABLE
case CONST_INIT_ARRAY_FULL:
{
bool was_modified = false;
Type *array_type = const_init->type;
Type *element_type = array_type->array.base;
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
ConstInitializer **elements = const_init->init_array_full;
ArrayIndex size = array_type->array.len;
assert(size > 0);
LLVMValueRef *parts = VECNEW(LLVMValueRef, size);
for (ArrayIndex i = 0; i < size; i++)
{
vec_add(parts, llvm_emit_const_initializer(c, elements[i], &was_modified));
}
if (was_modified) *modified = was_modified;
if (was_modified)
{
return LLVMConstStructInContext(c->context, parts, vec_size(parts), true);
}
return LLVMConstArray(element_type_llvm, parts, vec_size(parts));
}
case CONST_INIT_ARRAY:
{
bool was_modified = false;
Type *array_type = const_init->type;
Type *element_type = array_type->array.base;
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
ConstInitializer **elements = const_init->init_array.elements;
unsigned element_count = vec_size(elements);
assert(element_count > 0 && "Array should always have gotten at least one element.");
ArrayIndex current_index = 0;
unsigned alignment = 0;
LLVMValueRef *parts = NULL;
bool pack = false;
VECEACH(elements, i)
{
ConstInitializer *element = elements[i];
assert(element->kind == CONST_INIT_ARRAY_VALUE);
ArrayIndex element_index = element->init_array_value.index;
IndexDiff diff = element_index - current_index;
unsigned curr_alignment = type_abi_alignment(element->type);
if (alignment && curr_alignment != alignment)
{
pack = true;
}
alignment = curr_alignment;
// Add zeroes
if (diff > 0)
{
vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified));
}
vec_add(parts, llvm_emit_const_initializer(c, element->init_array_value.element, &was_modified));
current_index = element_index + 1;
}
IndexDiff end_diff = array_type->array.len - current_index;
if (end_diff > 0)
{
vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, end_diff, &was_modified));
}
if (was_modified) *modified = was_modified;
if (was_modified)
{
return LLVMConstStructInContext(c->context, parts, vec_size(parts), pack);
}
return LLVMConstArray(element_type_llvm, parts, vec_size(parts));
}
case CONST_INIT_UNION:
{
Decl *decl = const_init->type->decl;
// Get the type of the member that is used for this particular
// constant value, for example if this is a union { double, int }
// then the type may be double or int.
Type *member_type = decl->strukt.members[const_init->init_union.index]->type->canonical;
// Emit our value.
LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element, modified);
// Get the union value
LLVMTypeRef union_type_llvm = llvm_get_type(c, decl->type);
// Take the first type in the union (note that there may be padding!)
LLVMTypeRef first_type = LLVMStructGetTypeAtIndex(union_type_llvm, 0);
// We need to calculate some possible padding.
ByteSize union_size = type_size(const_init->type);
ByteSize member_size = type_size(member_type);
ByteSize padding = union_size - member_size;
// Create the resulting values:
LLVMValueRef values[2] = { result, NULL };
unsigned value_count = 1;
// Add possible padding as and i8 array.
if (padding > 0)
{
values[1] = llvm_emit_const_padding(c, padding);
value_count = 2;
}
// Is this another type than usual for the union?
if (first_type != llvm_get_type(c, member_type))
{
// Yes, so the type needs to be modified.
*modified = true;
}
// If it is modified we simply create a packed struct for representation.
if (*modified)
{
return LLVMConstStructInContext(c->context, values, value_count, false);
}
return LLVMConstNamedStruct(union_type_llvm, values, value_count);
}
case CONST_INIT_STRUCT:
{
Decl *decl = const_init->type->decl;
Decl **members = decl->strukt.members;
MemberIndex count = vec_size(members);
LLVMValueRef *entries = NULL;
for (MemberIndex i = 0; i < count; i++)
{
if (members[i]->padding)
{
vec_add(entries, llvm_emit_const_padding(c, members[i]->padding));
}
vec_add(entries, llvm_emit_const_initializer(c, const_init->init_struct[i], modified));
}
if (decl->strukt.padding)
{
vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding));
}
if (*modified)
{
return LLVMConstStructInContext(c->context, entries, vec_size(entries), decl->is_packed);
}
return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, vec_size(entries));
}
case CONST_INIT_VALUE:
{
BEValue value;
llvm_emit_expr(c, &value, const_init->init_value);
LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value);
LLVMTypeRef expected_type = llvm_get_type(c, const_init->type);
if (expected_type != LLVMTypeOf(llvm_value)) *modified = true;
return llvm_value;
}
}
UNREACHABLE
}
static LLVMValueRef llvm_emit_const_initializer_simple(GenContext *c, Expr *expr, bool *modified)
{
Expr **elements = expr->initializer_expr.initializer_expr;
unsigned element_count = vec_size(elements);
LLVMValueRef* values = malloc_arena(element_count * sizeof(LLVMValueRef));
Type *expr_type = expr->type->canonical;
LLVMTypeRef array_type = expr_type->type_kind == TYPE_ARRAY ? llvm_get_type(c, expr_type->array.base) : NULL;
assert(array_type || type_is_structlike(expr_type));
for (unsigned i = 0; i < element_count; i++)
{
BEValue value;
llvm_emit_expr(c, &value, elements[i]);
LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value);
LLVMTypeRef expected_type = array_type ? array_type : llvm_get_type(c, expr_type->decl->strukt.members[0]->type);
if (expected_type != LLVMTypeOf(llvm_value)) *modified = true;
values[i] = llvm_value;
}
if (array_type)
{
if (*modified)
{
return LLVMConstStructInContext(c->context, values, element_count, true);
}
return LLVMConstArray(array_type, values, element_count);
}
if (*modified)
{
return LLVMConstStructInContext(c->context, values, element_count, true);
}
return LLVMConstNamedStruct(llvm_get_type(c, expr_type), values, element_count);
}
LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified)
{
switch (expr->initializer_expr.init_type)
{
case INITIALIZER_UNKNOWN:
case INITIALIZER_DESIGNATED:
UNREACHABLE
case INITIALIZER_CONST:
return llvm_emit_const_initializer(c, expr->initializer_expr.initializer, modified);
case INITIALIZER_NORMAL:
return llvm_emit_const_initializer_simple(c, expr, modified);
}
UNREACHABLE
}
static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl)
{
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
// Skip real constants.
if (!decl->type) return;
bool modified = false;
LLVMValueRef init_value;
ByteSize alignment = type_abi_alignment(decl->type);
if (decl->var.init_expr)
{
if (decl->var.init_expr->expr_kind == EXPR_INITIALIZER_LIST)
{
init_value = llvm_emit_const_aggregate(c, decl->var.init_expr, &modified);
}
else
{
BEValue value;
assert(decl->var.init_expr->expr_kind == EXPR_CONST);
llvm_emit_expr(c, &value, decl->var.init_expr);
init_value = llvm_value_rvalue_store(c, &value);
}
}
else
{
init_value = LLVMConstNull(llvm_get_type(c, decl->type));
}
// TODO fix name
decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name);
LLVMSetAlignment(decl->backend_ref, alignment);
if (decl->visibility != VISIBLE_EXTERN)
{
LLVMSetInitializer(decl->backend_ref, init_value);
}
LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST);
switch (decl->visibility)
{
case VISIBLE_MODULE:
LLVMSetVisibility(decl->backend_ref, LLVMProtectedVisibility);
break;
case VISIBLE_PUBLIC:
LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
break;
case VISIBLE_EXTERN:
LLVMSetLinkage(decl->backend_ref, LLVMExternalLinkage);
//LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
break;
case VISIBLE_LOCAL:
LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility);
break;
}
if (modified)
{
decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, decl->type));
}
// Should we set linkage here?
if (llvm_use_debug(c))
{
llvm_emit_debug_global_var(c, decl);
}
}
static void gencontext_verify_ir(GenContext *context)
{
char *error = NULL;
assert(context->module);
if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &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_triple);
char *layout = LLVMCopyStringRepOfTargetData(target_data_layout());
LLVMSetDataLayout(context->module, layout);
LLVMDisposeMessage(layout);
// Generate .o or .obj file
if (LLVMTargetMachineEmitToFile(target_machine(), context->module, context->object_filename, LLVMObjectFile, &err))
{
error_exit("Could not emit object file: %s", err);
}
}
void gencontext_print_llvm_ir(GenContext *context)
{
char *err = NULL;
if (LLVMPrintModuleToFile(context->module, context->ir_filename, &err))
{
error_exit("Could not emit ir to file: %s", err);
}
}
LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name)
{
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, type, name);
llvm_set_alignment(alloca, type, alignment);
LLVMPositionBuilderAtEnd(context->builder, current_block);
return alloca;
}
LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name)
{
return llvm_emit_alloca(c, llvm_get_type(c, type), type_alloca_alignment(type), name);
}
LLVMValueRef llvm_emit_decl_alloca(GenContext *c, Decl *decl)
{
LLVMTypeRef type = llvm_get_type(c, decl->type);
return llvm_emit_alloca(c,
type,
decl->alignment ?: type_alloca_alignment(decl->type),
decl->name ?: "anon");
}
/**
* 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;
}
}
static inline unsigned lookup_intrinsic(const char *name)
{
return LLVMLookupIntrinsicID(name, strlen(name));
}
static inline unsigned lookup_attribute(const char *name)
{
return LLVMGetEnumAttributeKindForName(name, strlen(name));
}
static bool intrinsics_setup = false;
unsigned intrinsic_id_trap;
unsigned intrinsic_id_assume;
unsigned intrinsic_id_ssub_overflow;
unsigned intrinsic_id_ssub_sat;
unsigned intrinsic_id_usub_overflow;
unsigned intrinsic_id_usub_sat;
unsigned intrinsic_id_sadd_overflow;
unsigned intrinsic_id_sadd_sat;
unsigned intrinsic_id_uadd_overflow;
unsigned intrinsic_id_uadd_sat;
unsigned intrinsic_id_smul_overflow;
unsigned intrinsic_id_umul_overflow;
unsigned intrinsic_id_sshl_sat;
unsigned intrinsic_id_ushl_sat;
unsigned intrinsic_id_fmuladd;
unsigned intrinsic_id_rint;
unsigned intrinsic_id_trunc;
unsigned intrinsic_id_ceil;
unsigned intrinsic_id_floor;
unsigned intrinsic_id_sqrt;
unsigned intrinsic_id_nearbyint;
unsigned intrinsic_id_roundeven;
unsigned intrinsic_in_lround;
unsigned intrinsic_in_llround;
unsigned intrinsic_in_lrint;
unsigned intrinsic_in_llrint;
unsigned intrinsic_id_powi;
unsigned intrinsic_id_pow;
unsigned intrinsic_id_sin;
unsigned intrinsic_id_cos;
unsigned intrinsic_id_exp;
unsigned intrinsic_id_exp2;
unsigned intrinsic_id_log;
unsigned intrinsic_id_log10;
unsigned intrinsic_id_fabs;
unsigned intrinsic_id_fma;
unsigned intrinsic_id_copysign;
unsigned intrinsic_id_minnum;
unsigned intrinsic_id_maxnum;
unsigned intrinsic_id_minimum;
unsigned intrinsic_id_maximum;
unsigned intrinsic_id_smax;
unsigned intrinsic_id_smin;
unsigned intrinsic_id_umax;
unsigned intrinsic_id_umin;
unsigned intrinsic_id_abs;
unsigned intrinsic_id_fshl;
unsigned intrinsic_id_fshr;
unsigned intrinsic_id_bitreverse;
unsigned intrinsic_id_bswap;
unsigned intrinsic_id_ctpop;
unsigned intrinsic_id_ctlz;
unsigned intrinsic_id_cttz;
unsigned intrinsic_id_convert_from_fp16;
unsigned intrinsic_id_convert_to_fp16;
unsigned attribute_noinline;
unsigned attribute_alwaysinline;
unsigned attribute_inlinehint;
unsigned attribute_noreturn;
unsigned attribute_nounwind;
unsigned attribute_writeonly;
unsigned attribute_readonly;
unsigned attribute_optnone;
unsigned attribute_align;
unsigned attribute_noalias;
unsigned attribute_sret;
unsigned attribute_zext;
unsigned attribute_sext;
unsigned attribute_byval;
unsigned attribute_inreg;
void llvm_codegen_setup()
{
assert(intrinsics_setup == false);
intrinsic_id_trap = lookup_intrinsic("llvm.trap");
intrinsic_id_assume = lookup_intrinsic("llvm.assume");
intrinsic_id_ssub_overflow = lookup_intrinsic("llvm.ssub.with.overflow");
intrinsic_id_ssub_sat = lookup_intrinsic("llvm.ssub.sat");
intrinsic_id_usub_overflow = lookup_intrinsic("llvm.usub.with.overflow");
intrinsic_id_usub_sat = lookup_intrinsic("llvm.usub.sat");
intrinsic_id_sadd_overflow = lookup_intrinsic("llvm.sadd.with.overflow");
intrinsic_id_sadd_sat = lookup_intrinsic("llvm.sadd.sat");
intrinsic_id_uadd_overflow = lookup_intrinsic("llvm.uadd.with.overflow");
intrinsic_id_uadd_sat = lookup_intrinsic("llvm.uadd.sat");
intrinsic_id_smul_overflow = lookup_intrinsic("llvm.smul.with.overflow");
intrinsic_id_umul_overflow = lookup_intrinsic("llvm.umul.with.overflow");
//intrinsic_id_sshl_sat = lookup_intrinsic("llvm.sshl.sat");
//intrinsic_id_ushl_sat = lookup_intrinsic("llvm.ushl.sat");
intrinsic_id_fshl = lookup_intrinsic("llvm.fshl");
intrinsic_id_fshr = lookup_intrinsic("llvm.fshr");
intrinsic_id_bitreverse = lookup_intrinsic("llvm.bitreverse");
intrinsic_id_bswap = lookup_intrinsic("llvm.bswap");
intrinsic_id_ctpop = lookup_intrinsic("llvm.ctpop");
intrinsic_id_cttz = lookup_intrinsic("llvm.cttz");
intrinsic_id_ctlz = lookup_intrinsic("llvm.ctlz");
intrinsic_id_rint = lookup_intrinsic("llvm.rint");
intrinsic_id_trunc = lookup_intrinsic("llvm.trunc");
intrinsic_id_ceil = lookup_intrinsic("llvm.ceil");
intrinsic_id_floor = lookup_intrinsic("llvm.floor");
intrinsic_id_sqrt = lookup_intrinsic("llvm.sqrt");
intrinsic_id_powi = lookup_intrinsic("llvm.powi");
intrinsic_id_pow = lookup_intrinsic("llvm.pow");
intrinsic_id_sin = lookup_intrinsic("llvm.sin");
intrinsic_id_cos = lookup_intrinsic("llvm.cos");
intrinsic_id_exp = lookup_intrinsic("llvm.exp");
intrinsic_id_exp2 = lookup_intrinsic("llvm.exp2");
intrinsic_id_log = lookup_intrinsic("llvm.log");
intrinsic_id_log10 = lookup_intrinsic("llvm.log10");
intrinsic_id_fabs = lookup_intrinsic("llvm.fabs");
intrinsic_id_fma = lookup_intrinsic("llvm.fma");
intrinsic_id_copysign = lookup_intrinsic("llvm.copysign");
intrinsic_id_minnum = lookup_intrinsic("llvm.minnum");
intrinsic_id_maxnum = lookup_intrinsic("llvm.maxnum");
intrinsic_id_minimum = lookup_intrinsic("llvm.minimum");
intrinsic_id_maximum = lookup_intrinsic("llvm.maximum");
intrinsic_id_convert_to_fp16 = lookup_intrinsic("llvm.convert.to.fp16");
intrinsic_id_convert_from_fp16 = lookup_intrinsic("llvm.convert.from.fp16");
intrinsic_id_nearbyint = lookup_intrinsic("llvm.nearbyint");
intrinsic_id_roundeven = lookup_intrinsic("llvm.roundeven");
intrinsic_in_lround = lookup_intrinsic("llvm.lround");
intrinsic_in_llround = lookup_intrinsic("llvm.llround");
intrinsic_in_lrint = lookup_intrinsic("llvm.lrint");
intrinsic_in_llrint = lookup_intrinsic("llvm.llrint");
//intrinsic_id_abs = lookup_intrinsic("llvm.abs");
intrinsic_id_smax = lookup_intrinsic("llvm.smax");
intrinsic_id_smin = lookup_intrinsic("llvm.smin");
intrinsic_id_umax = lookup_intrinsic("llvm.umax");
intrinsic_id_umin = lookup_intrinsic("llvm.umin");
attribute_noinline = lookup_attribute("noinline");
attribute_alwaysinline = lookup_attribute("alwaysinline");
attribute_inlinehint = lookup_attribute("inlinehint");
attribute_noreturn = lookup_attribute("noreturn");
attribute_nounwind = lookup_attribute("nounwind");
attribute_writeonly = lookup_attribute("writeonly");
attribute_readonly = lookup_attribute("readonly");
attribute_optnone = lookup_attribute("optnone");
attribute_sret = lookup_attribute("sret");
attribute_noalias = lookup_attribute("noalias");
attribute_zext = lookup_attribute("zeroext");
attribute_sext = lookup_attribute("signext");
attribute_align = lookup_attribute("align");
attribute_byval = lookup_attribute("byval");
attribute_inreg = lookup_attribute("inreg");
intrinsics_setup = true;
}
void gencontext_emit_introspection_type(GenContext *context, Decl *decl)
{
llvm_get_type(context, decl->type);
if (decl_is_struct_type(decl))
{
Decl **decls = decl->strukt.members;
VECEACH(decls, i)
{
Decl *member_decl = decls[i];
if (decl_is_struct_type(member_decl))
{
gencontext_emit_introspection_type(context, member_decl);
}
}
}
LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_get_type(context, type_byte), decl->name ? decl->name : "anon");
LLVMSetGlobalConstant(global_name, 1);
LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(context, type_byte), 1, false));
decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(context, type_typeid));
switch (decl->visibility)
{
case VISIBLE_MODULE:
case VISIBLE_PUBLIC:
LLVMSetLinkage(global_name, LLVMLinkOnceODRLinkage);
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
break;
case VISIBLE_EXTERN:
case VISIBLE_LOCAL:
LLVMSetVisibility(global_name, LLVMHiddenVisibility);
LLVMSetLinkage(global_name, LLVMLinkerPrivateLinkage);
break;
}
}
static inline uint32_t upper_power_of_two(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value)
{
value->value = llvm_value;
value->alignment = type_abi_alignment(type_bool);
value->kind = BE_BOOLEAN;
value->type = type_bool;
}
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type)
{
value->value = llvm_value;
value->alignment = type_abi_alignment(type);
value->kind = BE_VALUE;
value->type = type;
}
bool llvm_value_is_const(BEValue *value)
{
return LLVMIsConstant(value->value);
}
void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, unsigned alignment)
{
value->value = llvm_value;
value->alignment = alignment;
value->kind = BE_ADDRESS;
value->type = type;
}
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type)
{
llvm_value_set_address_align(value, llvm_value, type, type_abi_alignment(type));
}
void llvm_value_fold_failable(GenContext *c, BEValue *value)
{
if (value->kind == BE_ADDRESS_FAILABLE)
{
LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check");
// TODO optimize load.
LLVMValueRef error_value = gencontext_emit_load(c, type_error, value->failable);
BEValue comp;
llvm_value_set_bool(&comp, llvm_emit_is_no_error(c, error_value));
if (c->error_var)
{
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error");
llvm_emit_cond_br(c, &comp, after_block, error_block);
llvm_emit_block(c, error_block);
llvm_store_aligned(c, c->error_var, error_value, type_abi_alignment(type_usize));
llvm_emit_br(c, c->catch_block);
}
else
{
llvm_emit_cond_br(c, &comp, after_block, c->catch_block);
}
llvm_emit_block(c, after_block);
value->kind = BE_ADDRESS;
}
}
LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value)
{
llvm_value_fold_failable(c, value);
switch (value->kind)
{
case BE_VALUE:
return value->value;
case BE_ADDRESS_FAILABLE:
UNREACHABLE
case BE_ADDRESS:
return llvm_emit_load_aligned(c,
llvm_get_type(c, value->type),
value->value,
value->alignment ?: type_abi_alignment(value->type),
"");
case BE_BOOLEAN:
return LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
}
UNREACHABLE
}
LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name)
{
return LLVMCreateBasicBlockInContext(c->context, name);
}
void llvm_value_addr(GenContext *c, BEValue *value)
{
llvm_value_fold_failable(c, value);
if (value->kind == BE_ADDRESS) return;
LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "tempaddr");
llvm_store_self_aligned(c, temp, value->value, value->type);
llvm_value_set_address(value, temp, value->type);
}
void llvm_value_rvalue(GenContext *c, BEValue *value)
{
if (value->kind != BE_ADDRESS && value->kind != BE_ADDRESS_FAILABLE) return;
llvm_value_fold_failable(c, value);
value->value = llvm_emit_load_aligned(c,
llvm_get_type(c, value->type),
value->value,
value->alignment ?: type_abi_alignment(value->type),
"");
if (value->type->type_kind == TYPE_BOOL)
{
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "");
value->kind = BE_BOOLEAN;
return;
}
value->kind = BE_VALUE;
}
static void gencontext_emit_decl(GenContext *context, Decl *decl)
{
switch (decl->decl_kind)
{
case DECL_POISONED:
UNREACHABLE;
case DECL_FUNC:
// TODO
break;
case DECL_VAR:
// TODO
break;
case DECL_TYPEDEF:
break;
case DECL_ENUM_CONSTANT:
// TODO
break;;
case DECL_STRUCT:
case DECL_UNION:
case DECL_ERR:
gencontext_emit_introspection_type(context, decl);
break;
case DECL_ENUM:
// TODO
break;
case NON_TYPE_DECLS:
UNREACHABLE
}
}
void llvm_codegen(void *context)
{
GenContext *gen_context = context;
LLVMModuleRef module = gen_context->module;
// 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(module);
LLVMAddAnalysisPasses(target_machine(), function_pass_manager);
LLVMAddAnalysisPasses(target_machine(), 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(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, module);
LLVMDisposePassManager(pass_manager);
// Serialize the LLVM IR, if requested, also verify the IR in this case
if (build_options.emit_llvm)
{
gencontext_print_llvm_ir(gen_context);
gencontext_verify_ir(gen_context);
}
if (build_options.emit_bitcode) gencontext_emit_object_file(gen_context);
gencontext_end_module(gen_context);
gencontext_destroy(gen_context);
}
void *llvm_gen(Context *context)
{
assert(intrinsics_setup);
GenContext *gen_context = calloc(sizeof(GenContext), 1);
gencontext_init(gen_context, context);
gencontext_begin_module(gen_context);
// EmitDeferred()
VECEACH(context->external_symbol_list, i)
{
llvm_emit_extern_decl(gen_context, context->external_symbol_list[i]);
}
VECEACH(context->methods, i)
{
llvm_emit_function_decl(gen_context, context->methods[i]);
}
VECEACH(context->functions, i)
{
llvm_emit_function_decl(gen_context, context->functions[i]);
}
VECEACH(context->types, i)
{
gencontext_emit_decl(gen_context, context->types[i]);
}
VECEACH(context->vars, i)
{
gencontext_emit_global_variable_definition(gen_context, context->vars[i]);
}
VECEACH(context->functions, i)
{
Decl *decl = context->functions[i];
if (decl->func.body) llvm_emit_function_body(gen_context, decl);
}
VECEACH(context->methods, i)
{
Decl *decl = context->methods[i];
if (decl->func.body) llvm_emit_function_body(gen_context, decl);
}
if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(gen_context->debug.builder);
// If it's in test, then we want to serialize the IR before it is optimized.
if (build_options.test_mode)
{
gencontext_print_llvm_ir(gen_context);
gencontext_verify_ir(gen_context);
}
return gen_context;
}
void llvm_attribute_add_int(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, uint64_t val, int index)
{
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute_id, val);
LLVMAddAttributeAtIndex(value_to_add_attribute_to, index, llvm_attr);
}
void llvm_attribute_add(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index)
{
llvm_attribute_add_int(context, value_to_add_attribute_to, attribute_id, 0, index);
}
void llvm_attribute_add_range(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index_start, int index_end)
{
for (int i = index_start; i <= index_end; i++)
{
llvm_attribute_add_int(context, value_to_add_attribute_to, attribute_id, 0, i);
}
}
void llvm_attribute_add_string(GenContext *context, LLVMValueRef value_to_add_attribute_to, const char *attribute, const char *value, int index)
{
LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(context->context, attribute, strlen(attribute), value, strlen(value));
LLVMAddAttributeAtIndex(value_to_add_attribute_to, index, llvm_attr);
}
unsigned llvm_abi_size(LLVMTypeRef type)
{
return LLVMABISizeOfType(target_data_layout(), type);
}
AlignSize llvm_abi_alignment(LLVMTypeRef type)
{
return (AlignSize)LLVMABIAlignmentOfType(target_data_layout(), type);
}
void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment)
{
// If we have an address but not an aggregate, do a load.
llvm_value_fold_failable(c, value);
if (value->kind == BE_ADDRESS && !type_is_abi_aggregate(value->type))
{
value->value = llvm_emit_load_aligned(c, llvm_get_type(c, value->type), value->value, value->alignment, "");
value->kind = BE_VALUE;
}
switch (value->kind)
{
case BE_BOOLEAN:
value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, "");
FALLTHROUGH;
case BE_VALUE:
llvm_store_aligned(c, destination, value->value, alignment ?: type_abi_alignment(value->type));
return;
case BE_ADDRESS_FAILABLE:
UNREACHABLE
case BE_ADDRESS:
{
// Here we do an optimized(?) memcopy.
ByteSize size = type_size(value->type);
LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size);
destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_byte), "");
LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_byte), "");
LLVMBuildMemCpy(c->builder, destination, alignment ?: type_abi_alignment(value->type),
source, value->alignment ?: type_abi_alignment(value->type), copy_size);
return;
}
}
UNREACHABLE
}
void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value)
{
llvm_store_bevalue_aligned(c, destination, value, LLVMGetAlignment(destination));
}
void llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value)
{
assert(llvm_value_is_addr(destination));
llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment);
}
void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value)
{
assert(llvm_value_is_addr(destination));
llvm_store_aligned(c, destination->value, raw_value, destination->alignment);
}
void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type)
{
llvm_store_aligned(context, pointer, value, type_abi_alignment(type));
}
void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment)
{
LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer);
if (alignment) LLVMSetAlignment(ref, alignment);
}
void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value)
{
llvm_store_aligned(context, decl->backend_ref, value, decl->alignment);
}
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len)
{
assert(dest_align && src_align);
if (len <= UINT32_MAX)
{
LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len));
return;
}
LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len));
}
void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment)
{
if (source_alignment == 0) source_alignment = type_abi_alignment(decl->type);
llvm_emit_memcpy(c, decl->backend_ref, decl->alignment ?: type_abi_alignment(decl->type),
source, source_alignment, type_size(decl->type));
}
LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name)
{
LLVMValueRef value = LLVMBuildLoad2(context->builder, type, pointer, name);
llvm_set_alignment(value, type, alignment);
return value;
}
unsigned llvm_store_size(LLVMTypeRef type)
{
return LLVMStoreSizeOfType(target_data_layout(), type);
}