diff options
| author | gingerBill <bill@gingerbill.org> | 2022-11-23 16:25:09 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2022-11-23 16:25:09 +0000 |
| commit | 7ab591667a1c647926fe79fda18efec8ce706198 (patch) | |
| tree | 9c319a86d6f2a3ec4cc2d086c6f6441dff830b35 /src/llvm_abi.cpp | |
| parent | 0a0db23b1751c0b7021cc1b3af3329b5d93cf9da (diff) | |
Basic support for new ABI experiment on Win64
Diffstat (limited to 'src/llvm_abi.cpp')
| -rw-r--r-- | src/llvm_abi.cpp | 111 |
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; +} |