aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_abi.cpp
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2022-11-23 16:25:09 +0000
committergingerBill <bill@gingerbill.org>2022-11-23 16:25:09 +0000
commit7ab591667a1c647926fe79fda18efec8ce706198 (patch)
tree9c319a86d6f2a3ec4cc2d086c6f6441dff830b35 /src/llvm_abi.cpp
parent0a0db23b1751c0b7021cc1b3af3329b5d93cf9da (diff)
Basic support for new ABI experiment on Win64
Diffstat (limited to 'src/llvm_abi.cpp')
-rw-r--r--src/llvm_abi.cpp111
1 files changed, 90 insertions, 21 deletions
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 298041aa6..31773e1a2 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -1,3 +1,5 @@
+#define ALLOW_SPLIT_MULTI_RETURNS true
+
enum lbArgKind {
lbArg_Direct,
lbArg_Indirect,
@@ -48,8 +50,16 @@ struct lbFunctionType {
ProcCallingConvention calling_convention;
Array<lbArgType> args;
lbArgType ret;
+
+ LLVMTypeRef multiple_return_original_type; // nullptr if not used
+ isize original_arg_count;
};
+gbAllocator lb_function_type_args_allocator(void) {
+ return heap_allocator();
+}
+
+
i64 llvm_align_formula(i64 off, i64 a) {
return (off + a - 1) / a * a;
}
@@ -100,7 +110,9 @@ LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
}
args[arg_index++] = arg_type;
} else if (arg->kind == lbArg_Indirect) {
- GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+ if (ft->multiple_return_original_type == nullptr || i < ft->original_arg_count) {
+ GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+ }
args[arg_index++] = LLVMPointerType(arg->type, 0);
} else if (arg->kind == lbArg_Ignore) {
// ignore
@@ -147,6 +159,13 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
}
+ if (ft->multiple_return_original_type) {
+ if (ft->original_arg_count <= i) {
+ LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
+ LLVMAddAttributeAtIndex(fn, arg_index+1, nonnull_attr);
+ }
+ }
+
arg_index++;
}
@@ -307,7 +326,7 @@ 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, ProcCallingConvention calling_convention)
+#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)
typedef LB_ABI_INFO(lbAbiInfoType);
@@ -353,7 +372,7 @@ namespace lbAbi386 {
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
- auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+ auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
@@ -392,19 +411,19 @@ namespace lbAbi386 {
namespace lbAbiAmd64Win64 {
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
-
+ lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple);
LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count);
- ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+ ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
ft->calling_convention = calling_convention;
return ft;
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
- auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+ auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
@@ -428,6 +447,45 @@ namespace lbAbiAmd64Win64 {
}
return args;
}
+
+ lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+ i64 sz = lb_sizeof(return_type);
+ switch (sz) {
+ case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
+ case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+ case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+ case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+ }
+
+ if (return_is_tuple) {
+ GB_ASSERT(lb_is_type_kind(return_type, LLVMStructTypeKind));
+ unsigned field_count = LLVMCountStructElementTypes(return_type);
+
+ if (field_count > 1) {
+ ft->original_arg_count = ft->args.count;
+ ft->multiple_return_original_type = return_type;
+
+ for (unsigned 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);
+ }
+
+ // override the return type for the last field
+ LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
+ return compute_return_type(ft, c, new_return_type, true, false);
+ }
+ }
+
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+ return lb_arg_type_indirect(return_type, attr);
+ }
+ return lbAbi386::non_struct(c, return_type, true);
+ }
};
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
@@ -490,7 +548,7 @@ namespace lbAbiAmd64SysV {
ft->ctx = c;
ft->calling_convention = calling_convention;
- ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+ ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
}
@@ -1056,7 +1114,7 @@ namespace lbAbiArm64 {
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
- auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+ 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];
@@ -1188,7 +1246,7 @@ namespace lbAbiWasm {
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
- auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+ auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
@@ -1266,7 +1324,7 @@ namespace lbAbiArm32 {
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
- auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+ auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
@@ -1307,14 +1365,14 @@ namespace lbAbiArm32 {
};
-LB_ABI_INFO(lb_get_abi_info) {
+LB_ABI_INFO(lb_get_abi_info_internal) {
switch (calling_convention) {
case ProcCC_None:
case ProcCC_InlineAsm:
{
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
- ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+ ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = lb_arg_type_direct(arg_types[i]);
}
@@ -1328,32 +1386,43 @@ LB_ABI_INFO(lb_get_abi_info) {
}
case ProcCC_Win64:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
- return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case ProcCC_SysV:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
- return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} else if (build_context.metrics.abi == TargetABI_SysV) {
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
} else {
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
}
case TargetArch_i386:
- return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_arm32:
- return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_arm64:
- return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
case TargetArch_wasm32:
case TargetArch_wasm64:
- return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
}
GB_PANIC("Unsupported ABI");
return {};
}
+
+
+LB_ABI_INFO(lb_get_abi_info) {
+ lbFunctionType *ft = lb_get_abi_info_internal(c, arg_types, arg_count, return_type, return_is_defined, ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple, calling_convention);
+ if (calling_convention == ProcCC_Odin) {
+ // append the `context` pointer
+ lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
+ array_add(&ft->args, context_param);
+ }
+ return ft;
+}