Commit 9fa723ee50

Andrew Kelley <andrew@ziglang.org>
2021-09-20 00:07:51
stage2: implement `@atomicStore`
1 parent 2a0c44f
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,
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(
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);
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);
+}
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();