mirror of
https://github.com/ziglang/zig.git
synced 2024-11-21 19:42:56 +00:00
Merge 8fd99f8b73
into f845fa04a0
This commit is contained in:
commit
782e9a625d
22
src/Air.zig
22
src/Air.zig
@ -1610,6 +1610,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
|
||||
const data = air.instructions.items(.data)[@intFromEnum(inst)];
|
||||
return switch (air.instructions.items(.tag)[@intFromEnum(inst)]) {
|
||||
.arg,
|
||||
.assembly,
|
||||
.block,
|
||||
.loop,
|
||||
.repeat,
|
||||
@ -1750,12 +1751,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
|
||||
.cmp_vector_optimized,
|
||||
.is_null,
|
||||
.is_non_null,
|
||||
.is_null_ptr,
|
||||
.is_non_null_ptr,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.bool_and,
|
||||
.bool_or,
|
||||
.int_from_ptr,
|
||||
@ -1770,7 +1767,6 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
|
||||
.unwrap_errunion_payload,
|
||||
.unwrap_errunion_err,
|
||||
.unwrap_errunion_payload_ptr,
|
||||
.unwrap_errunion_err_ptr,
|
||||
.wrap_errunion_payload,
|
||||
.wrap_errunion_err,
|
||||
.struct_field_ptr,
|
||||
@ -1815,17 +1811,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
|
||||
.work_group_id,
|
||||
=> false,
|
||||
|
||||
.assembly => {
|
||||
const extra = air.extraData(Air.Asm, data.ty_pl.payload);
|
||||
const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
|
||||
return is_volatile or if (extra.data.outputs_len == 1)
|
||||
@as(Air.Inst.Ref, @enumFromInt(air.extra[extra.end])) != .none
|
||||
else
|
||||
extra.data.outputs_len > 1;
|
||||
},
|
||||
.load => air.typeOf(data.ty_op.operand, ip).isVolatilePtrIp(ip),
|
||||
.is_non_null_ptr, .is_null_ptr, .is_non_err_ptr, .is_err_ptr => air.typeOf(data.un_op, ip).isVolatilePtrIp(ip),
|
||||
.load, .unwrap_errunion_err_ptr => air.typeOf(data.ty_op.operand, ip).isVolatilePtrIp(ip),
|
||||
.slice_elem_val, .ptr_elem_val => air.typeOf(data.bin_op.lhs, ip).isVolatilePtrIp(ip),
|
||||
.atomic_load => air.typeOf(data.atomic_load.ptr, ip).isVolatilePtrIp(ip),
|
||||
.atomic_load => switch (data.atomic_load.order) {
|
||||
.unordered, .monotonic => air.typeOf(data.atomic_load.ptr, ip).isVolatilePtrIp(ip),
|
||||
else => true, // Stronger memory orderings have inter-thread side effects.
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1503,7 +1503,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
|
||||
|
||||
.mul,
|
||||
.mul_wrap,
|
||||
.div_trunc,
|
||||
.div_trunc,
|
||||
.div_exact,
|
||||
.rem,
|
||||
|
||||
@ -1521,13 +1521,13 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
|
||||
.max,
|
||||
=> try func.airBinOp(inst, tag),
|
||||
|
||||
|
||||
|
||||
.ptr_add,
|
||||
.ptr_sub => try func.airPtrArithmetic(inst, tag),
|
||||
|
||||
.mod,
|
||||
.div_float,
|
||||
.div_floor,
|
||||
.div_float,
|
||||
.div_floor,
|
||||
=> return func.fail("TODO: {s}", .{@tagName(tag)}),
|
||||
|
||||
.sqrt,
|
||||
@ -1681,7 +1681,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
|
||||
.ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst),
|
||||
|
||||
.array_elem_val => try func.airArrayElemVal(inst),
|
||||
|
||||
|
||||
.slice_elem_val => try func.airSliceElemVal(inst),
|
||||
.slice_elem_ptr => try func.airSliceElemPtr(inst),
|
||||
|
||||
@ -1811,8 +1811,15 @@ fn finishAirBookkeeping(func: *Func) void {
|
||||
fn finishAirResult(func: *Func, inst: Air.Inst.Index, result: MCValue) void {
|
||||
if (func.liveness.isUnused(inst)) switch (result) {
|
||||
.none, .dead, .unreach => {},
|
||||
else => unreachable, // Why didn't the result die?
|
||||
// Why didn't the result die?
|
||||
.register => |r| if (r != .zero) unreachable,
|
||||
else => unreachable,
|
||||
} else {
|
||||
switch (result) {
|
||||
.register => |r| if (r == .zero) unreachable, // Why did we discard a used result?
|
||||
else => {},
|
||||
}
|
||||
|
||||
tracking_log.debug("%{d} => {} (birth)", .{ inst, result });
|
||||
func.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(result));
|
||||
// In some cases, an operand may be reused as the result.
|
||||
@ -7785,48 +7792,56 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
|
||||
const pt = func.pt;
|
||||
const zcu = pt.zcu;
|
||||
const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
|
||||
const order: std.builtin.AtomicOrder = atomic_load.order;
|
||||
const result: MCValue = result: {
|
||||
const order: std.builtin.AtomicOrder = atomic_load.order;
|
||||
|
||||
const ptr_ty = func.typeOf(atomic_load.ptr);
|
||||
const elem_ty = ptr_ty.childType(zcu);
|
||||
const ptr_mcv = try func.resolveInst(atomic_load.ptr);
|
||||
const ptr_ty = func.typeOf(atomic_load.ptr);
|
||||
const elem_ty = ptr_ty.childType(zcu);
|
||||
const ptr_mcv = try func.resolveInst(atomic_load.ptr);
|
||||
|
||||
const bit_size = elem_ty.bitSize(zcu);
|
||||
if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{});
|
||||
const bit_size = elem_ty.bitSize(zcu);
|
||||
if (bit_size > 64) return func.fail("TODO: airAtomicLoad > 64 bits", .{});
|
||||
|
||||
const result_mcv = try func.allocRegOrMem(elem_ty, inst, true);
|
||||
assert(result_mcv == .register); // should be less than 8 bytes
|
||||
const unused = func.liveness.isUnused(inst);
|
||||
|
||||
if (order == .seq_cst) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .fence,
|
||||
.data = .{ .fence = .{
|
||||
.pred = .rw,
|
||||
.succ = .rw,
|
||||
} },
|
||||
});
|
||||
}
|
||||
const result_mcv: MCValue = if (func.liveness.isUnused(inst))
|
||||
.{ .register = .zero }
|
||||
else
|
||||
try func.allocRegOrMem(elem_ty, inst, true);
|
||||
assert(result_mcv == .register); // should be less than 8 bytes
|
||||
|
||||
try func.load(result_mcv, ptr_mcv, ptr_ty);
|
||||
|
||||
switch (order) {
|
||||
// Don't guarnetee other memory operations to be ordered after the load.
|
||||
.unordered => {},
|
||||
.monotonic => {},
|
||||
// Make sure all previous reads happen before any reading or writing accurs.
|
||||
.seq_cst, .acquire => {
|
||||
if (order == .seq_cst) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .fence,
|
||||
.data = .{ .fence = .{
|
||||
.pred = .r,
|
||||
.pred = .rw,
|
||||
.succ = .rw,
|
||||
} },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
return func.finishAir(inst, result_mcv, .{ atomic_load.ptr, .none, .none });
|
||||
try func.load(result_mcv, ptr_mcv, ptr_ty);
|
||||
|
||||
switch (order) {
|
||||
// Don't guarantee other memory operations to be ordered after the load.
|
||||
.unordered, .monotonic => {},
|
||||
// Make sure all previous reads happen before any reading or writing occurs.
|
||||
.acquire, .seq_cst => {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .fence,
|
||||
.data = .{ .fence = .{
|
||||
.pred = .r,
|
||||
.succ = .rw,
|
||||
} },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
break :result if (unused) .unreach else result_mcv;
|
||||
};
|
||||
|
||||
return func.finishAir(inst, result, .{ atomic_load.ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
|
||||
@ -7856,6 +7871,17 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
|
||||
}
|
||||
|
||||
try func.store(ptr_mcv, val_mcv, ptr_ty);
|
||||
|
||||
if (order == .seq_cst) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .fence,
|
||||
.data = .{ .fence = .{
|
||||
.pred = .rw,
|
||||
.succ = .rw,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
|
@ -16596,23 +16596,29 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
|
||||
|
||||
fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const atomic_load = self.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
|
||||
const result: MCValue = result: {
|
||||
const ptr_ty = self.typeOf(atomic_load.ptr);
|
||||
const ptr_mcv = try self.resolveInst(atomic_load.ptr);
|
||||
const ptr_lock = switch (ptr_mcv) {
|
||||
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
|
||||
else => null,
|
||||
};
|
||||
defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
const ptr_ty = self.typeOf(atomic_load.ptr);
|
||||
const ptr_mcv = try self.resolveInst(atomic_load.ptr);
|
||||
const ptr_lock = switch (ptr_mcv) {
|
||||
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
|
||||
else => null,
|
||||
const unused = self.liveness.isUnused(inst);
|
||||
|
||||
const dst_mcv: MCValue = if (unused)
|
||||
.{ .register = try self.register_manager.allocReg(null, self.regClassForType(ptr_ty.childType(self.pt.zcu))) }
|
||||
else if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
|
||||
ptr_mcv
|
||||
else
|
||||
try self.allocRegOrMem(inst, true);
|
||||
|
||||
try self.load(dst_mcv, ptr_ty, ptr_mcv);
|
||||
|
||||
break :result if (unused) .unreach else dst_mcv;
|
||||
};
|
||||
defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
const dst_mcv =
|
||||
if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
|
||||
ptr_mcv
|
||||
else
|
||||
try self.allocRegOrMem(inst, true);
|
||||
|
||||
try self.load(dst_mcv, ptr_ty, ptr_mcv);
|
||||
return self.finishAir(inst, dst_mcv, .{ atomic_load.ptr, .none, .none });
|
||||
return self.finishAir(inst, result, .{ atomic_load.ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
|
||||
|
@ -1696,7 +1696,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) {
|
||||
@ -1774,8 +1777,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);
|
||||
|
||||
@ -1866,6 +1867,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);
|
||||
@ -4723,7 +4735,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) {
|
||||
@ -5039,6 +5054,15 @@ pub const FuncGen = struct {
|
||||
|
||||
sync_scope: Builder.SyncScope,
|
||||
|
||||
/// 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),
|
||||
@ -5775,7 +5799,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) {
|
||||
@ -5919,7 +5946,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,
|
||||
);
|
||||
const owner_mod = self.ng.ownerModule();
|
||||
if (owner_mod.valgrind) {
|
||||
@ -6152,8 +6179,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, "");
|
||||
@ -6604,6 +6631,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);
|
||||
}
|
||||
|
||||
@ -6627,10 +6657,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;
|
||||
}
|
||||
@ -6640,7 +6673,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,
|
||||
@ -7149,10 +7182,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);
|
||||
}
|
||||
|
||||
@ -7222,10 +7259,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);
|
||||
}
|
||||
|
||||
@ -7627,6 +7669,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.*) {
|
||||
@ -7733,10 +7777,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;
|
||||
@ -7759,7 +7804,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;
|
||||
}
|
||||
@ -7918,9 +7969,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;
|
||||
}
|
||||
@ -7945,9 +8000,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)) {
|
||||
@ -7965,14 +8026,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(
|
||||
@ -7992,6 +8053,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
|
||||
@ -8001,9 +8065,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, "");
|
||||
@ -8015,7 +8081,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, "");
|
||||
}
|
||||
@ -8048,12 +8114,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)) {
|
||||
@ -8065,8 +8138,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;
|
||||
@ -8152,18 +8228,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}, "");
|
||||
@ -8175,22 +8259,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;
|
||||
@ -8405,6 +8498,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));
|
||||
@ -9757,6 +9854,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,
|
||||
@ -9771,6 +9870,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;
|
||||
@ -9813,6 +9914,9 @@ pub const FuncGen = struct {
|
||||
if (!canElideLoad(fg, body_tail)) break :elide;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
fg.maybeMarkAllowZeroAccess(ptr_info);
|
||||
|
||||
return fg.load(ptr, ptr_ty);
|
||||
}
|
||||
|
||||
@ -9872,6 +9976,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,
|
||||
@ -9923,6 +10029,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(
|
||||
@ -9986,6 +10094,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(
|
||||
@ -10035,6 +10145,9 @@ pub const FuncGen = struct {
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
|
||||
|
||||
try self.store(ptr, ptr_ty, element, ordering);
|
||||
return .none;
|
||||
}
|
||||
@ -10052,6 +10165,8 @@ pub const FuncGen = struct {
|
||||
const access_kind: Builder.MemoryAccessKind =
|
||||
if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
|
||||
|
||||
self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
|
||||
|
||||
// Any WebAssembly runtime will trap when the destination pointer is out-of-bounds, regardless
|
||||
// of the length. This means we need to emit a check where we skip the memset when the length
|
||||
// is 0 as we allow for undefined pointers in 0-sized slices.
|
||||
@ -10208,6 +10323,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));
|
||||
|
||||
// When bulk-memory is enabled, this will be lowered to WebAssembly's memory.copy instruction.
|
||||
// This instruction will trap on an invalid address, regardless of the length.
|
||||
// For this reason we must add a check for 0-sized slices as its pointer field can be undefined.
|
||||
@ -10252,20 +10370,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;
|
||||
}
|
||||
|
||||
@ -11162,12 +11287,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}, "");
|
||||
};
|
||||
@ -11475,7 +11601,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, "");
|
||||
|
||||
@ -11488,7 +11614,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
|
||||
|
Loading…
Reference in New Issue
Block a user