mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-21 05:45:19 +00:00
add support for linux_riscv64 and freestanding_riscv64
This commit is contained in:
269
src/llvm_abi.cpp
269
src/llvm_abi.cpp
@@ -332,7 +332,8 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
|
||||
}
|
||||
|
||||
|
||||
#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
|
||||
#define LB_ABI_INFO(name) lbFunctionType *name(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
|
||||
#define LB_ABI_INFO_CTX(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
|
||||
typedef LB_ABI_INFO(lbAbiInfoType);
|
||||
|
||||
#define LB_ABI_COMPUTE_RETURN_TYPE(name) lbArgType name(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple)
|
||||
@@ -379,7 +380,7 @@ namespace lbAbi386 {
|
||||
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
|
||||
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||
@@ -460,7 +461,7 @@ namespace lbAbiAmd64Win64 {
|
||||
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
|
||||
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||
@@ -570,7 +571,7 @@ namespace lbAbiAmd64SysV {
|
||||
gb_internal Array<RegClass> classify(LLVMTypeRef t);
|
||||
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes, LLVMTypeRef type);
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->calling_convention = calling_convention;
|
||||
@@ -1008,7 +1009,7 @@ namespace lbAbiArm64 {
|
||||
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
|
||||
gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||
@@ -1242,7 +1243,7 @@ namespace lbAbiWasm {
|
||||
|
||||
enum {MAX_DIRECT_STRUCT_SIZE = 32};
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->calling_convention = calling_convention;
|
||||
@@ -1407,7 +1408,7 @@ namespace lbAbiArm32 {
|
||||
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
|
||||
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
gb_internal LB_ABI_INFO_CTX(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = c;
|
||||
ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
|
||||
@@ -1485,8 +1486,256 @@ namespace lbAbiArm32 {
|
||||
}
|
||||
};
|
||||
|
||||
namespace lbAbiRiscv64 {
|
||||
|
||||
gb_internal bool is_register(LLVMTypeRef type) {
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||
switch (kind) {
|
||||
case LLVMIntegerTypeKind:
|
||||
case LLVMHalfTypeKind:
|
||||
case LLVMFloatTypeKind:
|
||||
case LLVMDoubleTypeKind:
|
||||
case LLVMPointerTypeKind:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool is_float(LLVMTypeRef type) {
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||
switch (kind) {
|
||||
case LLVMHalfTypeKind:
|
||||
case LLVMFloatTypeKind:
|
||||
case LLVMDoubleTypeKind:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
|
||||
LLVMAttributeRef attr = nullptr;
|
||||
LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
|
||||
if (type == i1) {
|
||||
attr = lb_create_enum_attribute(c, "zeroext");
|
||||
}
|
||||
return lb_arg_type_direct(type, nullptr, nullptr, attr);
|
||||
}
|
||||
|
||||
gb_internal void flatten(lbModule *m, Array<LLVMTypeRef> *fields, LLVMTypeRef type, bool with_padding) {
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||
switch (kind) {
|
||||
case LLVMStructTypeKind: {
|
||||
if (LLVMIsPackedStruct(type)) {
|
||||
array_add(fields, type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!with_padding) {
|
||||
auto field_remapping = map_get(&m->struct_field_remapping, cast(void *)type);
|
||||
if (field_remapping) {
|
||||
auto remap = *field_remapping;
|
||||
for_array(i, remap) {
|
||||
flatten(m, fields, LLVMStructGetTypeAtIndex(type, remap[i]), with_padding);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
debugf("no field mapping for type: %s\n", LLVMPrintTypeToString(type));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned elem_count = LLVMCountStructElementTypes(type);
|
||||
for (unsigned i = 0; i < elem_count; i += 1) {
|
||||
flatten(m, fields, LLVMStructGetTypeAtIndex(type, i), with_padding);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LLVMArrayTypeKind: {
|
||||
unsigned len = LLVMGetArrayLength(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
|
||||
for (unsigned i = 0; i < len; i += 1) {
|
||||
flatten(m, fields, elem, with_padding);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
array_add(fields, type);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal lbArgType compute_arg_type(lbModule *m, LLVMTypeRef type, int *gprs_left, int *fprs_left, Type *odin_type) {
|
||||
LLVMContextRef c = m->ctx;
|
||||
|
||||
int xlen = 8; // 8 byte int register size for riscv64.
|
||||
|
||||
// NOTE: we are requiring both of these to be enabled so we can just hard-code 8.
|
||||
// int flen = 0;
|
||||
// if (check_target_feature_is_enabled(str_lit("d"), nullptr)) {
|
||||
// flen = 8; // Double precision floats are enabled.
|
||||
// } else if (check_target_feature_is_enabled(str_lit("f"), nullptr)) {
|
||||
// flen = 4; // Single precision floats are enabled.
|
||||
// }
|
||||
int flen = 8;
|
||||
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||
i64 size = lb_sizeof(type);
|
||||
|
||||
if (size == 0) {
|
||||
return lb_arg_type_direct(type, LLVMStructTypeInContext(c, nullptr, 0, false), nullptr, nullptr);
|
||||
}
|
||||
|
||||
LLVMTypeRef orig_type = type;
|
||||
|
||||
// Flatten down the type so it is easier to check all the ABI conditions.
|
||||
// Note that we also need to remove all implicit padding fields Odin adds so we keep ABI
|
||||
// compatibility for struct declarations.
|
||||
if (kind == LLVMStructTypeKind && size <= gb_max(2*xlen, 2*flen)) {
|
||||
Array<LLVMTypeRef> fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, LLVMCountStructElementTypes(type));
|
||||
flatten(m, &fields, type, false);
|
||||
|
||||
if (fields.count == 1) {
|
||||
type = fields[0];
|
||||
} else {
|
||||
type = LLVMStructTypeInContext(c, fields.data, cast(unsigned)fields.count, false);
|
||||
}
|
||||
|
||||
kind = LLVMGetTypeKind(type);
|
||||
size = lb_sizeof(type);
|
||||
GB_ASSERT_MSG(size == lb_sizeof(orig_type), "flattened: %s of size %d, original: %s of size %d", LLVMPrintTypeToString(type), size, LLVMPrintTypeToString(orig_type), lb_sizeof(orig_type));
|
||||
}
|
||||
|
||||
if (is_float(type) && size <= flen && *fprs_left >= 1) {
|
||||
*fprs_left -= 1;
|
||||
return non_struct(c, orig_type);
|
||||
}
|
||||
|
||||
if (kind == LLVMStructTypeKind && size <= 2*flen) {
|
||||
unsigned elem_count = LLVMCountStructElementTypes(type);
|
||||
if (elem_count == 2) {
|
||||
LLVMTypeRef ty1 = LLVMStructGetTypeAtIndex(type, 0);
|
||||
i64 ty1s = lb_sizeof(ty1);
|
||||
LLVMTypeRef ty2 = LLVMStructGetTypeAtIndex(type, 1);
|
||||
i64 ty2s = lb_sizeof(ty2);
|
||||
|
||||
if (is_float(ty1) && is_float(ty2) && ty1s <= flen && ty2s <= flen && *fprs_left >= 2) {
|
||||
*fprs_left -= 2;
|
||||
return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (is_float(ty1) && is_register(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) {
|
||||
*fprs_left -= 1;
|
||||
*gprs_left -= 1;
|
||||
return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (is_register(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) {
|
||||
*fprs_left -= 1;
|
||||
*gprs_left -= 1;
|
||||
return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point all the cases for floating point registers are exhausted, fit it into
|
||||
// integer registers or the stack.
|
||||
// LLVM automatically handles putting args on the stack so we don't check the amount of registers that are left here.
|
||||
|
||||
if (size <= xlen) {
|
||||
*gprs_left -= 1;
|
||||
if (is_register(type)) {
|
||||
return non_struct(c, orig_type);
|
||||
} else {
|
||||
return lb_arg_type_direct(orig_type, LLVMIntTypeInContext(c, cast(unsigned)(size*8)), nullptr, nullptr);
|
||||
}
|
||||
} else if (size <= 2*xlen) {
|
||||
LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, 2);
|
||||
fields[0] = LLVMIntTypeInContext(c, cast(unsigned)(xlen*8));
|
||||
fields[1] = LLVMIntTypeInContext(c, cast(unsigned)((size-xlen)*8));
|
||||
|
||||
*gprs_left -= 2;
|
||||
return lb_arg_type_direct(orig_type, LLVMStructTypeInContext(c, fields, 2, false), nullptr, nullptr);
|
||||
} else {
|
||||
return lb_arg_type_indirect(orig_type, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal Array<lbArgType> compute_arg_types(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *odin_type, int *gprs, int *fprs) {
|
||||
auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
|
||||
|
||||
for (unsigned i = 0; i < arg_count; i++) {
|
||||
LLVMTypeRef type = arg_types[i];
|
||||
args[i] = compute_arg_type(m, type, gprs, fprs, odin_type);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
gb_internal lbArgType compute_return_type(lbFunctionType *ft, lbModule *m, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type *odin_type, int *agprs) {
|
||||
LLVMContextRef c = m->ctx;
|
||||
|
||||
if (!return_is_defined) {
|
||||
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
|
||||
}
|
||||
|
||||
// There are two registers for return types.
|
||||
int gprs = 2;
|
||||
int fprs = 2;
|
||||
lbArgType ret = compute_arg_type(m, return_type, &gprs, &fprs, odin_type);
|
||||
|
||||
// Return didn't fit into the return registers, so caller allocates and it is returned via
|
||||
// an out-pointer.
|
||||
if (ret.kind == lbArg_Indirect) {
|
||||
|
||||
// Transform multiple return into out pointers if possible.
|
||||
if (return_is_tuple) {
|
||||
if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
|
||||
int field_count = cast(int)LLVMCountStructElementTypes(return_type);
|
||||
if (field_count > 1 && field_count <= *agprs) {
|
||||
ft->original_arg_count = ft->args.count;
|
||||
ft->multiple_return_original_type = return_type;
|
||||
|
||||
for (int i = 0; i < field_count-1; i++) {
|
||||
LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i);
|
||||
LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0);
|
||||
lbArgType ret_partial = lb_arg_type_direct(field_pointer_type);
|
||||
array_add(&ft->args, ret_partial);
|
||||
*agprs -= 1;
|
||||
}
|
||||
GB_ASSERT(*agprs >= 0);
|
||||
|
||||
// override the return type for the last field
|
||||
LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
|
||||
return compute_return_type(ft, m, new_return_type, true, false, odin_type, agprs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", ret.type);
|
||||
return lb_arg_type_indirect(ret.type, attr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gb_internal LB_ABI_INFO(abi_info) {
|
||||
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
|
||||
ft->ctx = m->ctx;
|
||||
ft->calling_convention = calling_convention;
|
||||
|
||||
int gprs = 8;
|
||||
int fprs = 8;
|
||||
|
||||
ft->args = compute_arg_types(m, arg_types, arg_count, calling_convention, original_type, &gprs, &fprs);
|
||||
ft->ret = compute_return_type(ft, m, return_type, return_is_defined, return_is_tuple, original_type, &gprs);
|
||||
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
|
||||
LLVMContextRef c = m->ctx;
|
||||
|
||||
switch (calling_convention) {
|
||||
case ProcCC_None:
|
||||
case ProcCC_InlineAsm:
|
||||
@@ -1534,6 +1783,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
|
||||
return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
|
||||
case TargetArch_wasm64p32:
|
||||
return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
|
||||
case TargetArch_riscv64:
|
||||
return lbAbiRiscv64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
|
||||
}
|
||||
|
||||
GB_PANIC("Unsupported ABI");
|
||||
@@ -1543,7 +1794,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
|
||||
|
||||
gb_internal LB_ABI_INFO(lb_get_abi_info) {
|
||||
lbFunctionType *ft = lb_get_abi_info_internal(
|
||||
c,
|
||||
m,
|
||||
arg_types, arg_count,
|
||||
return_type, return_is_defined,
|
||||
ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
|
||||
@@ -1555,7 +1806,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
|
||||
// This is to make it consistent when and how it is handled
|
||||
if (calling_convention == ProcCC_Odin) {
|
||||
// append the `context` pointer
|
||||
lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
|
||||
lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0));
|
||||
array_add(&ft->args, context_param);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user