| author | |
| committer | |
| log | 19691c0b174f283ffe5b6c3fe8533ef458736064 |
| tree | 802390ce8ad5d29cc72450262efd4b13140f5f20 |
| parent | e5fd45003e56f152364a4bdc609fda07a6b524fd |
13 files changed, 82 insertions(+), 23 deletions(-)
src/Air.zig+6| ... | ... | @@ -127,6 +127,10 @@ pub const Inst = struct { |
| 127 | 127 | /// Lowers to a hardware trap instruction, or the next best thing. |
| 128 | 128 | /// Result type is always void. |
| 129 | 129 | breakpoint, |
| 130 | /// Lowers to a memory fence instruction. | |
| 131 | /// Result type is always void. | |
| 132 | /// Uses the `fence` field. | |
| 133 | fence, | |
| 130 | 134 | /// Function call. |
| 131 | 135 | /// Result type is the return type of the function being called. |
| 132 | 136 | /// Uses the `pl_op` field with the `Call` payload. operand is the callee. |
| ... | ... | @@ -380,6 +384,7 @@ pub const Inst = struct { |
| 380 | 384 | line: u32, |
| 381 | 385 | column: u32, |
| 382 | 386 | }, |
| 387 | fence: std.builtin.AtomicOrder, | |
| 383 | 388 | |
| 384 | 389 | // Make sure we don't accidentally add a field to make this union |
| 385 | 390 | // 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 | 571 | .breakpoint, |
| 567 | 572 | .dbg_stmt, |
| 568 | 573 | .store, |
| 574 | .fence, | |
| 569 | 575 | => return Type.initTag(.void), |
| 570 | 576 | |
| 571 | 577 | .ptrtoint, |
src/AstGen.zig+5-1| ... | ... | @@ -7116,9 +7116,13 @@ fn builtinCall( |
| 7116 | 7116 | }); |
| 7117 | 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 | }, | |
| 7119 | 7124 | |
| 7120 | 7125 | .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint), |
| 7121 | .fence => return simpleNoOpVoid(gz, rl, node, .fence), | |
| 7122 | 7126 | |
| 7123 | 7127 | .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node), |
| 7124 | 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 | 264 | .breakpoint, |
| 265 | 265 | .dbg_stmt, |
| 266 | 266 | .unreach, |
| 267 | .fence, | |
| 267 | 268 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), |
| 268 | 269 | |
| 269 | 270 | .not, |
src/Sema.zig+16-13| ... | ... | @@ -377,7 +377,9 @@ pub fn analyzeBody( |
| 377 | 377 | // We also know that they cannot be referenced later, so we avoid |
| 378 | 378 | // putting them into the map. |
| 379 | 379 | .breakpoint => { |
| 380 | try sema.zirBreakpoint(block, inst); | |
| 380 | if (!block.is_comptime) { | |
| 381 | _ = try block.addNoOp(.breakpoint); | |
| 382 | } | |
| 381 | 383 | i += 1; |
| 382 | 384 | continue; |
| 383 | 385 | }, |
| ... | ... | @@ -2308,20 +2310,21 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C |
| 2308 | 2310 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); |
| 2309 | 2311 | } |
| 2310 | 2312 | |
| 2311 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2312 | const tracy = trace(@src()); | |
| 2313 | defer tracy.end(); | |
| 2313 | fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2314 | if (block.is_comptime) return; | |
| 2314 | 2315 | |
| 2315 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 2316 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 2317 | try sema.requireRuntimeBlock(block, src); | |
| 2318 | _ = try block.addNoOp(.breakpoint); | |
| 2319 | } | |
| 2316 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 2317 | const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2318 | const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand); | |
| 2320 | 2319 | |
| 2321 | fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2322 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 2323 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 2324 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{}); | |
| 2320 | if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) { | |
| 2321 | return sema.mod.fail(&block.base, order_src, "atomic ordering must be Acquire or stricter", .{}); | |
| 2322 | } | |
| 2323 | ||
| 2324 | _ = try block.addInst(.{ | |
| 2325 | .tag = .fence, | |
| 2326 | .data = .{ .fence = order }, | |
| 2327 | }); | |
| 2325 | 2328 | } |
| 2326 | 2329 | |
| 2327 | 2330 | fn 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 | 731 | size_of, |
| 732 | 732 | /// Implements the `@bitSizeOf` builtin. Uses `un_node`. |
| 733 | 733 | bit_size_of, |
| 734 | /// Implements the `@fence` builtin. Uses `node`. | |
| 734 | /// Implements the `@fence` builtin. Uses `un_node`. | |
| 735 | 735 | fence, |
| 736 | 736 | |
| 737 | 737 | /// Implement builtin `@ptrToInt`. Uses `un_node`. |
| ... | ... | @@ -1416,7 +1416,7 @@ pub const Inst = struct { |
| 1416 | 1416 | .type_info = .un_node, |
| 1417 | 1417 | .size_of = .un_node, |
| 1418 | 1418 | .bit_size_of = .un_node, |
| 1419 | .fence = .node, | |
| 1419 | .fence = .un_node, | |
| 1420 | 1420 | |
| 1421 | 1421 | .ptr_to_int = .un_node, |
| 1422 | 1422 | .error_to_int = .un_node, |
| ... | ... | @@ -3016,6 +3016,7 @@ const Writer = struct { |
| 3016 | 3016 | .@"resume", |
| 3017 | 3017 | .@"await", |
| 3018 | 3018 | .await_nosuspend, |
| 3019 | .fence, | |
| 3019 | 3020 | => try self.writeUnNode(stream, inst), |
| 3020 | 3021 | |
| 3021 | 3022 | .ref, |
| ... | ... | @@ -3187,7 +3188,6 @@ const Writer = struct { |
| 3187 | 3188 | .as_node => try self.writeAs(stream, inst), |
| 3188 | 3189 | |
| 3189 | 3190 | .breakpoint, |
| 3190 | .fence, | |
| 3191 | 3191 | .repeat, |
| 3192 | 3192 | .repeat_inline, |
| 3193 | 3193 | .alloc_inferred, |
src/codegen.zig+6| ... | ... | @@ -833,6 +833,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 833 | 833 | .block => try self.airBlock(inst), |
| 834 | 834 | .br => try self.airBr(inst), |
| 835 | 835 | .breakpoint => try self.airBreakpoint(), |
| 836 | .fence => try self.airFence(), | |
| 836 | 837 | .call => try self.airCall(inst), |
| 837 | 838 | .cond_br => try self.airCondBr(inst), |
| 838 | 839 | .dbg_stmt => try self.airDbgStmt(inst), |
| ... | ... | @@ -2549,6 +2550,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2549 | 2550 | return self.finishAirBookkeeping(); |
| 2550 | 2551 | } |
| 2551 | 2552 | |
| 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 | 2558 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2553 | 2559 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2554 | 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 | 842 | |
| 843 | 843 | .breakpoint => try airBreakpoint(o), |
| 844 | 844 | .unreach => try airUnreach(o), |
| 845 | .fence => try airFence(o, inst), | |
| 845 | 846 | |
| 846 | 847 | // TODO use a different strategy for add that communicates to the optimizer |
| 847 | 848 | // that wrapping is UB. |
| ... | ... | @@ -1439,6 +1440,17 @@ fn airBreakpoint(o: *Object) !CValue { |
| 1439 | 1440 | return CValue.none; |
| 1440 | 1441 | } |
| 1441 | 1442 | |
| 1443 | fn 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 | ||
| 1442 | 1454 | fn airUnreach(o: *Object) !CValue { |
| 1443 | 1455 | try o.writer().writeAll("zig_unreachable();\n"); |
| 1444 | 1456 | return CValue.none; |
src/codegen/llvm.zig+9| ... | ... | @@ -1059,6 +1059,7 @@ pub const FuncGen = struct { |
| 1059 | 1059 | .array_to_slice => try self.airArrayToSlice(inst), |
| 1060 | 1060 | .cmpxchg_weak => try self.airCmpxchg(inst, true), |
| 1061 | 1061 | .cmpxchg_strong => try self.airCmpxchg(inst, false), |
| 1062 | .fence => try self.airFence(inst), | |
| 1062 | 1063 | |
| 1063 | 1064 | .struct_field_ptr => try self.airStructFieldPtr(inst), |
| 1064 | 1065 | .struct_field_val => try self.airStructFieldVal(inst), |
| ... | ... | @@ -2005,6 +2006,14 @@ pub const FuncGen = struct { |
| 2005 | 2006 | return null; |
| 2006 | 2007 | } |
| 2007 | 2008 | |
| 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 | 2017 | fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value { |
| 2009 | 2018 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2010 | 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 | 522 | Else: *const Value, |
| 523 | 523 | Name: [*:0]const u8, |
| 524 | 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 | }; |
| 526 | 534 | |
| 527 | 535 | pub const IntPredicate = enum(c_uint) { |
src/link/C/zig.h+3| ... | ... | @@ -64,12 +64,15 @@ |
| 64 | 64 | #include <stdatomic.h> |
| 65 | 65 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) |
| 66 | 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 | 68 | #elif __GNUC__ |
| 68 | 69 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired) |
| 69 | 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 | 72 | #else |
| 71 | 73 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented() |
| 72 | 74 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented() |
| 75 | #define zig_fence(order) zig_unimplemented() | |
| 73 | 76 | #endif |
| 74 | 77 | |
| 75 | 78 | #include <stdint.h> |
src/print_air.zig+7| ... | ... | @@ -192,6 +192,7 @@ const Writer = struct { |
| 192 | 192 | .cond_br => try w.writeCondBr(s, inst), |
| 193 | 193 | .switch_br => try w.writeSwitchBr(s, inst), |
| 194 | 194 | .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), |
| 195 | .fence => try w.writeFence(s, inst), | |
| 195 | 196 | } |
| 196 | 197 | } |
| 197 | 198 | |
| ... | ... | @@ -276,6 +277,12 @@ const Writer = struct { |
| 276 | 277 | }); |
| 277 | 278 | } |
| 278 | 279 | |
| 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 | 286 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 280 | 287 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 281 | 288 | const val = w.air.values[ty_pl.payload]; |
test/behavior/atomics.zig+6| ... | ... | @@ -24,3 +24,9 @@ fn testCmpxchg() !void { |
| 24 | 24 | try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); |
| 25 | 25 | try expect(x == 42); |
| 26 | 26 | } |
| 27 | ||
| 28 | test "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 | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | 5 | |
| 6 | test "fence" { | |
| 7 | var x: i32 = 1234; | |
| 8 | @fence(.SeqCst); | |
| 9 | x = 5678; | |
| 10 | } | |
| 11 | ||
| 12 | 6 | test "atomicrmw and atomicload" { |
| 13 | 7 | var data: u8 = 200; |
| 14 | 8 | try testAtomicRmw(&data); |