Fix darwin arm64 argument passing abi.

Darwin arm64 expects caller to take responsibility of zero/sign extending any arguments < 32 bits.

Fixes #6809 and #6816
This commit is contained in:
Urjasvi Suthar
2026-07-14 10:26:52 +05:30
parent 6953670191
commit b27f28d1ea
4 changed files with 84 additions and 9 deletions

View File

@@ -1124,7 +1124,7 @@ namespace lbAbiAmd64SysV {
namespace lbAbiArm64 {
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, Type* original_type);
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
@@ -1132,7 +1132,7 @@ namespace lbAbiArm64 {
LLVMContextRef c = m->ctx;
lbFunctionType *ft = permanent_alloc_item<lbFunctionType>();
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count);
ft->args = compute_arg_types(c, arg_types, arg_count, original_type);
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
ft->calling_convention = calling_convention;
return ft;
@@ -1155,11 +1155,23 @@ namespace lbAbiArm64 {
return false;
}
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, Type* original_type) {
LLVMAttributeRef attr = nullptr;
LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
if (type == i1) {
attr = lb_create_enum_attribute(c, "zeroext");
// https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
// Darwin expects caller to take responsibility of zero/sign extending any arguments < 32bits.
if (build_context.metrics.os == TargetOs_darwin && original_type != nullptr) {
if ((is_type_integer_like(original_type) || is_type_enum(original_type)) && lb_sizeof(type) < 4) {
if (is_type_unsigned(original_type) || is_type_boolean(original_type)) {
attr = lb_create_enum_attribute(c, "zeroext");
} else {
attr = lb_create_enum_attribute(c, "signext");
}
}
} else {
if (type == i1) {
attr = lb_create_enum_attribute(c, "zeroext");
}
}
return lb_arg_type_direct(type, nullptr, nullptr, attr);
}
@@ -1260,7 +1272,7 @@ namespace lbAbiArm64 {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (is_register(return_type)) {
return non_struct(c, return_type);
return non_struct(c, return_type, nullptr);
} else if (is_homogenous_aggregate(c, return_type, &homo_base_type, &homo_member_count)) {
if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
return lb_arg_type_direct(return_type, llvm_array_type(homo_base_type, homo_member_count), nullptr, nullptr);
@@ -1297,17 +1309,24 @@ namespace lbAbiArm64 {
}
}
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, Type* original_type) {
auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
GB_ASSERT(original_type->kind == Type_Proc);
auto const &params = original_type->Proc.params->Tuple.variables;
for (unsigned i = 0, j = 0; i < arg_count; i++, j++) {
while (params[j]->kind != Entity_Variable) {
j++;
}
Type *ptype = params[j]->type;
LLVMTypeRef type = arg_types[i];
LLVMTypeRef homo_base_type = {};
unsigned homo_member_count = 0;
if (is_register(type)) {
args[i] = non_struct(c, type);
args[i] = non_struct(c, type, ptype);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
args[i] = lb_arg_type_direct(type, llvm_array_type(homo_base_type, homo_member_count), nullptr, nullptr);