mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
1033 lines
33 KiB
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);
|
|
}
|