Commit b2150094ba

David Rubin <daviru007@icloud.com>
2024-03-17 23:07:32
riscv: implement basic logical shifting
1 parent 664e3e1
Changed files (3)
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 });
 }
 
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,
     }
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.