Files
c3c/src/compiler/llvm_codegen.c
2026-01-07 00:16:49 +01:00

1854 lines
62 KiB
C

// Copyright (c) 2019-2025 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"
#include "compiler_tests/benchmark.h"
#include "c3_llvm.h"
#include <llvm-c/Comdat.h>
#include <llvm-c/Linker.h>
#include <llvm-c/Transforms/PassBuilder.h>
const char *varargslots_name = "varargslots";
const char *temp_name = "$$temp";
static void llvm_emit_constructors_and_destructors(GenContext *c);
static void llvm_codegen_setup();
static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context);
const char* llvm_version = LLVM_VERSION_STRING;
const char* llvm_target = LLVM_DEFAULT_TARGET_TRIPLE;
static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context)
{
char *message = LLVMGetDiagInfoDescription(ref);
LLVMDiagnosticSeverity severity = LLVMGetDiagInfoSeverity(ref);
const char *severity_name;
switch (severity)
{
case LLVMDSError:
error_exit("LLVM error generating code for %s: %s", ((GenContext *)context)->code_module->name->module, message);
case LLVMDSWarning:
severity_name = "warning";
break;
case LLVMDSRemark:
severity_name = "remark";
break;
case LLVMDSNote:
severity_name = "note";
break;
default:
severity_name = "message";
break;
}
#ifdef NDEBUG
// Avoid warnings when not in debug.
(void)severity_name; (void)message;
#endif
DEBUG_LOG("LLVM %s: %s ", severity_name, message);
LLVMDisposeMessage(message);
}
bool module_should_weaken(Module *module)
{
Module *top = module->top_module;
return top && (top->name->module == kw_std || top->name->module == kw_libc);
}
static void gencontext_init(GenContext *context, Module *module, LLVMContextRef shared_context)
{
ASSERT(LLVMIsMultithreaded());
memset(context, 0, sizeof(GenContext));
if (shared_context)
{
context->shared_context = true;
context->context = shared_context;
}
else
{
context->context = LLVMContextCreate();
}
if (debug_log)
{
LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context);
}
if (!compiler.build.emit_llvm && !compiler.build.test_output && !compiler.build.benchmark_output ) LLVMContextSetDiscardValueNames(context->context, true);
context->code_module = module;
}
static void gencontext_destroy(GenContext *context)
{
ASSERT(llvm_is_global_eval(context));
LLVMDisposeBuilder(context->global_builder);
if (!context->shared_context) LLVMContextDispose(context->context);
LLVMDisposeTargetData(context->target_data);
LLVMDisposeTargetMachine(context->machine);
free(context);
}
LLVMBuilderRef llvm_create_builder(GenContext *c)
{
LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context);
LLVMBuilderSetFastMathFlags(builder, (FastMathOption)compiler.build.feature.fp_math);
return builder;
}
LLVMValueRef llvm_emit_is_no_opt(GenContext *c, LLVMValueRef error_value)
{
LLVMValueRef compare = LLVMBuildICmp(c->builder, LLVMIntEQ, error_value, llvm_get_zero(c, type_fault), "not_err");
return llvm_emit_expect_raw(c, compare);
}
LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ptr, uint64_t size, AlignSize align)
{
return LLVMBuildMemSet(c->builder, ptr, llvm_get_zero(c, type_char), llvm_const_int(c, type_usz, size), align);
}
INLINE void llvm_emit_xtor(GenContext *c, LLVMValueRef *list, const char *name)
{
if (!list) return;
unsigned len = vec_size(list);
LLVMTypeRef type = LLVMTypeOf(list[0]);
LLVMValueRef array = LLVMConstArray(type, list, len);
LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), name);
LLVMSetLinkage(global, LLVMAppendingLinkage);
LLVMSetInitializer(global, array);
}
LLVMValueRef llvm_get_selector(GenContext *c, const char *name)
{
scratch_buffer_clear();
scratch_buffer_printf("$sel.%s", name);
const char *sel_name = scratch_buffer_to_string();
LLVMValueRef selector_old = LLVMGetNamedGlobal(c->module, sel_name);
if (selector_old) return selector_old;
size_t name_len = strlen(name);
LLVMTypeRef char_array_type = LLVMArrayType(c->byte_type, name_len + 1);
LLVMValueRef selector = llvm_add_global_raw(c, sel_name, char_array_type, 0);
LLVMSetGlobalConstant(selector, 1);
LLVMSetInitializer(selector, llvm_get_zstring(c, name, name_len));
llvm_set_selector_linkage(c, selector);
return selector;
}
static LLVMValueRef llvm_emit_macho_xtor_use(GenContext *c, LLVMValueRef global, const char *name)
{
LLVMValueRef initializer = LLVMAddFunction(c->module, name, c->xtor_func_type);
LLVMSetLinkage(initializer, LLVMInternalLinkage);
LLVMSetAlignment(initializer, 8);
LLVMValueRef vals_fn[3] = { llvm_const_int(c, type_int, 1), initializer, llvm_get_zero(c, type_voidptr) };
LLVMValueRef result = LLVMConstNamedStruct(c->xtor_entry_type, vals_fn, 3);
LLVMBasicBlockRef last_block;
LLVMBuilderRef builder = llvm_create_function_entry(c, initializer, &last_block);
LLVMValueRef load = LLVMBuildLoad2(builder, LLVMTypeOf(LLVMGetInitializer(global)), global, ".retain_global");
LLVMSetVolatile(load, true);
LLVMBuildRetVoid(builder);
LLVMDisposeBuilder(builder);
return result;
}
static LLVMValueRef llvm_emit_macho_xtor(GenContext *c, LLVMValueRef *list, const char *name, const char *retain_name)
{
unsigned len = vec_size(list);
if (!len) return NULL;
scratch_buffer_clear();
scratch_buffer_append(".list$");
scratch_buffer_append(name);
LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, list, vec_size(list));
LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), scratch_buffer_to_string());
LLVMSetNoSanitizeAddress(global);
scratch_buffer_clear();
scratch_buffer_append("__DATA,__");
scratch_buffer_append(name);
LLVMSetLinkage(global, LLVMInternalLinkage);
LLVMSetInitializer(global, array);
LLVMSetSection(global, scratch_buffer_to_string());
LLVMSetAlignment(global, llvm_abi_alignment(c, c->xtor_entry_type));
return llvm_emit_macho_xtor_use(c, global, retain_name);
}
void llvm_emit_constructors_and_destructors(GenContext *c)
{
if (compiler.platform.object_format == OBJ_FORMAT_MACHO)
{
LLVMValueRef c3_dynamic = LLVMGetNamedGlobal(c->module, "$c3_dynamic");
LLVMValueRef dtor_global = llvm_emit_macho_xtor(c, c->constructors, "c3ctor", ".c3_ctor_retain");
LLVMValueRef ctor_global = llvm_emit_macho_xtor(c, c->destructors, "c3dtor", ".c3_dtor_retain");
LLVMValueRef runtime_start = LLVMGetNamedFunction(c->module, "__c3_runtime_startup");
int len = 0;
LLVMValueRef ctors[5];
if (ctor_global) ctors[len++] = ctor_global;
if (dtor_global) ctors[len++] = dtor_global;
if (c3_dynamic) ctors[len++] = llvm_emit_macho_xtor_use(c, c3_dynamic, ".c3_dynamic_retain");
if (!runtime_start || !LLVMGetFirstBasicBlock(runtime_start))
{
goto EMIT_CTORS;
}
LLVMValueRef runtime_end = LLVMGetNamedFunction(c->module, "__c3_runtime_finalize");
if (!runtime_end || !LLVMGetFirstBasicBlock(runtime_end)) error_exit("Failed to find __c3_runtime_finalize in the same module as __c3_runtime_startup.");
LLVMValueRef vals[3] = { llvm_const_int(c, type_int, 65535), runtime_end, llvm_get_zero(c, type_voidptr) };
LLVMValueRef entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3);
LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, &entry, 1);
LLVMValueRef global_dtor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_dtors");
LLVMSetNoSanitizeAddress(global_dtor);
LLVMSetLinkage(global_dtor, LLVMAppendingLinkage);
LLVMSetInitializer(global_dtor, array);
vals[1] = runtime_start;
entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3);
ctors[len++] = entry;
EMIT_CTORS:
if (len == 0) return;
array = LLVMConstArray(c->xtor_entry_type, ctors, len);
LLVMValueRef global_ctor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_ctors");
LLVMSetNoSanitizeAddress(global_ctor);
LLVMSetLinkage(global_ctor, LLVMAppendingLinkage);
LLVMSetInitializer(global_ctor, array);
return;
}
llvm_emit_xtor(c, c->constructors, "llvm.global_ctors");
llvm_emit_xtor(c, c->destructors, "llvm.global_dtors");
}
/**
* Consider the case when we have int[5] x = { [0] = 1, [1] = 3 }
* In this case we want this: { i32 0, i32 2, [8 x i32] zeroinitializer }
* If it's just a single element we don't use [1 x i32] but just i32 0. If it's
* an array then this is modifying the original type.
*/
static LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified)
{
if (diff == 1) return llvm_get_zero_raw(element_type);
*modified = true;
return llvm_get_zero_raw(LLVMArrayType(element_type, (unsigned)diff));
}
LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init, bool in_aggregate)
{
ASSERT(const_init->type == type_flatten(const_init->type));
Type *type = in_aggregate && const_init->type->type_kind == TYPE_VECTOR ? type_array_from_vector(const_init->type) : const_init->type;
switch (const_init->kind)
{
case CONST_INIT_ZERO:
return llvm_get_zero(c, type);
case CONST_INIT_ARRAY_VALUE:
UNREACHABLE
case CONST_INIT_ARRAY_FULL:
{
ASSERT(type->type_kind != TYPE_SLICE);
bool was_modified = false;
Type *element_type = type->array.base;
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
ConstInitializer **elements = const_init->init_array_full;
ASSERT(type_is_arraylike(type));
ArraySize size = type->array.len;
ASSERT(size > 0);
LLVMValueRef *parts = VECNEW(LLVMValueRef, size);
for (ArrayIndex i = 0; i < (ArrayIndex)size; i++)
{
LLVMValueRef element = llvm_emit_const_initializer(c, elements[i], true);
if (element_type_llvm != LLVMTypeOf(element)) was_modified = true;
vec_add(parts, element);
}
if (type->type_kind == TYPE_VECTOR || type->type_kind == TYPE_SIMD_VECTOR)
{
return LLVMConstVector(parts, vec_size(parts));
}
if (was_modified)
{
return llvm_get_unnamed_struct(c, parts, true);
}
return llvm_get_array(element_type_llvm, parts, vec_size(parts));
}
case CONST_INIT_ARRAY:
{
ASSERT(type->type_kind != TYPE_SLICE);
bool was_modified = false;
Type *element_type = type->array.base;
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
AlignSize expected_align = llvm_abi_alignment(c, element_type_llvm);
ConstInitializer **elements = const_init->init_array.elements;
ASSERT(vec_size(elements) > 0 && "Array should always have gotten at least one element.");
if (elements > 0 && type->type_kind == TYPE_FLEXIBLE_ARRAY) was_modified = true;
ArrayIndex current_index = 0;
unsigned alignment = 0;
LLVMValueRef *parts = NULL;
bool pack = false;
bool is_vec = type->type_kind == TYPE_SIMD_VECTOR || type->type_kind == TYPE_VECTOR;
FOREACH(ConstInitializer *, element, elements)
{
ASSERT(element->kind == CONST_INIT_ARRAY_VALUE);
ArrayIndex element_index = element->init_array_value.index;
IndexDiff diff = element_index - current_index;
if (alignment && expected_align != alignment)
{
pack = true;
}
alignment = expected_align;
// Add zeroes
if (diff > 0)
{
if (is_vec)
{
for (int i = 0; i < diff; i++)
{
vec_add(parts, llvm_get_zero_raw(element_type_llvm));
}
}
else
{
vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified));
}
}
LLVMValueRef value = llvm_emit_const_initializer(c, element->init_array_value.element, true);
if (LLVMTypeOf(value) != element_type_llvm) was_modified = true;
vec_add(parts, value);
current_index = element_index + 1;
}
IndexDiff end_diff = (ArrayIndex)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)
{
return llvm_get_unnamed_struct(c, parts, pack);
}
if (type_flat_is_vector(type))
{
return LLVMConstVector(parts, vec_size(parts));
}
return llvm_get_array(element_type_llvm, parts, vec_size(parts));
}
case CONST_INIT_UNION:
{
Decl *decl = const_init->type->decl;
// Emit our value.
LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element, true);
LLVMTypeRef result_type = LLVMTypeOf(result);
// 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.
TypeSize union_size = type_size(const_init->type);
TypeSize member_size = llvm_abi_size(c, result_type);
// Create the resulting values:
LLVMValueRef values[2] = { result, NULL };
unsigned value_count = 1;
// Add possible padding as and i8 array.
if (union_size > member_size)
{
values[1] = llvm_emit_const_padding(c, union_size - member_size);
value_count = 2;
}
// Is this another type than usual for the union?
if (first_type != result_type)
{
// Yes, so the type needs to be modified.
return llvm_get_struct(c, values, value_count);
}
return llvm_get_struct_named(union_type_llvm, values, value_count);
}
case CONST_INIT_STRUCT:
{
if (const_init->type->type_kind == TYPE_BITSTRUCT)
{
return llvm_emit_const_bitstruct(c, const_init);
}
Decl *decl = const_init->type->decl;
Decl **members = decl->strukt.members;
bool is_packed = decl->is_packed;
uint32_t count = vec_size(members);
if (decl->decl_kind == DECL_UNION && count) count = 1;
LLVMValueRef *entries = NULL;
bool was_modified = false;
ByteSize prev_size = 0;
for (ArrayIndex i = 0; i < count; i++)
{
Decl *member = members[i];
if (member->padding)
{
vec_add(entries, llvm_emit_const_padding(c, member->padding));
}
LLVMValueRef element = llvm_emit_const_initializer(c, const_init->init_struct[i], true);
LLVMTypeRef element_type = LLVMTypeOf(element);
//ASSERT(LLVMIsConstant(element));
if (llvm_get_type(c, member->type) != element_type)
{
was_modified = true;
}
// We may need to adjust alignment here due to the lack
// of LLVM union support (while still being strict about structs)
if (i > 0 && was_modified)
{
// Let's look at the old offset.
ByteSize old_offset = members[i - 1]->offset;
// What is the expected offset we would get?
ByteSize new_offset = is_packed ? old_offset + prev_size : aligned_offset(old_offset + prev_size, llvm_abi_alignment(c, element_type));
// Add the padding we have built in.
new_offset += member->padding;
// If this offset is too small, add const padding.
if (new_offset < member->offset)
{
vec_add(entries, llvm_emit_const_padding(c, member->offset - new_offset));
}
}
prev_size = llvm_abi_size(c, element_type);
vec_add(entries, element);
}
if (decl->strukt.padding)
{
vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding));
}
if (was_modified)
{
return llvm_get_unnamed_struct(c, entries, decl->is_packed);
}
return llvm_get_struct_of_type(c, const_init->type, entries, vec_size(entries));
}
case CONST_INIT_VALUE:
{
BEValue value;
llvm_emit_expr_global_value(c, &value, const_init->init_value);
return llvm_load_value_store(c, &value);
}
}
UNREACHABLE
}
void llvm_emit_ptr_from_array(GenContext *c, BEValue *value)
{
switch (value->type->type_kind)
{
case TYPE_POINTER:
llvm_value_rvalue(c, value);
value->kind = BE_ADDRESS;
return;
case TYPE_ARRAY:
case VECTORS:
case TYPE_FLEXIBLE_ARRAY:
return;
case TYPE_SLICE:
{
BEValue member;
llvm_emit_slice_pointer(c, value, &member);
llvm_value_rvalue(c, &member);
llvm_value_set_address(c,
value,
member.value,
type_get_ptr(value->type->array.base), type_abi_alignment(value->type->array.base));
return;
}
default:
UNREACHABLE_VOID
}
}
void llvm_set_global_tls(Decl *decl)
{
if (!decl->var.is_threadlocal) return;
LLVMThreadLocalMode thread_local_mode = LLVMGeneralDynamicTLSModel;
if (!decl->var.is_addr && decl_is_local(decl))
{
thread_local_mode = LLVMLocalDynamicTLSModel;
}
LLVMSetThreadLocal(decl->backend_ref, true);
LLVMSetThreadLocalMode(decl->backend_ref, thread_local_mode);
void *optional_ref = decl->var.optional_ref;
if (optional_ref)
{
LLVMSetThreadLocal(optional_ref, true);
LLVMSetThreadLocalMode(optional_ref, thread_local_mode);
}
}
void llvm_set_weak(GenContext *c, LLVMValueRef global)
{
LLVMSetLinkage(global, compiler.platform.os == OS_TYPE_WIN32 ? LLVMWeakODRLinkage : LLVMWeakAnyLinkage);
LLVMSetVisibility(global, LLVMDefaultVisibility);
llvm_set_comdat(c, global);
}
static void llvm_set_external_reference(LLVMValueRef ref, bool is_weak)
{
if (compiler.platform.os == OS_TYPE_WIN32) is_weak = false;
LLVMSetLinkage(ref, is_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage);
LLVMSetVisibility(ref, LLVMDefaultVisibility);
}
void llvm_set_decl_linkage(GenContext *c, Decl *decl)
{
bool is_var = decl->decl_kind == DECL_VAR;
bool is_weak = decl->is_weak;
bool should_weaken = is_weak || (!decl->is_extern && (decl->is_templated || module_should_weaken(decl->unit->module)));
LLVMValueRef ref = decl->backend_ref;
LLVMValueRef opt_ref = is_var ? decl->var.optional_ref : NULL;
bool is_static = is_var && decl->var.is_static;
// Static variables in a different modules should be copied to the current module.
bool same_module = is_static || decl->unit->module == c->code_module;
if (decl->is_extern || !same_module)
{
llvm_set_external_reference(ref, should_weaken);
if (opt_ref) llvm_set_external_reference(opt_ref, should_weaken);
return;
}
if (decl_is_externally_visible(decl) && !is_static)
{
if (decl->is_export && compiler.platform.os == OS_TYPE_WIN32 && !compiler.build.win.def && decl->name != kw_main && decl->name != kw_mainstub)
{
LLVMSetDLLStorageClass(ref, LLVMDLLExportStorageClass);
}
if (should_weaken)
{
llvm_set_weak(c, ref);
if (opt_ref) llvm_set_weak(c, opt_ref);
return;
}
LLVMSetVisibility(ref, LLVMDefaultVisibility);
if (opt_ref) LLVMSetVisibility(opt_ref, LLVMDefaultVisibility);
return;
}
LLVMSetLinkage(ref, decl->is_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
if (opt_ref) LLVMSetLinkage(opt_ref, LLVMInternalLinkage);
}
void llvm_set_internal_linkage(LLVMValueRef alloc)
{
LLVMSetLinkage(alloc, LLVMInternalLinkage);
LLVMSetVisibility(alloc, LLVMDefaultVisibility);
}
void llvm_set_private_declaration(LLVMValueRef alloc)
{
LLVMSetLinkage(alloc, LLVMPrivateLinkage);
LLVMSetVisibility(alloc, LLVMDefaultVisibility);
LLVMSetUnnamedAddress(alloc, LLVMGlobalUnnamedAddr);
}
void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
{
ASSERT(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST || decl->var.is_static);
// Skip real constants.
if (!decl->type) return;
decl_append_links_to_global_during_codegen(decl);
LLVMValueRef init_value;
Type *var_type = type_lowering(decl->type);
Expr *init_expr = decl->var.init_expr;
// Fold "source" of the init.
while (init_expr && init_expr->expr_kind == EXPR_IDENTIFIER)
{
Decl *inner_decl = decl_flatten(init_expr->ident_expr);
if (inner_decl->decl_kind != DECL_VAR) break;
if (inner_decl->var.kind != VARDECL_CONST) break;
init_expr = inner_decl->var.init_expr;
}
if (init_expr && init_expr->expr_kind != EXPR_OPTIONAL)
{
if (expr_is_const_initializer(init_expr))
{
ASSERT(type_flatten(init_expr->type)->type_kind != TYPE_SLICE);
ConstInitializer *list = init_expr->const_expr.initializer;
init_value = llvm_emit_const_initializer(c, list, false);
}
else
{
BEValue value;
llvm_emit_expr_global_value(c, &value, init_expr);
init_value = llvm_load_value_store(c, &value);
}
}
else
{
init_value = decl->var.no_init ? llvm_get_undef(c, var_type) : llvm_get_zero(c, var_type);
}
LLVMValueRef old = decl->backend_ref;
scratch_buffer_set_extern_decl_name(decl, true);
char *name = scratch_buffer_copy();
LLVMValueRef global_ref = decl->backend_ref = llvm_add_global_raw(c, name, LLVMTypeOf(init_value), decl->alignment);
if (llvm_use_debug(c))
{
SourceSpan loc = decl->span;
decl->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression(
c->debug.builder,
c->debug.file.debug_file,
decl->name,
strlen(decl->name),
name,
strlen(name),
c->debug.file.debug_file,
loc.row ? loc.row : 1,
llvm_get_debug_type(c, decl->type),
decl_is_local(decl),
LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0),
NULL,
decl->alignment * 8);
LLVMGlobalSetMetadata(llvm_get_ref(c, decl), 0, decl->var.backend_debug_ref);
}
if (decl->var.is_addr)
{
LLVMSetUnnamedAddress(global_ref, LLVMNoUnnamedAddr);
}
else
{
LLVMSetUnnamedAddress(decl->backend_ref,
decl_is_local(decl) ? LLVMGlobalUnnamedAddr : LLVMLocalUnnamedAddr);
}
if (decl->attrs_resolved && decl->attrs_resolved->section)
{
LLVMSetSection(global_ref, decl->attrs_resolved->section);
}
llvm_set_global_tls(decl);
LLVMValueRef optional_ref = decl->var.optional_ref;
if (optional_ref)
{
llvm_set_alignment(optional_ref, type_alloca_alignment(type_fault));
LLVMSetUnnamedAddress(optional_ref, LLVMGlobalUnnamedAddr);
}
LLVMValueRef optional_value = NULL;
if (init_expr && IS_OPTIONAL(init_expr) && init_expr->expr_kind == EXPR_OPTIONAL)
{
Expr *inner = init_expr->inner_expr;
ASSERT(expr_is_const(inner) && inner->const_expr.const_kind == CONST_FAULT);
BEValue value;
llvm_emit_expr_global_value(c, &value, inner);
optional_value = llvm_load_value_store(c, &value);
}
if (!decl->is_extern)
{
LLVMSetInitializer(decl->backend_ref, init_value);
if (optional_ref)
{
LLVMSetInitializer(optional_ref, optional_value ? optional_value : llvm_get_zero(c, type_fault));
}
}
LLVMSetGlobalConstant(global_ref, decl->var.kind == VARDECL_CONST);
decl->backend_ref = global_ref;
if (old)
{
LLVMReplaceAllUsesWith(old, global_ref);
LLVMDeleteGlobal(old);
}
llvm_set_decl_linkage(c, decl);
}
static void gencontext_verify_ir(GenContext *context)
{
char *error = NULL;
ASSERT(context->module);
if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error))
{
if (*error)
{
eprintf("----------------------------------IR integrity failure:\n");
LLVMDumpModule(context->module);
error_exit("Could not verify IR: %s", error);
}
error_exit("Could not verify module IR.");
}
}
static void llvm_emit_file(GenContext *c, const char *filename, LLVMCodeGenFileType llvm_codegen_type, bool clone_module)
{
DEBUG_LOG("Target: %s", compiler.platform.target_triple);
LLVMModuleRef module = clone_module ? LLVMCloneModule(c->module) : c->module;
LLVMSetTarget(module, compiler.platform.target_triple);
char *layout = LLVMCopyStringRepOfTargetData(c->target_data);
LLVMSetDataLayout(module, layout);
LLVMDisposeMessage(layout);
char *err = "";
FILE *file = NULL;
LLVMMemoryBufferRef buffer = NULL;
if (LLVMTargetMachineEmitToMemoryBuffer(c->machine, module, llvm_codegen_type, &err, &buffer))
{
goto ERR;
}
if (clone_module)
{
LLVMDisposeModule(module);
}
file = fopen(filename, "wb");
if (!file)
{
err = "File could not be opened";
goto ERR;
}
size_t len = LLVMGetBufferSize(buffer);
const char *ptr = LLVMGetBufferStart(buffer);
while (len > 0)
{
size_t written = fwrite(ptr, 1, len, file);
if (written == 0)
{
err = "Failed to write to file";
goto ERR;
}
ptr += written;
len -= written;
}
fclose(file);
LLVMDisposeMemoryBuffer(buffer);
return;
ERR:
if (file) fclose(file);
if (buffer) LLVMDisposeMemoryBuffer(buffer);
error_exit("Could not emit '%s': %s", filename, 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 '%s' to file: %s", context->ir_filename, err);
}
}
INLINE LLVMValueRef llvm_emit_alloca_internal(GenContext *c, LLVMTypeRef type, unsigned alignment, const char *name)
{
ASSERT(LLVMGetTypeKind(type) != LLVMVoidTypeKind);
ASSERT(!llvm_is_global_eval(c));
ASSERT(alignment > 0);
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(c->builder);
LLVMPositionBuilderBefore(c->builder, c->alloca_point);
ASSERT(LLVMGetTypeContext(type) == c->context);
LLVMValueRef alloca = LLVMBuildAlloca(c->builder, type, name);
llvm_set_alignment(alloca, alignment);
LLVMPositionBuilderAtEnd(c->builder, current_block);
return alloca;
}
BEValue llvm_emit_alloca_b(GenContext *c, Type *type, const char *name)
{
type = type_lowering(type);
if (type->type_kind == TYPE_VECTOR)
{
type = type_get_vector(type->array.base, TYPE_SIMD_VECTOR, type->array.len);
}
LLVMTypeRef llvm_type = llvm_get_type(c, type);
AlignSize alignment = type_alloca_alignment(type);
LLVMValueRef alloca = llvm_emit_alloca_internal(c, llvm_type, alignment, name);
return (BEValue){.value = alloca, .alignment = alignment, .kind = BE_ADDRESS, .type = type};
}
BEValue llvm_emit_alloca_b_realign(GenContext *c, Type *type, AlignSize alignment, const char *name)
{
ASSERT(alignment != 0);
type = type_lowering(type);
LLVMTypeRef llvm_type = llvm_get_type(c, type);
LLVMValueRef alloca = llvm_emit_alloca_internal(c, llvm_type, alignment, name);
return (BEValue){.value = alloca, .alignment = alignment, .kind = BE_ADDRESS, .type = type};
}
LLVMValueRef llvm_emit_alloca(GenContext *c, LLVMTypeRef type, unsigned alignment, const char *name)
{
return llvm_emit_alloca_internal(c, type, alignment, name);
}
void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl)
{
Type *type = type_lowering(decl->type);
if (type == type_void) return;
ASSERT(!decl->backend_ref && !decl->is_value);
decl->backend_ref = llvm_emit_alloca_internal(c, llvm_get_type(c, type), decl->alignment, decl->name ? decl->name : ".anon");
}
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl)
{
ASSERT(!decl->var.is_static && decl->var.kind != VARDECL_CONST);
llvm_emit_and_set_decl_alloca(c, decl);
if (llvm_use_debug(c))
{
llvm_emit_debug_local_var(c, decl);
}
}
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;
LLVMAttributes attribute_id;
LLVMIntrinsics intrinsic_id;
static void llvm_codegen_setup()
{
if (intrinsics_setup) return;
intrinsic_id.abs = lookup_intrinsic("llvm.abs");
intrinsic_id.assume = lookup_intrinsic("llvm.assume");
intrinsic_id.bitreverse = lookup_intrinsic("llvm.bitreverse");
intrinsic_id.bswap = lookup_intrinsic("llvm.bswap");
intrinsic_id.ceil = lookup_intrinsic("llvm.ceil");
intrinsic_id.convert_from_fp16 = lookup_intrinsic("llvm.convert.from.fp16");
intrinsic_id.convert_to_fp16 = lookup_intrinsic("llvm.convert.to.fp16");
intrinsic_id.copysign = lookup_intrinsic("llvm.copysign");
intrinsic_id.cos = lookup_intrinsic("llvm.cos");
intrinsic_id.ctlz = lookup_intrinsic("llvm.ctlz");
intrinsic_id.ctpop = lookup_intrinsic("llvm.ctpop");
intrinsic_id.cttz = lookup_intrinsic("llvm.cttz");
intrinsic_id.debugtrap = lookup_intrinsic("llvm.debugtrap");
intrinsic_id.exp = lookup_intrinsic("llvm.exp");
intrinsic_id.exp2 = lookup_intrinsic("llvm.exp2");
intrinsic_id.expect = lookup_intrinsic("llvm.expect");
intrinsic_id.expect_with_probability = lookup_intrinsic("llvm.expect.with.probability");
intrinsic_id.fabs = lookup_intrinsic("llvm.fabs");
intrinsic_id.floor = lookup_intrinsic("llvm.floor");
intrinsic_id.fma = lookup_intrinsic("llvm.fma");
intrinsic_id.frameaddress = lookup_intrinsic("llvm.frameaddress");
intrinsic_id.fshl = lookup_intrinsic("llvm.fshl");
intrinsic_id.fshr = lookup_intrinsic("llvm.fshr");
intrinsic_id.gather = lookup_intrinsic("llvm.masked.gather");
#if LLVM_VERSION_MAJOR < 16
intrinsic_id.get_rounding = lookup_intrinsic("llvm.flt.rounds");
#else
intrinsic_id.get_rounding = lookup_intrinsic("llvm.get.rounding");
#endif
intrinsic_id.lifetime_end = lookup_intrinsic("llvm.lifetime.end");
intrinsic_id.lifetime_start = lookup_intrinsic("llvm.lifetime.start");
intrinsic_id.llrint = lookup_intrinsic("llvm.llrint");
intrinsic_id.llround = lookup_intrinsic("llvm.llround");
intrinsic_id.log = lookup_intrinsic("llvm.log");
intrinsic_id.log2 = lookup_intrinsic("llvm.log2");
intrinsic_id.log10 = lookup_intrinsic("llvm.log10");
intrinsic_id.lrint = lookup_intrinsic("llvm.lrint");
intrinsic_id.lround = lookup_intrinsic("llvm.lround");
intrinsic_id.masked_compressstore = lookup_intrinsic("llvm.masked.compressstore");
intrinsic_id.masked_expandload = lookup_intrinsic("llvm.masked.expandload");
intrinsic_id.masked_load = lookup_intrinsic("llvm.masked.load");
intrinsic_id.masked_store = lookup_intrinsic("llvm.masked.store");
intrinsic_id.maximum = lookup_intrinsic("llvm.maximum");
intrinsic_id.maxnum = lookup_intrinsic("llvm.maxnum");
intrinsic_id.matrix_multiply = lookup_intrinsic("llvm.matrix.multiply");
intrinsic_id.matrix_transpose = lookup_intrinsic("llvm.matrix.transpose");
intrinsic_id.memcpy = lookup_intrinsic("llvm.memcpy");
intrinsic_id.memcpy_inline = lookup_intrinsic("llvm.memcpy.inline");
intrinsic_id.memmove = lookup_intrinsic("llvm.memmove");
intrinsic_id.memset = lookup_intrinsic("llvm.memset");
intrinsic_id.memset_inline = lookup_intrinsic("llvm.memset.inline");
intrinsic_id.minimum = lookup_intrinsic("llvm.minimum");
intrinsic_id.minnum = lookup_intrinsic("llvm.minnum");
intrinsic_id.fmuladd = lookup_intrinsic("llvm.fmuladd");
intrinsic_id.nearbyint = lookup_intrinsic("llvm.nearbyint");
intrinsic_id.pow = lookup_intrinsic("llvm.pow");
intrinsic_id.powi = lookup_intrinsic("llvm.powi");
intrinsic_id.prefetch = lookup_intrinsic("llvm.prefetch");
intrinsic_id.readcyclecounter = lookup_intrinsic("llvm.readcyclecounter");
intrinsic_id.returnaddress = lookup_intrinsic("llvm.returnaddress");
intrinsic_id.rint = lookup_intrinsic("llvm.rint");
intrinsic_id.round = lookup_intrinsic("llvm.round");
intrinsic_id.roundeven = lookup_intrinsic("llvm.roundeven");
intrinsic_id.sadd_overflow = lookup_intrinsic("llvm.sadd.with.overflow");
intrinsic_id.sadd_sat = lookup_intrinsic("llvm.sadd.sat");
intrinsic_id.scatter = lookup_intrinsic("llvm.masked.scatter");
intrinsic_id.set_rounding = lookup_intrinsic("llvm.set.rounding");
intrinsic_id.sin = lookup_intrinsic("llvm.sin");
intrinsic_id.sshl_sat = lookup_intrinsic("llvm.sshl.sat");
intrinsic_id.smax = lookup_intrinsic("llvm.smax");
intrinsic_id.smin = lookup_intrinsic("llvm.smin");
intrinsic_id.smul_overflow = lookup_intrinsic("llvm.smul.with.overflow");
intrinsic_id.sqrt = lookup_intrinsic("llvm.sqrt");
intrinsic_id.ssub_overflow = lookup_intrinsic("llvm.ssub.with.overflow");
intrinsic_id.ssub_sat = lookup_intrinsic("llvm.ssub.sat");
intrinsic_id.smul_fixed_sat = lookup_intrinsic("llvm.smul.fix.sat");
intrinsic_id.threadlocal_address = lookup_intrinsic("llvm.threadlocal.address");
intrinsic_id.trap = lookup_intrinsic("llvm.trap");
intrinsic_id.trunc = lookup_intrinsic("llvm.trunc");
intrinsic_id.uadd_overflow = lookup_intrinsic("llvm.uadd.with.overflow");
intrinsic_id.uadd_sat = lookup_intrinsic("llvm.uadd.sat");
intrinsic_id.umax = lookup_intrinsic("llvm.umax");
intrinsic_id.umin = lookup_intrinsic("llvm.umin");
intrinsic_id.umul_overflow = lookup_intrinsic("llvm.umul.with.overflow");
intrinsic_id.usub_overflow = lookup_intrinsic("llvm.usub.with.overflow");
intrinsic_id.ushl_sat = lookup_intrinsic("llvm.ushl.sat");
intrinsic_id.usub_sat = lookup_intrinsic("llvm.usub.sat");
intrinsic_id.umul_fixed_sat = lookup_intrinsic("llvm.umul.fix.sat");
intrinsic_id.vector_reduce_fmax = lookup_intrinsic("llvm.vector.reduce.fmax");
intrinsic_id.vector_reduce_fmin = lookup_intrinsic("llvm.vector.reduce.fmin");
intrinsic_id.vector_reduce_smax = lookup_intrinsic("llvm.vector.reduce.smax");
intrinsic_id.vector_reduce_smin = lookup_intrinsic("llvm.vector.reduce.smin");
intrinsic_id.vector_reduce_umax = lookup_intrinsic("llvm.vector.reduce.umax");
intrinsic_id.vector_reduce_umin = lookup_intrinsic("llvm.vector.reduce.umin");
intrinsic_id.vector_reduce_add = lookup_intrinsic("llvm.vector.reduce.add");
intrinsic_id.vector_reduce_fadd = lookup_intrinsic("llvm.vector.reduce.fadd");
intrinsic_id.vector_reduce_mul = lookup_intrinsic("llvm.vector.reduce.mul");
intrinsic_id.vector_reduce_fmul = lookup_intrinsic("llvm.vector.reduce.fmul");
intrinsic_id.vector_reduce_and = lookup_intrinsic("llvm.vector.reduce.and");
intrinsic_id.vector_reduce_or = lookup_intrinsic("llvm.vector.reduce.or");
intrinsic_id.vector_reduce_xor = lookup_intrinsic("llvm.vector.reduce.xor");
intrinsic_id.vector_predicate_select = lookup_intrinsic("llvm.vp.select");
intrinsic_id.wasm_memory_grow = lookup_intrinsic("llvm.wasm.memory.grow");
intrinsic_id.wasm_memory_size = lookup_intrinsic("llvm.wasm.memory.size");
attribute_id.afn = lookup_attribute("afn");
attribute_id.align = lookup_attribute("align");
attribute_id.alwaysinline = lookup_attribute("alwaysinline");
attribute_id.arcp = lookup_attribute("arcp");
attribute_id.byval = lookup_attribute("byval");
attribute_id.contract = lookup_attribute("contract");
attribute_id.elementtype = lookup_attribute("elementtype");
attribute_id.fast = lookup_attribute("fast");
attribute_id.inlinehint = lookup_attribute("inlinehint");
attribute_id.inreg = lookup_attribute("inreg");
attribute_id.naked = lookup_attribute("naked");
attribute_id.ninf = lookup_attribute("ninf");
attribute_id.nnan = lookup_attribute("nnan");
attribute_id.noalias = lookup_attribute("noalias");
attribute_id.noinline = lookup_attribute("noinline");
attribute_id.noreturn = lookup_attribute("noreturn");
attribute_id.nounwind = lookup_attribute("nounwind");
attribute_id.nsz = lookup_attribute("nsz");
attribute_id.optnone = lookup_attribute("optnone");
attribute_id.readonly = lookup_attribute("readonly");
attribute_id.reassoc = lookup_attribute("reassoc");
attribute_id.sanitize_address = lookup_attribute("sanitize_address");
attribute_id.sanitize_memory = lookup_attribute("sanitize_memory");
attribute_id.sanitize_thread = lookup_attribute("sanitize_thread");
attribute_id.sext = lookup_attribute("signext");
attribute_id.sret = lookup_attribute("sret");
attribute_id.ssp = lookup_attribute("ssp");
attribute_id.target_features = lookup_attribute("target-features");
attribute_id.uwtable = lookup_attribute("uwtable");
attribute_id.writeonly = lookup_attribute("writeonly");
attribute_id.zext = lookup_attribute("zeroext");
intrinsics_setup = true;
}
void llvm_set_comdat(GenContext *c, LLVMValueRef global)
{
if (!compiler.platform.use_comdat) return;
LLVMComdatRef comdat = LLVMGetOrInsertComdat(c->module, LLVMGetValueName(global));
LLVMSetComdatSelectionKind(comdat, LLVMAnyComdatSelectionKind);
LLVMSetComdat(global, comdat);
}
void llvm_set_selector_linkage(GenContext *c, LLVMValueRef selector)
{
LLVMSetVisibility(selector, LLVMDefaultVisibility);
LLVMSetLinkage(selector, LLVMLinkOnceODRLinkage);
llvm_set_comdat(c, selector);
}
void llvm_set_linkonce(GenContext *c, LLVMValueRef global)
{
LLVMSetLinkage(global, LLVMLinkOnceAnyLinkage);
LLVMSetVisibility(global, LLVMDefaultVisibility);
llvm_set_comdat(c, global);
}
void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i)
{
llvm_value_set(value, llvm_const_int(c, type, i), type);
}
bool llvm_value_is_const(BEValue *value)
{
return LLVMIsConstant(value->value);
}
void llvm_value_set_decl(GenContext *c, BEValue *value, Decl *decl)
{
decl = decl_flatten(decl);
if (decl->is_value)
{
llvm_value_set(value, decl->backend_value, decl->type);
return;
}
llvm_value_set_decl_address(c, value, decl);
}
LLVMBuilderRef llvm_create_function_entry(GenContext *c, LLVMValueRef func, LLVMBasicBlockRef *entry_block_ref)
{
LLVMBasicBlockRef entry = llvm_append_basic_block(c, func, "entry");
LLVMBuilderRef builder = llvm_create_builder(c);
LLVMPositionBuilderAtEnd(builder, entry);
if (entry_block_ref) *entry_block_ref = entry;
return builder;
}
LLVMBasicBlockRef llvm_append_basic_block(GenContext *c, LLVMValueRef func, const char *name)
{
return LLVMAppendBasicBlockInContext(c->context, func, name);
}
LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name)
{
return LLVMCreateBasicBlockInContext(c->context, name);
}
static void llvm_emit_type_decls(GenContext *context, Decl *decl)
{
switch (decl->decl_kind)
{
case NON_TYPE_DECLS:
case DECL_ERASED:
case DECL_FNTYPE:
UNREACHABLE_VOID;
case DECL_TYPE_ALIAS:
if (decl->type_alias_decl.is_func)
{
REMINDER("Emit func typeid");
}
break;
case DECL_FUNC:
// Never directly invoked.
UNREACHABLE_VOID
case DECL_INTERFACE:
break;
case DECL_TYPEDEF:
case DECL_STRUCT:
case DECL_UNION:
case DECL_ENUM:
case DECL_BITSTRUCT:
case DECL_CONST_ENUM:
llvm_get_typeid(context, decl->type);
break;
}
}
static inline void llvm_optimize(GenContext *c)
{
bool should_debug = false;
#ifndef NDEBUG
should_debug = debug_log;
#endif
LLVMOptLevels level = LLVM_O0;
switch (compiler.build.optsize)
{
case SIZE_OPTIMIZATION_SMALL:
level = LLVM_Os;
break;
case SIZE_OPTIMIZATION_TINY:
level = LLVM_Oz;
break;
default:
switch (compiler.build.optlevel)
{
case OPTIMIZATION_NONE:
case OPTIMIZATION_NOT_SET:
level = LLVM_O0;
break;
case OPTIMIZATION_LESS:
level = LLVM_O1;
break;
case OPTIMIZATION_MORE:
level = LLVM_O2;
break;
case OPTIMIZATION_AGGRESSIVE:
level = LLVM_O3;
break;
}
break;
}
LLVMPasses passes = {
.opt_level = level,
.should_verify = compiler.build.emit_llvm,
.should_debug = should_debug,
.is_kernel = compiler.build.kernel_build,
.opt.vectorize_loops = compiler.build.loop_vectorization == VECTORIZATION_ON,
.opt.slp_vectorize = compiler.build.slp_vectorization == VECTORIZATION_ON,
.opt.unroll_loops = compiler.build.unroll_loops == UNROLL_LOOPS_ON,
.opt.interleave_loops = compiler.build.unroll_loops == UNROLL_LOOPS_ON,
.opt.merge_functions = compiler.build.merge_functions == MERGE_FUNCTIONS_ON,
.sanitizer.address_sanitize = compiler.build.feature.sanitize_address,
.sanitizer.mem_sanitize = compiler.build.feature.sanitize_memory,
.sanitizer.thread_sanitize = compiler.build.feature.sanitize_thread
};
if (!llvm_run_passes(c->module, c->machine, &passes))
{
error_exit("Failed to run passes.");
}
}
const char *llvm_codegen(void *context)
{
GenContext *c = context;
if (!compiler_should_output_file(c->base_name)) return NULL;
llvm_optimize(c);
// Serialize the LLVM IR, if requested, also verify the IR in this case
if (compiler.build.emit_llvm)
{
gencontext_print_llvm_ir(c);
gencontext_verify_ir(c);
}
const char *object_name = NULL;
if (compiler.build.emit_asm)
{
// Clone if there will be object file output.
llvm_emit_file(c, c->asm_filename, LLVMAssemblyFile, compiler.build.emit_object_files);
}
if (compiler.build.emit_object_files)
{
llvm_emit_file(c, c->object_filename, LLVMObjectFile, false);
object_name = c->object_filename;
}
gencontext_end_module(c);
gencontext_destroy(c);
return object_name;
}
void llvm_add_global_decl(GenContext *c, Decl *decl)
{
ASSERT_SPAN(decl, decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
bool same_module = decl->unit->module == c->code_module;
LLVMTypeRef type = llvm_get_type(c, decl->type);
if (same_module)
{
// If we initialize it later, we must use a temp name.
decl->backend_ref = llvm_add_global_raw(c, ".tempglobal", type, decl->alignment);
}
else
{
scratch_buffer_set_extern_decl_name(decl, true);
decl->backend_ref = llvm_add_global_raw(c, scratch_buffer_to_string(), type, decl->alignment);
}
if (decl->var.kind == VARDECL_CONST)
{
LLVMSetGlobalConstant(decl->backend_ref, true);
}
if (IS_OPTIONAL(decl))
{
LLVMTypeRef fault = llvm_get_type(c, type_fault);
scratch_buffer_set_extern_decl_name(decl, true);
scratch_buffer_append(".f");
decl->var.optional_ref = llvm_add_global_raw(c, scratch_buffer_to_string(), fault, 0);
}
llvm_set_decl_linkage(c, decl);
llvm_set_global_tls(decl);
}
LLVMValueRef llvm_get_opt_ref(GenContext *c, Decl *decl)
{
llvm_get_ref(c, decl);
decl = decl_flatten(decl);
if (decl->decl_kind != DECL_VAR) return NULL;
return decl->var.optional_ref;
}
static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABIArgInfo *info, bool is_return,
int index, int last_index, Decl *decl)
{
ASSERT(last_index == index || info->kind == ABI_ARG_DIRECT_PAIR || info->kind == ABI_ARG_IGNORE
|| info->kind == ABI_ARG_EXPAND || info->kind == ABI_ARG_DIRECT || info->kind == ABI_ARG_DIRECT_COERCE
|| info->kind == ABI_ARG_DIRECT_COERCE_INT || info->kind == ABI_ARG_EXPAND_COERCE
|| info->kind == ABI_ARG_DIRECT_SPLIT_STRUCT_I32);
if (info->attributes.zeroext)
{
// Direct only
ASSERT(index == last_index);
llvm_attribute_add(c, function, attribute_id.zext, index);
}
if (info->attributes.signext)
{
// Direct only
ASSERT(index == last_index);
llvm_attribute_add(c, function, attribute_id.sext, index);
}
if (info->attributes.by_reg)
{
llvm_attribute_add_range(c, function, attribute_id.inreg, index, last_index);
}
switch (info->kind)
{
case ABI_ARG_DIRECT:
if (decl && decl->var.no_alias)
{
llvm_attribute_add(c, function, attribute_id.noalias, 1);
}
break;
case ABI_ARG_EXPAND:
case ABI_ARG_IGNORE:
case ABI_ARG_DIRECT_SPLIT_STRUCT_I32:
case ABI_ARG_DIRECT_COERCE:
case ABI_ARG_DIRECT_COERCE_INT:
case ABI_ARG_DIRECT_PAIR:
case ABI_ARG_EXPAND_COERCE:
break;
case ABI_ARG_INDIRECT:
if (is_return)
{
ASSERT(info->indirect.type);
llvm_attribute_add_type(c, function, attribute_id.sret, llvm_get_type(c, info->indirect.type), 1);
llvm_attribute_add(c, function, attribute_id.noalias, 1);
llvm_attribute_add_int(c, function, attribute_id.align, info->indirect.alignment, 1);
}
else
{
if (info->attributes.by_val) llvm_attribute_add_type(c, function, attribute_id.byval, llvm_get_type(c, info->indirect.type), index);
llvm_attribute_add_int(c, function, attribute_id.align, info->indirect.alignment, index);
}
break;
}
}
void llvm_append_function_attributes(GenContext *c, Decl *decl)
{
FunctionPrototype *prototype = type_get_resolved_prototype(decl->type);
LLVMValueRef function = decl->backend_ref;
ABIArgInfo *ret_abi_info = prototype->ret_abi_info;
llvm_emit_param_attributes(c, function, ret_abi_info, true, 0, 0, NULL);
if (c->debug.enable_stacktrace)
{
llvm_attribute_add_string(c, function, "frame-pointer", "all", -1);
llvm_attribute_add(c, function, attribute_id.ssp, -1);
}
llvm_attribute_add_string(c, function, "stack-protector-buffer-size", "8", -1);
llvm_attribute_add_string(c, function, "no-trapping-math", "true", -1);
int offset = prototype->ret_rewrite == RET_OPTIONAL_VALUE ? 1 : 0;
Signature *sig = prototype->raw_type->function.signature;
for (unsigned i = offset; i < prototype->param_count; i++)
{
ABIArgInfo *info = prototype->abi_args[i];
Decl *param_decl = sig->params[i - offset];
llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end, param_decl);
}
// We ignore decl->func_decl.attr_inline and place it in every call instead.
if (decl->func_decl.attr_noinline)
{
llvm_attribute_add(c, function, attribute_id.noinline, -1);
}
if (decl->func_decl.signature.attrs.noreturn)
{
llvm_attribute_add(c, function, attribute_id.noreturn, -1);
}
if (decl->is_export && arch_is_wasm(compiler.platform.arch))
{
if (c->code_module == decl->unit->module)
{
scratch_buffer_set_extern_decl_name(decl, true);
llvm_attribute_add_string(c, function, "wasm-export-name", scratch_buffer_to_string(), -1);
}
}
if (decl->is_extern && arch_is_wasm(compiler.platform.arch))
{
scratch_buffer_set_extern_decl_name(decl, true);
llvm_attribute_add_string(c, function, "wasm-import-name", scratch_buffer_to_string(), -1);
if (decl->attrs_resolved && decl->attrs_resolved->wasm_module)
{
llvm_attribute_add_string(c, function, "wasm-import-module", decl->attrs_resolved->wasm_module, -1);
}
}
if (decl->alignment != type_abi_alignment(decl->type))
{
llvm_set_alignment(function, decl->alignment);
}
llvm_attribute_add(c, function, attribute_id.nounwind, -1);
llvm_attribute_add_int(c, function, attribute_id.uwtable, UWTABLE, -1);
if (decl->func_decl.attr_naked)
{
llvm_attribute_add(c, function, attribute_id.naked, -1);
}
if (compiler.build.feature.sanitize_address && !decl->func_decl.attr_nosanitize_address)
{
llvm_attribute_add(c, function, attribute_id.sanitize_address, -1);
}
if (compiler.build.feature.sanitize_memory && !decl->func_decl.attr_nosanitize_memory)
{
llvm_attribute_add(c, function, attribute_id.sanitize_memory, -1);
}
if (compiler.build.feature.sanitize_thread && !decl->func_decl.attr_nosanitize_thread)
{
llvm_attribute_add(c, function, attribute_id.sanitize_thread, -1);
}
LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(prototype->call_abi));
}
static LLVMValueRef llvm_create_fault(GenContext *c, Decl *decl)
{
scratch_buffer_set_extern_decl_name(decl, true);
LLVMValueRef fault = llvm_add_global_raw(c, scratch_buffer_to_string(), c->chars_type, 0);
LLVMSetGlobalConstant(fault, 1);
scratch_buffer_append(".nameof");
if (decl->obfuscate)
{
LLVMSetInitializer(fault, llvm_emit_string_const(c, "<FAULT>", scratch_buffer_to_string()));
}
else
{
const char *module_name = decl->unit->module->name->module;
size_t last = 0;
for (size_t i = 0;; i++)
{
if (module_name[i] == 0) break;
if (module_name[i] == ':')
{
i++;
last = i + 1;
}
}
const char *new_name = str_printf("%s::%s", &module_name[last], decl->name);
LLVMSetInitializer(fault, llvm_emit_string_const(c, new_name, scratch_buffer_to_string()));
}
llvm_set_linkonce(c, fault);
decl->backend_ref = fault;
return fault;
}
LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
{
ASSERT(!decl->is_value);
LLVMValueRef backend_ref = decl->backend_ref;
if (backend_ref)
{
if (!LLVMIsAGlobalValue(backend_ref) || LLVMGetGlobalParent(backend_ref) == c->module) return backend_ref;
}
switch (decl->decl_kind)
{
case DECL_ERASED:
case DECL_FNTYPE:
UNREACHABLE
case DECL_VAR:
if (decl->var.kind == VARDECL_UNWRAPPED)
{
return decl->backend_ref = llvm_get_ref(c, decl->var.alias);
}
ASSERT_SPAN(decl, decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
llvm_add_global_decl(c, decl);
return decl->backend_ref;
case DECL_FUNC:
if (decl->func_decl.attr_interface_method)
{
return decl->backend_ref = llvm_get_selector(c, decl->name);
}
LLVMTypeRef type = llvm_get_type(c, decl->type);
scratch_buffer_set_extern_decl_name(decl, true);
if (decl->name == kw_memcmp && c->memcmp_function)
{
backend_ref = decl->backend_ref = c->memcmp_function;
}
else
{
const char *name = scratch_buffer_to_string();
if (decl->is_extern && (backend_ref = LLVMGetNamedFunction(c->module, name))) // NOLINT
{
return decl->backend_ref = backend_ref;
}
backend_ref = decl->backend_ref = LLVMAddFunction(c->module, name, type);
}
llvm_append_function_attributes(c, decl);
llvm_set_decl_linkage(c, decl);
return backend_ref;
case DECL_ALIAS:
return llvm_get_ref(c, decl->define_decl.alias);
case DECL_FAULT:
return llvm_create_fault(c, decl);
case DECL_POISONED:
case DECL_ATTRIBUTE:
case DECL_BITSTRUCT:
case DECL_CT_ASSERT:
case DECL_TYPEDEF:
case DECL_ENUM:
case DECL_CONST_ENUM:
case DECL_ENUM_CONSTANT:
case DECL_IMPORT:
case DECL_ALIAS_PATH:
case DECL_LABEL:
case DECL_MACRO:
case DECL_STRUCT:
case DECL_TYPE_ALIAS:
case DECL_UNION:
case DECL_DECLARRAY:
case DECL_BODYPARAM:
case DECL_CT_ECHO:
case DECL_CT_EXEC:
case DECL_CT_INCLUDE:
case DECL_GROUP:
case DECL_INTERFACE:
case DECL_GENERIC:
case DECL_GENERIC_INSTANCE:
UNREACHABLE;
}
UNREACHABLE
}
INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMContextRef shared_context)
{
Path *test_path = path_create_from_string("_$test", 5, INVALID_SPAN);
Module *test_module = compiler_find_or_create_module(test_path);
DebugInfo actual_debug_info = compiler.build.debug_info;
compiler.build.debug_info = DEBUG_INFO_NONE;
GenContext *c = cmalloc(sizeof(GenContext));
gencontext_init(c, test_module, shared_context);
gencontext_begin_module(c);
LLVMValueRef *names = NULL;
LLVMValueRef *decls = NULL;
LLVMTypeRef opt_test = LLVMFunctionType(llvm_get_type(c, type_fault), NULL, 0, false);
for (unsigned i = 0; i < module_count; i++)
{
Module *module = modules[i];
FOREACH(Decl *, test, module->tests)
{
LLVMTypeRef type = opt_test;
scratch_buffer_set_extern_decl_name(test, true);
LLVMValueRef ref = LLVMAddFunction(c->module, scratch_buffer_to_string(), type);
scratch_buffer_clear();
scratch_buffer_printf("%s::%s", module->name->module, test->name);
LLVMValueRef name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".test.name");
vec_add(names, name);
vec_add(decls, ref);
}
}
unsigned test_count = vec_size(decls);
LLVMValueRef name_ref;
LLVMValueRef decl_ref;
if (test_count)
{
LLVMValueRef array_of_names = LLVMConstArray(c->chars_type, names, test_count);
LLVMValueRef array_of_decls = LLVMConstArray(c->ptr_type, decls, test_count);
LLVMTypeRef arr_type = LLVMTypeOf(array_of_names);
name_ref = llvm_add_global_raw(c, ".test_names", arr_type, 0);
decl_ref = llvm_add_global_raw(c, ".test_decls", LLVMTypeOf(array_of_decls), 0);
llvm_set_internal_linkage(name_ref);
llvm_set_internal_linkage(decl_ref);
LLVMSetGlobalConstant(name_ref, 1);
LLVMSetGlobalConstant(decl_ref, 1);
LLVMSetInitializer(name_ref, array_of_names);
LLVMSetInitializer(decl_ref, array_of_decls);
}
else
{
name_ref = LLVMConstNull(c->ptr_type);
decl_ref = LLVMConstNull(c->ptr_type);
}
LLVMValueRef count = llvm_const_int(c, type_usz, test_count);
Type *chars_array = type_get_slice(type_chars);
LLVMValueRef name_list = llvm_add_global(c, test_names_var_name, chars_array, type_alloca_alignment(chars_array));
LLVMSetGlobalConstant(name_list, 1);
LLVMSetInitializer(name_list, llvm_emit_aggregate_two(c, chars_array, name_ref, count));
Type *decls_array_type = type_get_slice(type_voidptr);
LLVMValueRef decl_list = llvm_add_global(c, test_fns_var_name, decls_array_type, type_alloca_alignment(decls_array_type));
LLVMSetGlobalConstant(decl_list, 1);
LLVMSetInitializer(decl_list, llvm_emit_aggregate_two(c, decls_array_type, decl_ref, count));
compiler.build.debug_info = actual_debug_info;
return c;
}
INLINE GenContext *llvm_gen_benchmarks(Module** modules, unsigned module_count, LLVMContextRef shared_context)
{
Path *benchmark_path = path_create_from_string("$benchmark", 10, INVALID_SPAN);
Module *benchmark_module = compiler_find_or_create_module(benchmark_path);
DebugInfo actual_debug_info = compiler.build.debug_info;
compiler.build.debug_info = DEBUG_INFO_NONE;
GenContext *c = cmalloc(sizeof(GenContext));
gencontext_init(c, benchmark_module, shared_context);
gencontext_begin_module(c);
LLVMValueRef *names = NULL;
LLVMValueRef *decls = NULL;
LLVMTypeRef opt_benchmark = LLVMFunctionType(llvm_get_type(c, type_fault), NULL, 0, false);
for (unsigned i = 0; i < module_count; i++)
{
Module *module = modules[i];
FOREACH(Decl *, benchmark, module->benchmarks)
{
LLVMTypeRef type = opt_benchmark;
scratch_buffer_set_extern_decl_name(benchmark, true);
LLVMValueRef ref = LLVMAddFunction(c->module, scratch_buffer_to_string(), type);
scratch_buffer_clear();
scratch_buffer_printf("%s::%s", module->name->module, benchmark->name);
LLVMValueRef name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".benchmark.name");
vec_add(names, name);
vec_add(decls, ref);
}
}
unsigned benchmark_count = vec_size(decls);
LLVMValueRef name_ref;
LLVMValueRef decl_ref;
if (benchmark_count)
{
LLVMValueRef array_of_names = LLVMConstArray(c->chars_type, names, benchmark_count);
LLVMValueRef array_of_decls = LLVMConstArray(c->ptr_type, decls, benchmark_count);
LLVMTypeRef arr_type = LLVMTypeOf(array_of_names);
name_ref = llvm_add_global_raw(c, ".benchmark_names", arr_type, 0);
decl_ref = llvm_add_global_raw(c, ".benchmark_decls", LLVMTypeOf(array_of_decls), 0);
llvm_set_internal_linkage(name_ref);
llvm_set_internal_linkage(decl_ref);
LLVMSetGlobalConstant(name_ref, 1);
LLVMSetGlobalConstant(decl_ref, 1);
LLVMSetInitializer(name_ref, array_of_names);
LLVMSetInitializer(decl_ref, array_of_decls);
}
else
{
name_ref = LLVMConstNull(c->ptr_type);
decl_ref = LLVMConstNull(c->ptr_type);
}
LLVMValueRef count = llvm_const_int(c, type_usz, benchmark_count);
Type *chars_array = type_get_slice(type_chars);
LLVMValueRef name_list = llvm_add_global(c, benchmark_names_var_name, chars_array, type_alloca_alignment(chars_array));
LLVMSetGlobalConstant(name_list, 1);
LLVMSetInitializer(name_list, llvm_emit_aggregate_two(c, chars_array, name_ref, count));
Type *decls_array_type = type_get_slice(type_voidptr);
LLVMValueRef decl_list = llvm_add_global(c, benchmark_fns_var_name, decls_array_type, type_alloca_alignment(decls_array_type));
LLVMSetGlobalConstant(decl_list, 1);
LLVMSetInitializer(decl_list, llvm_emit_aggregate_two(c, decls_array_type, decl_ref, count));
compiler.build.debug_info = actual_debug_info;
return c;
}
void **llvm_gen(Module** modules, unsigned module_count)
{
if (!module_count) return NULL;
GenContext **gen_contexts = NULL;
llvm_codegen_setup();
if (compiler.build.single_module == SINGLE_MODULE_ON)
{
LLVMContextRef context = LLVMContextCreate();
for (int i = 0; i < module_count; i++)
{
GenContext *result = llvm_gen_module(modules[i], context);
if (!result) continue;
vec_add(gen_contexts, result);
}
if (!gen_contexts) return NULL;
GenContext *first = gen_contexts[0];
if (compiler.build.benchmarking)
{
vec_add(gen_contexts, llvm_gen_benchmarks(modules, module_count, context));
}
if (compiler.build.testing)
{
vec_add(gen_contexts, llvm_gen_tests(modules, module_count, context));
}
unsigned count = vec_size(gen_contexts);
for (unsigned i = 1; i < count; i++)
{
GenContext *other = gen_contexts[i];
LLVMLinkModules2(first->module, other->module);
gencontext_destroy(other);
}
vec_resize(gen_contexts, 1);
return (void**)gen_contexts;
}
for (unsigned i = 0; i < module_count; i++)
{
GenContext *result = llvm_gen_module(modules[i], NULL);
if (!result) continue;
vec_add(gen_contexts, result);
}
if (compiler.build.benchmarking)
{
vec_add(gen_contexts, llvm_gen_benchmarks(modules, module_count, NULL));
}
if (compiler.build.testing)
{
vec_add(gen_contexts, llvm_gen_tests(modules, module_count, NULL));
}
return (void**)gen_contexts;
}
LLVMMetadataRef llvm_get_debug_file(GenContext *c, FileId file_id)
{
FOREACH(DebugFile, file, c->debug.debug_files)
{
if (file.file_id == file_id) return file.debug_file; // NOLINT
}
File *f = source_file_by_id(file_id);
LLVMMetadataRef file = LLVMDIBuilderCreateFile(c->debug.builder,
f->name,
strlen(f->name),
f->dir_path,
strlen(f->dir_path));
DebugFile debug_file = { .file_id = file_id, .debug_file = file };
vec_add(c->debug.debug_files, debug_file);
return file;
}
static bool module_is_stdlib(Module *module)
{
if (module->name->len < 3) return false;
if (module->name->len == 3 && strcmp(module->name->module, "std") == 0) return true;
if (module->name->len > 5 && memcmp(module->name->module, "std::", 5) == 0) return true;
if (module->name->len == 4 && strcmp(module->name->module, "libc") == 0) return true;
if (module->name->len > 6 && memcmp(module->name->module, "libc::", 6) == 0) return true;
return false;
}
static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context)
{
if (!vec_size(module->units)) return NULL;
if (compiler.build.emit_stdlib == EMIT_STDLIB_OFF && module_is_stdlib(module)) return NULL;
ASSERT(intrinsics_setup);
bool has_elements = false;
GenContext *gen_context = cmalloc(sizeof(GenContext));
gencontext_init(gen_context, module, shared_context);
gencontext_begin_module(gen_context);
bool only_used = strip_unused();
FOREACH(CompilationUnit *, unit, module->units)
{
gencontext_init_file_emit(gen_context, unit);
gen_context->debug.compile_unit = unit->llvm.debug_compile_unit;
gen_context->debug.file = (DebugFile){ .debug_file = unit->llvm.debug_file, .file_id = unit->file->file_id };
FOREACH(Decl *, method, unit->methods)
{
if (only_used && !method->is_live) continue;
llvm_emit_function_decl(gen_context, method);
}
FOREACH(Decl *, type_decl, unit->types)
{
if (only_used && !type_decl->is_live) continue;
llvm_emit_type_decls(gen_context, type_decl);
}
FOREACH(Decl *, enum_decl, unit->enums)
{
if (only_used && !enum_decl->is_live) continue;
llvm_emit_type_decls(gen_context, enum_decl);
}
FOREACH(Decl *, func, unit->functions)
{
if (only_used && !func->is_live) continue;
if (func->func_decl.attr_test)
{
if (!compiler.build.testing) continue;
vec_add(module->tests, func);
}
if (func->func_decl.attr_benchmark)
{
if (!compiler.build.benchmarking) continue;
vec_add(module->benchmarks, func);
}
llvm_emit_function_decl(gen_context, func);
}
FOREACH(Decl *, func, unit->lambdas)
{
if (only_used && !func->is_live) continue;
has_elements = true;
llvm_emit_function_decl(gen_context, func);
}
if (unit->main_function && unit->main_function->is_synthetic)
{
has_elements = true;
llvm_emit_function_decl(gen_context, unit->main_function);
}
}
FOREACH(CompilationUnit *, unit, module->units)
{
gen_context->debug.compile_unit = unit->llvm.debug_compile_unit;
gen_context->debug.file = (DebugFile){
.debug_file = unit->llvm.debug_file,
.file_id = unit->file->file_id };
FOREACH(Decl *, var, unit->vars)
{
if (only_used && !var->is_live) continue;
has_elements = true;
llvm_get_ref(gen_context, var);
}
FOREACH(Decl *, var, unit->vars)
{
if (only_used && !var->is_live) continue;
has_elements = true;
llvm_emit_global_variable_init(gen_context, var);
}
FOREACH(Decl *, decl, unit->functions)
{
if (decl->func_decl.attr_test && !compiler.build.testing) continue;
if (decl->func_decl.attr_benchmark && !compiler.build.benchmarking) continue;
if (only_used && !decl->is_live) continue;
if (decl->func_decl.body)
{
has_elements = true;
llvm_emit_function_body(gen_context, decl);
}
}
FOREACH(Decl *, func, unit->lambdas)
{
if (only_used && !func->is_live) continue;
has_elements = true;
llvm_emit_function_body(gen_context, func);
}
if (unit->main_function && unit->main_function->is_synthetic)
{
has_elements = true;
llvm_emit_function_body(gen_context, unit->main_function);
}
FOREACH(Decl *, decl, unit->methods)
{
if (only_used && !decl->is_live) continue;
if (!decl->func_decl.body) continue;
has_elements = true;
llvm_emit_function_body(gen_context, decl);
}
gencontext_end_file_emit(gen_context, unit);
}
llvm_emit_dynamic_functions(gen_context, gen_context->dynamic_functions);
llvm_emit_constructors_and_destructors(gen_context);
if (llvm_use_debug(gen_context))
{
LLVMDIBuilderFinalize(gen_context->debug.builder);
LLVMDisposeDIBuilder(gen_context->debug.builder);
}
// If it's in test or benchmark, then we want to serialize the IR before it is optimized.
if (compiler.build.test_output || compiler.build.benchmark_output)
{
gencontext_print_llvm_ir(gen_context);
gencontext_verify_ir(gen_context);
}
if (!has_elements) return NULL;
return gen_context;
}
void llvm_attribute_add_int(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute, uint64_t val, int index)
{
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute, val);
LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr);
}
void llvm_attribute_add_type(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute, LLVMTypeRef type, int index)
{
LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type);
LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr);
}
void llvm_attribute_add(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index)
{
llvm_attribute_add_int(c, value_to_add_attribute_to, attribute, 0, index);
}
void llvm_attribute_add_call_type(GenContext *c, LLVMValueRef call, unsigned attribute, int index, LLVMTypeRef type)
{
LLVMAttributeRef llvm_attr = LLVMCreateTypeAttribute(c->context, attribute, type);
LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr);
}
void llvm_attribute_add_call(GenContext *c, LLVMValueRef call, unsigned attribute, int index, int64_t value)
{
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(c->context, attribute, (uint64_t)value);
LLVMAddCallSiteAttribute(call, (LLVMAttributeIndex)index, llvm_attr);
}
void llvm_attribute_add_range(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute, int index_start, int index_end)
{
for (int i = index_start; i <= index_end; i++)
{
llvm_attribute_add_int(c, value_to_add_attribute_to, attribute, 0, i);
}
}
void llvm_attribute_add_string(GenContext *c, LLVMValueRef value_to_add_attribute_to, const char *attribute, const char *value, int index)
{
LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(c->context, attribute, (unsigned)strlen(attribute), value, (unsigned)strlen(value));
LLVMAddAttributeAtIndex(value_to_add_attribute_to, (LLVMAttributeIndex)index, llvm_attr);
}
BitSize llvm_bitsize(GenContext *c, LLVMTypeRef type)
{
return LLVMSizeOfTypeInBits(c->target_data, type);
}
TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type)
{
return (TypeSize)LLVMABISizeOfType(c->target_data, type);
}
AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type)
{
return (AlignSize)LLVMABIAlignmentOfType(c->target_data, type);
}
LLVMValueRef 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)
{
return 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));
}
TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type)
{
return (TypeSize)LLVMStoreSizeOfType(c->target_data, type);
}
TypeSize llvm_alloc_size(GenContext *c, LLVMTypeRef type)
{
return (TypeSize)aligned_offset((AlignSize)LLVMABISizeOfType(c->target_data, type), llvm_abi_alignment(c, type));
}