| 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,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, | ||
| 383 | 388 | ||
| 384 | // Make sure we don't accidentally add a field to make this union | 389 | // Make sure we don't accidentally add a field to make this union |
| 385 | // bigger than expected. Note that in Debug builds, Zig is allowed | 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,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), |
| 570 | 576 | ||
| 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 | }, | ||
| 7119 | 7124 | ||
| 7120 | .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint), | 7125 | .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint), |
| 7121 | .fence => return simpleNoOpVoid(gz, rl, node, .fence), | ||
| 7122 | 7126 | ||
| 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 }), |
| 268 | 269 | ||
| 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 avoid | 377 | // 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 | } |
| 2310 | 2312 | ||
| 2311 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | 2313 | fn 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(); | ||
| 2314 | 2315 | ||
| 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 | } | ||
| 2320 | 2319 | ||
| 2321 | fn 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 | } |
| 2326 | 2329 | ||
| 2327 | fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { | 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,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, |
| 736 | 736 | ||
| 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, |
| 1420 | 1420 | ||
| 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), |
| 3020 | 3021 | ||
| 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), |
| 3188 | 3189 | ||
| 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 | } |
| 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 | 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 |
| 842 | 842 | ||
| 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), | ||
| 845 | 846 | ||
| 846 | // TODO use a different strategy for add that communicates to the optimizer | 847 | // 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 | } |
| 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 | fn airUnreach(o: *Object) !CValue { | 1454 | fn 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), | ||
| 1062 | 1063 | ||
| 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 | } |
| 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 | 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 | }; |
| 526 | 534 | ||
| 527 | pub const IntPredicate = enum(c_uint) { | 535 | pub 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 | #else | 72 | #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 | #endif | 76 | #endif |
| 74 | 77 | ||
| 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 | } |
| 197 | 198 | ||
| ... | @@ -276,6 +277,12 @@ const Writer = struct { | ... | @@ -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 | 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 | |||
| 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,12 +3,6 @@ const expect = std.testing.expect; |
| 3 | const expectEqual = std.testing.expectEqual; | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | 5 | ||
| 6 | test "fence" { | ||
| 7 | var x: i32 = 1234; | ||
| 8 | @fence(.SeqCst); | ||
| 9 | x = 5678; | ||
| 10 | } | ||
| 11 | |||
| 12 | test "atomicrmw and atomicload" { | 6 | test "atomicrmw and atomicload" { |
| 13 | var data: u8 = 200; | 7 | var data: u8 = 200; |
| 14 | try testAtomicRmw(&data); | 8 | try testAtomicRmw(&data); |