add support for linux_riscv64 and freestanding_riscv64

This commit is contained in:
Laytan
2024-08-15 20:39:35 +02:00
committed by Laytan Laats
parent 29838da782
commit ca6ef95b03
28 changed files with 1395 additions and 116 deletions

View File

@@ -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 &reg_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);
}