aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alex@alexrp.com>2025-05-13 10:42:05 +0200
committerGitHub <noreply@github.com>2025-05-13 10:42:05 +0200
commit5b606d435d139487c23d6fd30e8061dbad741ada (patch)
tree7c1cb233429d26ea85249b21f7cb7f80e8527476 /src/codegen
parenta365971a337116dd23df2b1bc86468d54885b4b2 (diff)
parent9d8adb38a18169a16707acac2812dd6850de99be (diff)
downloadzig-5b606d435d139487c23d6fd30e8061dbad741ada.tar.gz
zig-5b606d435d139487c23d6fd30e8061dbad741ada.zip
Merge pull request #21882 from alexrp/compiler-fixes
compiler: Fix some real and theoretical miscompilations with `allowzero` and `volatile`
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/llvm.zig202
1 files changed, 164 insertions, 38 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 891cc0dc52..050e47b68a 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1341,7 +1341,10 @@ pub const Object = struct {
try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
}
}
- if (param_ty.zigTypeTag(zcu) != .optional) {
+ if (param_ty.zigTypeTag(zcu) != .optional and
+ !ptr_info.flags.is_allowzero and
+ ptr_info.flags.address_space == .generic)
+ {
try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
}
if (ptr_info.flags.is_const) {
@@ -1419,8 +1422,6 @@ pub const Object = struct {
}
}
- function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
-
const file, const subprogram = if (!wip.strip) debug_info: {
const file = try o.getDebugFile(file_scope);
@@ -1517,6 +1518,17 @@ pub const Object = struct {
else => |e| return e,
};
+ // If we saw any loads or stores involving `allowzero` pointers, we need to mark the whole
+ // function as considering null pointers valid so that LLVM's optimizers don't remove these
+ // operations on the assumption that they're undefined behavior.
+ if (fg.allowzero_access) {
+ try attributes.addFnAttr(.null_pointer_is_valid, &o.builder);
+ } else {
+ _ = try attributes.removeFnAttr(.null_pointer_is_valid);
+ }
+
+ function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
+
if (fg.fuzz) |*f| {
{
const array_llvm_ty = try o.builder.arrayType(f.pcs.items.len, .i8);
@@ -4349,7 +4361,10 @@ pub const Object = struct {
try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
}
}
- if (!param_ty.isPtrLikeOptional(zcu) and !ptr_info.flags.is_allowzero) {
+ if (!param_ty.isPtrLikeOptional(zcu) and
+ !ptr_info.flags.is_allowzero and
+ ptr_info.flags.address_space == .generic)
+ {
try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
}
switch (fn_info.cc) {
@@ -4667,6 +4682,15 @@ pub const FuncGen = struct {
disable_intrinsics: bool,
+ /// Have we seen loads or stores involving `allowzero` pointers?
+ allowzero_access: bool = false,
+
+ pub fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void {
+ // LLVM already considers null pointers to be valid in non-generic address spaces, so avoid
+ // pessimizing optimization for functions with accesses to such pointers.
+ if (info.flags.address_space == .generic and info.flags.is_allowzero) self.allowzero_access = true;
+ }
+
const Fuzz = struct {
counters_variable: Builder.Variable.Index,
pcs: std.ArrayListUnmanaged(Builder.Constant),
@@ -5392,7 +5416,10 @@ pub const FuncGen = struct {
try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
}
}
- if (param_ty.zigTypeTag(zcu) != .optional) {
+ if (param_ty.zigTypeTag(zcu) != .optional and
+ !ptr_info.flags.is_allowzero and
+ ptr_info.flags.address_space == .generic)
+ {
try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
}
if (ptr_info.flags.is_const) {
@@ -5519,7 +5546,7 @@ pub const FuncGen = struct {
ptr_ty.ptrAlignment(zcu).toLlvm(),
try o.builder.intValue(.i8, 0xaa),
len,
- if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,
+ .normal,
self.disable_intrinsics,
);
const owner_mod = self.ng.ownerModule();
@@ -5754,8 +5781,8 @@ pub const FuncGen = struct {
// of optionals that are not pointers.
const is_by_ref = isByRef(scalar_ty, zcu);
const opt_llvm_ty = try o.lowerType(scalar_ty);
- const lhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, lhs, is_by_ref);
- const rhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, rhs, is_by_ref);
+ const lhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, lhs, is_by_ref, .normal);
+ const rhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, rhs, is_by_ref, .normal);
const llvm_i2 = try o.builder.intType(2);
const lhs_non_null_i2 = try self.wip.cast(.zext, lhs_non_null, llvm_i2, "");
const rhs_non_null_i2 = try self.wip.cast(.zext, rhs_non_null, llvm_i2, "");
@@ -6206,6 +6233,9 @@ pub const FuncGen = struct {
const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu);
const is_unused = self.liveness.isUnused(inst);
+
+ self.maybeMarkAllowZeroAccess(self.typeOf(extra.data.ptr).ptrInfo(zcu));
+
return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold);
}
@@ -6229,10 +6259,13 @@ pub const FuncGen = struct {
if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
const loaded = loaded: {
+ const access_kind: Builder.MemoryAccessKind =
+ if (err_union_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
if (!payload_has_bits) {
// TODO add alignment to this load
break :loaded if (operand_is_ptr)
- try fg.wip.load(.normal, error_type, err_union, .default, "")
+ try fg.wip.load(access_kind, error_type, err_union, .default, "")
else
err_union;
}
@@ -6242,7 +6275,7 @@ pub const FuncGen = struct {
try fg.wip.gepStruct(err_union_llvm_ty, err_union, err_field_index, "");
// TODO add alignment to this load
break :loaded try fg.wip.load(
- .normal,
+ if (operand_is_ptr) access_kind else .normal,
error_type,
err_field_ptr,
.default,
@@ -6751,10 +6784,14 @@ pub const FuncGen = struct {
if (self.canElideLoad(body_tail))
return ptr;
+ self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
+
const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();
- return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);
+ return self.loadByRef(ptr, elem_ty, elem_alignment, if (slice_ty.isVolatilePtr(zcu)) .@"volatile" else .normal);
}
+ self.maybeMarkAllowZeroAccess(slice_ty.ptrInfo(zcu));
+
return self.load(ptr, slice_ty);
}
@@ -6824,10 +6861,15 @@ pub const FuncGen = struct {
&.{rhs}, "");
if (isByRef(elem_ty, zcu)) {
if (self.canElideLoad(body_tail)) return ptr;
+
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm();
- return self.loadByRef(ptr, elem_ty, elem_alignment, .normal);
+ return self.loadByRef(ptr, elem_ty, elem_alignment, if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal);
}
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
return self.load(ptr, ptr_ty);
}
@@ -7235,6 +7277,8 @@ pub const FuncGen = struct {
}),
}
+ self.maybeMarkAllowZeroAccess(output_ty.ptrInfo(zcu));
+
// Pass any non-return outputs indirectly, if the constraint accepts a memory location
is_indirect.* = constraintAllowsMemory(constraint);
if (is_indirect.*) {
@@ -7341,10 +7385,11 @@ pub const FuncGen = struct {
// In the case of indirect inputs, LLVM requires the callsite to have
// an elementtype(<ty>) attribute.
- llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*')
- try o.lowerPtrElemTy(if (is_by_ref) arg_ty else arg_ty.childType(zcu))
- else
- .none;
+ llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*') blk: {
+ if (!is_by_ref) self.maybeMarkAllowZeroAccess(arg_ty.ptrInfo(zcu));
+
+ break :blk try o.lowerPtrElemTy(if (is_by_ref) arg_ty else arg_ty.childType(zcu));
+ } else .none;
llvm_param_i += 1;
total_i += 1;
@@ -7367,7 +7412,13 @@ pub const FuncGen = struct {
llvm_param_types[llvm_param_i] = llvm_rw_val.typeOfWip(&self.wip);
} else {
const alignment = rw_ty.abiAlignment(zcu).toLlvm();
- const loaded = try self.wip.load(.normal, llvm_elem_ty, llvm_rw_val, alignment, "");
+ const loaded = try self.wip.load(
+ if (rw_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,
+ llvm_elem_ty,
+ llvm_rw_val,
+ alignment,
+ "",
+ );
llvm_param_values[llvm_param_i] = loaded;
llvm_param_types[llvm_param_i] = llvm_elem_ty;
}
@@ -7530,9 +7581,13 @@ pub const FuncGen = struct {
if (output != .none) {
const output_ptr = try self.resolveInst(output);
const output_ptr_ty = self.typeOf(output);
-
const alignment = output_ptr_ty.ptrAlignment(zcu).toLlvm();
- _ = try self.wip.store(.normal, output_value, output_ptr, alignment);
+ _ = try self.wip.store(
+ if (output_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,
+ output_value,
+ output_ptr,
+ alignment,
+ );
} else {
ret_val = output_value;
}
@@ -7557,9 +7612,15 @@ pub const FuncGen = struct {
const optional_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty;
const optional_llvm_ty = try o.lowerType(optional_ty);
const payload_ty = optional_ty.optionalChild(zcu);
+
+ const access_kind: Builder.MemoryAccessKind =
+ if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
+ if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
+
if (optional_ty.optionalReprIsPayload(zcu)) {
const loaded = if (operand_is_ptr)
- try self.wip.load(.normal, optional_llvm_ty, operand, .default, "")
+ try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "")
else
operand;
if (payload_ty.isSlice(zcu)) {
@@ -7577,14 +7638,14 @@ pub const FuncGen = struct {
if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
const loaded = if (operand_is_ptr)
- try self.wip.load(.normal, optional_llvm_ty, operand, .default, "")
+ try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "")
else
operand;
return self.wip.icmp(cond, loaded, try o.builder.intValue(.i8, 0), "");
}
const is_by_ref = operand_is_ptr or isByRef(optional_ty, zcu);
- return self.optCmpNull(cond, optional_llvm_ty, operand, is_by_ref);
+ return self.optCmpNull(cond, optional_llvm_ty, operand, is_by_ref, access_kind);
}
fn airIsErr(
@@ -7604,6 +7665,9 @@ pub const FuncGen = struct {
const error_type = try o.errorIntType();
const zero = try o.builder.intValue(error_type, 0);
+ const access_kind: Builder.MemoryAccessKind =
+ if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
const val: Builder.Constant = switch (cond) {
.eq => .true, // 0 == 0
@@ -7613,9 +7677,11 @@ pub const FuncGen = struct {
return val.toValue();
}
+ if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
+
if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
const loaded = if (operand_is_ptr)
- try self.wip.load(.normal, try o.lowerType(err_union_ty), operand, .default, "")
+ try self.wip.load(access_kind, try o.lowerType(err_union_ty), operand, .default, "")
else
operand;
return self.wip.icmp(cond, loaded, zero, "");
@@ -7627,7 +7693,7 @@ pub const FuncGen = struct {
const err_union_llvm_ty = try o.lowerType(err_union_ty);
const err_field_ptr =
try self.wip.gepStruct(err_union_llvm_ty, operand, err_field_index, "");
- break :loaded try self.wip.load(.normal, error_type, err_field_ptr, .default, "");
+ break :loaded try self.wip.load(access_kind, error_type, err_field_ptr, .default, "");
} else try self.wip.extractValue(operand, &.{err_field_index}, "");
return self.wip.icmp(cond, loaded, zero, "");
}
@@ -7660,12 +7726,19 @@ pub const FuncGen = struct {
const zcu = pt.zcu;
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const operand = try self.resolveInst(ty_op.operand);
- const optional_ty = self.typeOf(ty_op.operand).childType(zcu);
+ const optional_ptr_ty = self.typeOf(ty_op.operand);
+ const optional_ty = optional_ptr_ty.childType(zcu);
const payload_ty = optional_ty.optionalChild(zcu);
const non_null_bit = try o.builder.intValue(.i8, 1);
+
+ const access_kind: Builder.MemoryAccessKind =
+ if (optional_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
+ self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu));
+
// We have a pointer to a i8. We need to set it to 1 and then return the same pointer.
- _ = try self.wip.store(.normal, non_null_bit, operand, .default);
+ _ = try self.wip.store(access_kind, non_null_bit, operand, .default);
return operand;
}
if (optional_ty.optionalReprIsPayload(zcu)) {
@@ -7677,8 +7750,11 @@ pub const FuncGen = struct {
// First set the non-null bit.
const optional_llvm_ty = try o.lowerType(optional_ty);
const non_null_ptr = try self.wip.gepStruct(optional_llvm_ty, operand, 1, "");
+
+ self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu));
+
// TODO set alignment on this store
- _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default);
+ _ = try self.wip.store(access_kind, non_null_bit, non_null_ptr, .default);
// Then return the payload pointer (only if it's used).
if (self.liveness.isUnused(inst)) return .none;
@@ -7764,18 +7840,26 @@ pub const FuncGen = struct {
}
}
+ const access_kind: Builder.MemoryAccessKind =
+ if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
const payload_ty = err_union_ty.errorUnionPayload(zcu);
if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
if (!operand_is_ptr) return operand;
- return self.wip.load(.normal, error_type, operand, .default, "");
+
+ self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
+
+ return self.wip.load(access_kind, error_type, operand, .default, "");
}
const offset = try errUnionErrorOffset(payload_ty, pt);
if (operand_is_ptr or isByRef(err_union_ty, zcu)) {
+ if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
+
const err_union_llvm_ty = try o.lowerType(err_union_ty);
const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, "");
- return self.wip.load(.normal, error_type, err_field_ptr, .default, "");
+ return self.wip.load(access_kind, error_type, err_field_ptr, .default, "");
}
return self.wip.extractValue(operand, &.{offset}, "");
@@ -7787,22 +7871,31 @@ pub const FuncGen = struct {
const zcu = pt.zcu;
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const operand = try self.resolveInst(ty_op.operand);
- const err_union_ty = self.typeOf(ty_op.operand).childType(zcu);
+ const err_union_ptr_ty = self.typeOf(ty_op.operand);
+ const err_union_ty = err_union_ptr_ty.childType(zcu);
const payload_ty = err_union_ty.errorUnionPayload(zcu);
const non_error_val = try o.builder.intValue(try o.errorIntType(), 0);
+
+ const access_kind: Builder.MemoryAccessKind =
+ if (err_union_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
- _ = try self.wip.store(.normal, non_error_val, operand, .default);
+ self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu));
+
+ _ = try self.wip.store(access_kind, non_error_val, operand, .default);
return operand;
}
const err_union_llvm_ty = try o.lowerType(err_union_ty);
{
+ self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu));
+
const err_int_ty = try pt.errorIntType();
const error_alignment = err_int_ty.abiAlignment(zcu).toLlvm();
const error_offset = try errUnionErrorOffset(payload_ty, pt);
// First set the non-error value.
const non_null_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, error_offset, "");
- _ = try self.wip.store(.normal, non_error_val, non_null_ptr, error_alignment);
+ _ = try self.wip.store(access_kind, non_error_val, non_null_ptr, error_alignment);
}
// Then return the payload pointer (only if it is used).
if (self.liveness.isUnused(inst)) return .none;
@@ -8017,6 +8110,10 @@ pub const FuncGen = struct {
const index = try self.resolveInst(extra.lhs);
const operand = try self.resolveInst(extra.rhs);
+ self.maybeMarkAllowZeroAccess(vector_ptr_ty.ptrInfo(zcu));
+
+ // TODO: Emitting a load here is a violation of volatile semantics. Not fixable in general.
+ // https://github.com/ziglang/zig/issues/18652#issuecomment-2452844908
const access_kind: Builder.MemoryAccessKind =
if (vector_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
const elem_llvm_ty = try o.lowerType(vector_ptr_ty.childType(zcu));
@@ -9482,6 +9579,8 @@ pub const FuncGen = struct {
return .none;
}
+ self.maybeMarkAllowZeroAccess(ptr_info);
+
const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(zcu));
_ = try self.wip.callMemSet(
dest_ptr,
@@ -9497,6 +9596,8 @@ pub const FuncGen = struct {
return .none;
}
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
const src_operand = try self.resolveInst(bin_op.rhs);
try self.store(dest_ptr, ptr_ty, src_operand, .none);
return .none;
@@ -9539,6 +9640,9 @@ pub const FuncGen = struct {
if (!canElideLoad(fg, body_tail)) break :elide;
return ptr;
}
+
+ fg.maybeMarkAllowZeroAccess(ptr_info);
+
return fg.load(ptr, ptr_ty);
}
@@ -9598,6 +9702,8 @@ pub const FuncGen = struct {
new_value = try self.wip.conv(signedness, new_value, llvm_abi_ty, "");
}
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
const result = try self.wip.cmpxchg(
kind,
if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal,
@@ -9649,6 +9755,8 @@ pub const FuncGen = struct {
if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
if (llvm_abi_ty != .none) {
// operand needs widening and truncating or bitcasting.
return self.wip.cast(if (is_float) .bitcast else .trunc, try self.wip.atomicrmw(
@@ -9712,6 +9820,8 @@ pub const FuncGen = struct {
if (info.flags.is_volatile) .@"volatile" else .normal;
const elem_llvm_ty = try o.lowerType(elem_ty);
+ self.maybeMarkAllowZeroAccess(info);
+
if (llvm_abi_ty != .none) {
// operand needs widening and truncating
const loaded = try self.wip.loadAtomic(
@@ -9761,6 +9871,9 @@ pub const FuncGen = struct {
"",
);
}
+
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
try self.store(ptr, ptr_ty, element, ordering);
return .none;
}
@@ -9778,6 +9891,8 @@ pub const FuncGen = struct {
const access_kind: Builder.MemoryAccessKind =
if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+ self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
+
if (try self.air.value(bin_op.rhs, pt)) |elem_val| {
if (elem_val.isUndefDeep(zcu)) {
// Even if safety is disabled, we still emit a memset to undefined since it conveys
@@ -9916,6 +10031,9 @@ pub const FuncGen = struct {
const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or
dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+ self.maybeMarkAllowZeroAccess(dest_ptr_ty.ptrInfo(zcu));
+ self.maybeMarkAllowZeroAccess(src_ptr_ty.ptrInfo(zcu));
+
_ = try self.wip.callMemCpy(
dest_ptr,
dest_ptr_ty.ptrAlignment(zcu).toLlvm(),
@@ -9959,20 +10077,27 @@ pub const FuncGen = struct {
const pt = o.pt;
const zcu = pt.zcu;
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
- const un_ty = self.typeOf(bin_op.lhs).childType(zcu);
+ const un_ptr_ty = self.typeOf(bin_op.lhs);
+ const un_ty = un_ptr_ty.childType(zcu);
const layout = un_ty.unionGetLayout(zcu);
if (layout.tag_size == 0) return .none;
+
+ const access_kind: Builder.MemoryAccessKind =
+ if (un_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
+
+ self.maybeMarkAllowZeroAccess(un_ptr_ty.ptrInfo(zcu));
+
const union_ptr = try self.resolveInst(bin_op.lhs);
const new_tag = try self.resolveInst(bin_op.rhs);
if (layout.payload_size == 0) {
// TODO alignment on this store
- _ = try self.wip.store(.normal, new_tag, union_ptr, .default);
+ _ = try self.wip.store(access_kind, new_tag, union_ptr, .default);
return .none;
}
const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));
const tag_field_ptr = try self.wip.gepStruct(try o.lowerType(un_ty), union_ptr, tag_index, "");
// TODO alignment on this store
- _ = try self.wip.store(.normal, new_tag, tag_field_ptr, .default);
+ _ = try self.wip.store(access_kind, new_tag, tag_field_ptr, .default);
return .none;
}
@@ -10869,12 +10994,13 @@ pub const FuncGen = struct {
opt_llvm_ty: Builder.Type,
opt_handle: Builder.Value,
is_by_ref: bool,
+ access_kind: Builder.MemoryAccessKind,
) Allocator.Error!Builder.Value {
const o = self.ng.object;
const field = b: {
if (is_by_ref) {
const field_ptr = try self.wip.gepStruct(opt_llvm_ty, opt_handle, 1, "");
- break :b try self.wip.load(.normal, .i8, field_ptr, .default, "");
+ break :b try self.wip.load(access_kind, .i8, field_ptr, .default, "");
}
break :b try self.wip.extractValue(opt_handle, &.{1}, "");
};
@@ -11183,7 +11309,7 @@ pub const FuncGen = struct {
const vec_elem_ty = try o.lowerType(elem_ty);
const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty);
- const loaded_vector = try self.wip.load(access_kind, vec_ty, ptr, ptr_alignment, "");
+ const loaded_vector = try self.wip.load(.normal, vec_ty, ptr, ptr_alignment, "");
const modified_vector = try self.wip.insertElement(loaded_vector, elem, index_u32, "");
@@ -11196,7 +11322,7 @@ pub const FuncGen = struct {
const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8));
assert(ordering == .none);
const containing_int =
- try self.wip.load(access_kind, containing_int_ty, ptr, ptr_alignment, "");
+ try self.wip.load(.normal, containing_int_ty, ptr, ptr_alignment, "");
const elem_bits = ptr_ty.childType(zcu).bitSize(zcu);
const shift_amt = try o.builder.intConst(containing_int_ty, info.packed_offset.bit_offset);
// Convert to equally-sized integer type in order to perform the bit