authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-10-31 09:18:46+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-05-12 17:07:49+02:00
loge95e7651ecd988d4955a46c7adfea29c0996c62f
tree86ff483ac9358ad6d40db92d5eb504025eaacbfb
parent5c2e300f427efd76f52aefdaa8ad7fb85bb1d2d5
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

llvm: Set null_pointer_is_valid attribute when accessing allowzero pointers.

This informs optimization passes that they shouldn't assume that a load from a null pointer invokes undefined behavior. Closes #15816.

1 files changed, 84 insertions(+), 7 deletions(-)

src/codegen/llvm.zig+84-7
...@@ -1419,8 +1419,6 @@ pub const Object = struct {...@@ -1419,8 +1419,6 @@ pub const Object = struct {
1419 }1419 }
1420 }1420 }
14211421
1422 function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
1423
1424 const file, const subprogram = if (!wip.strip) debug_info: {1422 const file, const subprogram = if (!wip.strip) debug_info: {
1425 const file = try o.getDebugFile(file_scope);1423 const file = try o.getDebugFile(file_scope);
14261424
...@@ -1517,6 +1515,17 @@ pub const Object = struct {...@@ -1517,6 +1515,17 @@ pub const Object = struct {
1517 else => |e| return e,1515 else => |e| return e,
1518 };1516 };
15191517
1518 // If we saw any loads or stores involving `allowzero` pointers, we need to mark the whole
1519 // function as considering null pointers valid so that LLVM's optimizers don't remove these
1520 // operations on the assumption that they're undefined behavior.
1521 if (fg.allowzero_access) {
1522 try attributes.addFnAttr(.null_pointer_is_valid, &o.builder);
1523 } else {
1524 _ = try attributes.removeFnAttr(.null_pointer_is_valid);
1525 }
1526
1527 function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
1528
1520 if (fg.fuzz) |*f| {1529 if (fg.fuzz) |*f| {
1521 {1530 {
1522 const array_llvm_ty = try o.builder.arrayType(f.pcs.items.len, .i8);1531 const array_llvm_ty = try o.builder.arrayType(f.pcs.items.len, .i8);
...@@ -4667,6 +4676,15 @@ pub const FuncGen = struct {...@@ -4667,6 +4676,15 @@ pub const FuncGen = struct {
46674676
4668 disable_intrinsics: bool,4677 disable_intrinsics: bool,
46694678
4679 /// Have we seen loads or stores involving `allowzero` pointers?
4680 allowzero_access: bool = false,
4681
4682 pub fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void {
4683 // LLVM already considers null pointers to be valid in non-generic address spaces, so avoid
4684 // pessimizing optimization for functions with accesses to such pointers.
4685 if (info.flags.address_space == .generic and info.flags.is_allowzero) self.allowzero_access = true;
4686 }
4687
4670 const Fuzz = struct {4688 const Fuzz = struct {
4671 counters_variable: Builder.Variable.Index,4689 counters_variable: Builder.Variable.Index,
4672 pcs: std.ArrayListUnmanaged(Builder.Constant),4690 pcs: std.ArrayListUnmanaged(Builder.Constant),
...@@ -6206,6 +6224,9 @@ pub const FuncGen = struct {...@@ -6206,6 +6224,9 @@ pub const FuncGen = struct {
6206 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);6224 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
6207 const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu);6225 const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu);
6208 const is_unused = self.liveness.isUnused(inst);6226 const is_unused = self.liveness.isUnused(inst);
6227
6228 self.maybeMarkAllowZeroAccess(self.typeOf(extra.data.ptr).ptrInfo(zcu));
6229
6209 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold);6230 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold);
6210 }6231 }
62116232
...@@ -6751,10 +6772,14 @@ pub const FuncGen = struct {...@@ -6751,10 +6772,14 @@ pub const FuncGen = struct {
6751 if (self.canElideLoad(body_tail))6772 if (self.canElideLoad(body_tail))
6752 return ptr;6773 return ptr;
67536774
6775 self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
6776
6754 const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();6777 const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();
6755 return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);6778 return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);
6756 }6779 }
67576780
6781 self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
6782
6758 return self.load(ptr, slice_ty);6783 return self.load(ptr, slice_ty);
6759 }6784 }
67606785
...@@ -6824,10 +6849,15 @@ pub const FuncGen = struct {...@@ -6824,10 +6849,15 @@ pub const FuncGen = struct {
6824 &.{rhs}, "");6849 &.{rhs}, "");
6825 if (isByRef(elem_ty, zcu)) {6850 if (isByRef(elem_ty, zcu)) {
6826 if (self.canElideLoad(body_tail)) return ptr;6851 if (self.canElideLoad(body_tail)) return ptr;
6852
6853 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
6854
6827 const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();6855 const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();
6828 return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);6856 return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);
6829 }6857 }
68306858
6859 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
6860
6831 return self.load(ptr, ptr_ty);6861 return self.load(ptr, ptr_ty);
6832 }6862 }
68336863
...@@ -7235,6 +7265,8 @@ pub const FuncGen = struct {...@@ -7235,6 +7265,8 @@ pub const FuncGen = struct {
7235 }),7265 }),
7236 }7266 }
72377267
7268 self.maybeMarkAllowZeroAccess(output_ty.ptrInfo(zcu));
7269
7238 // Pass any non-return outputs indirectly, if the constraint accepts a memory location7270 // Pass any non-return outputs indirectly, if the constraint accepts a memory location
7239 is_indirect.* = constraintAllowsMemory(constraint);7271 is_indirect.* = constraintAllowsMemory(constraint);
7240 if (is_indirect.*) {7272 if (is_indirect.*) {
...@@ -7341,10 +7373,11 @@ pub const FuncGen = struct {...@@ -7341,10 +7373,11 @@ pub const FuncGen = struct {
73417373
7342 // In the case of indirect inputs, LLVM requires the callsite to have7374 // In the case of indirect inputs, LLVM requires the callsite to have
7343 // an elementtype(<ty>) attribute.7375 // an elementtype(<ty>) attribute.
7344 llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*')7376 llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*') blk: {
7345 try o.lowerPtrElemTy(if (is_by_ref) arg_ty else arg_ty.childType(zcu))7377 if (!is_by_ref) self.maybeMarkAllowZeroAccess(arg_ty.ptrInfo(zcu));
7346 else7378
7347 .none;7379 break :blk try o.lowerPtrElemTy(if (is_by_ref) arg_ty else arg_ty.childType(zcu));
7380 } else .none;
73487381
7349 llvm_param_i += 1;7382 llvm_param_i += 1;
7350 total_i += 1;7383 total_i += 1;
...@@ -7530,7 +7563,6 @@ pub const FuncGen = struct {...@@ -7530,7 +7563,6 @@ pub const FuncGen = struct {
7530 if (output != .none) {7563 if (output != .none) {
7531 const output_ptr = try self.resolveInst(output);7564 const output_ptr = try self.resolveInst(output);
7532 const output_ptr_ty = self.typeOf(output);7565 const output_ptr_ty = self.typeOf(output);
7533
7534 const alignment = output_ptr_ty.ptrAlignment(zcu).toLlvm();7566 const alignment = output_ptr_ty.ptrAlignment(zcu).toLlvm();
7535 _ = try self.wip.store(.normal, output_value, output_ptr, alignment);7567 _ = try self.wip.store(.normal, output_value, output_ptr, alignment);
7536 } else {7568 } else {
...@@ -7557,6 +7589,9 @@ pub const FuncGen = struct {...@@ -7557,6 +7589,9 @@ pub const FuncGen = struct {
7557 const optional_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty;7589 const optional_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty;
7558 const optional_llvm_ty = try o.lowerType(optional_ty);7590 const optional_llvm_ty = try o.lowerType(optional_ty);
7559 const payload_ty = optional_ty.optionalChild(zcu);7591 const payload_ty = optional_ty.optionalChild(zcu);
7592
7593 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
7594
7560 if (optional_ty.optionalReprIsPayload(zcu)) {7595 if (optional_ty.optionalReprIsPayload(zcu)) {
7561 const loaded = if (operand_is_ptr)7596 const loaded = if (operand_is_ptr)
7562 try self.wip.load(.normal, optional_llvm_ty, operand, .default, "")7597 try self.wip.load(.normal, optional_llvm_ty, operand, .default, "")
...@@ -7613,6 +7648,8 @@ pub const FuncGen = struct {...@@ -7613,6 +7648,8 @@ pub const FuncGen = struct {
7613 return val.toValue();7648 return val.toValue();
7614 }7649 }
76157650
7651 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
7652
7616 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {7653 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
7617 const loaded = if (operand_is_ptr)7654 const loaded = if (operand_is_ptr)
7618 try self.wip.load(.normal, try o.lowerType(err_union_ty), operand, .default, "")7655 try self.wip.load(.normal, try o.lowerType(err_union_ty), operand, .default, "")
...@@ -7664,6 +7701,8 @@ pub const FuncGen = struct {...@@ -7664,6 +7701,8 @@ pub const FuncGen = struct {
7664 const payload_ty = optional_ty.optionalChild(zcu);7701 const payload_ty = optional_ty.optionalChild(zcu);
7665 const non_null_bit = try o.builder.intValue(.i8, 1);7702 const non_null_bit = try o.builder.intValue(.i8, 1);
7666 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {7703 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
7704 self.maybeMarkAllowZeroAccess(self.typeOf(ty_op.operand).ptrInfo(zcu));
7705
7667 // We have a pointer to a i8. We need to set it to 1 and then return the same pointer.7706 // We have a pointer to a i8. We need to set it to 1 and then return the same pointer.
7668 _ = try self.wip.store(.normal, non_null_bit, operand, .default);7707 _ = try self.wip.store(.normal, non_null_bit, operand, .default);
7669 return operand;7708 return operand;
...@@ -7677,6 +7716,9 @@ pub const FuncGen = struct {...@@ -7677,6 +7716,9 @@ pub const FuncGen = struct {
7677 // First set the non-null bit.7716 // First set the non-null bit.
7678 const optional_llvm_ty = try o.lowerType(optional_ty);7717 const optional_llvm_ty = try o.lowerType(optional_ty);
7679 const non_null_ptr = try self.wip.gepStruct(optional_llvm_ty, operand, 1, "");7718 const non_null_ptr = try self.wip.gepStruct(optional_llvm_ty, operand, 1, "");
7719
7720 self.maybeMarkAllowZeroAccess(self.typeOf(ty_op.operand).ptrInfo(zcu));
7721
7680 // TODO set alignment on this store7722 // TODO set alignment on this store
7681 _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default);7723 _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default);
76827724
...@@ -7767,12 +7809,17 @@ pub const FuncGen = struct {...@@ -7767,12 +7809,17 @@ pub const FuncGen = struct {
7767 const payload_ty = err_union_ty.errorUnionPayload(zcu);7809 const payload_ty = err_union_ty.errorUnionPayload(zcu);
7768 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {7810 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
7769 if (!operand_is_ptr) return operand;7811 if (!operand_is_ptr) return operand;
7812
7813 self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
7814
7770 return self.wip.load(.normal, error_type, operand, .default, "");7815 return self.wip.load(.normal, error_type, operand, .default, "");
7771 }7816 }
77727817
7773 const offset = try errUnionErrorOffset(payload_ty, pt);7818 const offset = try errUnionErrorOffset(payload_ty, pt);
77747819
7775 if (operand_is_ptr or isByRef(err_union_ty, zcu)) {7820 if (operand_is_ptr or isByRef(err_union_ty, zcu)) {
7821 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
7822
7776 const err_union_llvm_ty = try o.lowerType(err_union_ty);7823 const err_union_llvm_ty = try o.lowerType(err_union_ty);
7777 const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, "");7824 const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, "");
7778 return self.wip.load(.normal, error_type, err_field_ptr, .default, "");7825 return self.wip.load(.normal, error_type, err_field_ptr, .default, "");
...@@ -7792,11 +7839,15 @@ pub const FuncGen = struct {...@@ -7792,11 +7839,15 @@ pub const FuncGen = struct {
7792 const payload_ty = err_union_ty.errorUnionPayload(zcu);7839 const payload_ty = err_union_ty.errorUnionPayload(zcu);
7793 const non_error_val = try o.builder.intValue(try o.errorIntType(), 0);7840 const non_error_val = try o.builder.intValue(try o.errorIntType(), 0);
7794 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {7841 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
7842 self.maybeMarkAllowZeroAccess(self.typeOf(ty_op.operand).ptrInfo(zcu));
7843
7795 _ = try self.wip.store(.normal, non_error_val, operand, .default);7844 _ = try self.wip.store(.normal, non_error_val, operand, .default);
7796 return operand;7845 return operand;
7797 }7846 }
7798 const err_union_llvm_ty = try o.lowerType(err_union_ty);7847 const err_union_llvm_ty = try o.lowerType(err_union_ty);
7799 {7848 {
7849 self.maybeMarkAllowZeroAccess(self.typeOf(ty_op.operand).ptrInfo(zcu));
7850
7800 const err_int_ty = try pt.errorIntType();7851 const err_int_ty = try pt.errorIntType();
7801 const error_alignment = err_int_ty.abiAlignment(zcu).toLlvm();7852 const error_alignment = err_int_ty.abiAlignment(zcu).toLlvm();
7802 const error_offset = try errUnionErrorOffset(payload_ty, pt);7853 const error_offset = try errUnionErrorOffset(payload_ty, pt);
...@@ -8017,6 +8068,8 @@ pub const FuncGen = struct {...@@ -8017,6 +8068,8 @@ pub const FuncGen = struct {
8017 const index = try self.resolveInst(extra.lhs);8068 const index = try self.resolveInst(extra.lhs);
8018 const operand = try self.resolveInst(extra.rhs);8069 const operand = try self.resolveInst(extra.rhs);
80198070
8071 self.maybeMarkAllowZeroAccess(vector_ptr_ty.ptrInfo(zcu));
8072
8020 const access_kind: Builder.MemoryAccessKind =8073 const access_kind: Builder.MemoryAccessKind =
8021 if (vector_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;8074 if (vector_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
8022 const elem_llvm_ty = try o.lowerType(vector_ptr_ty.childType(zcu));8075 const elem_llvm_ty = try o.lowerType(vector_ptr_ty.childType(zcu));
...@@ -9482,6 +9535,8 @@ pub const FuncGen = struct {...@@ -9482,6 +9535,8 @@ pub const FuncGen = struct {
9482 return .none;9535 return .none;
9483 }9536 }
94849537
9538 self.maybeMarkAllowZeroAccess(ptr_info);
9539
9485 const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(zcu));9540 const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(zcu));
9486 _ = try self.wip.callMemSet(9541 _ = try self.wip.callMemSet(
9487 dest_ptr,9542 dest_ptr,
...@@ -9497,6 +9552,8 @@ pub const FuncGen = struct {...@@ -9497,6 +9552,8 @@ pub const FuncGen = struct {
9497 return .none;9552 return .none;
9498 }9553 }
94999554
9555 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
9556
9500 const src_operand = try self.resolveInst(bin_op.rhs);9557 const src_operand = try self.resolveInst(bin_op.rhs);
9501 try self.store(dest_ptr, ptr_ty, src_operand, .none);9558 try self.store(dest_ptr, ptr_ty, src_operand, .none);
9502 return .none;9559 return .none;
...@@ -9539,6 +9596,9 @@ pub const FuncGen = struct {...@@ -9539,6 +9596,9 @@ pub const FuncGen = struct {
9539 if (!canElideLoad(fg, body_tail)) break :elide;9596 if (!canElideLoad(fg, body_tail)) break :elide;
9540 return ptr;9597 return ptr;
9541 }9598 }
9599
9600 fg.maybeMarkAllowZeroAccess(ptr_info);
9601
9542 return fg.load(ptr, ptr_ty);9602 return fg.load(ptr, ptr_ty);
9543 }9603 }
95449604
...@@ -9598,6 +9658,8 @@ pub const FuncGen = struct {...@@ -9598,6 +9658,8 @@ pub const FuncGen = struct {
9598 new_value = try self.wip.conv(signedness, new_value, llvm_abi_ty, "");9658 new_value = try self.wip.conv(signedness, new_value, llvm_abi_ty, "");
9599 }9659 }
96009660
9661 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
9662
9601 const result = try self.wip.cmpxchg(9663 const result = try self.wip.cmpxchg(
9602 kind,9664 kind,
9603 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,9665 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,
...@@ -9649,6 +9711,8 @@ pub const FuncGen = struct {...@@ -9649,6 +9711,8 @@ pub const FuncGen = struct {
9649 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;9711 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
9650 const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm();9712 const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
96519713
9714 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
9715
9652 if (llvm_abi_ty != .none) {9716 if (llvm_abi_ty != .none) {
9653 // operand needs widening and truncating or bitcasting.9717 // operand needs widening and truncating or bitcasting.
9654 return self.wip.cast(if (is_float) .bitcast else .trunc, try self.wip.atomicrmw(9718 return self.wip.cast(if (is_float) .bitcast else .trunc, try self.wip.atomicrmw(
...@@ -9712,6 +9776,8 @@ pub const FuncGen = struct {...@@ -9712,6 +9776,8 @@ pub const FuncGen = struct {
9712 if (info.flags.is_volatile) .@"volatile" else .normal;9776 if (info.flags.is_volatile) .@"volatile" else .normal;
9713 const elem_llvm_ty = try o.lowerType(elem_ty);9777 const elem_llvm_ty = try o.lowerType(elem_ty);
97149778
9779 self.maybeMarkAllowZeroAccess(info);
9780
9715 if (llvm_abi_ty != .none) {9781 if (llvm_abi_ty != .none) {
9716 // operand needs widening and truncating9782 // operand needs widening and truncating
9717 const loaded = try self.wip.loadAtomic(9783 const loaded = try self.wip.loadAtomic(
...@@ -9761,6 +9827,9 @@ pub const FuncGen = struct {...@@ -9761,6 +9827,9 @@ pub const FuncGen = struct {
9761 "",9827 "",
9762 );9828 );
9763 }9829 }
9830
9831 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
9832
9764 try self.store(ptr, ptr_ty, element, ordering);9833 try self.store(ptr, ptr_ty, element, ordering);
9765 return .none;9834 return .none;
9766 }9835 }
...@@ -9778,6 +9847,8 @@ pub const FuncGen = struct {...@@ -9778,6 +9847,8 @@ pub const FuncGen = struct {
9778 const access_kind: Builder.MemoryAccessKind =9847 const access_kind: Builder.MemoryAccessKind =
9779 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;9848 if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
97809849
9850 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
9851
9781 if (try self.air.value(bin_op.rhs, pt)) |elem_val| {9852 if (try self.air.value(bin_op.rhs, pt)) |elem_val| {
9782 if (elem_val.isUndefDeep(zcu)) {9853 if (elem_val.isUndefDeep(zcu)) {
9783 // Even if safety is disabled, we still emit a memset to undefined since it conveys9854 // Even if safety is disabled, we still emit a memset to undefined since it conveys
...@@ -9916,6 +9987,9 @@ pub const FuncGen = struct {...@@ -9916,6 +9987,9 @@ pub const FuncGen = struct {
9916 const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or9987 const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or
9917 dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;9988 dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
99189989
9990 self.maybeMarkAllowZeroAccess(dest_ptr_ty.ptrInfo(zcu));
9991 self.maybeMarkAllowZeroAccess(src_ptr_ty.ptrInfo(zcu));
9992
9919 _ = try self.wip.callMemCpy(9993 _ = try self.wip.callMemCpy(
9920 dest_ptr,9994 dest_ptr,
9921 dest_ptr_ty.ptrAlignment(zcu).toLlvm(),9995 dest_ptr_ty.ptrAlignment(zcu).toLlvm(),
...@@ -9962,6 +10036,9 @@ pub const FuncGen = struct {...@@ -9962,6 +10036,9 @@ pub const FuncGen = struct {
9962 const un_ty = self.typeOf(bin_op.lhs).childType(zcu);10036 const un_ty = self.typeOf(bin_op.lhs).childType(zcu);
9963 const layout = un_ty.unionGetLayout(zcu);10037 const layout = un_ty.unionGetLayout(zcu);
9964 if (layout.tag_size == 0) return .none;10038 if (layout.tag_size == 0) return .none;
10039
10040 self.maybeMarkAllowZeroAccess(self.typeOf(bin_op.lhs).ptrInfo(zcu));
10041
9965 const union_ptr = try self.resolveInst(bin_op.lhs);10042 const union_ptr = try self.resolveInst(bin_op.lhs);
9966 const new_tag = try self.resolveInst(bin_op.rhs);10043 const new_tag = try self.resolveInst(bin_op.rhs);
9967 if (layout.payload_size == 0) {10044 if (layout.payload_size == 0) {