From 9fa723ee5043a9d2cb017e417f2e27041f671146 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Sep 2021 15:07:51 -0700 Subject: [PATCH] stage2: implement `@atomicStore` --- src/AstGen.zig | 2 +- src/Sema.zig | 70 +++++++++++++++++++++++++++----- src/Zir.zig | 19 +++++++-- test/behavior/atomics.zig | 8 ++++ test/behavior/atomics_stage1.zig | 8 ---- 5 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4bdfa1811c7a..be3613dfb936 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2118,7 +2118,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .select, .atomic_load, .atomic_rmw, - .atomic_store, .mul_add, .builtin_call, .field_ptr_type, @@ -2164,6 +2163,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .@"export", .set_eval_branch_quota, .ensure_err_payload_void, + .atomic_store, .store, .store_node, .store_to_block_ptr, diff --git a/src/Sema.zig b/src/Sema.zig index 80cab6d00fae..54717953174b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -314,7 +314,6 @@ pub fn analyzeBody( .select => try sema.zirSelect(block, inst), .atomic_load => try sema.zirAtomicLoad(block, inst), .atomic_rmw => try sema.zirAtomicRmw(block, inst), - .atomic_store => try sema.zirAtomicStore(block, inst), .mul_add => try sema.zirMulAdd(block, inst), .builtin_call => try sema.zirBuiltinCall(block, inst), .field_ptr_type => try sema.zirFieldPtrType(block, inst), @@ -413,6 +412,11 @@ pub fn analyzeBody( i += 1; continue; }, + .atomic_store => { + try sema.zirAtomicStore(block, inst); + i += 1; + continue; + }, .store => { try sema.zirStore(block, inst); i += 1; @@ -7669,6 +7673,8 @@ fn zirCmpxchg( if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| { if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| { if (expected_val.isUndef() or new_val.isUndef()) { + // TODO: this should probably cause the memory stored at the pointer + // to become undef as well return sema.addConstUndef(result_ty); } const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; @@ -7830,10 +7836,38 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE }); } -fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicStore", .{}); + // zig fmt: off + const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; + const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; + // zig fmt: on + const ptr = sema.resolveInst(extra.ptr); + const operand_ty = sema.typeOf(ptr).elemType(); + try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); + const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + + const air_tag: Air.Inst.Tag = switch (order) { + .Acquire, .AcqRel => { + return sema.mod.fail( + &block.base, + order_src, + "@atomicStore atomic ordering must not be Acquire or AcqRel", + .{}, + ); + }, + .Unordered => .atomic_store_unordered, + .Monotonic => .atomic_store_monotonic, + .Release => .atomic_store_release, + .SeqCst => .atomic_store_seq_cst, + }; + + return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); } fn zirMulAdd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9310,25 +9344,39 @@ fn coerceVarArgParam( return inst; } +// TODO migrate callsites to use storePtr2 instead. fn storePtr( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ptr: Air.Inst.Ref, - uncasted_value: Air.Inst.Ref, + uncasted_operand: Air.Inst.Ref, +) !void { + return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, .store); +} + +fn storePtr2( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + ptr: Air.Inst.Ref, + ptr_src: LazySrcLoc, + uncasted_operand: Air.Inst.Ref, + operand_src: LazySrcLoc, + air_tag: Air.Inst.Tag, ) !void { const ptr_ty = sema.typeOf(ptr); if (ptr_ty.isConstPtr()) return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); const elem_ty = ptr_ty.elemType(); - const value = try sema.coerce(block, elem_ty, uncasted_value, src); + const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) return; - if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| { + const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| { - const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse + const const_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); if (decl_ref_mut.data.runtime_index < block.runtime_index) { @@ -9365,11 +9413,13 @@ fn storePtr( old_arena.deinit(); return; } - } + break :rs operand_src; + } else ptr_src; + // TODO handle if the element type requires comptime - try sema.requireRuntimeBlock(block, src); - _ = try block.addBinOp(.store, ptr, value); + try sema.requireRuntimeBlock(block, runtime_src); + _ = try block.addBinOp(air_tag, ptr, operand); } fn bitcast( diff --git a/src/Zir.zig b/src/Zir.zig index fcc2d5f330c3..f8dbef2534fe 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3058,7 +3058,6 @@ const Writer = struct { .shuffle, .select, .atomic_rmw, - .atomic_store, .mul_add, .builtin_call, .field_parent_ptr, @@ -3071,9 +3070,8 @@ const Writer = struct { .struct_init_ref, => try self.writeStructInit(stream, inst), - .cmpxchg_strong, - .cmpxchg_weak, - => try self.writeCmpxchg(stream, inst), + .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), + .atomic_store => try self.writeAtomicStore(stream, inst), .struct_init_anon, .struct_init_anon_ref, @@ -3493,6 +3491,19 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeAtomicStore(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.AtomicStore, inst_data.payload_index).data; + + try self.writeInstRef(stream, extra.ptr); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.operand); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.ordering); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.StructInitAnon, inst_data.payload_index); diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index efd63f1ac598..75e33477bcd6 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -130,3 +130,11 @@ test "atomic load and rmw with enum" { try expect(@atomicLoad(Value, &x, .SeqCst) != .a); try expect(@atomicLoad(Value, &x, .SeqCst) != .b); } + +test "atomic store" { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 1); + @atomicStore(u32, &x, 12345678, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); +} diff --git a/test/behavior/atomics_stage1.zig b/test/behavior/atomics_stage1.zig index 936c06b15526..22e3ae593907 100644 --- a/test/behavior/atomics_stage1.zig +++ b/test/behavior/atomics_stage1.zig @@ -3,14 +3,6 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); -test "atomic store" { - var x: u32 = 0; - @atomicStore(u32, &x, 1, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 1); - @atomicStore(u32, &x, 12345678, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); -} - test "atomic store comptime" { comptime try testAtomicStore(); try testAtomicStore();