aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_abi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm_abi.cpp')
-rw-r--r--src/llvm_abi.cpp182
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");