Commit 3e73f37d0a

David Rubin <daviru007@icloud.com>
2024-06-01 09:42:29
riscv: implement `@fence`
1 parent 7a02878
src/arch/riscv64/CodeGen.zig
@@ -1318,7 +1318,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
             .breakpoint      => try func.airBreakpoint(),
             .ret_addr        => try func.airRetAddr(inst),
             .frame_addr      => try func.airFrameAddress(inst),
-            .fence           => try func.airFence(),
+            .fence           => try func.airFence(inst),
             .cond_br         => try func.airCondBr(inst),
             .dbg_stmt        => try func.airDbgStmt(inst),
             .fptrunc         => try func.airFptrunc(inst),
@@ -4238,9 +4238,28 @@ fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void {
     return func.finishAir(inst, dst_mcv, .{ .none, .none, .none });
 }
 
-fn airFence(func: *Func) !void {
-    return func.fail("TODO implement fence() for {}", .{func.target.cpu.arch});
-    //return func.finishAirBookkeeping();
+fn airFence(func: *Func, inst: Air.Inst.Index) !void {
+    const order = func.air.instructions.items(.data)[@intFromEnum(inst)].fence;
+    const pred: Mir.Barrier, const succ: Mir.Barrier = switch (order) {
+        .unordered, .monotonic => unreachable,
+        .acquire => .{ .r, .rw },
+        .release => .{ .rw, .r },
+        .acq_rel => .{ .rw, .rw },
+        .seq_cst => .{ .rw, .rw },
+    };
+
+    _ = try func.addInst(.{
+        .tag = .pseudo,
+        .ops = .pseudo_fence,
+        .data = .{
+            .fence = .{
+                .pred = pred,
+                .succ = succ,
+                .fm = if (order == .acq_rel) .tso else .none,
+            },
+        },
+    });
+    return func.finishAirBookkeeping();
 }
 
 fn airCall(func: *Func, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
@@ -6264,12 +6283,13 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
 
     if (order == .seq_cst) {
         _ = try func.addInst(.{
-            .tag = .fence,
-            .ops = .fence,
+            .tag = .pseudo,
+            .ops = .pseudo_fence,
             .data = .{
                 .fence = .{
                     .pred = .rw,
                     .succ = .rw,
+                    .fm = .none,
                 },
             },
         });
@@ -6284,12 +6304,13 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
         // Make sure all previous reads happen before any reading or writing accurs.
         .seq_cst, .acquire => {
             _ = try func.addInst(.{
-                .tag = .fence,
-                .ops = .fence,
+                .tag = .pseudo,
+                .ops = .pseudo_fence,
                 .data = .{
                     .fence = .{
                         .pred = .r,
                         .succ = .rw,
+                        .fm = .none,
                     },
                 },
             });
@@ -6313,12 +6334,13 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
         .unordered, .monotonic => {},
         .release, .seq_cst => {
             _ = try func.addInst(.{
-                .tag = .fence,
-                .ops = .fence,
+                .tag = .pseudo,
+                .ops = .pseudo_fence,
                 .data = .{
                     .fence = .{
                         .pred = .rw,
                         .succ = .w,
+                        .fm = .none,
                     },
                 },
             });
src/arch/riscv64/Encoding.zig
@@ -45,6 +45,11 @@ const AmoWidth = enum(u3) {
     D = 0b011,
 };
 
+const FenceMode = enum(u4) {
+    none = 0b0000,
+    tso = 0b1000,
+};
+
 const Enc = struct {
     opcode: OpCode,
 
@@ -58,6 +63,10 @@ const Enc = struct {
             funct5: u5,
             width: AmoWidth,
         },
+        fence: struct {
+            funct3: u3,
+            fm: FenceMode,
+        },
         /// funct5 + rm + fmt
         fmt: struct {
             funct5: u5,
@@ -210,6 +219,7 @@ pub const Mnemonic = enum {
 
     // MISC
     fence,
+    fencetso,
 
     // AMO
     amoswapw,
@@ -406,9 +416,12 @@ pub const Mnemonic = enum {
             
             .unimp   => .{ .opcode = .NONE, .data = .{ .f = .{ .funct3 = 0b000 } } },
 
+
             // MISC_MEM
 
-            .fence   => .{ .opcode = .MISC_MEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
+            .fence    => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } },
+            .fencetso => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso  } } },
+
 
             // AMO
 
@@ -437,7 +450,6 @@ pub const Mnemonic = enum {
             .amomaxud  => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
 
                     
-
             // zig fmt: on
         };
     }
@@ -583,6 +595,7 @@ pub const InstEnc = enum {
             => .system,
 
             .fence,
+            .fencetso,
             => .fence,
 
             .amoswapw,
@@ -689,7 +702,7 @@ pub const Data = union(InstEnc) {
         rs1: u5 = 0,
         succ: u4,
         pred: u4,
-        _ignored: u4 = 0,
+        fm: u4,
     },
     amo: packed struct {
         opcode: u7,
@@ -711,11 +724,9 @@ pub const Data = union(InstEnc) {
 
     pub fn toU32(self: Data) u32 {
         return switch (self) {
-            // zig fmt: off
-            .B  => |v| @as(u32, @intCast(v.opcode)) + (@as(u32, @intCast(v.imm11)) << 7) + (@as(u32, @intCast(v.imm1_4)) << 8) + (@as(u32, @intCast(v.funct3)) << 12) + (@as(u32, @intCast(v.rs1)) << 15) + (@as(u32, @intCast(v.rs2)) << 20) + (@as(u32, @intCast(v.imm5_10)) << 25) + (@as(u32, @intCast(v.imm12)) << 31),
+            .fence => |v| @as(u32, @intCast(v.opcode)) + (@as(u32, @intCast(v.rd)) << 7) + (@as(u32, @intCast(v.funct3)) << 12) + (@as(u32, @intCast(v.rs1)) << 15) + (@as(u32, @intCast(v.succ)) << 20) + (@as(u32, @intCast(v.pred)) << 24) + (@as(u32, @intCast(v.fm)) << 28),
             inline else => |v| @bitCast(v),
             .system => unreachable,
-            // zig fmt: on
         };
     }
 
@@ -869,16 +880,17 @@ pub const Data = union(InstEnc) {
             .fence => {
                 assert(ops.len == 2);
 
-                const succ = ops[0];
-                const pred = ops[1];
+                const succ = ops[0].barrier;
+                const pred = ops[1].barrier;
 
                 return .{
                     .fence = .{
-                        .succ = @intFromEnum(succ.barrier),
-                        .pred = @intFromEnum(pred.barrier),
+                        .succ = @intFromEnum(succ),
+                        .pred = @intFromEnum(pred),
 
                         .opcode = @intFromEnum(enc.opcode),
-                        .funct3 = enc.data.f.funct3,
+                        .funct3 = enc.data.fence.funct3,
+                        .fm = @intFromEnum(enc.data.fence.fm),
                     },
                 };
             },
@@ -891,7 +903,7 @@ pub const Data = union(InstEnc) {
                 const rl = ops[3];
                 const aq = ops[4];
 
-                const ret: Data = .{
+                return .{
                     .amo = .{
                         .rd = rd.reg.encodeId(),
                         .rs1 = rs1.reg.encodeId(),
@@ -906,9 +918,6 @@ pub const Data = union(InstEnc) {
                         .funct5 = enc.data.amo.funct5,
                     },
                 };
-
-                std.debug.print("ret: {}, {}", .{ ret.amo.rl, rl.barrier == .rl });
-                return ret;
             },
 
             else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
src/arch/riscv64/Lower.zig
@@ -443,6 +443,18 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
                 });
             },
 
+            .pseudo_fence => {
+                const fence = inst.data.fence;
+
+                try lower.emit(switch (fence.fm) {
+                    .tso => .fencetso,
+                    .none => .fence,
+                }, &.{
+                    .{ .barrier = fence.succ },
+                    .{ .barrier = fence.pred },
+                });
+            },
+
             else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}),
         },
     }
@@ -485,10 +497,6 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .reg = inst.data.r_type.rs1 },
             .{ .reg = inst.data.r_type.rs2 },
         },
-        .fence => &.{
-            .{ .barrier = inst.data.fence.succ },
-            .{ .barrier = inst.data.fence.pred },
-        },
         else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
     });
 }
