diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2022-07-28 16:01:18 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-28 16:01:18 +0200 |
| commit | 674ebe395f1feba70becaac75c4fb9451f74f84c (patch) | |
| tree | 78589612bd458269904d53de46d0accf4f58a7af /src/llvm_abi.cpp | |
| parent | 9746e25784c208cf8a7883bcb2e1ac2317117aeb (diff) | |
| parent | 96eecaab54cea02499e9cf418aa39eb5a9c9ec75 (diff) | |
Merge branch 'master' into master
Diffstat (limited to 'src/llvm_abi.cpp')
| -rw-r--r-- | src/llvm_abi.cpp | 182 |
1 files changed, 149 insertions, 33 deletions
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 310df6639..b22a839b3 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -233,7 +233,7 @@ i64 lb_sizeof(LLVMTypeRef type) { i64 elem_size = lb_sizeof(elem); i64 count = LLVMGetVectorSize(type); i64 size = count * elem_size; - return gb_clamp(next_pow2(size), 1, build_context.max_align); + return next_pow2(size); } } @@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV { bool is_register(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); + i64 sz = lb_sizeof(type); + if (sz == 0) { + return false; + } switch (kind) { case LLVMIntegerTypeKind: case LLVMHalfTypeKind: @@ -797,16 +801,23 @@ namespace lbAbiAmd64SysV { i64 elem_sz = lb_sizeof(elem); LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); RegClass reg = RegClass_NoClass; + unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_kind) { case LLVMIntegerTypeKind: case LLVMHalfTypeKind: - switch (LLVMGetIntTypeWidth(elem)) { - case 8: reg = RegClass_SSEInt8; - case 16: reg = RegClass_SSEInt16; - case 32: reg = RegClass_SSEInt32; - case 64: reg = RegClass_SSEInt64; + switch (elem_width) { + case 8: reg = RegClass_SSEInt8; break; + case 16: reg = RegClass_SSEInt16; break; + case 32: reg = RegClass_SSEInt32; break; + case 64: reg = RegClass_SSEInt64; break; default: - GB_PANIC("Unhandled integer width for vector type"); + if (elem_width > 64) { + for (i64 i = 0; i < len; i++) { + classify_with(elem, cls, ix, off + i*elem_sz); + } + break; + } + GB_PANIC("Unhandled integer width for vector type %u", elem_width); } break; case LLVMFloatTypeKind: @@ -1052,10 +1063,18 @@ namespace lbAbiArm64 { } } -namespace lbAbiWasm32 { +namespace lbAbiWasm { + /* + NOTE(bill): All of this is custom since there is not an "official" + ABI definition for WASM, especially for Odin. + The approach taken optimizes for passing things in multiple + registers/arguments if possible rather than by pointer. + */ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + enum {MAX_DIRECT_STRUCT_SIZE = 32}; + LB_ABI_INFO(abi_info) { lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); ft->ctx = c; @@ -1083,7 +1102,7 @@ namespace lbAbiWasm32 { return lb_arg_type_direct(type, nullptr, nullptr, attr); } - bool is_struct_valid_elem_type(LLVMTypeRef type) { + bool is_basic_register_type(LLVMTypeRef type) { switch (LLVMGetTypeKind(type)) { case LLVMHalfTypeKind: case LLVMFloatTypeKind: @@ -1095,38 +1114,44 @@ namespace lbAbiWasm32 { } return false; } - - lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) { + + bool type_can_be_direct(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); - GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind); - i64 sz = lb_sizeof(type); if (sz == 0) { - return lb_arg_type_ignore(type); + return false; } - if (sz <= 16) { + if (sz <= MAX_DIRECT_STRUCT_SIZE) { if (kind == LLVMArrayTypeKind) { - LLVMTypeRef elem = LLVMGetElementType(type); - if (is_struct_valid_elem_type(elem)) { - return lb_arg_type_direct(type); + if (is_basic_register_type(LLVMGetElementType(type))) { + return true; } } else if (kind == LLVMStructTypeKind) { - bool can_be_direct = true; unsigned count = LLVMCountStructElementTypes(type); for (unsigned i = 0; i < count; i++) { LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i); - if (!is_struct_valid_elem_type(elem)) { - can_be_direct = false; - break; + if (!is_basic_register_type(elem)) { + return false; } - - } - if (can_be_direct) { - return lb_arg_type_direct(type); + } + return true; } } + return false; + } + + lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind); + i64 sz = lb_sizeof(type); + if (sz == 0) { + return lb_arg_type_ignore(type); + } + if (type_can_be_direct(type)) { + return lb_arg_type_direct(type); + } return lb_arg_type_indirect(type, nullptr); } @@ -1150,6 +1175,10 @@ namespace lbAbiWasm32 { 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)) { + if (type_can_be_direct(return_type)) { + return lb_arg_type_direct(return_type); + } + i64 sz = lb_sizeof(return_type); switch (sz) { case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); @@ -1164,6 +1193,88 @@ namespace lbAbiWasm32 { } } +namespace lbAbiArm32 { + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + + 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, calling_convention); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + bool is_register(LLVMTypeRef type, bool is_return) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMHalfTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + return true; + case LLVMIntegerTypeKind: + return lb_sizeof(type) <= 8; + case LLVMFunctionTypeKind: + return true; + case LLVMPointerTypeKind: + return true; + case LLVMVectorTypeKind: + return true; + } + return false; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) { + 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); + } + + 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); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + if (is_register(t, false)) { + args[i] = non_struct(c, t, false); + } else { + i64 sz = lb_sizeof(t); + i64 a = lb_alignof(t); + if (is_calling_convention_odin(calling_convention) && sz > 8) { + // Minor change to improve performance using the Odin calling conventions + args[i] = lb_arg_type_indirect(t, nullptr); + } else if (a <= 4) { + unsigned n = cast(unsigned)((sz + 3) / 4); + args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n)); + } else { + unsigned n = cast(unsigned)((sz + 7) / 8); + args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n)); + } + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (!is_register(return_type, true)) { + switch (lb_sizeof(return_type)) { + case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr); + case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr); + case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr); + } + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); + return lb_arg_type_indirect(return_type, attr); + } + return non_struct(c, return_type, true); + } +}; + LB_ABI_INFO(lb_get_abi_info) { switch (calling_convention) { @@ -1184,27 +1295,32 @@ LB_ABI_INFO(lb_get_abi_info) { ft->calling_convention = calling_convention; return ft; } + 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); + 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); } switch (build_context.metrics.arch) { case TargetArch_amd64: - if (build_context.metrics.os == TargetOs_windows) { + 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); + } else if (build_context.metrics.abi == TargetABI_SysV) { + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } else { return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } case TargetArch_i386: return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case TargetArch_arm32: + return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_arm64: return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_wasm32: - // TODO(bill): implement wasm32's ABI correct - // NOTE(bill): this ABI is only an issue for WASI compatibility - return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_wasm64: - // TODO(bill): implement wasm64's ABI correct - // NOTE(bill): this ABI is only an issue for WASI compatibility - return lbAbiAmd64SysV::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, calling_convention); } GB_PANIC("Unsupported ABI"); |