authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-15 12:37:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-15 12:37:32-07:00
log19691c0b174f283ffe5b6c3fe8533ef458736064
tree802390ce8ad5d29cc72450262efd4b13140f5f20
parente5fd45003e56f152364a4bdc609fda07a6b524fd

stage2: implement `@fence`


13 files changed, 82 insertions(+), 23 deletions(-)

src/Air.zig+6
...@@ -127,6 +127,10 @@ pub const Inst = struct {...@@ -127,6 +127,10 @@ pub const Inst = struct {
127 /// Lowers to a hardware trap instruction, or the next best thing.127 /// Lowers to a hardware trap instruction, or the next best thing.
128 /// Result type is always void.128 /// Result type is always void.
129 breakpoint,129 breakpoint,
130 /// Lowers to a memory fence instruction.
131 /// Result type is always void.
132 /// Uses the `fence` field.
133 fence,
130 /// Function call.134 /// Function call.
131 /// Result type is the return type of the function being called.135 /// Result type is the return type of the function being called.
132 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.136 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
...@@ -380,6 +384,7 @@ pub const Inst = struct {...@@ -380,6 +384,7 @@ pub const Inst = struct {
380 line: u32,384 line: u32,
381 column: u32,385 column: u32,
382 },386 },
387 fence: std.builtin.AtomicOrder,
383388
384 // Make sure we don't accidentally add a field to make this union389 // Make sure we don't accidentally add a field to make this union
385 // bigger than expected. Note that in Debug builds, Zig is allowed390 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -566,6 +571,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -566,6 +571,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
566 .breakpoint,571 .breakpoint,
567 .dbg_stmt,572 .dbg_stmt,
568 .store,573 .store,
574 .fence,
569 => return Type.initTag(.void),575 => return Type.initTag(.void),
570576
571 .ptrtoint,577 .ptrtoint,
src/AstGen.zig+5-1
...@@ -7116,9 +7116,13 @@ fn builtinCall(...@@ -7116,9 +7116,13 @@ fn builtinCall(
7116 });7116 });
7117 return rvalue(gz, rl, result, node);7117 return rvalue(gz, rl, result, node);
7118 },7118 },
7119 .fence => {
7120 const order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[0]);
7121 const result = try gz.addUnNode(.fence, order, node);
7122 return rvalue(gz, rl, result, node);
7123 },
71197124
7120 .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),7125 .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),
7121 .fence => return simpleNoOpVoid(gz, rl, node, .fence),
71227126
7123 .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),7127 .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),
7124 .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),7128 .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),
src/Liveness.zig+1
...@@ -264,6 +264,7 @@ fn analyzeInst(...@@ -264,6 +264,7 @@ fn analyzeInst(
264 .breakpoint,264 .breakpoint,
265 .dbg_stmt,265 .dbg_stmt,
266 .unreach,266 .unreach,
267 .fence,
267 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),268 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
268269
269 .not,270 .not,
src/Sema.zig+16-13
...@@ -377,7 +377,9 @@ pub fn analyzeBody(...@@ -377,7 +377,9 @@ pub fn analyzeBody(
377 // We also know that they cannot be referenced later, so we avoid377 // We also know that they cannot be referenced later, so we avoid
378 // putting them into the map.378 // putting them into the map.
379 .breakpoint => {379 .breakpoint => {
380 try sema.zirBreakpoint(block, inst);380 if (!block.is_comptime) {
381 _ = try block.addNoOp(.breakpoint);
382 }
381 i += 1;383 i += 1;
382 continue;384 continue;
383 },385 },
...@@ -2308,20 +2310,21 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C...@@ -2308,20 +2310,21 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C
2308 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);2310 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
2309}2311}
23102312
2311fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2313fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2312 const tracy = trace(@src());2314 if (block.is_comptime) return;
2313 defer tracy.end();
23142315
2315 const src_node = sema.code.instructions.items(.data)[inst].node;2316 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2316 const src: LazySrcLoc = .{ .node_offset = src_node };2317 const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2317 try sema.requireRuntimeBlock(block, src);2318 const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand);
2318 _ = try block.addNoOp(.breakpoint);
2319}
23202319
2321fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2320 if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) {
2322 const src_node = sema.code.instructions.items(.data)[inst].node;2321 return sema.mod.fail(&block.base, order_src, "atomic ordering must be Acquire or stricter", .{});
2323 const src: LazySrcLoc = .{ .node_offset = src_node };2322 }
2324 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});2323
2324 _ = try block.addInst(.{
2325 .tag = .fence,
2326 .data = .{ .fence = order },
2327 });
2325}2328}
23262329
2327fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {2330fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
src/Zir.zig+3-3
...@@ -731,7 +731,7 @@ pub const Inst = struct {...@@ -731,7 +731,7 @@ pub const Inst = struct {
731 size_of,731 size_of,
732 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.732 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
733 bit_size_of,733 bit_size_of,
734 /// Implements the `@fence` builtin. Uses `node`.734 /// Implements the `@fence` builtin. Uses `un_node`.
735 fence,735 fence,
736736
737 /// Implement builtin `@ptrToInt`. Uses `un_node`.737 /// Implement builtin `@ptrToInt`. Uses `un_node`.
...@@ -1416,7 +1416,7 @@ pub const Inst = struct {...@@ -1416,7 +1416,7 @@ pub const Inst = struct {
1416 .type_info = .un_node,1416 .type_info = .un_node,
1417 .size_of = .un_node,1417 .size_of = .un_node,
1418 .bit_size_of = .un_node,1418 .bit_size_of = .un_node,
1419 .fence = .node,1419 .fence = .un_node,
14201420
1421 .ptr_to_int = .un_node,1421 .ptr_to_int = .un_node,
1422 .error_to_int = .un_node,1422 .error_to_int = .un_node,
...@@ -3016,6 +3016,7 @@ const Writer = struct {...@@ -3016,6 +3016,7 @@ const Writer = struct {
3016 .@"resume",3016 .@"resume",
3017 .@"await",3017 .@"await",
3018 .await_nosuspend,3018 .await_nosuspend,
3019 .fence,
3019 => try self.writeUnNode(stream, inst),3020 => try self.writeUnNode(stream, inst),
30203021
3021 .ref,3022 .ref,
...@@ -3187,7 +3188,6 @@ const Writer = struct {...@@ -3187,7 +3188,6 @@ const Writer = struct {
3187 .as_node => try self.writeAs(stream, inst),3188 .as_node => try self.writeAs(stream, inst),
31883189
3189 .breakpoint,3190 .breakpoint,
3190 .fence,
3191 .repeat,3191 .repeat,
3192 .repeat_inline,3192 .repeat_inline,
3193 .alloc_inferred,3193 .alloc_inferred,
src/codegen.zig+6
...@@ -833,6 +833,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -833,6 +833,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
833 .block => try self.airBlock(inst),833 .block => try self.airBlock(inst),
834 .br => try self.airBr(inst),834 .br => try self.airBr(inst),
835 .breakpoint => try self.airBreakpoint(),835 .breakpoint => try self.airBreakpoint(),
836 .fence => try self.airFence(),
836 .call => try self.airCall(inst),837 .call => try self.airCall(inst),
837 .cond_br => try self.airCondBr(inst),838 .cond_br => try self.airCondBr(inst),
838 .dbg_stmt => try self.airDbgStmt(inst),839 .dbg_stmt => try self.airDbgStmt(inst),
...@@ -2549,6 +2550,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2549,6 +2550,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2549 return self.finishAirBookkeeping();2550 return self.finishAirBookkeeping();
2550 }2551 }
25512552
2553 fn airFence(self: *Self) !void {
2554 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
2555 //return self.finishAirBookkeeping();
2556 }
2557
2552 fn airCall(self: *Self, inst: Air.Inst.Index) !void {2558 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2553 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2559 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2554 const fn_ty = self.air.typeOf(pl_op.operand);2560 const fn_ty = self.air.typeOf(pl_op.operand);
src/codegen/c.zig+12
...@@ -842,6 +842,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -842,6 +842,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
842842
843 .breakpoint => try airBreakpoint(o),843 .breakpoint => try airBreakpoint(o),
844 .unreach => try airUnreach(o),844 .unreach => try airUnreach(o),
845 .fence => try airFence(o, inst),
845846
846 // TODO use a different strategy for add that communicates to the optimizer847 // TODO use a different strategy for add that communicates to the optimizer
847 // that wrapping is UB.848 // that wrapping is UB.
...@@ -1439,6 +1440,17 @@ fn airBreakpoint(o: *Object) !CValue {...@@ -1439,6 +1440,17 @@ fn airBreakpoint(o: *Object) !CValue {
1439 return CValue.none;1440 return CValue.none;
1440}1441}
14411442
1443fn airFence(o: *Object, inst: Air.Inst.Index) !CValue {
1444 const atomic_order = o.air.instructions.items(.data)[inst].fence;
1445 const writer = o.writer();
1446
1447 try writer.writeAll("zig_fence(");
1448 try writeMemoryOrder(writer, atomic_order);
1449 try writer.writeAll(");\n");
1450
1451 return CValue.none;
1452}
1453
1442fn airUnreach(o: *Object) !CValue {1454fn airUnreach(o: *Object) !CValue {
1443 try o.writer().writeAll("zig_unreachable();\n");1455 try o.writer().writeAll("zig_unreachable();\n");
1444 return CValue.none;1456 return CValue.none;
src/codegen/llvm.zig+9
...@@ -1059,6 +1059,7 @@ pub const FuncGen = struct {...@@ -1059,6 +1059,7 @@ pub const FuncGen = struct {
1059 .array_to_slice => try self.airArrayToSlice(inst),1059 .array_to_slice => try self.airArrayToSlice(inst),
1060 .cmpxchg_weak => try self.airCmpxchg(inst, true),1060 .cmpxchg_weak => try self.airCmpxchg(inst, true),
1061 .cmpxchg_strong => try self.airCmpxchg(inst, false),1061 .cmpxchg_strong => try self.airCmpxchg(inst, false),
1062 .fence => try self.airFence(inst),
10621063
1063 .struct_field_ptr => try self.airStructFieldPtr(inst),1064 .struct_field_ptr => try self.airStructFieldPtr(inst),
1064 .struct_field_val => try self.airStructFieldVal(inst),1065 .struct_field_val => try self.airStructFieldVal(inst),
...@@ -2005,6 +2006,14 @@ pub const FuncGen = struct {...@@ -2005,6 +2006,14 @@ pub const FuncGen = struct {
2005 return null;2006 return null;
2006 }2007 }
20072008
2009 fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2010 const atomic_order = self.air.instructions.items(.data)[inst].fence;
2011 const llvm_memory_order = toLlvmAtomicOrdering(atomic_order);
2012 const single_threaded = llvm.Bool.fromBool(self.single_threaded);
2013 _ = self.builder.buildFence(llvm_memory_order, single_threaded, "");
2014 return null;
2015 }
2016
2008 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {2017 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {
2009 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2018 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2010 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;2019 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
src/codegen/llvm/bindings.zig+8
...@@ -522,6 +522,14 @@ pub const Builder = opaque {...@@ -522,6 +522,14 @@ pub const Builder = opaque {
522 Else: *const Value,522 Else: *const Value,
523 Name: [*:0]const u8,523 Name: [*:0]const u8,
524 ) *const Value;524 ) *const Value;
525
526 pub const buildFence = LLVMBuildFence;
527 extern fn LLVMBuildFence(
528 B: *const Builder,
529 ordering: AtomicOrdering,
530 singleThread: Bool,
531 Name: [*:0]const u8,
532 ) *const Value;
525};533};
526534
527pub const IntPredicate = enum(c_uint) {535pub const IntPredicate = enum(c_uint) {
src/link/C/zig.h+3
...@@ -64,12 +64,15 @@...@@ -64,12 +64,15 @@
64#include <stdatomic.h>64#include <stdatomic.h>
65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail)65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail)
66#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail)66#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail)
67#define zig_fence(order) atomic_thread_fence(order)
67#elif __GNUC__68#elif __GNUC__
68#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)69#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
69#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)70#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
71#define zig_fence(order) __sync_synchronize(order)
70#else72#else
71#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented()73#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented()
72#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented()74#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented()
75#define zig_fence(order) zig_unimplemented()
73#endif76#endif
7477
75#include <stdint.h>78#include <stdint.h>
src/print_air.zig+7
...@@ -192,6 +192,7 @@ const Writer = struct {...@@ -192,6 +192,7 @@ const Writer = struct {
192 .cond_br => try w.writeCondBr(s, inst),192 .cond_br => try w.writeCondBr(s, inst),
193 .switch_br => try w.writeSwitchBr(s, inst),193 .switch_br => try w.writeSwitchBr(s, inst),
194 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),194 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
195 .fence => try w.writeFence(s, inst),
195 }196 }
196 }197 }
197198
...@@ -276,6 +277,12 @@ const Writer = struct {...@@ -276,6 +277,12 @@ const Writer = struct {
276 });277 });
277 }278 }
278279
280 fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
281 const atomic_order = w.air.instructions.items(.data)[inst].fence;
282
283 try s.print("{s}", .{@tagName(atomic_order)});
284 }
285
279 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {286 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
280 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;287 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
281 const val = w.air.values[ty_pl.payload];288 const val = w.air.values[ty_pl.payload];
test/behavior/atomics.zig+6
...@@ -24,3 +24,9 @@ fn testCmpxchg() !void {...@@ -24,3 +24,9 @@ fn testCmpxchg() !void {
24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
25 try expect(x == 42);25 try expect(x == 42);
26}26}
27
28test "fence" {
29 var x: i32 = 1234;
30 @fence(.SeqCst);
31 x = 5678;
32}
test/behavior/atomics_stage1.zig-6
...@@ -3,12 +3,6 @@ const expect = std.testing.expect;...@@ -3,12 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const builtin = @import("builtin");4const builtin = @import("builtin");
55
6test "fence" {
7 var x: i32 = 1234;
8 @fence(.SeqCst);
9 x = 5678;
10}
11
12test "atomicrmw and atomicload" {6test "atomicrmw and atomicload" {
13 var data: u8 = 200;7 var data: u8 = 200;
14 try testAtomicRmw(&data);8 try testAtomicRmw(&data);