src/arch/riscv64/Mir.zig
@@ -80,8 +80,6 @@ pub const Inst = struct {
         sh,
         sb,
 
-        fence,
-
         // M extension
         mul,
         mulw,
@@ -256,6 +254,10 @@ pub const Inst = struct {
         fence: struct {
             pred: Barrier,
             succ: Barrier,
+            fm: enum {
+                none,
+                tso,
+            },
         },
 
         amo: struct {
@@ -355,7 +357,7 @@ pub const Inst = struct {
         pseudo_extern_fn_reloc,
 
         /// IORW, IORW
-        fence,
+        pseudo_fence,
 
         /// Ordering, Src, Addr, Dest
         pseudo_amo,
@@ -396,8 +398,8 @@ pub const FrameLoc = struct {
 
 pub const Barrier = enum(u4) {
     // Fence
-    r = 0b0001,
-    w = 0b0010,
+    w = 0b0001,
+    r = 0b0010,
     rw = 0b0011,
 
     // Amo
test/behavior/atomics.zig
@@ -42,7 +42,6 @@ test "fence" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
 
     var x: i32 = 1234;
     @fence(.seq_cst);
test/behavior/builtin_functions_returning_void_or_noreturn.zig
@@ -11,7 +11,6 @@ test {
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
 
     var val: u8 = undefined;
     try testing.expectEqual({}, @atomicStore(u8, &val, 0, .unordered));