authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-17 19:38:27+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-18 01:02:29+01:00
log85837de476a919e42da101d42da848a5f181fb38
tree727f3d0fb0581aa5e5d77652f84e832dda90879e
parent0a2f6632810ceb6a17bffbafd846c5f3aaa24e64

llvm: solve a bunch of alignment bugs

I went over a bunch of calls to `std.zig.llvm.Builder.{load,store}` and added correct alignments where we previously passed the default alignment. This fixes some miscompilations, particularly when using underaligned pointers or *overaligned* slices. I have definitely missed some cases, but it's better to get some fixes in than for this to sit in a `git stash` forever. Resolves: https://codeberg.org/ziglang/zig/issues/31473 Resolves: https://codeberg.org/ziglang/zig/issues/30566

1 files changed, 47 insertions(+), 38 deletions(-)

src/codegen/llvm.zig+47-38
......@@ -1438,8 +1438,7 @@ pub const Object = struct {
14381438 const param = wip.arg(llvm_arg_i);
14391439 llvm_arg_i += 1;
14401440 const field_ptr = try wip.gepStruct(llvm_ty, arg_ptr, field_i, "");
1441 const alignment =
1442 Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
1441 const alignment = Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
14431442 _ = try wip.store(.normal, param, field_ptr, alignment);
14441443 }
14451444
......@@ -6151,7 +6150,7 @@ pub const FuncGen = struct {
61516150 const body = unwrapped_try.else_body;
61526151 const err_union_ty = self.typeOf(unwrapped_try.error_union);
61536152 const is_unused = self.liveness.isUnused(inst);
6154 return lowerTry(self, err_union, body, err_union_ty, false, false, is_unused, err_cold);
6153 return lowerTry(self, err_union, body, err_union_ty, false, .none, false, is_unused, err_cold);
61556154 }
61566155
61576156 fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value {
......@@ -6159,12 +6158,13 @@ pub const FuncGen = struct {
61596158 const unwrapped_try = self.air.unwrapTryPtr(inst);
61606159 const err_union_ptr = try self.resolveInst(unwrapped_try.error_union_ptr);
61616160 const body = unwrapped_try.else_body;
6162 const err_union_ty = self.typeOf(unwrapped_try.error_union_ptr).childType(zcu);
6161 const err_union_ptr_ty = self.typeOf(unwrapped_try.error_union_ptr);
6162 const err_union_ty = err_union_ptr_ty.childType(zcu);
61636163 const is_unused = self.liveness.isUnused(inst);
61646164
61656165 self.maybeMarkAllowZeroAccess(self.typeOf(unwrapped_try.error_union_ptr).ptrInfo(zcu));
61666166
6167 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold);
6167 return lowerTry(self, err_union_ptr, body, err_union_ty, true, err_union_ptr_ty.ptrAlignment(zcu), true, is_unused, err_cold);
61686168 }
61696169
61706170 fn lowerTry(
......@@ -6173,6 +6173,7 @@ pub const FuncGen = struct {
61736173 body: []const Air.Inst.Index,
61746174 err_union_ty: Type,
61756175 operand_is_ptr: bool,
6176 operand_ptr_align: InternPool.Alignment,
61766177 can_elide_load: bool,
61776178 is_unused: bool,
61786179 err_cold: bool,
......@@ -6185,15 +6186,19 @@ pub const FuncGen = struct {
61856186 const err_union_llvm_ty = try o.lowerType(pt, err_union_ty);
61866187 const error_type = try o.errorIntType(pt);
61876188
6189 const err_set_align: InternPool.Alignment, const payload_align: InternPool.Alignment = if (operand_is_ptr) .{
6190 operand_ptr_align.minStrict(Type.anyerror.abiAlignment(zcu)),
6191 operand_ptr_align.minStrict(payload_ty.abiAlignment(zcu)),
6192 } else .{ .none, .none };
6193
61886194 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
61896195 const loaded = loaded: {
61906196 const access_kind: Builder.MemoryAccessKind =
61916197 if (err_union_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
61926198
61936199 if (!payload_has_bits) {
6194 // TODO add alignment to this load
61956200 break :loaded if (operand_is_ptr)
6196 try fg.wip.load(access_kind, error_type, err_union, .default, "")
6201 try fg.wip.load(access_kind, error_type, err_union, err_set_align.toLlvm(), "")
61976202 else
61986203 err_union;
61996204 }
......@@ -6201,12 +6206,11 @@ pub const FuncGen = struct {
62016206 if (operand_is_ptr or isByRef(err_union_ty, zcu)) {
62026207 const err_field_ptr =
62036208 try fg.wip.gepStruct(err_union_llvm_ty, err_union, err_field_index, "");
6204 // TODO add alignment to this load
62056209 break :loaded try fg.wip.load(
62066210 if (operand_is_ptr) access_kind else .normal,
62076211 error_type,
62086212 err_field_ptr,
6209 .default,
6213 err_set_align.toLlvm(),
62106214 "",
62116215 );
62126216 }
......@@ -6232,15 +6236,14 @@ pub const FuncGen = struct {
62326236 return fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, "");
62336237 } else if (isByRef(err_union_ty, zcu)) {
62346238 const payload_ptr = try fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, "");
6235 const payload_alignment = payload_ty.abiAlignment(zcu).toLlvm();
62366239 if (isByRef(payload_ty, zcu)) {
62376240 if (can_elide_load)
62386241 return payload_ptr;
62396242
6240 return fg.loadByRef(payload_ptr, payload_ty, payload_alignment, .normal);
6243 return fg.loadByRef(payload_ptr, payload_ty, payload_align.toLlvm(), .normal);
62416244 }
62426245 const load_ty = err_union_llvm_ty.structFields(&o.builder)[offset];
6243 return fg.wip.load(.normal, load_ty, payload_ptr, payload_alignment, "");
6246 return fg.wip.load(.normal, load_ty, payload_ptr, payload_align.toLlvm(), "");
62446247 }
62456248 return fg.wip.extractValue(err_union, &.{offset}, "");
62466249 }
......@@ -6713,20 +6716,20 @@ pub const FuncGen = struct {
67136716 const slice_ty = self.typeOf(bin_op.lhs);
67146717 const slice = try self.resolveInst(bin_op.lhs);
67156718 const index = try self.resolveInst(bin_op.rhs);
6716 const elem_ty = slice_ty.childType(zcu);
6719 const slice_info = slice_ty.ptrInfo(zcu);
6720 assert(slice_info.flags.size == .slice);
6721 const elem_ty: Type = .fromInterned(slice_info.child);
67176722 const llvm_elem_ty = try o.lowerType(pt, elem_ty);
67186723 const base_ptr = try self.wip.extractValue(slice, &.{0}, "");
67196724 const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, "");
6725 const elem_align = slice_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu));
6726 const access_kind: Builder.MemoryAccessKind = if (slice_info.flags.is_volatile) .@"volatile" else .normal;
6727 self.maybeMarkAllowZeroAccess(slice_info);
67206728 if (isByRef(elem_ty, zcu)) {
6721 self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
6722
6723 const slice_align = (slice_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu))).toLlvm();
6724 return self.loadByRef(ptr, elem_ty, slice_align, if (slice_ty.isVolatilePtr(zcu)) .@"volatile" else .normal);
6729 return self.loadByRef(ptr, elem_ty, elem_align.toLlvm(), access_kind);
6730 } else {
6731 return self.loadTruncate(access_kind, elem_ty, ptr, elem_align.toLlvm());
67256732 }
6726
6727 self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
6728
6729 return self.load(ptr, slice_ty);
67306733 }
67316734
67326735 fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
......@@ -7507,7 +7510,7 @@ pub const FuncGen = struct {
75077510
75087511 if (optional_ty.optionalReprIsPayload(zcu)) {
75097512 const loaded = if (operand_is_ptr)
7510 try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "")
7513 try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "")
75117514 else
75127515 operand;
75137516 if (payload_ty.isSlice(zcu)) {
......@@ -7525,7 +7528,7 @@ pub const FuncGen = struct {
75257528
75267529 if (!payload_ty.hasRuntimeBits(zcu)) {
75277530 const loaded = if (operand_is_ptr)
7528 try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "")
7531 try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "")
75297532 else
75307533 operand;
75317534 return self.wip.icmp(cond, loaded, try o.builder.intValue(.i8, 0), "");
......@@ -7568,7 +7571,7 @@ pub const FuncGen = struct {
75687571
75697572 if (!payload_ty.hasRuntimeBits(zcu)) {
75707573 const loaded = if (operand_is_ptr)
7571 try self.wip.load(access_kind, try o.lowerType(pt, err_union_ty), operand, .default, "")
7574 try self.wip.load(access_kind, try o.lowerType(pt, err_union_ty), operand, operand_ty.ptrAlignment(zcu).toLlvm(), "")
75727575 else
75737576 operand;
75747577 return self.wip.icmp(cond, loaded, zero, "");
......@@ -7578,9 +7581,13 @@ pub const FuncGen = struct {
75787581
75797582 const loaded = if (operand_is_ptr or isByRef(err_union_ty, zcu)) loaded: {
75807583 const err_union_llvm_ty = try o.lowerType(pt, err_union_ty);
7584 const err_alignment = if (operand_is_ptr)
7585 operand_ty.ptrAlignment(zcu).minStrict(Type.anyerror.abiAlignment(zcu))
7586 else
7587 .none;
75817588 const err_field_ptr =
75827589 try self.wip.gepStruct(err_union_llvm_ty, operand, err_field_index, "");
7583 break :loaded try self.wip.load(access_kind, error_type, err_field_ptr, .default, "");
7590 break :loaded try self.wip.load(access_kind, error_type, err_field_ptr, err_alignment.toLlvm(), "");
75847591 } else try self.wip.extractValue(operand, &.{err_field_index}, "");
75857592 return self.wip.icmp(cond, loaded, zero, "");
75867593 }
......@@ -7625,6 +7632,7 @@ pub const FuncGen = struct {
76257632 self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu));
76267633
76277634 // We have a pointer to a i8. We need to set it to 1 and then return the same pointer.
7635 // Default alignment store because align of the non null bit is 1 anyway.
76287636 _ = try self.wip.store(access_kind, non_null_bit, operand, .default);
76297637 return operand;
76307638 }
......@@ -7640,7 +7648,7 @@ pub const FuncGen = struct {
76407648
76417649 self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu));
76427650
7643 // TODO set alignment on this store
7651 // Default alignment store because align of the non null bit is 1 anyway.
76447652 _ = try self.wip.store(access_kind, non_null_bit, non_null_ptr, .default);
76457653
76467654 // Then return the payload pointer (only if it's used).
......@@ -7728,7 +7736,7 @@ pub const FuncGen = struct {
77287736
77297737 self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
77307738
7731 return self.wip.load(access_kind, error_type, operand, .default, "");
7739 return self.wip.load(access_kind, error_type, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "");
77327740 }
77337741
77347742 const offset = try errUnionErrorOffset(payload_ty, pt);
......@@ -7752,6 +7760,7 @@ pub const FuncGen = struct {
77527760 const operand = try self.resolveInst(ty_op.operand);
77537761 const err_union_ptr_ty = self.typeOf(ty_op.operand);
77547762 const err_union_ty = err_union_ptr_ty.childType(zcu);
7763 const err_union_ptr_align = err_union_ptr_ty.ptrAlignment(zcu);
77557764
77567765 const payload_ty = err_union_ty.errorUnionPayload(zcu);
77577766 const non_error_val = try o.builder.intValue(try o.errorIntType(pt), 0);
......@@ -7761,8 +7770,7 @@ pub const FuncGen = struct {
77617770
77627771 if (!payload_ty.hasRuntimeBits(zcu)) {
77637772 self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu));
7764
7765 _ = try self.wip.store(access_kind, non_error_val, operand, .default);
7773 _ = try self.wip.store(access_kind, non_error_val, operand, err_union_ptr_align.toLlvm());
77667774 return operand;
77677775 }
77687776 const err_union_llvm_ty = try o.lowerType(pt, err_union_ty);
......@@ -7770,7 +7778,7 @@ pub const FuncGen = struct {
77707778 self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu));
77717779
77727780 const err_int_ty = try pt.errorIntType();
7773 const error_alignment = err_int_ty.abiAlignment(zcu).toLlvm();
7781 const error_alignment = err_int_ty.abiAlignment(zcu).minStrict(err_union_ptr_align).toLlvm();
77747782 const error_offset = try errUnionErrorOffset(payload_ty, pt);
77757783 // First set the non-error value.
77767784 const non_null_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, error_offset, "");
......@@ -8451,9 +8459,8 @@ pub const FuncGen = struct {
84518459 _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment);
84528460 }
84538461 {
8454 const overflow_alignment = comptime Builder.Alignment.fromByteUnits(1);
84558462 const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, overflow_index, "");
8456 _ = try self.wip.store(.normal, overflow_bit, field_ptr, overflow_alignment);
8463 _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1));
84578464 }
84588465
84598466 return alloca_inst;
......@@ -8813,9 +8820,8 @@ pub const FuncGen = struct {
88138820 _ = try self.wip.store(.normal, result, field_ptr, result_alignment);
88148821 }
88158822 {
8816 const field_alignment = comptime Builder.Alignment.fromByteUnits(1);
88178823 const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, overflow_index, "");
8818 _ = try self.wip.store(.normal, overflow_bit, field_ptr, field_alignment);
8824 _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1));
88198825 }
88208826 return alloca_inst;
88218827 }
......@@ -9971,15 +9977,18 @@ pub const FuncGen = struct {
99719977
99729978 const union_ptr = try self.resolveInst(bin_op.lhs);
99739979 const new_tag = try self.resolveInst(bin_op.rhs);
9980 const union_ptr_align = un_ptr_ty.ptrAlignment(zcu);
99749981 if (layout.payload_size == 0) {
9975 // TODO alignment on this store
9976 _ = try self.wip.store(access_kind, new_tag, union_ptr, .default);
9982 _ = try self.wip.store(access_kind, new_tag, union_ptr, union_ptr_align.toLlvm());
99779983 return .none;
99789984 }
99799985 const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));
99809986 const tag_field_ptr = try self.wip.gepStruct(try o.lowerType(pt, un_ty), union_ptr, tag_index, "");
9981 // TODO alignment on this store
9982 _ = try self.wip.store(access_kind, new_tag, tag_field_ptr, .default);
9987 const tag_ptr_align: InternPool.Alignment = switch (layout.tagOffset()) {
9988 0 => union_ptr_align,
9989 else => |off| .minStrict(union_ptr_align, .fromLog2Units(@ctz(off))),
9990 };
9991 _ = try self.wip.store(access_kind, new_tag, tag_field_ptr, tag_ptr_align.toLlvm());
99839992 return .none;
99849993 }
99859994