authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-25 23:01:44-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-26 04:19:57-07:00
log7ff5709e1b04de4c33988ce6a27bc593dcf20f63
treeda08f073bec966042a0815579b5b68ceb9399c29
parenta1f6a8ef90f0778e4fc5d314eeb1f0a0a93fa53a
signaturelock-open Commit is signed but in an unrecognized format.

riscv: implement `lr/sr` loop logic for non-native atomics


7 files changed, 138 insertions(+), 113 deletions(-)

lib/std/start.zig+2-4
......@@ -478,10 +478,8 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
478478 std.os.argv = argv[0..argc];
479479 std.os.environ = envp;
480480
481 if (builtin.zig_backend != .stage2_riscv64) {
482 std.debug.maybeEnableSegfaultHandler();
483 maybeIgnoreSigpipe();
484 }
481 std.debug.maybeEnableSegfaultHandler();
482 maybeIgnoreSigpipe();
485483
486484 return callMain();
487485}
src/arch/riscv64/CodeGen.zig+113-53
......@@ -4717,14 +4717,11 @@ fn airFence(func: *Func, inst: Air.Inst.Index) !void {
47174717 };
47184718
47194719 _ = try func.addInst(.{
4720 .tag = .pseudo_fence,
4721 .data = .{
4722 .fence = .{
4723 .pred = pred,
4724 .succ = succ,
4725 .fm = if (order == .acq_rel) .tso else .none,
4726 },
4727 },
4720 .tag = if (order == .acq_rel) .fencetso else .fence,
4721 .data = .{ .fence = .{
4722 .pred = pred,
4723 .succ = succ,
4724 } },
47284725 });
47294726 return func.finishAirBookkeeping();
47304727}
......@@ -5278,12 +5275,12 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
52785275 .dead,
52795276 .undef,
52805277 .immediate,
5281 .register_pair,
52825278 .register_offset,
52835279 .lea_frame,
52845280 .lea_symbol,
52855281 .reserved_frame,
52865282 .air_ref,
5283 .register_pair,
52875284 => unreachable,
52885285
52895286 .register => |opt_reg| {
......@@ -7109,6 +7106,7 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void {
71097106
71107107fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71117108 const pt = func.pt;
7109 const zcu = pt.zcu;
71127110 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
71137111 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
71147112
......@@ -7131,11 +7129,11 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71317129 else => unreachable,
71327130 }
71337131
7134 switch (val_size) {
7135 1, 2 => return func.fail("TODO: airAtomicRmw {s} Int {}", .{ @tagName(op), val_size }),
7136 4, 8 => {},
7132 const method: enum { amo, loop } = switch (val_size) {
7133 1, 2 => .loop,
7134 4, 8 => .amo,
71377135 else => unreachable,
7138 }
7136 };
71397137
71407138 const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
71417139 defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
......@@ -7145,6 +7143,7 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71457143
71467144 const result_mcv = try func.allocRegOrMem(val_ty, inst, true);
71477145 assert(result_mcv == .register); // should fit into 8 bytes
7146 const result_reg = result_mcv.register;
71487147
71497148 const aq, const rl = switch (order) {
71507149 .unordered => unreachable,
......@@ -7155,28 +7154,96 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71557154 .seq_cst => .{ true, true },
71567155 };
71577156
7158 _ = try func.addInst(.{
7159 .tag = .pseudo_amo,
7160 .data = .{ .amo = .{
7161 .rd = result_mcv.register,
7162 .rs1 = ptr_register,
7163 .rs2 = val_register,
7164 .aq = if (aq) .aq else .none,
7165 .rl = if (rl) .rl else .none,
7166 .op = switch (op) {
7167 .Xchg => .SWAP,
7168 .Add => .ADD,
7169 .Sub => return func.fail("TODO: airAtomicRmw SUB", .{}),
7170 .And => .AND,
7171 .Nand => return func.fail("TODO: airAtomicRmw NAND", .{}),
7172 .Or => .OR,
7173 .Xor => .XOR,
7174 .Max => .MAX,
7175 .Min => .MIN,
7176 },
7177 .ty = val_ty,
7178 } },
7179 });
7157 switch (method) {
7158 .amo => {
7159 const is_d = val_ty.abiSize(pt) == 8;
7160 const is_un = val_ty.isUnsignedInt(zcu);
7161
7162 const mnem: Mnemonic = switch (op) {
7163 // zig fmt: off
7164 .Xchg => if (is_d) .amoswapd else .amoswapw,
7165 .Add => if (is_d) .amoaddd else .amoaddw,
7166 .And => if (is_d) .amoandd else .amoandw,
7167 .Or => if (is_d) .amoord else .amoorw,
7168 .Xor => if (is_d) .amoxord else .amoxorw,
7169 .Max => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw,
7170 .Min => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw,
7171 else => return func.fail("TODO: airAtomicRmw amo {s}", .{@tagName(op)}),
7172 // zig fmt: on
7173 };
7174
7175 _ = try func.addInst(.{
7176 .tag = mnem,
7177 .data = .{ .amo = .{
7178 .rd = result_reg,
7179 .rs1 = ptr_register,
7180 .rs2 = val_register,
7181 .aq = if (aq) .aq else .none,
7182 .rl = if (rl) .rl else .none,
7183 } },
7184 });
7185 },
7186 .loop => {
7187 // where we'll jump back when the sc fails
7188 const jump_back = try func.addInst(.{
7189 .tag = .lrw,
7190 .data = .{ .amo = .{
7191 .rd = result_reg,
7192 .rs1 = ptr_register,
7193 .rs2 = .zero,
7194 .aq = if (aq) .aq else .none,
7195 .rl = if (rl) .rl else .none,
7196 } },
7197 });
7198
7199 const after_reg, const after_lock = try func.allocReg(.int);
7200 defer func.register_manager.unlockReg(after_lock);
7201
7202 switch (op) {
7203 .Add => {
7204 _ = try func.genBinOp(
7205 .add,
7206 .{ .register = result_reg },
7207 val_ty,
7208 .{ .register = val_register },
7209 val_ty,
7210 after_reg,
7211 );
7212 },
7213 .Sub => {
7214 _ = try func.genBinOp(
7215 .sub,
7216 .{ .register = result_reg },
7217 val_ty,
7218 .{ .register = val_register },
7219 val_ty,
7220 after_reg,
7221 );
7222 },
7223 else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}),
7224 }
7225
7226 _ = try func.addInst(.{
7227 .tag = .scw,
7228 .data = .{ .amo = .{
7229 .rd = after_reg,
7230 .rs1 = ptr_register,
7231 .rs2 = after_reg,
7232 .aq = if (aq) .aq else .none,
7233 .rl = if (rl) .rl else .none,
7234 } },
7235 });
7236
7237 _ = try func.addInst(.{
7238 .tag = .bne,
7239 .data = .{ .b_type = .{
7240 .inst = jump_back,
7241 .rs1 = after_reg,
7242 .rs2 = .zero,
7243 } },
7244 });
7245 },
7246 }
71807247
71817248 return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none });
71827249}
......@@ -7199,11 +7266,10 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
71997266
72007267 if (order == .seq_cst) {
72017268 _ = try func.addInst(.{
7202 .tag = .pseudo_fence,
7269 .tag = .fence,
72037270 .data = .{ .fence = .{
72047271 .pred = .rw,
72057272 .succ = .rw,
7206 .fm = .none,
72077273 } },
72087274 });
72097275 }
......@@ -7217,14 +7283,11 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
72177283 // Make sure all previous reads happen before any reading or writing accurs.
72187284 .seq_cst, .acquire => {
72197285 _ = try func.addInst(.{
7220 .tag = .pseudo_fence,
7221 .data = .{
7222 .fence = .{
7223 .pred = .r,
7224 .succ = .rw,
7225 .fm = .none,
7226 },
7227 },
7286 .tag = .fence,
7287 .data = .{ .fence = .{
7288 .pred = .r,
7289 .succ = .rw,
7290 } },
72287291 });
72297292 },
72307293 else => unreachable,
......@@ -7249,14 +7312,11 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
72497312 .unordered, .monotonic => {},
72507313 .release, .seq_cst => {
72517314 _ = try func.addInst(.{
7252 .tag = .pseudo_fence,
7253 .data = .{
7254 .fence = .{
7255 .pred = .rw,
7256 .succ = .w,
7257 .fm = .none,
7258 },
7259 },
7315 .tag = .fence,
7316 .data = .{ .fence = .{
7317 .pred = .rw,
7318 .succ = .w,
7319 } },
72607320 });
72617321 },
72627322 else => unreachable,
src/arch/riscv64/Lower.zig+11-38
......@@ -446,44 +446,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
446446 .{ .imm = Immediate.s(0) },
447447 });
448448 },
449
450 .pseudo_amo => {
451 const amo = inst.data.amo;
452 const is_d = amo.ty.abiSize(pt) == 8;
453 const is_un = amo.ty.isUnsignedInt(pt.zcu);
454
455 const mnem: Mnemonic = switch (amo.op) {
456 // zig fmt: off
457 .SWAP => if (is_d) .amoswapd else .amoswapw,
458 .ADD => if (is_d) .amoaddd else .amoaddw,
459 .AND => if (is_d) .amoandd else .amoandw,
460 .OR => if (is_d) .amoord else .amoorw,
461 .XOR => if (is_d) .amoxord else .amoxorw,
462 .MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw,
463 .MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw,
464 // zig fmt: on
465 };
466
467 try lower.emit(mnem, &.{
468 .{ .reg = inst.data.amo.rd },
469 .{ .reg = inst.data.amo.rs1 },
470 .{ .reg = inst.data.amo.rs2 },
471 .{ .barrier = inst.data.amo.rl },
472 .{ .barrier = inst.data.amo.aq },
473 });
474 },
475
476 .pseudo_fence => {
477 const fence = inst.data.fence;
478
479 try lower.emit(switch (fence.fm) {
480 .tso => .fencetso,
481 .none => .fence,
482 }, &.{
483 .{ .barrier = fence.succ },
484 .{ .barrier = fence.pred },
485 });
486 },
487449 }
488450
489451 return .{
......@@ -524,6 +486,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
524486 .{ .reg = csr.rs1 },
525487 .{ .reg = csr.rd },
526488 },
489 .amo => |amo| &.{
490 .{ .reg = amo.rd },
491 .{ .reg = amo.rs1 },
492 .{ .reg = amo.rs2 },
493 .{ .barrier = amo.rl },
494 .{ .barrier = amo.aq },
495 },
496 .fence => |fence| &.{
497 .{ .barrier = fence.succ },
498 .{ .barrier = fence.pred },
499 },
527500 else => return lower.fail("TODO: generic lower {s}", .{@tagName(inst.data)}),
528501 });
529502}
src/arch/riscv64/Mir.zig-6
......@@ -73,10 +73,6 @@ pub const Inst = struct {
7373 fence: struct {
7474 pred: Barrier,
7575 succ: Barrier,
76 fm: enum {
77 none,
78 tso,
79 },
8076 },
8177 amo: struct {
8278 rd: Register,
......@@ -84,8 +80,6 @@ pub const Inst = struct {
8480 rs2: Register,
8581 aq: Barrier,
8682 rl: Barrier,
87 op: AmoOp,
88 ty: Type,
8983 },
9084 csr: struct {
9185 csr: CSR,
src/arch/riscv64/abi.zig+1-4
......@@ -125,10 +125,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
125125 result[0] = .integer;
126126 return result;
127127 }
128 result[0] = .integer;
129 if (ty.optionalChild(zcu).abiSize(pt) == 0) return result;
130 result[1] = .integer;
131 return result;
128 return memory_class;
132129 },
133130 .Int, .Enum, .ErrorSet => {
134131 const int_bits = ty.intInfo(pt.zcu).bits;
src/arch/riscv64/encoding.zig+6-6
......@@ -353,6 +353,7 @@ pub const Lir = struct {
353353 // BRANCH
354354
355355 .beq => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b000 } } },
356 .bne => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b001 } } },
356357
357358
358359 // SYSTEM
......@@ -378,8 +379,8 @@ pub const Lir = struct {
378379
379380 .amoaddw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } },
380381 .amoswapw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } },
381 // LR.W
382 // SC.W
382 .lrw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00010 } } },
383 .scw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00011 } } },
383384 .amoxorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } },
384385 .amoandw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } },
385386 .amoorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } },
......@@ -388,10 +389,11 @@ pub const Lir = struct {
388389 .amominuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } },
389390 .amomaxuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } },
390391
392
391393 .amoaddd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } },
392394 .amoswapd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } },
393 // LR.D
394 // SC.D
395 .lrd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00010 } } },
396 .scd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00011 } } },
395397 .amoxord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } },
396398 .amoandd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } },
397399 .amoord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } },
......@@ -434,8 +436,6 @@ pub const Lir = struct {
434436 .pseudo_compare,
435437 .pseudo_not,
436438 .pseudo_extern_fn_reloc,
437 .pseudo_fence,
438 .pseudo_amo,
439439 .nop,
440440 => std.debug.panic("lir: didn't catch pseudo {s}", .{@tagName(mnem)}),
441441 // zig fmt: on
src/arch/riscv64/mnem.zig+5-2
......@@ -40,6 +40,7 @@ pub const Mnemonic = enum(u16) {
4040 jal,
4141
4242 beq,
43 bne,
4344
4445 // Memory
4546 lui,
......@@ -178,6 +179,8 @@ pub const Mnemonic = enum(u16) {
178179 fence,
179180 fencetso,
180181
182 lrw,
183 scw,
181184 amoswapw,
182185 amoaddw,
183186 amoandw,
......@@ -188,6 +191,8 @@ pub const Mnemonic = enum(u16) {
188191 amomaxuw,
189192 amominuw,
190193
194 lrd,
195 scd,
191196 amoswapd,
192197 amoaddd,
193198 amoandd,
......@@ -237,8 +242,6 @@ pub const Mnemonic = enum(u16) {
237242 pseudo_compare,
238243 pseudo_not,
239244 pseudo_extern_fn_reloc,
240 pseudo_fence,
241 pseudo_amo,
242245};
243246
244247pub const Pseudo = enum(u8) {