From 5e010b6deac7ad34f0cd06d507fc468fd98f9abc Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 22 Mar 2024 20:14:10 -0700 Subject: [PATCH] riscv: reorganize `binOp` and implement `cmp_imm_gte` MIR this was an annoying one to do, as there is no (to my knowledge) myriad sequence that will allow us to do `gte` compares with an immediate without allocating a register. RISC-V provides a single instruction to do compares, that being `lt`, and so you need to use more than one for other variants, but in this case, i believe you need to allocate a register. --- src/arch/riscv64/CodeGen.zig | 414 ++++++++++++++++++----------------- src/arch/riscv64/Emit.zig | 59 +++-- src/arch/riscv64/Mir.zig | 15 +- 3 files changed, 268 insertions(+), 220 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 4529ab6eec0a5d1403d4fa8f584700c411716fa6..c9a71ee79e236c10b20b4dbcac88a504dca752c0 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -850,181 +850,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -/// Don't call this function directly. Use binOp instead. -/// -/// Calling this function signals an intention to generate a Mir -/// instruction of the form -/// -/// op dest, lhs, rhs -/// -/// Asserts that generating an instruction of that form is possible. -fn binOpRegister( - self: *Self, - tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, - lhs: MCValue, - rhs: MCValue, - lhs_ty: Type, - rhs_ty: Type, -) !MCValue { - const lhs_is_register = lhs == .register; - const rhs_is_register = rhs == .register; - - const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.lockReg(lhs.register) - else - null; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - - const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - break :inst bin_op.lhs.toIndex().?; - } else null; - - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; - }; - const new_lhs_lock = self.register_manager.lockReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); - - const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - break :inst bin_op.rhs.toIndex().?; - } else null; - - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; - }; - const new_rhs_lock = self.register_manager.lockReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); - - const dest_reg = if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { - break :blk rhs_reg; - } else { - break :blk try self.register_manager.allocReg(inst, gp); - } - } else try self.register_manager.allocReg(null, gp); - - if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); - if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add, - .sub => .sub, - .cmp_eq => .cmp_eq, - .cmp_gt => .cmp_gt, - .shl => .sllw, - .shr => .srlw, - else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = .{ - .r_type = .{ - .rd = dest_reg, - .rs1 = lhs_reg, - .rs2 = rhs_reg, - }, - }, - }); - - // generate the struct for OF checks - - return MCValue{ .register = dest_reg }; -} - -/// Don't call this function directly. Use binOp instead. -/// -/// Call this function if rhs is an immediate. Generates I version of binops. -/// -/// Asserts that rhs is an immediate MCValue -fn binOpImm( - self: *Self, - tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, - lhs: MCValue, - rhs: MCValue, - lhs_ty: Type, - rhs_ty: Type, -) !MCValue { - _ = rhs_ty; - assert(rhs == .immediate); - - const lhs_is_register = lhs == .register; - - const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.lockReg(lhs.register) - else - null; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - - const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - break :inst bin_op.lhs.toIndex().?; - } else null; - - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; - }; - const new_lhs_lock = self.register_manager.lockReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); - - const dest_reg = if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - break :blk lhs_reg; - } else { - break :blk try self.register_manager.allocReg(inst, gp); - } - } else try self.register_manager.allocReg(null, gp); - - if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .shl => .slli, - .shr => .srli, - else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = .{ - .i_type = .{ - .rd = dest_reg, - .rs1 = lhs_reg, - .imm12 = math.cast(i12, rhs.immediate) orelse { - return self.fail("TODO: binOpImm larger than i12 i_type payload", .{}); - }, - }, - }, - }); - - // generate the struct for OF checks - - return MCValue{ .register = dest_reg }; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.typeOf(bin_op.lhs); + const rhs_ty = self.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } /// For all your binary operation needs, this function will generate @@ -1108,7 +942,7 @@ fn binOp( } }, - // These instructions have unsymteric bit sizes. + // These instructions have unsymteric bit sizes on RHS and LHS. .shr, .shl, => { @@ -1132,16 +966,199 @@ fn binOp( else => unreachable, } } +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, rhs +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpRegister( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; -fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.lockReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + break :inst bin_op.lhs.toIndex().?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst, gp); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + break :inst bin_op.rhs.toIndex().?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst, gp); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst, gp); + } + } else try self.register_manager.allocReg(null, gp); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add, + .sub => .sub, + .cmp_eq => .cmp_eq, + .cmp_gt => .cmp_gt, + .shl => .sllw, + .shr => .srlw, + else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = .{ + .r_type = .{ + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + }, + }, + }); + + // generate the struct for OF checks + + return MCValue{ .register = dest_reg }; +} + +/// Don't call this function directly. Use binOp instead. +/// +/// Call this function if rhs is an immediate. Generates I version of binops. +/// +/// Asserts that rhs is an immediate MCValue +fn binOpImm( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + assert(rhs == .immediate); + + const lhs_is_register = lhs == .register; + + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.lockReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + break :inst bin_op.lhs.toIndex().?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst, gp); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst, gp); + } + } else try self.register_manager.allocReg(null, gp); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .shl => .slli, + .shr => .srli, + .cmp_gte => .cmp_imm_gte, + else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), + }; + + // apply some special operations needed + switch (mir_tag) { + .slli, + .srli, + => { + _ = try self.addInst(.{ + .tag = mir_tag, + .data = .{ .i_type = .{ + .rd = dest_reg, + .rs1 = lhs_reg, + .imm12 = math.cast(i12, rhs.immediate) orelse { + return self.fail("TODO: binOpImm larger than i12 i_type payload", .{}); + }, + } }, + }); + }, + .cmp_imm_gte => { + const imm_reg = try self.copyToTmpRegister(rhs_ty, .{ .immediate = rhs.immediate - 1 }); + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = .{ .r_type = .{ + .rd = dest_reg, + .rs1 = imm_reg, + .rs2 = lhs_reg, + } }, + }); + }, + else => unreachable, + } + + // generate the struct for overflow checks + + return MCValue{ .register = dest_reg }; } fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { @@ -2101,8 +2118,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); const liveness_condbr = self.liveness.getCondBr(inst); - // A branch to the false section. Uses beq - const reloc = try self.condBr(cond_ty, cond); + const cond_reg = try self.register_manager.allocReg(inst, gp); + const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg); + defer self.register_manager.unlockReg(cond_reg_lock); + + // A branch to the false section. Uses bne + const reloc = try self.condBr(cond_ty, cond, cond_reg); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -2233,19 +2254,14 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } } -fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { - _ = cond_ty; - - const reg = switch (condition) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, condition), - }; +fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !Mir.Inst.Index { + try self.genSetReg(cond_ty, cond_reg, condition); return try self.addInst(.{ .tag = .bne, .data = .{ .b_type = .{ - .rs1 = reg, + .rs1 = cond_reg, .rs2 = .zero, .inst = undefined, }, @@ -2739,6 +2755,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); }; + // setup the src pointer _ = try self.addInst(.{ .tag = .load_symbol, .data = .{ @@ -2789,7 +2806,7 @@ fn genInlineMemcpy( // compare count to length const compare_inst = try self.addInst(.{ - .tag = .cmp_gt, + .tag = .cmp_eq, .data = .{ .r_type = .{ .rd = tmp, .rs1 = count, @@ -2861,9 +2878,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! } }, }); } else { + // TODO: use a more advanced myriad seq to do this without a reg. + // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224 + const temp = try self.register_manager.allocReg(null, gp); - const maybe_temp_lock = self.register_manager.lockReg(temp); - defer if (maybe_temp_lock) |temp_lock| self.register_manager.unlockReg(temp_lock); + const temp_lock = self.register_manager.lockRegAssumeUnused(temp); + defer self.register_manager.unlockReg(temp_lock); const lo32: i32 = @truncate(x); const carry: i32 = if (lo32 < 0) 1 else 0; diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 55e1ff07e2dc372861d63f62a1e6e703f23c6424..6c98fca7b9d65f909d83fd64a0561a9b4f2e5961 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -59,6 +59,7 @@ pub fn emitMir( .cmp_eq => try emit.mirRType(inst), .cmp_gt => try emit.mirRType(inst), + .cmp_imm_gte => try emit.mirRType(inst), .beq => try emit.mirBType(inst), .bne => try emit.mirBType(inst), @@ -185,14 +186,27 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .add => try emit.writeInstruction(Instruction.add(rd, rs1, rs2)), .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)), - .cmp_gt => try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)), + .cmp_gt => { + // rs1 > rs2 + try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)); + }, .cmp_eq => { + // rs1 == rs2 + + // if equal, write 0 to rd try emit.writeInstruction(Instruction.xor(rd, rs1, rs2)); + // if rd == 0, set rd to 1 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1)); }, .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)), .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)), .@"or" => try emit.writeInstruction(Instruction.@"or"(rd, rs1, rs2)), + .cmp_imm_gte => { + // rd = rs1 >= imm12 + // see the docstring for cmp_imm_gte to see why we use r_type here + try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)); + try emit.writeInstruction(Instruction.xori(rd, rd, 1)); + }, else => unreachable, } } @@ -220,30 +234,34 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const i_type = emit.mir.instructions.items(.data)[inst].i_type; + const rd = i_type.rd; + const rs1 = i_type.rs1; + const imm12 = i_type.imm12; + switch (tag) { - .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)), - .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)), + .addi => try emit.writeInstruction(Instruction.addi(rd, rs1, imm12)), + .jalr => try emit.writeInstruction(Instruction.jalr(rd, imm12, rs1)), - .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)), - .lw => try emit.writeInstruction(Instruction.lw(i_type.rd, i_type.imm12, i_type.rs1)), - .lh => try emit.writeInstruction(Instruction.lh(i_type.rd, i_type.imm12, i_type.rs1)), - .lb => try emit.writeInstruction(Instruction.lb(i_type.rd, i_type.imm12, i_type.rs1)), + .ld => try emit.writeInstruction(Instruction.ld(rd, imm12, rs1)), + .lw => try emit.writeInstruction(Instruction.lw(rd, imm12, rs1)), + .lh => try emit.writeInstruction(Instruction.lh(rd, imm12, rs1)), + .lb => try emit.writeInstruction(Instruction.lb(rd, imm12, rs1)), - .sd => try emit.writeInstruction(Instruction.sd(i_type.rd, i_type.imm12, i_type.rs1)), - .sw => try emit.writeInstruction(Instruction.sw(i_type.rd, i_type.imm12, i_type.rs1)), - .sh => try emit.writeInstruction(Instruction.sh(i_type.rd, i_type.imm12, i_type.rs1)), - .sb => try emit.writeInstruction(Instruction.sb(i_type.rd, i_type.imm12, i_type.rs1)), + .sd => try emit.writeInstruction(Instruction.sd(rd, imm12, rs1)), + .sw => try emit.writeInstruction(Instruction.sw(rd, imm12, rs1)), + .sh => try emit.writeInstruction(Instruction.sh(rd, imm12, rs1)), + .sb => try emit.writeInstruction(Instruction.sb(rd, imm12, rs1)), - .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(i_type.rd, i_type.rs1, .sp)), + .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(rd, rs1, .sp)), .abs => { - try emit.writeInstruction(Instruction.sraiw(i_type.rd, i_type.rs1, @intCast(i_type.imm12))); - try emit.writeInstruction(Instruction.xor(i_type.rs1, i_type.rs1, i_type.rd)); - try emit.writeInstruction(Instruction.subw(i_type.rs1, i_type.rs1, i_type.rd)); + try emit.writeInstruction(Instruction.sraiw(rd, rs1, @intCast(imm12))); + try emit.writeInstruction(Instruction.xor(rs1, rs1, rd)); + try emit.writeInstruction(Instruction.subw(rs1, rs1, rd)); }, - .srli => try emit.writeInstruction(Instruction.srli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))), - .slli => try emit.writeInstruction(Instruction.slli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))), + .srli => try emit.writeInstruction(Instruction.srli(rd, rs1, @intCast(imm12))), + .slli => try emit.writeInstruction(Instruction.slli(rd, rs1, @intCast(imm12))), else => unreachable, } @@ -471,12 +489,13 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { .dbg_prologue_end, => 0, - .psuedo_epilogue => 12, // 3 * 4 - .psuedo_prologue => 16, // 4 * 4 + .psuedo_epilogue => 12, + .psuedo_prologue => 16, - .abs => 12, // 3 * 4 + .abs => 12, .cmp_eq => 8, + .cmp_imm_gte => 8, else => 4, }; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 8a0ad039df01a274bd5cfc4119adde8b2c4e208e..68b314bd1265590ccdd858d3da053dd21c3b8791 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -57,12 +57,21 @@ pub const Inst = struct { /// Jumps. Uses `inst` payload. j, - // TODO: Maybe create a special data for compares that includes the ops - /// Compare equal, uses r_type + // NOTE: Maybe create a special data for compares that includes the ops + /// Register `==`, uses r_type cmp_eq, - /// Compare greater than, uses r_type + /// Register `>`, uses r_type cmp_gt, + /// Immediate `>=`, uses r_type + /// + /// Note: this uses r_type because RISC-V does not provide a good way + /// to do `>=` comparisons on immediates. Usually we would just subtract + /// 1 from the immediate and do a `>` comparison, however there is no `>` + /// register to immedate comparison in RISC-V. This leads us to need to + /// allocate a register for temporary use. + cmp_imm_gte, + /// Branch if equal Uses b_type beq, /// Branch if not eql Uses b_type -- 2.54.0