authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-24 16:58:42-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log09b7aabe094c11d7e4772c2e0c67ec7c28672266
treec17f8c036558cd0228866d9bd071ffbdd55065c4
parent08452b1adde34f5ef20738970141f643709b6eb9

riscv: add `allocReg` helper, and clean up some comparing logic

- Added the basic framework for panicing with an overflow in `airAddWithOverflow`, but there is no check done yet. - added the `cmp_lt`, `cmp_gte`, and `cmp_imm_eq` MIR instructions, and their respective functionality.

3 files changed, 114 insertions(+), 99 deletions(-)

src/arch/riscv64/CodeGen.zig+76-89
......@@ -736,6 +736,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
736736 return MCValue{ .stack_offset = stack_offset };
737737}
738738
739/// Allocates a register from the general purpose set and returns the Register and the Lock.
740///
741/// Up to the user to unlock the register later.
742fn allocReg(self: *Self) !struct { Register, RegisterLock } {
743 const reg = try self.register_manager.allocReg(null, gp);
744 const lock = self.register_manager.lockRegAssumeUnused(reg);
745 return .{ reg, lock };
746}
747
739748pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
740749 const stack_mcv = try self.allocRegOrMem(inst, false);
741750 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
......@@ -983,67 +992,36 @@ fn binOpRegister(
983992 lhs_ty: Type,
984993 rhs_ty: Type,
985994) !MCValue {
986 const lhs_is_register = lhs == .register;
987 const rhs_is_register = rhs == .register;
988
989 const lhs_lock: ?RegisterLock = if (lhs_is_register)
990 self.register_manager.lockReg(lhs.register)
991 else
992 null;
993 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
994
995 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
996
997 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
998 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
999 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1000 break :inst bin_op.lhs.toIndex().?;
1001 } else null;
1002
1003 const reg = try self.register_manager.allocReg(track_inst, gp);
995 _ = maybe_inst;
1004996
1005 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
997 const lhs_reg, const lhs_lock = blk: {
998 if (lhs == .register) break :blk .{ lhs.register, null };
1006999
1007 break :blk reg;
1000 const lhs_reg, const lhs_lock = try self.allocReg();
1001 try self.genSetReg(lhs_ty, lhs_reg, lhs);
1002 break :blk .{ lhs_reg, lhs_lock };
10081003 };
1009 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1010 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
1004 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
10111005
1012 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1013 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1014 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1015 break :inst bin_op.rhs.toIndex().?;
1016 } else null;
1017
1018 const reg = try self.register_manager.allocReg(track_inst, gp);
1019
1020 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1006 const rhs_reg, const rhs_lock = blk: {
1007 if (rhs == .register) break :blk .{ rhs.register, null };
10211008
1022 break :blk reg;
1009 const rhs_reg, const rhs_lock = try self.allocReg();
1010 try self.genSetReg(rhs_ty, rhs_reg, rhs);
1011 break :blk .{ rhs_reg, rhs_lock };
10231012 };
1024 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
1025 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
1013 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
10261014
1027 const dest_reg = if (maybe_inst) |inst| blk: {
1028 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1029
1030 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1031 break :blk lhs_reg;
1032 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1033 break :blk rhs_reg;
1034 } else {
1035 break :blk try self.register_manager.allocReg(inst, gp);
1036 }
1037 } else try self.register_manager.allocReg(null, gp);
1038
1039 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1040 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1015 const dest_reg, const dest_lock = try self.allocReg();
1016 defer self.register_manager.unlockReg(dest_lock);
10411017
10421018 const mir_tag: Mir.Inst.Tag = switch (tag) {
10431019 .add => .add,
10441020 .sub => .sub,
10451021 .cmp_eq => .cmp_eq,
10461022 .cmp_gt => .cmp_gt,
1023 .cmp_gte => .cmp_gte,
1024 .cmp_lt => .cmp_lt,
10471025 .shl => .sllw,
10481026 .shr => .srlw,
10491027 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
......@@ -1080,48 +1058,28 @@ fn binOpImm(
10801058 rhs_ty: Type,
10811059) !MCValue {
10821060 assert(rhs == .immediate);
1061 _ = maybe_inst;
10831062
1084 const lhs_is_register = lhs == .register;
1063 // TODO: use `maybe_inst` to track instead of forcing a lock.
10851064
1086 const lhs_lock: ?RegisterLock = if (lhs_is_register)
1087 self.register_manager.lockReg(lhs.register)
1088 else
1089 null;
1090 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1065 const lhs_reg, const lhs_lock = blk: {
1066 if (lhs == .register) break :blk .{ lhs.register, null };
10911067
1092 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1093
1094 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1095 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1096 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1097 break :inst bin_op.lhs.toIndex().?;
1098 } else null;
1099
1100 const reg = try self.register_manager.allocReg(track_inst, gp);
1101
1102 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1103
1104 break :blk reg;
1068 const lhs_reg, const lhs_lock = try self.allocReg();
1069 try self.genSetReg(lhs_ty, lhs_reg, lhs);
1070 break :blk .{ lhs_reg, lhs_lock };
11051071 };
1106 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1107 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
1072 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
11081073
1109 const dest_reg = if (maybe_inst) |inst| blk: {
1110 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1111
1112 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1113 break :blk lhs_reg;
1114 } else {
1115 break :blk try self.register_manager.allocReg(inst, gp);
1116 }
1117 } else try self.register_manager.allocReg(null, gp);
1118
1119 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1074 const dest_reg, const dest_lock = try self.allocReg();
1075 defer self.register_manager.unlockReg(dest_lock);
11201076
11211077 const mir_tag: Mir.Inst.Tag = switch (tag) {
11221078 .shl => .slli,
11231079 .shr => .srli,
11241080 .cmp_gte => .cmp_imm_gte,
1081 .cmp_eq => .cmp_imm_eq,
1082 .add => .addi,
11251083 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),
11261084 };
11271085
......@@ -1129,6 +1087,8 @@ fn binOpImm(
11291087 switch (mir_tag) {
11301088 .slli,
11311089 .srli,
1090 .addi,
1091 .cmp_imm_eq,
11321092 => {
11331093 _ = try self.addInst(.{
11341094 .tag = mir_tag,
......@@ -1156,8 +1116,6 @@ fn binOpImm(
11561116 else => unreachable,
11571117 }
11581118
1159 // generate the struct for overflow checks
1160
11611119 return MCValue{ .register = dest_reg };
11621120}
11631121
......@@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
12161174}
12171175
12181176fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1177 const mod = self.bin_file.comp.module.?;
12191178 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
12201179 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
12211180
......@@ -1225,7 +1184,28 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
12251184 const lhs_ty = self.typeOf(extra.lhs);
12261185 const rhs_ty = self.typeOf(extra.rhs);
12271186
1228 break :result try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty);
1187 const partial_mcv = try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty);
1188
1189 const tuple_ty = self.typeOfIndex(inst);
1190
1191 // TODO: optimization, set this to true. needs the other struct access stuff to support
1192 // accessing registers.
1193 const result_mcv = try self.allocRegOrMem(inst, false);
1194 const offset = result_mcv.stack_offset;
1195
1196 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;
1197 const result_offset = tuple_ty.structFieldOffset(0, mod) + offset;
1198
1199 const overflow_mcv = try self.binOp(.cmp_lt, null, partial_mcv, lhs, lhs_ty, lhs_ty);
1200
1201 const overflow_reg, const overflow_lock = try self.allocReg();
1202 defer self.register_manager.unlockReg(overflow_lock);
1203
1204 try self.genSetReg(lhs_ty, overflow_reg, overflow_mcv);
1205
1206 try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv);
1207 try self.genSetStack(lhs_ty, @intCast(result_offset), partial_mcv);
1208 break :result result_mcv;
12291209 };
12301210
12311211 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -1749,6 +1729,15 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:
17491729 .dead => unreachable,
17501730 .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, value),
17511731
1732 .stack_offset => {
1733 const pointer_reg, const lock = try self.allocReg();
1734 defer self.register_manager.unlockReg(lock);
1735
1736 try self.genSetReg(ptr_ty, pointer_reg, pointer);
1737
1738 return self.store(.{ .register = pointer_reg }, value, ptr_ty, value_ty);
1739 },
1740
17521741 .register => |reg| {
17531742 const value_reg = try self.copyToTmpRegister(value_ty, value);
17541743
......@@ -2165,7 +2154,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
21652154 const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg);
21662155 defer self.register_manager.unlockReg(cond_reg_lock);
21672156
2168 // A branch to the false section. Uses bne
2157 // A branch to the false section. Uses beq. 1 is the default "true" state.
21692158 const reloc = try self.condBr(cond_ty, cond, cond_reg);
21702159
21712160 // If the condition dies here in this condbr instruction, process
......@@ -2301,7 +2290,7 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !M
23012290 try self.genSetReg(cond_ty, cond_reg, condition);
23022291
23032292 return try self.addInst(.{
2304 .tag = .bne,
2293 .tag = .beq,
23052294 .data = .{
23062295 .b_type = .{
23072296 .rs1 = cond_reg,
......@@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
27292718 => {
27302719 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with
27312720 // a register allocation.
2732 const reg = try self.register_manager.allocReg(null, gp);
2733 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
2721 const reg, const reg_lock = try self.allocReg();
27342722 defer self.register_manager.unlockReg(reg_lock);
27352723
27362724 try self.genSetReg(ty, reg, src_val);
......@@ -2939,8 +2927,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
29392927 // TODO: use a more advanced myriad seq to do this without a reg.
29402928 // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224
29412929
2942 const temp = try self.register_manager.allocReg(null, gp);
2943 const temp_lock = self.register_manager.lockRegAssumeUnused(temp);
2930 const temp, const temp_lock = try self.allocReg();
29442931 defer self.register_manager.unlockReg(temp_lock);
29452932
29462933 const lo32: i32 = @truncate(x);
src/arch/riscv64/Emit.zig+31-10
......@@ -59,7 +59,10 @@ pub fn emitMir(
5959
6060 .cmp_eq => try emit.mirRType(inst),
6161 .cmp_gt => try emit.mirRType(inst),
62 .cmp_gte => try emit.mirRType(inst),
63 .cmp_lt => try emit.mirRType(inst),
6264 .cmp_imm_gte => try emit.mirRType(inst),
65 .cmp_imm_eq => try emit.mirIType(inst),
6366
6467 .beq => try emit.mirBType(inst),
6568 .bne => try emit.mirBType(inst),
......@@ -188,7 +191,12 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
188191 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
189192 .cmp_gt => {
190193 // rs1 > rs2
191 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));
194 try emit.writeInstruction(Instruction.sltu(rd, rs2, rs1));
195 },
196 .cmp_gte => {
197 // rs1 >= rs2
198 try emit.writeInstruction(Instruction.sltu(rd, rs1, rs2));
199 try emit.writeInstruction(Instruction.xori(rd, rd, 1));
192200 },
193201 .cmp_eq => {
194202 // rs1 == rs2
......@@ -198,14 +206,19 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
198206 // if rd == 0, set rd to 1
199207 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
200208 },
209 .cmp_lt => {
210 // rd = 1 if rs1 < rs2
211 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));
212 },
201213 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
202214 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
203215 .@"or" => try emit.writeInstruction(Instruction.@"or"(rd, rs1, rs2)),
204216 .cmp_imm_gte => {
205 // rd = rs1 >= imm12
206 // see the docstring for cmp_imm_gte to see why we use r_type here
207 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));
208 try emit.writeInstruction(Instruction.xori(rd, rd, 1));
217 // rd = 1 if rs1 >= imm12
218 // see the docstring of cmp_imm_gte to see why we use r_type here
219
220 // (rs1 >= imm12) == !(imm12 > rs1)
221 try emit.writeInstruction(Instruction.sltu(rd, rs1, rs2));
209222 },
210223 else => unreachable,
211224 }
......@@ -263,6 +276,10 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
263276 .srli => try emit.writeInstruction(Instruction.srli(rd, rs1, @intCast(imm12))),
264277 .slli => try emit.writeInstruction(Instruction.slli(rd, rs1, @intCast(imm12))),
265278
279 .cmp_imm_eq => {
280 try emit.writeInstruction(Instruction.xori(rd, rs1, imm12));
281 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
282 },
266283 else => unreachable,
267284 }
268285}
......@@ -490,13 +507,17 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
490507 .dbg_prologue_end,
491508 => 0,
492509
493 .psuedo_epilogue => 12,
494 .psuedo_prologue => 16,
510 .psuedo_prologue,
511 => 16,
495512
496 .abs => 12,
513 .psuedo_epilogue,
514 .abs,
515 => 12,
497516
498 .cmp_eq => 8,
499 .cmp_imm_gte => 8,
517 .cmp_eq,
518 .cmp_imm_eq,
519 .cmp_gte,
520 => 8,
500521
501522 else => 4,
502523 };
src/arch/riscv64/Mir.zig+7
......@@ -62,6 +62,10 @@ pub const Inst = struct {
6262 cmp_eq,
6363 /// Register `>`, uses r_type
6464 cmp_gt,
65 /// Register `<`, uses r_type
66 cmp_lt,
67 /// Register `>=`, uses r_type
68 cmp_gte,
6569
6670 /// Immediate `>=`, uses r_type
6771 ///
......@@ -72,6 +76,9 @@ pub const Inst = struct {
7276 /// allocate a register for temporary use.
7377 cmp_imm_gte,
7478
79 /// Immediate `==`, uses i_type
80 cmp_imm_eq,
81
7582 /// Branch if equal, Uses b_type
7683 beq,
7784 /// Branch if not equal, Uses b_type