diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 89d370144..1619c342b 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -30,6 +30,7 @@ enum TargetArchKind : u16 { TargetArch_amd64, TargetArch_i386, + TargetArch_arm32, TargetArch_arm64, TargetArch_wasm32, TargetArch_wasm64, @@ -75,6 +76,7 @@ String target_arch_names[TargetArch_COUNT] = { str_lit(""), str_lit("amd64"), str_lit("i386"), + str_lit("arm32"), str_lit("arm64"), str_lit("wasm32"), str_lit("wasm64"), @@ -98,6 +100,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, + TargetEndian_Little, }; #ifndef ODIN_VERSION_RAW @@ -367,7 +370,16 @@ gb_global TargetMetrics target_linux_arm64 = { 8, 16, str_lit("aarch64-linux-elf"), - str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"), + str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), +}; + +gb_global TargetMetrics target_linux_arm32 = { + TargetOs_linux, + TargetArch_arm32, + 4, + 8, + str_lit("aapcs-linux-gnu"), + str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), }; gb_global TargetMetrics target_darwin_amd64 = { @@ -483,6 +495,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("linux_i386"), &target_linux_i386 }, { str_lit("linux_amd64"), &target_linux_amd64 }, { str_lit("linux_arm64"), &target_linux_arm64 }, + { str_lit("linux_arm32"), &target_linux_arm32 }, { str_lit("windows_i386"), &target_windows_i386 }, { str_lit("windows_amd64"), &target_windows_amd64 }, { str_lit("freebsd_i386"), &target_freebsd_i386 }, diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 07d2dd6e3..c6ff12f95 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -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: @@ -1164,6 +1168,88 @@ namespace lbAbiWasm32 { } } +namespace lbAbiArm32 { + Array 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 compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) { + auto args = array_make(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) { @@ -1203,6 +1289,8 @@ LB_ABI_INFO(lb_get_abi_info) { } 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: