Skip to content

Commit 5b606d4

Browse files
authored
Merge pull request #21882 from alexrp/compiler-fixes
compiler: Fix some real and theoretical miscompilations with `allowzero` and `volatile`
2 parents a365971 + 9d8adb3 commit 5b606d4

File tree

12 files changed

+512
-88
lines changed

12 files changed

+512
-88
lines changed

build.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ pub fn build(b: *std.Build) !void {
550550
.skip_non_native = skip_non_native,
551551
.skip_libc = skip_libc,
552552
})) |test_debugger_step| test_step.dependOn(test_debugger_step);
553+
if (tests.addLlvmIrTests(b, .{
554+
.enable_llvm = enable_llvm,
555+
.test_filters = test_filters,
556+
.test_target_filters = test_target_filters,
557+
})) |test_llvm_ir_step| test_step.dependOn(test_llvm_ir_step);
553558

554559
try addWasiUpdateStep(b, version);
555560

lib/std/Build/Module.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ omit_frame_pointer: ?bool,
3333
error_tracing: ?bool,
3434
link_libc: ?bool,
3535
link_libcpp: ?bool,
36+
no_builtin: ?bool,
3637

3738
/// Symbols to be exported when compiling to WebAssembly.
3839
export_symbol_names: []const []const u8 = &.{},
@@ -268,6 +269,7 @@ pub const CreateOptions = struct {
268269
/// more difficult to obtain stack traces. Has target-dependent effects.
269270
omit_frame_pointer: ?bool = null,
270271
error_tracing: ?bool = null,
272+
no_builtin: ?bool = null,
271273
};
272274

273275
pub const Import = struct {
@@ -314,6 +316,7 @@ pub fn init(
314316
.omit_frame_pointer = options.omit_frame_pointer,
315317
.error_tracing = options.error_tracing,
316318
.export_symbol_names = &.{},
319+
.no_builtin = options.no_builtin,
317320
};
318321

319322
m.import_table.ensureUnusedCapacity(allocator, options.imports.len) catch @panic("OOM");
@@ -564,6 +567,7 @@ pub fn appendZigProcessFlags(
564567
try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
565568
try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
566569
try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
570+
try addFlag(zig_args, m.no_builtin, "-fno-builtin", "-fbuiltin");
567571

568572
if (m.sanitize_c) |sc| switch (sc) {
569573
.off => try zig_args.append("-fno-sanitize-c"),

lib/std/Build/Step/Compile.zig

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,6 @@ is_linking_libc: bool = false,
229229
/// Computed during make().
230230
is_linking_libcpp: bool = false,
231231

232-
no_builtin: bool = false,
233-
234232
/// Populated during the make phase when there is a long-lived compiler process.
235233
/// Managed by the build runner, not user build script.
236234
zig_process: ?*Step.ZigProcess,
@@ -1646,10 +1644,6 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
16461644
}
16471645
}
16481646

1649-
if (compile.no_builtin) {
1650-
try zig_args.append("-fno-builtin");
1651-
}
1652-
16531647
if (b.sysroot) |sysroot| {
16541648
try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
16551649
}

src/Air.zig

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
16731673
const data = air.instructions.items(.data)[@intFromEnum(inst)];
16741674
return switch (air.instructions.items(.tag)[@intFromEnum(inst)]) {
16751675
.arg,
1676+
.assembly,
16761677
.block,
16771678
.loop,
16781679
.repeat,
@@ -1816,12 +1817,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
18161817
.cmp_vector_optimized,
18171818
.is_null,
18181819
.is_non_null,
1819-
.is_null_ptr,
1820-
.is_non_null_ptr,
18211820
.is_err,
18221821
.is_non_err,
1823-
.is_err_ptr,
1824-
.is_non_err_ptr,
18251822
.bool_and,
18261823
.bool_or,
18271824
.fptrunc,
@@ -1834,7 +1831,6 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
18341831
.unwrap_errunion_payload,
18351832
.unwrap_errunion_err,
18361833
.unwrap_errunion_payload_ptr,
1837-
.unwrap_errunion_err_ptr,
18381834
.wrap_errunion_payload,
18391835
.wrap_errunion_err,
18401836
.struct_field_ptr,
@@ -1879,17 +1875,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
18791875
.work_group_id,
18801876
=> false,
18811877

1882-
.assembly => {
1883-
const extra = air.extraData(Air.Asm, data.ty_pl.payload);
1884-
const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
1885-
return is_volatile or if (extra.data.outputs_len == 1)
1886-
@as(Air.Inst.Ref, @enumFromInt(air.extra[extra.end])) != .none
1887-
else
1888-
extra.data.outputs_len > 1;
1889-
},
1890-
.load => air.typeOf(data.ty_op.operand, ip).isVolatilePtrIp(ip),
1878+
.is_non_null_ptr, .is_null_ptr, .is_non_err_ptr, .is_err_ptr => air.typeOf(data.un_op, ip).isVolatilePtrIp(ip),
1879+
.load, .unwrap_errunion_err_ptr => air.typeOf(data.ty_op.operand, ip).isVolatilePtrIp(ip),
18911880
.slice_elem_val, .ptr_elem_val => air.typeOf(data.bin_op.lhs, ip).isVolatilePtrIp(ip),
1892-
.atomic_load => air.typeOf(data.atomic_load.ptr, ip).isVolatilePtrIp(ip),
1881+
.atomic_load => switch (data.atomic_load.order) {
1882+
.unordered, .monotonic => air.typeOf(data.atomic_load.ptr, ip).isVolatilePtrIp(ip),
1883+
else => true, // Stronger memory orderings have inter-thread side effects.
1884+
},
18931885
};
18941886
}
18951887

src/arch/riscv64/CodeGen.zig

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
14601460

14611461
.mul,
14621462
.mul_wrap,
1463-
.div_trunc,
1463+
.div_trunc,
14641464
.div_exact,
14651465
.rem,
14661466

@@ -1478,13 +1478,13 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
14781478
.max,
14791479
=> try func.airBinOp(inst, tag),
14801480

1481-
1481+
14821482
.ptr_add,
14831483
.ptr_sub => try func.airPtrArithmetic(inst, tag),
14841484

14851485
.mod,
1486-
.div_float,
1487-
.div_floor,
1486+
.div_float,
1487+
.div_floor,
14881488
=> return func.fail("TODO: {s}", .{@tagName(tag)}),
14891489

14901490
.sqrt,
@@ -1639,7 +1639,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
16391639
.ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst),
16401640

16411641
.array_elem_val => try func.airArrayElemVal(inst),
1642-
1642+
16431643
.slice_elem_val => try func.airSliceElemVal(inst),
16441644
.slice_elem_ptr => try func.airSliceElemPtr(inst),
16451645

@@ -1769,8 +1769,15 @@ fn finishAirBookkeeping(func: *Func) void {
17691769
fn finishAirResult(func: *Func, inst: Air.Inst.Index, result: MCValue) void {
17701770
if (func.liveness.isUnused(inst)) switch (result) {
17711771
.none, .dead, .unreach => {},
1772-
else => unreachable, // Why didn't the result die?
1772+
// Why didn't the result die?
1773+
.register => |r| if (r != .zero) unreachable,
1774+
else => unreachable,
17731775
} else {
1776+
switch (result) {
1777+
.register => |r| if (r == .zero) unreachable, // Why did we discard a used result?
1778+
else => {},
1779+
}
1780+
17741781
tracking_log.debug("%{d} => {} (birth)", .{ inst, result });
17751782
func.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(result));
17761783
// In some cases, an operand may be reused as the result.
@@ -7729,9 +7736,12 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
77297736
const ptr_mcv = try func.resolveInst(atomic_load.ptr);
77307737

77317738
const bit_size = elem_ty.bitSize(zcu);
7732-
if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{});
7739+
if (bit_size > 64) return func.fail("TODO: airAtomicLoad > 64 bits", .{});
77337740

7734-
const result_mcv = try func.allocRegOrMem(elem_ty, inst, true);
7741+
const result_mcv: MCValue = if (func.liveness.isUnused(inst))
7742+
.{ .register = .zero }
7743+
else
7744+
try func.allocRegOrMem(elem_ty, inst, true);
77357745
assert(result_mcv == .register); // should be less than 8 bytes
77367746

77377747
if (order == .seq_cst) {
@@ -7747,11 +7757,10 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
77477757
try func.load(result_mcv, ptr_mcv, ptr_ty);
77487758

77497759
switch (order) {
7750-
// Don't guarnetee other memory operations to be ordered after the load.
7751-
.unordered => {},
7752-
.monotonic => {},
7753-
// Make sure all previous reads happen before any reading or writing accurs.
7754-
.seq_cst, .acquire => {
7760+
// Don't guarantee other memory operations to be ordered after the load.
7761+
.unordered, .monotonic => {},
7762+
// Make sure all previous reads happen before any reading or writing occurs.
7763+
.acquire, .seq_cst => {
77557764
_ = try func.addInst(.{
77567765
.tag = .fence,
77577766
.data = .{ .fence = .{
@@ -7793,6 +7802,17 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
77937802
}
77947803

77957804
try func.store(ptr_mcv, val_mcv, ptr_ty);
7805+
7806+
if (order == .seq_cst) {
7807+
_ = try func.addInst(.{
7808+
.tag = .fence,
7809+
.data = .{ .fence = .{
7810+
.pred = .rw,
7811+
.succ = .rw,
7812+
} },
7813+
});
7814+
}
7815+
77967816
return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
77977817
}
77987818

src/arch/x86_64/CodeGen.zig

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106219,23 +106219,29 @@ fn airAtomicRmw(self: *CodeGen, inst: Air.Inst.Index) !void {
106219106219

106220106220
fn airAtomicLoad(self: *CodeGen, inst: Air.Inst.Index) !void {
106221106221
const atomic_load = self.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
106222+
const result: MCValue = result: {
106223+
const ptr_ty = self.typeOf(atomic_load.ptr);
106224+
const ptr_mcv = try self.resolveInst(atomic_load.ptr);
106225+
const ptr_lock = switch (ptr_mcv) {
106226+
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
106227+
else => null,
106228+
};
106229+
defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
106222106230

106223-
const ptr_ty = self.typeOf(atomic_load.ptr);
106224-
const ptr_mcv = try self.resolveInst(atomic_load.ptr);
106225-
const ptr_lock = switch (ptr_mcv) {
106226-
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
106227-
else => null,
106228-
};
106229-
defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
106231+
const unused = self.liveness.isUnused(inst);
106230106232

106231-
const dst_mcv =
106232-
if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
106233+
const dst_mcv: MCValue = if (unused)
106234+
.{ .register = try self.register_manager.allocReg(null, self.regSetForType(ptr_ty.childType(self.pt.zcu))) }
106235+
else if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
106233106236
ptr_mcv
106234106237
else
106235106238
try self.allocRegOrMem(inst, true);
106236106239

106237-
try self.load(dst_mcv, ptr_ty, ptr_mcv);
106238-
return self.finishAir(inst, dst_mcv, .{ atomic_load.ptr, .none, .none });
106240+
try self.load(dst_mcv, ptr_ty, ptr_mcv);
106241+
106242+
break :result if (unused) .unreach else dst_mcv;
106243+
};
106244+
return self.finishAir(inst, result, .{ atomic_load.ptr, .none, .none });
106239106245
}
106240106246

106241106247
fn airAtomicStore(self: *CodeGen, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {

0 commit comments

Comments
 (0)