commit df3aba6cc08622b908c47b6ae3aab2c1795e652e (tree)
parent ea45895a2dd75200c36f9f51cb54384476224f89
Author: Jacob Young <jacobly0@users.noreply.github.com>
Date: Wed, 22 Apr 2026 11:47:50 -0400
llvm: fix `multiple_llvm_types` handling and aarch64 return types
Closes #31994
Diffstat:
5 files changed, 77 insertions(+), 77 deletions(-)
diff --git a/src/InternPool.zig b/src/InternPool.zig
@@ -5888,6 +5888,12 @@ pub const Alignment = enum(u6) {
return @enumFromInt(@min(@intFromEnum(lhs), @intFromEnum(rhs)));
}
+ /// Given a base address known to be aligned to `a`,
+ /// computes the known alignment of base address plus `off`.
+ pub fn offset(a: Alignment, off: u64) Alignment {
+ return .fromLog2Units(@min(a.toLog2Units(), @ctz(off)));
+ }
+
/// Align an address forwards to this alignment.
pub fn forward(a: Alignment, addr: u64) u64 {
assert(a != .none);
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -1358,24 +1358,22 @@ pub const Object = struct {
},
.multiple_llvm_types => {
assert(!it.byval_attr);
- const field_types = it.types_buffer[0..it.types_len];
const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]);
const param_llvm_ty = try o.lowerType(param_ty);
- const param_alignment = param_ty.abiAlignment(zcu).toLlvm();
- const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, param_alignment, target);
- const llvm_ty = try o.builder.structType(.normal, field_types);
- const llvm_args_start = it.llvm_index - field_types.len;
- for (0..field_types.len, llvm_args_start..) |field_i, llvm_arg_index| {
+ const param_alignment = param_ty.abiAlignment(zcu);
+ const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8);
+ const arg_ptr = try buildAllocaInner(&wip, llvm_ty, param_alignment.toLlvm(), target);
+ const llvm_args_start = it.llvm_index - it.types_len;
+ for (llvm_args_start.., it.offsets_buffer[0..it.types_len]) |llvm_arg_index, offset| {
const param = wip.arg(@intCast(llvm_arg_index));
- const field_ptr = try wip.gepStruct(llvm_ty, arg_ptr, field_i, "");
- const alignment = Type.ptrAbiAlignment(target).toLlvm();
- _ = try wip.store(.normal, param, field_ptr, alignment);
+ const part_ptr = try o.ptraddConst(&wip, arg_ptr, offset);
+ _ = try wip.store(.normal, param, part_ptr, param_alignment.offset(offset).toLlvm());
}
if (isByRef(param_ty, zcu)) {
args.appendAssumeCapacity(arg_ptr);
} else {
- args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, arg_ptr, param_alignment, ""));
+ args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, arg_ptr, param_alignment.toLlvm(), ""));
}
},
.float_array => {
@@ -4362,6 +4360,13 @@ pub const Object = struct {
toLlvmAddressSpace(.generic, o.zcu.getTarget()),
);
}
+
+ pub fn ptraddConst(o: *Object, wip: *Builder.WipFunction, ptr: Builder.Value, offset: u64) Allocator.Error!Builder.Value {
+ if (offset == 0) return ptr;
+ const llvm_usize_ty = try o.lowerType(.usize);
+ const offset_val = try o.builder.intValue(llvm_usize_ty, offset);
+ return wip.gep(.inbounds, .i8, ptr, &.{offset_val}, "");
+ }
};
const CallingConventionInfo = struct {
diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig
@@ -709,22 +709,25 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
.multiple_llvm_types => {
const arg = args[it.zig_index - 1];
const param_ty = self.typeOf(arg);
- const llvm_types = it.types_buffer[0..it.types_len];
const llvm_arg = try self.resolveInst(arg);
const is_by_ref = isByRef(param_ty, zcu);
- const arg_ptr = if (is_by_ref) llvm_arg else ptr: {
- const alignment = param_ty.abiAlignment(zcu).toLlvm();
- const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment);
- _ = try self.wip.store(.normal, llvm_arg, ptr, alignment);
- break :ptr ptr;
- };
+ const param_alignment = param_ty.abiAlignment(zcu);
+ const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8);
+ const arg_ptr = try self.buildAlloca(llvm_ty, param_alignment.toLlvm());
+ if (is_by_ref) _ = try self.wip.callMemCpy(
+ arg_ptr,
+ param_alignment.toLlvm(),
+ llvm_arg,
+ param_alignment.toLlvm(),
+ try o.builder.intValue(try o.lowerType(.usize), param_ty.abiSize(zcu)),
+ .normal,
+ self.disable_intrinsics,
+ ) else _ = try self.wip.store(.normal, llvm_arg, arg_ptr, param_alignment.toLlvm());
- const llvm_ty = try o.builder.structType(.normal, llvm_types);
try llvm_args.ensureUnusedCapacity(it.types_len);
- for (llvm_types, 0..) |field_ty, i| {
- const alignment: Builder.Alignment = .fromByteUnits(@divExact(target.ptrBitWidth(), 8));
- const field_ptr = try self.wip.gepStruct(llvm_ty, arg_ptr, i, "");
- const loaded = try self.wip.load(.normal, field_ty, field_ptr, alignment, "");
+ for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| {
+ const field_ptr = try self.ptraddConst(arg_ptr, offset);
+ const loaded = try self.wip.load(.normal, field_ty, field_ptr, param_alignment.offset(offset).toLlvm(), "");
llvm_args.appendAssumeCapacity(loaded);
}
},
@@ -2269,10 +2272,7 @@ fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Build
const struct_ptr_align = struct_ty.abiAlignment(zcu);
const field_ptr = try self.ptraddConst(struct_llvm_val, offset);
- const field_ptr_align: InternPool.Alignment = switch (offset) {
- 0 => struct_ptr_align,
- else => struct_ptr_align.minStrict(.fromLog2Units(@ctz(offset))),
- };
+ const field_ptr_align = struct_ptr_align.offset(offset);
if (isByRef(field_ty, zcu)) {
return self.loadByRef(field_ptr, field_ty, field_ptr_align.toLlvm(), .normal);
@@ -3120,11 +3120,7 @@ fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) Allocator.Er
const field_ty = struct_ty.fieldType(field_index, zcu);
const field_offset = struct_ty.structFieldOffset(field_index, zcu);
- const field_align = switch (field_offset) {
- 0 => struct_ty.abiAlignment(zcu),
- else => struct_ty.abiAlignment(zcu).minStrict(.fromLog2Units(@ctz(field_offset))),
- };
-
+ const field_align = struct_ty.abiAlignment(zcu).offset(field_offset);
const field_ptr = try self.ptraddConst(self.err_ret_trace, field_offset);
return self.load(field_ptr, field_ty, field_align.toLlvm(), .normal);
}
@@ -5279,10 +5275,7 @@ fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.
return .none;
}
const tag_field_ptr = try self.ptraddConst(union_ptr, layout.tagOffset());
- const tag_ptr_align: InternPool.Alignment = switch (layout.tagOffset()) {
- 0 => union_ptr_align,
- else => |off| .minStrict(union_ptr_align, .fromLog2Units(@ctz(off))),
- };
+ const tag_ptr_align = union_ptr_align.offset(layout.tagOffset());
_ = try self.wip.store(access_kind, new_tag, tag_field_ptr, tag_ptr_align.toLlvm());
return .none;
}
@@ -5922,10 +5915,7 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde
if (!field_ty.hasRuntimeBits(zcu)) continue;
const offset = result_ty.structFieldOffset(field_index, zcu);
const field_ptr = try self.ptraddConst(alloca_inst, offset);
- const field_ptr_align: InternPool.Alignment = switch (offset) {
- 0 => struct_align,
- else => struct_align.minStrict(.fromLog2Units(@ctz(offset))),
- };
+ const field_ptr_align = struct_align.offset(offset);
const llvm_field_val = try self.resolveInst(elem);
@@ -6581,6 +6571,7 @@ const ParamTypeIterator = struct {
llvm_index: u32,
types_len: u32,
types_buffer: [8]Builder.Type,
+ offsets_buffer: [9]u64,
byval_attr: bool,
const Lowering = union(enum) {
@@ -6672,7 +6663,12 @@ const ParamTypeIterator = struct {
.memory => return .byref_mut,
.float_array => |len| return Lowering{ .float_array = len },
.byval => return .byval,
- .integer => return .abi_sized_int,
+ .integer => {
+ it.types_len = 1;
+ it.types_buffer[0..1].* = .{.i64};
+ it.offsets_buffer[0..2].* = .{ 0, 8 };
+ return .multiple_llvm_types;
+ },
.double_integer => return Lowering{ .i64_array = 2 },
}
},
@@ -6711,12 +6707,17 @@ const ParamTypeIterator = struct {
.double_integer => return Lowering{ .i64_array = 2 },
.fields => {
it.types_len = 0;
+ var offset: u64 = 0;
for (0..ty.structFieldCount(zcu)) |field_index| {
const field_ty = ty.fieldType(field_index, zcu);
if (!field_ty.hasRuntimeBits(zcu)) continue;
+ offset = field_ty.abiAlignment(zcu).forward(offset);
it.types_buffer[it.types_len] = try it.object.lowerType(field_ty);
+ it.offsets_buffer[it.types_len] = offset;
it.types_len += 1;
+ offset += field_ty.abiSize(zcu);
}
+ it.offsets_buffer[it.types_len] = offset;
it.llvm_index += it.types_len - 1;
return .multiple_llvm_types;
},
@@ -6729,9 +6730,8 @@ const ParamTypeIterator = struct {
it.llvm_index += 1;
return .byval;
} else {
- var types_buffer: [8]Builder.Type = undefined;
- types_buffer[0] = try it.object.lowerType(scalar_ty);
- it.types_buffer = types_buffer;
+ it.types_buffer[0..1].* = .{try it.object.lowerType(scalar_ty)};
+ it.offsets_buffer[0..2].* = .{ 0, scalar_ty.abiSize(zcu) };
it.types_len = 1;
it.llvm_index += 1;
it.zig_index += 1;
@@ -6804,31 +6804,36 @@ const ParamTypeIterator = struct {
return .byval;
}
var types_index: u32 = 0;
- var types_buffer: [8]Builder.Type = undefined;
+ var offset: u64 = 0;
for (classes) |class| {
switch (class) {
.integer => {
- types_buffer[types_index] = .i64;
+ it.types_buffer[types_index] = .i64;
+ it.offsets_buffer[types_index] = offset;
types_index += 1;
},
.sse => {
- types_buffer[types_index] = .double;
+ it.types_buffer[types_index] = .double;
+ it.offsets_buffer[types_index] = offset;
types_index += 1;
},
.sseup => {
- if (types_buffer[types_index - 1] == .double) {
- types_buffer[types_index - 1] = .fp128;
+ if (it.types_buffer[types_index - 1] == .double) {
+ it.types_buffer[types_index - 1] = .fp128;
} else {
- types_buffer[types_index] = .double;
+ it.types_buffer[types_index] = .double;
+ it.offsets_buffer[types_index] = offset;
types_index += 1;
}
},
.float => {
- types_buffer[types_index] = .float;
+ it.types_buffer[types_index] = .float;
+ it.offsets_buffer[types_index] = offset;
types_index += 1;
},
.float_combine => {
- types_buffer[types_index] = try it.object.builder.vectorType(.normal, 2, .float);
+ it.types_buffer[types_index] = try it.object.builder.vectorType(.normal, 2, .float);
+ it.offsets_buffer[types_index] = offset;
types_index += 1;
},
.x87 => {
@@ -6845,6 +6850,7 @@ const ParamTypeIterator = struct {
@panic("TODO");
},
}
+ offset += 8;
}
const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer});
if (first_non_integer == null or classes[first_non_integer.?] == .none) {
@@ -6865,15 +6871,15 @@ const ParamTypeIterator = struct {
const size = ty.abiSize(zcu);
assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);
if (size % 8 > 0) {
- types_buffer[types_index - 1] =
+ it.types_buffer[types_index - 1] =
try it.object.builder.intType(@intCast(size % 8 * 8));
}
},
else => {},
}
}
+ it.offsets_buffer[types_index] = offset;
it.types_len = types_index;
- it.types_buffer = types_buffer;
it.llvm_index += types_index;
it.zig_index += 1;
return .multiple_llvm_types;
@@ -6887,6 +6893,7 @@ pub fn iterateParamTypes(object: *Object, fn_info: InternPool.Key.FuncType) Para
.llvm_index = 0,
.types_len = 0,
.types_buffer = undefined,
+ .offsets_buffer = undefined,
.byval_attr = false,
};
}
@@ -6965,7 +6972,7 @@ pub fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Erro
.memory => return .void,
.float_array => return o.lowerType(return_type),
.byval => return o.lowerType(return_type),
- .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))),
+ .integer => return .i64,
.double_integer => return o.builder.arrayType(2, .i64),
},
.arm_aapcs, .arm_aapcs_vfp => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) {
@@ -7285,11 +7292,7 @@ fn getAtomicAbiType(fg: *const FuncGen, ty: Type, is_rmw_xchg: bool) Allocator.E
}
fn ptraddConst(fg: *FuncGen, ptr: Builder.Value, offset: u64) Allocator.Error!Builder.Value {
- if (offset == 0) return ptr;
- const o = fg.object;
- const llvm_usize_ty = try o.lowerType(.usize);
- const offset_val = try o.builder.intValue(llvm_usize_ty, offset);
- return fg.wip.gep(.inbounds, .i8, ptr, &.{offset_val}, "");
+ return fg.object.ptraddConst(&fg.wip, ptr, offset);
}
fn ptraddScaled(fg: *FuncGen, ptr: Builder.Value, index: Builder.Value, scale: u64) Allocator.Error!Builder.Value {
if (scale == 0) return ptr;
diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c
@@ -2802,7 +2802,6 @@ void run_c_tests(void) {
}
#endif
-#if !defined(__AARCH_BIG_ENDIAN)
#if !defined(__mips64)
#if !defined(ZIG_PPC32)
#if !defined(__s390x__)
@@ -2814,9 +2813,7 @@ void run_c_tests(void) {
#endif
#endif
#endif
-#endif
-#if !defined(__AARCH_BIG_ENDIAN)
#if !defined(__mips64)
#if !defined(ZIG_PPC32)
#if !defined(__s390x__)
@@ -2828,9 +2825,7 @@ void run_c_tests(void) {
#endif
#endif
#endif
-#endif
-#if !defined(__AARCH_BIG_ENDIAN)
#if !defined(__mips64)
#if !defined(ZIG_PPC32)
#if !defined(__s390x__)
@@ -2842,7 +2837,6 @@ void run_c_tests(void) {
#endif
#endif
#endif
-#endif
#if !defined(ZIG_PPC32)
#if !defined(ZIG_RISCV32)
@@ -2872,7 +2866,6 @@ void run_c_tests(void) {
zig_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, (struct Struct_u64_u64){ .a = 19, .b = 20 }, 9);
}
-#if !defined(ZIG_RISCV64)
#if !defined(__mips64__)
{
struct Struct_f32 s = zig_ret_struct_f32();
@@ -2908,7 +2901,6 @@ void run_c_tests(void) {
}
#endif
#endif
-#endif
#if !defined(__powerpc__) && !defined(__loongarch__) && !defined(__mips64__)
{
@@ -2933,8 +2925,8 @@ void run_c_tests(void) {
#endif
#endif
-#if !defined __i386__ && !defined __arm__ && !defined(__AARCH_BIG_ENDIAN) && \
- !defined __powerpc__ && !defined ZIG_RISCV64 && !defined(__loongarch__) && \
+#if !defined __i386__ && !defined __arm__ && \
+ !defined __powerpc__ && !defined(__loongarch__) && \
!defined(__mips64__) && !defined(__hexagon__) && !defined(__s390x__)
{
struct SmallStructInts s = {1, 2, 3, 4};
@@ -2942,8 +2934,8 @@ void run_c_tests(void) {
}
#endif
-#if !defined __arm__ && !defined(__AARCH_BIG_ENDIAN) && \
- !defined __powerpc__ && !defined ZIG_RISCV64 && !defined(__loongarch__) && \
+#if !defined __arm__ && \
+ !defined __powerpc__ && !defined(__loongarch__) && \
!defined(__mips64__) && !defined(__hexagon__) && !defined(__s390x__)
{
struct MedStructInts s = {1, 2, 3};
diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig
@@ -287,7 +287,6 @@ extern fn c_ret_struct_u8() Struct_u8;
extern fn c_struct_u8(Struct_u8, usize) void;
test "C ABI struct u8" {
- if (builtin.cpu.arch == .aarch64_be) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
@@ -315,7 +314,6 @@ extern fn c_ret_struct_u16() Struct_u16;
extern fn c_struct_u16(Struct_u16, usize) void;
test "C ABI struct u16" {
- if (builtin.cpu.arch == .aarch64_be) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
@@ -343,7 +341,6 @@ extern fn c_ret_struct_u32() Struct_u32;
extern fn c_struct_u32(Struct_u32, usize) void;
test "C ABI struct u32" {
- if (builtin.cpu.arch == .aarch64_be) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
@@ -770,7 +767,6 @@ test "C ABI small struct of ints" {
if (builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64_be) return error.SkipZigTest;
if (builtin.cpu.arch.isLoongArch()) return error.SkipZigTest;
if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
@@ -5613,7 +5609,6 @@ extern fn c_ret_ptr_size_float_struct() Vector2;
test "C ABI pointer sized float struct" {
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
- if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
if (builtin.cpu.arch.isArm() and builtin.abi.float() == .soft) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
@@ -5978,7 +5973,6 @@ extern fn stdcall_coord2(Coord2, Coord2, Coord2) callconv(stdcall_callconv) Coor
test "Stdcall ABI structs" {
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64_be) return error.SkipZigTest;
if (builtin.cpu.arch.isLoongArch()) return error.SkipZigTest;
if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
if (builtin.cpu.arch == .s390x) return error.SkipZigTest;