From b2150094badd3c14411a811ee0e508183b2142a2 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Sun, 17 Mar 2024 15:07:32 -0700 Subject: [PATCH] riscv: implement basic logical shifting --- src/arch/riscv64/CodeGen.zig | 122 +++++++++++++++++++++++++++++++++-- src/arch/riscv64/Emit.zig | 21 ++++-- src/arch/riscv64/Mir.zig | 8 ++- 3 files changed, 141 insertions(+), 10 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 2c2c0bf583..edd3c9bf57 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -928,6 +928,8 @@ fn binOpRegister( .sub => .sub, .cmp_eq => .cmp_eq, .cmp_gt => .cmp_gt, + .shl => .sllw, + .shr => .srlw, else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), }; @@ -947,6 +949,84 @@ fn binOpRegister( 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 }; +} + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -989,8 +1069,10 @@ fn binOp( assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(mod); if (int_info.bits <= 64) { - // TODO immediate operands - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + if (rhs == .immediate) { + return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } + return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1025,6 +1107,28 @@ fn binOp( else => unreachable, } }, + + // These instructions have unsymteric bit sizes. + .shr, + .shl, + => { + switch (lhs_ty.zigTypeTag(mod)) { + .Float => return self.fail("TODO binary operations on floats", .{}), + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(mod); + if (int_info.bits <= 64) { + if (rhs == .immediate) { + return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } + return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, else => unreachable, } } @@ -1163,7 +1267,13 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void { fn airShl(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + 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); + break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty); + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1426,7 +1536,11 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void { fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + if (true) + return self.fail("TODO: airByteSwap", .{}); + break :result undefined; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 7a14a39e43..e120e5ce23 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -98,7 +98,11 @@ pub fn emitMir( .sh => try emit.mirIType(inst), .sb => try emit.mirIType(inst), + .srlw => try emit.mirRType(inst), + .sllw => try emit.mirRType(inst), + .srli => try emit.mirIType(inst), + .slli => try emit.mirIType(inst), .ldr_ptr_stack => try emit.mirIType(inst), @@ -173,14 +177,20 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const r_type = emit.mir.instructions.items(.data)[inst].r_type; + const rd = r_type.rd; + const rs1 = r_type.rs1; + const rs2 = r_type.rs2; + switch (tag) { - .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)), - .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)), - .cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)), + .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_eq => { - try emit.writeInstruction(Instruction.xor(r_type.rd, r_type.rs1, r_type.rs2)); - try emit.writeInstruction(Instruction.sltiu(r_type.rd, r_type.rd, 1)); + try emit.writeInstruction(Instruction.xor(rd, rs1, rs2)); + 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)), else => unreachable, } } @@ -231,6 +241,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void { }, .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))), else => unreachable, } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 098e0c5655..7527f0b216 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -41,8 +41,14 @@ pub const Inst = struct { /// Absolute Value, uses i_type payload. abs, - /// Logical Right Shift, uses i_type payload + /// Immediate Logical Right Shift, uses i_type payload srli, + /// Immediate Logical Left Shift, uses i_type payload + slli, + /// Register Logical Left Shift, uses r_type payload + sllw, + /// Register Logical Right Shit, uses r_type payload + srlw, jal, /// Jumps. Uses `inst` payload.