mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
* fixes for RISC-V ABI Implementation #1567 Fixed RISC-V floating-point ABI by correcting the target triple to `riscv64-unknown-linux-gnu`, adding the `target-abi` module flag, and ensuring ABI-required CPU features are enabled. I tested this with: ```bash build/c3c compile-only --target linux-riscv64 rv_hello.c3 readelf -h obj/linux-riscv64/rv_hello.o | grep Flags # Output: Flags: 0x5, RVC, double-float ABI ``` ```bash # and qemu because I don't have a riscv machine :/ qemu-riscv64-static -L /usr/riscv64-linux-gnu ./rv_hello ``` --- @lerno I purposedly left these two failing tests to clearly see the difference. `test/test_suite/abi/riscv64-lp64-lp64f-abi-1.c3t` `test/test_suite/abi/riscv64-lp64-abi.c3t` * improve Linux cross-compilation, specifically for RISC-V - Implement automatic sysroot and CRT object discovery for RISC-V. - Fix dynamic linker paths and emulation flags for LLD. - Link against libgcc to resolve required arithmetic symbols. * Update tests. * fix linker CRT detection by centralizing host arch check use target_host_arch() in linker.c move target_host_arch() from hostinfo.c to target.c * missing debug info --------- Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
260 lines
9.3 KiB
C
260 lines
9.3 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"
|
|
|
|
|
|
/**
|
|
* Create the introspection type { byte kind, void* dtable, usz sizeof, typeid inner, usz len, [0xtypeid] additional }
|
|
*
|
|
* @param c the context
|
|
* @return the generated introspection type.
|
|
*/
|
|
static inline LLVMTypeRef create_introspection_type(GenContext *c)
|
|
{
|
|
LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".introspect");
|
|
LLVMTypeRef introspect_type[INTROSPECT_INDEX_TOTAL] = {
|
|
[INTROSPECT_INDEX_KIND] = c->byte_type,
|
|
[INTROSPECT_INDEX_PARENTOF] = c->typeid_type,
|
|
[INTROSPECT_INDEX_DTABLE] = c->ptr_type,
|
|
[INTROSPECT_INDEX_SIZEOF] = c->size_type,
|
|
[INTROSPECT_INDEX_INNER] = c->typeid_type,
|
|
[INTROSPECT_INDEX_LEN] = c->size_type,
|
|
[INTROSPECT_INDEX_ADDITIONAL] = LLVMArrayType(c->typeid_type, 0),
|
|
};
|
|
LLVMStructSetBody(type, introspect_type, INTROSPECT_INDEX_TOTAL, false);
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* Create the fault type { typeid parent, String[] name, usz ordinal }
|
|
* @param c the context to use.
|
|
* @return the resulting LLVM type.
|
|
*/
|
|
static inline LLVMTypeRef create_fault_type(GenContext *c)
|
|
{
|
|
LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".fault");
|
|
LLVMTypeRef fault_type[] = { c->typeid_type, c->chars_type, c->size_type };
|
|
LLVMStructSetBody(type, fault_type, 3, false);
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* Set a module flag.
|
|
*
|
|
* @param c the context to use
|
|
* @param flag_behavior how the flag should be merged
|
|
* @param flag the flag name
|
|
* @param value the flag value
|
|
* @param type the type of the flag value
|
|
*/
|
|
static void llvm_set_module_flag(GenContext *c, LLVMModuleFlagBehavior flag_behavior, const char *flag, uint64_t value, Type *type)
|
|
{
|
|
LLVMMetadataRef val = LLVMValueAsMetadata(LLVMConstInt(LLVMIntTypeInContext(c->context, type_bit_size(type)), value, false));
|
|
LLVMAddModuleFlag(c->module, flag_behavior, flag, strlen(flag), val);
|
|
}
|
|
|
|
void gencontext_begin_module(GenContext *c)
|
|
{
|
|
ASSERT(!c->module && "Expected no module");
|
|
|
|
codegen_setup_object_names(c->code_module, &c->base_name, &c->ir_filename, &c->asm_filename, &c->object_filename);
|
|
DEBUG_LOG("Emit module %s.", c->code_module->name->module);
|
|
c->panic_var = compiler.context.panic_var;
|
|
c->panicf = compiler.context.panicf;
|
|
c->module = LLVMModuleCreateWithNameInContext(c->code_module->name->module, c->context);
|
|
c->machine = llvm_target_machine_create();
|
|
|
|
c->target_data = LLVMCreateTargetDataLayout(c->machine);
|
|
|
|
LLVMSetModuleDataLayout(c->module, c->target_data);
|
|
LLVMSetSourceFileName(c->module, c->code_module->name->module, strlen(c->code_module->name->module));
|
|
|
|
LLVMSetTarget(c->module, compiler.platform.target_triple);
|
|
|
|
// Setup all types. Not thread-safe, but at this point in time we can assume a single context.
|
|
// We need to remove the context from the cache after this.
|
|
// This would seem to indicate that we should change Type / actual type.
|
|
|
|
c->ast_alloca_addr_space = compiler.platform.alloca_address_space;
|
|
FOREACH(Type *, type, compiler.context.type)
|
|
{
|
|
type->backend_type = NULL;
|
|
type->backend_debug_type = NULL;
|
|
type->backend_typeid = NULL;
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_ENUM:
|
|
{
|
|
FOREACH(Decl *, value, type->decl->enums.values)
|
|
{
|
|
value->backend_ref = NULL;
|
|
}
|
|
FALLTHROUGH;
|
|
}
|
|
case TYPE_STRUCT:
|
|
case TYPE_UNION:
|
|
case TYPE_TYPEDEF:
|
|
type->decl->backend_ref = NULL;
|
|
break;
|
|
case TYPE_FUNC_RAW:
|
|
//REMINDER("Clear func when it has reflection");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
c->bool_type = LLVMInt1TypeInContext(c->context);
|
|
c->byte_type = LLVMInt8TypeInContext(c->context);
|
|
c->ptr_type = LLVMPointerType(c->byte_type, 0);
|
|
c->size_type = llvm_get_type(c, type_usz);
|
|
c->typeid_type = llvm_get_type(c, type_typeid);
|
|
LLVMTypeRef void_type = LLVMVoidTypeInContext(c->context);
|
|
LLVMTypeRef dtable_type[3] = { c->ptr_type, c->ptr_type, c->ptr_type };
|
|
c->dtable_type = LLVMStructTypeInContext(c->context, dtable_type, 3, false);
|
|
c->chars_type = llvm_get_type(c, type_chars);
|
|
LLVMTypeRef ctor_type[3] = { LLVMInt32TypeInContext(c->context), c->ptr_type, c->ptr_type };
|
|
c->xtor_entry_type = LLVMStructTypeInContext(c->context, ctor_type, 3, false);
|
|
c->xtor_func_type = LLVMFunctionType(void_type, NULL, 0, false);
|
|
c->introspect_type = create_introspection_type(c);
|
|
c->atexit_type = LLVMFunctionType(void_type, &c->ptr_type, 1, false);
|
|
c->fault_type = create_fault_type(c);
|
|
c->memcmp_function = NULL;
|
|
LLVMTypeRef memcmp_types[3] = {c->ptr_type, c->ptr_type, c->size_type };
|
|
c->memcmp_function_type = LLVMFunctionType(llvm_get_type(c, type_cint), memcmp_types, 3, false);
|
|
if (c->panic_var) c->panic_var->backend_ref = NULL;
|
|
if (c->panicf) c->panicf->backend_ref = NULL;
|
|
bool is_win = compiler.build.arch_os_target == WINDOWS_X64 || compiler.build.arch_os_target == WINDOWS_AARCH64;
|
|
bool is_codeview = compiler.build.feature.win_debug != WIN_DEBUG_DWARF && is_win;
|
|
if (is_codeview)
|
|
{
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorError, "CodeView", 1, type_uint);
|
|
}
|
|
else
|
|
{
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Dwarf Version", 4, type_uint);
|
|
}
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Debug Info Version", 3, type_uint);
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "wchar_size", is_win ? 2 : 4, type_uint);
|
|
static const char *pic_level = "PIC Level";
|
|
static const char *pie_level = "PIE Level";
|
|
switch (compiler.platform.reloc_model)
|
|
{
|
|
case RELOC_BIG_PIE:
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pie_level, (unsigned)2 /* PIE */, type_uint);
|
|
FALLTHROUGH;
|
|
case RELOC_BIG_PIC:
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pic_level, (unsigned)2 /* PIC */, type_uint);
|
|
break;
|
|
case RELOC_SMALL_PIE:
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pie_level, (unsigned)1 /* pie */, type_uint);
|
|
FALLTHROUGH;
|
|
case RELOC_SMALL_PIC:
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pic_level, (unsigned)1 /* pic */, type_uint);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorError, "uwtable", UWTABLE, type_uint);
|
|
if (is_win)
|
|
{
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorError, "MaxTLSAlign", 65536, type_uint);
|
|
}
|
|
else
|
|
{
|
|
unsigned frame_pointer = compiler.platform.arch == ARCH_TYPE_AARCH64 ? 1 : 2;
|
|
llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "frame-pointer", frame_pointer, type_uint);
|
|
if (compiler.platform.arch == ARCH_TYPE_RISCV64 || compiler.platform.arch == ARCH_TYPE_RISCV32)
|
|
{
|
|
const char *abi_str = NULL;
|
|
if (compiler.platform.arch == ARCH_TYPE_RISCV64)
|
|
{
|
|
unsigned flen = compiler.platform.riscv.flen;
|
|
if (flen == 8) abi_str = "lp64d";
|
|
else if (flen == 4) abi_str = "lp64f";
|
|
else abi_str = "lp64";
|
|
}
|
|
else
|
|
{
|
|
unsigned flen = compiler.platform.riscv.flen;
|
|
if (flen == 8) abi_str = "ilp32d";
|
|
else if (flen == 4) abi_str = "ilp32f";
|
|
else abi_str = "ilp32";
|
|
}
|
|
LLVMMetadataRef val = LLVMValueAsMetadata(LLVMMDStringInContext(c->context, abi_str, strlen(abi_str)));
|
|
LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorError, "target-abi", 10, val);
|
|
}
|
|
}
|
|
|
|
if (compiler.build.debug_info != DEBUG_INFO_NONE)
|
|
{
|
|
c->debug.runtime_version = 0;
|
|
c->debug.builder = LLVMCreateDIBuilder(c->module);
|
|
if (compiler.build.debug_info == DEBUG_INFO_FULL && safe_mode_enabled())
|
|
{
|
|
c->debug.enable_stacktrace = os_supports_stacktrace(compiler.platform.os);
|
|
}
|
|
c->debug.emit_expr_loc = !is_win;
|
|
}
|
|
c->global_builder = LLVMCreateBuilderInContext(c->context);
|
|
c->builder = c->global_builder;
|
|
}
|
|
|
|
void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit)
|
|
{
|
|
if (compiler.build.debug_info != DEBUG_INFO_NONE)
|
|
{
|
|
// Set runtime version here.
|
|
unit->llvm.debug_file = llvm_get_debug_file(c, unit->file->file_id);
|
|
|
|
bool is_optimized = compiler.build.optlevel != OPTIMIZATION_NONE;
|
|
const char *dwarf_flags = "";
|
|
unsigned runtime_version = 0;
|
|
LLVMDWARFEmissionKind emission_kind =
|
|
compiler.build.debug_info == DEBUG_INFO_FULL ? LLVMDWARFEmissionFull : LLVMDWARFEmissionLineTablesOnly;
|
|
const char *debug_output_file = "";
|
|
bool emit_debug_info_for_profiling = false;
|
|
bool split_debug_inlining = false;
|
|
const char *sysroot = "";
|
|
const char *sdk = "";
|
|
unsigned dwo_id = 0;
|
|
if (c->debug.compile_unit)
|
|
{
|
|
unit->llvm.debug_compile_unit = c->debug.compile_unit;
|
|
return;
|
|
}
|
|
unit->llvm.debug_compile_unit = LLVMDIBuilderCreateCompileUnit(c->debug.builder,
|
|
LLVMDWARFSourceLanguageC11,
|
|
unit->llvm.debug_file,
|
|
DWARF_PRODUCER_NAME,
|
|
strlen(DWARF_PRODUCER_NAME),
|
|
is_optimized,
|
|
dwarf_flags,
|
|
strlen(dwarf_flags),
|
|
runtime_version,
|
|
debug_output_file,
|
|
strlen(debug_output_file),
|
|
emission_kind,
|
|
dwo_id,
|
|
split_debug_inlining,
|
|
emit_debug_info_for_profiling,
|
|
sysroot,
|
|
strlen(sysroot),
|
|
sdk,
|
|
strlen(sdk)
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void gencontext_end_file_emit(GenContext *c UNUSED, CompilationUnit *ast UNUSED)
|
|
{
|
|
}
|
|
|
|
void gencontext_end_module(GenContext *context)
|
|
{
|
|
LLVMDisposeModule(context->module);
|
|
}
|