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 {...@@ -736,6 +736,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
736 return MCValue{ .stack_offset = stack_offset };736 return MCValue{ .stack_offset = stack_offset };
737}737}
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
739pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {748pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
740 const stack_mcv = try self.allocRegOrMem(inst, false);749 const stack_mcv = try self.allocRegOrMem(inst, false);
741 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });750 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
...@@ -983,67 +992,36 @@ fn binOpRegister(...@@ -983,67 +992,36 @@ fn binOpRegister(
983 lhs_ty: Type,992 lhs_ty: Type,
984 rhs_ty: Type,993 rhs_ty: Type,
985) !MCValue {994) !MCValue {
986 const lhs_is_register = lhs == .register;995 _ = maybe_inst;
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);
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 };
1008 };1003 };
1009 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);1004 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
1010 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
10111005
1012 const rhs_reg = if (rhs_is_register) rhs.register else blk: {1006 const rhs_reg, const rhs_lock = blk: {
1013 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {1007 if (rhs == .register) break :blk .{ rhs.register, null };
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 });
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 };
1023 };1012 };
1024 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);1013 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1025 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
10261014
1027 const dest_reg = if (maybe_inst) |inst| blk: {1015 const dest_reg, const dest_lock = try self.allocReg();
1028 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1016 defer self.register_manager.unlockReg(dest_lock);
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);
10411017
1042 const mir_tag: Mir.Inst.Tag = switch (tag) {1018 const mir_tag: Mir.Inst.Tag = switch (tag) {
1043 .add => .add,1019 .add => .add,
1044 .sub => .sub,1020 .sub => .sub,
1045 .cmp_eq => .cmp_eq,1021 .cmp_eq => .cmp_eq,
1046 .cmp_gt => .cmp_gt,1022 .cmp_gt => .cmp_gt,
1023 .cmp_gte => .cmp_gte,
1024 .cmp_lt => .cmp_lt,
1047 .shl => .sllw,1025 .shl => .sllw,
1048 .shr => .srlw,1026 .shr => .srlw,
1049 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),1027 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
...@@ -1080,48 +1058,28 @@ fn binOpImm(...@@ -1080,48 +1058,28 @@ fn binOpImm(
1080 rhs_ty: Type,1058 rhs_ty: Type,
1081) !MCValue {1059) !MCValue {
1082 assert(rhs == .immediate);1060 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)1065 const lhs_reg, const lhs_lock = blk: {
1087 self.register_manager.lockReg(lhs.register)1066 if (lhs == .register) break :blk .{ lhs.register, null };
1088 else
1089 null;
1090 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
10911067
1092 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1068 const lhs_reg, const lhs_lock = try self.allocReg();
10931069 try self.genSetReg(lhs_ty, lhs_reg, lhs);
1094 const lhs_reg = if (lhs_is_register) lhs.register else blk: {1070 break :blk .{ lhs_reg, lhs_lock };
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;
1105 };1071 };
1106 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);1072 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
1107 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
11081073
1109 const dest_reg = if (maybe_inst) |inst| blk: {1074 const dest_reg, const dest_lock = try self.allocReg();
1110 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1075 defer self.register_manager.unlockReg(dest_lock);
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);
11201076
1121 const mir_tag: Mir.Inst.Tag = switch (tag) {1077 const mir_tag: Mir.Inst.Tag = switch (tag) {
1122 .shl => .slli,1078 .shl => .slli,
1123 .shr => .srli,1079 .shr => .srli,
1124 .cmp_gte => .cmp_imm_gte,1080 .cmp_gte => .cmp_imm_gte,
1081 .cmp_eq => .cmp_imm_eq,
1082 .add => .addi,
1125 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),1083 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),
1126 };1084 };
11271085
...@@ -1129,6 +1087,8 @@ fn binOpImm(...@@ -1129,6 +1087,8 @@ fn binOpImm(
1129 switch (mir_tag) {1087 switch (mir_tag) {
1130 .slli,1088 .slli,
1131 .srli,1089 .srli,
1090 .addi,
1091 .cmp_imm_eq,
1132 => {1092 => {
1133 _ = try self.addInst(.{1093 _ = try self.addInst(.{
1134 .tag = mir_tag,1094 .tag = mir_tag,
...@@ -1156,8 +1116,6 @@ fn binOpImm(...@@ -1156,8 +1116,6 @@ fn binOpImm(
1156 else => unreachable,1116 else => unreachable,
1157 }1117 }
11581118
1159 // generate the struct for overflow checks
1160
1161 return MCValue{ .register = dest_reg };1119 return MCValue{ .register = dest_reg };
1162}1120}
11631121
...@@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1216}1174}
12171175
1218fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1176fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1177 const mod = self.bin_file.comp.module.?;
1219 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;1178 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1220 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;1179 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 {...@@ -1225,7 +1184,28 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1225 const lhs_ty = self.typeOf(extra.lhs);1184 const lhs_ty = self.typeOf(extra.lhs);
1226 const rhs_ty = self.typeOf(extra.rhs);1185 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;
1229 };1209 };
12301210
1231 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });1211 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:...@@ -1749,6 +1729,15 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:
1749 .dead => unreachable,1729 .dead => unreachable,
1750 .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, value),1730 .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
1752 .register => |reg| {1741 .register => |reg| {
1753 const value_reg = try self.copyToTmpRegister(value_ty, value);1742 const value_reg = try self.copyToTmpRegister(value_ty, value);
17541743
...@@ -2165,7 +2154,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2165,7 +2154,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2165 const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg);2154 const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg);
2166 defer self.register_manager.unlockReg(cond_reg_lock);2155 defer self.register_manager.unlockReg(cond_reg_lock);
21672156
2168 // A branch to the false section. Uses bne2157 // A branch to the false section. Uses beq. 1 is the default "true" state.
2169 const reloc = try self.condBr(cond_ty, cond, cond_reg);2158 const reloc = try self.condBr(cond_ty, cond, cond_reg);
21702159
2171 // If the condition dies here in this condbr instruction, process2160 // 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...@@ -2301,7 +2290,7 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !M
2301 try self.genSetReg(cond_ty, cond_reg, condition);2290 try self.genSetReg(cond_ty, cond_reg, condition);
23022291
2303 return try self.addInst(.{2292 return try self.addInst(.{
2304 .tag = .bne,2293 .tag = .beq,
2305 .data = .{2294 .data = .{
2306 .b_type = .{2295 .b_type = .{
2307 .rs1 = cond_reg,2296 .rs1 = cond_reg,
...@@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2729 => {2718 => {
2730 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with2719 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with
2731 // a register allocation.2720 // a register allocation.
2732 const reg = try self.register_manager.allocReg(null, gp);2721 const reg, const reg_lock = try self.allocReg();
2733 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
2734 defer self.register_manager.unlockReg(reg_lock);2722 defer self.register_manager.unlockReg(reg_lock);
27352723
2736 try self.genSetReg(ty, reg, src_val);2724 try self.genSetReg(ty, reg, src_val);
...@@ -2939,8 +2927,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -2939,8 +2927,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
2939 // TODO: use a more advanced myriad seq to do this without a reg.2927 // TODO: use a more advanced myriad seq to do this without a reg.
2940 // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L2242928 // 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);2930 const temp, const temp_lock = try self.allocReg();
2943 const temp_lock = self.register_manager.lockRegAssumeUnused(temp);
2944 defer self.register_manager.unlockReg(temp_lock);2931 defer self.register_manager.unlockReg(temp_lock);
29452932
2946 const lo32: i32 = @truncate(x);2933 const lo32: i32 = @truncate(x);
src/arch/riscv64/Emit.zig+31-10
...@@ -59,7 +59,10 @@ pub fn emitMir(...@@ -59,7 +59,10 @@ pub fn emitMir(
5959
60 .cmp_eq => try emit.mirRType(inst),60 .cmp_eq => try emit.mirRType(inst),
61 .cmp_gt => try emit.mirRType(inst),61 .cmp_gt => try emit.mirRType(inst),
62 .cmp_gte => try emit.mirRType(inst),
63 .cmp_lt => try emit.mirRType(inst),
62 .cmp_imm_gte => try emit.mirRType(inst),64 .cmp_imm_gte => try emit.mirRType(inst),
65 .cmp_imm_eq => try emit.mirIType(inst),
6366
64 .beq => try emit.mirBType(inst),67 .beq => try emit.mirBType(inst),
65 .bne => try emit.mirBType(inst),68 .bne => try emit.mirBType(inst),
...@@ -188,7 +191,12 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -188,7 +191,12 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
188 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),191 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
189 .cmp_gt => {192 .cmp_gt => {
190 // rs1 > rs2193 // 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));
192 },200 },
193 .cmp_eq => {201 .cmp_eq => {
194 // rs1 == rs2202 // rs1 == rs2
...@@ -198,14 +206,19 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -198,14 +206,19 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
198 // if rd == 0, set rd to 1206 // if rd == 0, set rd to 1
199 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));207 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
200 },208 },
209 .cmp_lt => {
210 // rd = 1 if rs1 < rs2
211 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));
212 },
201 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),213 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
202 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),214 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
203 .@"or" => try emit.writeInstruction(Instruction.@"or"(rd, rs1, rs2)),215 .@"or" => try emit.writeInstruction(Instruction.@"or"(rd, rs1, rs2)),
204 .cmp_imm_gte => {216 .cmp_imm_gte => {
205 // rd = rs1 >= imm12217 // rd = 1 if rs1 >= imm12
206 // see the docstring for cmp_imm_gte to see why we use r_type here218 // see the docstring of cmp_imm_gte to see why we use r_type here
207 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));219
208 try emit.writeInstruction(Instruction.xori(rd, rd, 1));220 // (rs1 >= imm12) == !(imm12 > rs1)
221 try emit.writeInstruction(Instruction.sltu(rd, rs1, rs2));
209 },222 },
210 else => unreachable,223 else => unreachable,
211 }224 }
...@@ -263,6 +276,10 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -263,6 +276,10 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
263 .srli => try emit.writeInstruction(Instruction.srli(rd, rs1, @intCast(imm12))),276 .srli => try emit.writeInstruction(Instruction.srli(rd, rs1, @intCast(imm12))),
264 .slli => try emit.writeInstruction(Instruction.slli(rd, rs1, @intCast(imm12))),277 .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 },
266 else => unreachable,283 else => unreachable,
267 }284 }
268}285}
...@@ -490,13 +507,17 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {...@@ -490,13 +507,17 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
490 .dbg_prologue_end,507 .dbg_prologue_end,
491 => 0,508 => 0,
492509
493 .psuedo_epilogue => 12,510 .psuedo_prologue,
494 .psuedo_prologue => 16,511 => 16,
495512
496 .abs => 12,513 .psuedo_epilogue,
514 .abs,
515 => 12,
497516
498 .cmp_eq => 8,517 .cmp_eq,
499 .cmp_imm_gte => 8,518 .cmp_imm_eq,
519 .cmp_gte,
520 => 8,
500521
501 else => 4,522 else => 4,
502 };523 };
src/arch/riscv64/Mir.zig+7
...@@ -62,6 +62,10 @@ pub const Inst = struct {...@@ -62,6 +62,10 @@ pub const Inst = struct {
62 cmp_eq,62 cmp_eq,
63 /// Register `>`, uses r_type63 /// Register `>`, uses r_type
64 cmp_gt,64 cmp_gt,
65 /// Register `<`, uses r_type
66 cmp_lt,
67 /// Register `>=`, uses r_type
68 cmp_gte,
6569
66 /// Immediate `>=`, uses r_type70 /// Immediate `>=`, uses r_type
67 ///71 ///
...@@ -72,6 +76,9 @@ pub const Inst = struct {...@@ -72,6 +76,9 @@ pub const Inst = struct {
72 /// allocate a register for temporary use.76 /// allocate a register for temporary use.
73 cmp_imm_gte,77 cmp_imm_gte,
7478
79 /// Immediate `==`, uses i_type
80 cmp_imm_eq,
81
75 /// Branch if equal, Uses b_type82 /// Branch if equal, Uses b_type
76 beq,83 beq,
77 /// Branch if not equal, Uses b_type84 /// Branch if not equal, Uses b_type