Commit 65c5ef52e9

Koakuma <koachan@protonmail.com>
2022-06-21 18:19:09
stage2: sparc64: Implement airRem, airMod, and SPARCv9 s/udivx
1 parent 27adee3
Changed files (4)
src/arch/sparc64/bits.zig
@@ -1281,6 +1281,22 @@ pub const Instruction = union(enum) {
         };
     }
 
+    pub fn sdivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b10, 0b10_1101, rs1, rs2, rd),
+            i13 => format3b(0b10, 0b10_1101, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
+    pub fn udivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b10, 0b00_1101, rs1, rs2, rd),
+            i13 => format3b(0b10, 0b00_1101, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
     pub fn nop() Instruction {
         return sethi(0, .g0);
     }
src/arch/sparc64/CodeGen.zig
@@ -507,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .mul             => @panic("TODO try self.airMul(inst)"),
             .mulwrap         => @panic("TODO try self.airMulWrap(inst)"),
             .mul_sat         => @panic("TODO try self.airMulSat(inst)"),
-            .rem             => @panic("TODO try self.airRem(inst)"),
-            .mod             => @panic("TODO try self.airMod(inst)"),
+            .rem             => try self.airRem(inst),
+            .mod             => try self.airMod(inst),
             .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
             .shl_sat         => @panic("TODO try self.airShlSat(inst)"),
             .min             => @panic("TODO try self.airMin(inst)"),
@@ -1602,6 +1602,144 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
 }
 
+fn airMod(self: *Self, inst: Air.Inst.Index) !void {
+    const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+    const lhs = try self.resolveInst(bin_op.lhs);
+    const rhs = try self.resolveInst(bin_op.rhs);
+    const lhs_ty = self.air.typeOf(bin_op.lhs);
+    const rhs_ty = self.air.typeOf(bin_op.rhs);
+    assert(lhs_ty.eql(rhs_ty, self.bin_file.options.module.?));
+
+    if (self.liveness.isUnused(inst))
+        return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+
+    // TODO add safety check
+
+    // We use manual assembly emission to generate faster code
+    // First, ensure lhs, rhs, rem, and added are in registers
+
+    const lhs_is_register = lhs == .register;
+    const rhs_is_register = rhs == .register;
+
+    const lhs_reg = if (lhs_is_register)
+        lhs.register
+    else
+        try self.register_manager.allocReg(null, gp);
+
+    const lhs_lock = self.register_manager.lockReg(lhs_reg);
+    defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+    const rhs_reg = if (rhs_is_register)
+        rhs.register
+    else
+        try self.register_manager.allocReg(null, gp);
+    const rhs_lock = self.register_manager.lockReg(rhs_reg);
+    defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+    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 regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
+    const regs_locks = self.register_manager.lockRegsAssumeUnused(2, regs);
+    defer for (regs_locks) |reg| {
+        self.register_manager.unlockReg(reg);
+    };
+
+    const add_reg = regs[0];
+    const mod_reg = regs[1];
+
+    // mod_reg = @rem(lhs_reg, rhs_reg)
+    _ = try self.addInst(.{
+        .tag = .sdivx,
+        .data = .{
+            .arithmetic_3op = .{
+                .is_imm = false,
+                .rd = mod_reg,
+                .rs1 = lhs_reg,
+                .rs2_or_imm = .{ .rs2 = rhs_reg },
+            },
+        },
+    });
+
+    _ = try self.addInst(.{
+        .tag = .mulx,
+        .data = .{
+            .arithmetic_3op = .{
+                .is_imm = false,
+                .rd = mod_reg,
+                .rs1 = mod_reg,
+                .rs2_or_imm = .{ .rs2 = rhs_reg },
+            },
+        },
+    });
+
+    _ = try self.addInst(.{
+        .tag = .sub,
+        .data = .{
+            .arithmetic_3op = .{
+                .is_imm = false,
+                .rd = mod_reg,
+                .rs1 = lhs_reg,
+                .rs2_or_imm = .{ .rs2 = mod_reg },
+            },
+        },
+    });
+
+    // add_reg = mod_reg + rhs_reg
+    _ = try self.addInst(.{
+        .tag = .add,
+        .data = .{
+            .arithmetic_3op = .{
+                .is_imm = false,
+                .rd = add_reg,
+                .rs1 = mod_reg,
+                .rs2_or_imm = .{ .rs2 = rhs_reg },
+            },
+        },
+    });
+
+    // if (add_reg == rhs_reg) add_reg = 0
+    _ = try self.addInst(.{
+        .tag = .cmp,
+        .data = .{
+            .arithmetic_2op = .{
+                .is_imm = false,
+                .rs1 = add_reg,
+                .rs2_or_imm = .{ .rs2 = rhs_reg },
+            },
+        },
+    });
+
+    _ = try self.addInst(.{
+        .tag = .movcc,
+        .data = .{
+            .conditional_move_int = .{
+                .is_imm = true,
+                .ccr = .xcc,
+                .cond = .{ .icond = .eq },
+                .rd = add_reg,
+                .rs2_or_imm = .{ .imm = 0 },
+            },
+        },
+    });
+
+    // if (lhs_reg < 0) mod_reg = add_reg
+    _ = try self.addInst(.{
+        .tag = .movr,
+        .data = .{
+            .conditional_move_reg = .{
+                .is_imm = false,
+                .cond = .lt_zero,
+                .rd = mod_reg,
+                .rs1 = lhs_reg,
+                .rs2_or_imm = .{ .rs2 = add_reg },
+            },
+        },
+    });
+
+    return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
@@ -1704,6 +1842,27 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
 }
 
+fn airRem(self: *Self, inst: Air.Inst.Index) !void {
+    const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+    const lhs = try self.resolveInst(bin_op.lhs);
+    const rhs = try self.resolveInst(bin_op.rhs);
+    const lhs_ty = self.air.typeOf(bin_op.lhs);
+    const rhs_ty = self.air.typeOf(bin_op.rhs);
+
+    // TODO add safety check
+
+    // result = lhs - @divTrunc(lhs, rhs) * rhs
+    const result: MCValue = if (self.liveness.isUnused(inst)) blk: {
+        break :blk .dead;
+    } else blk: {
+        const tmp0 = try self.binOp(.div_trunc, lhs, rhs, lhs_ty, rhs_ty, null);
+        const tmp1 = try self.binOp(.mul, tmp0, rhs, lhs_ty, rhs_ty, null);
+        break :blk try self.binOp(.sub, lhs, tmp1, lhs_ty, rhs_ty, null);
+    };
+
+    return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
     const un_op = self.air.instructions.items(.data)[inst].un_op;
     const operand = try self.resolveInst(un_op);
@@ -2077,7 +2236,10 @@ fn binOp(
 ) InnerError!MCValue {
     const mod = self.bin_file.options.module.?;
     switch (tag) {
-        .add, .cmp_eq => {
+        .add,
+        .sub,
+        .cmp_eq,
+        => {
             switch (lhs_ty.zigTypeTag()) {
                 .Float => return self.fail("TODO binary operations on floats", .{}),
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
@@ -2103,6 +2265,7 @@ fn binOp(
 
                         const mir_tag: Mir.Inst.Tag = switch (tag) {
                             .add => .add,
+                            .sub => .sub,
                             .cmp_eq => .cmp,
                             else => unreachable,
                         };
@@ -2124,6 +2287,39 @@ fn binOp(
             }
         },
 
+        .div_trunc => {
+            switch (lhs_ty.zigTypeTag()) {
+                .Vector => return self.fail("TODO binary operations on vectors", .{}),
+                .Int => {
+                    assert(lhs_ty.eql(rhs_ty, mod));
+                    const int_info = lhs_ty.intInfo(self.target.*);
+                    if (int_info.bits <= 64) {
+                        const rhs_immediate_ok = switch (tag) {
+                            .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
+                            else => unreachable,
+                        };
+
+                        const mir_tag: Mir.Inst.Tag = switch (tag) {
+                            .div_trunc => switch (int_info.signedness) {
+                                .signed => Mir.Inst.Tag.sdivx,
+                                .unsigned => Mir.Inst.Tag.udivx,
+                            },
+                            else => unreachable,
+                        };
+
+                        if (rhs_immediate_ok) {
+                            return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata);
+                        } else {
+                            return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
+                        }
+                    } else {
+                        return self.fail("TODO binary operations on int with bits > 64", .{});
+                    }
+                },
+                else => unreachable,
+            }
+        },
+
         .mul => {
             switch (lhs_ty.zigTypeTag()) {
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
@@ -2382,6 +2578,9 @@ fn binOpImmediate(
         .xor,
         .xnor,
         .mulx,
+        .sdivx,
+        .udivx,
+        .sub,
         .subcc,
         => .{
             .arithmetic_3op = .{
@@ -2503,6 +2702,9 @@ fn binOpRegister(
         .xor,
         .xnor,
         .mulx,
+        .sdivx,
+        .udivx,
+        .sub,
         .subcc,
         => .{
             .arithmetic_3op = .{
src/arch/sparc64/Emit.zig
@@ -105,6 +105,8 @@ pub fn emitMir(
             .movr => @panic("TODO implement sparc64 movr"),
 
             .mulx => try emit.mirArithmetic3Op(inst),
+            .sdivx => try emit.mirArithmetic3Op(inst),
+            .udivx => try emit.mirArithmetic3Op(inst),
 
             .nop => try emit.mirNop(),
 
@@ -234,6 +236,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
             .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)),
             .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)),
             .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)),
+            .sdivx => try emit.writeInstruction(Instruction.sdivx(i13, rs1, imm, rd)),
+            .udivx => try emit.writeInstruction(Instruction.udivx(i13, rs1, imm, rd)),
             .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
             .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
             .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)),
@@ -259,6 +263,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
             .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)),
             .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)),
             .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)),
+            .sdivx => try emit.writeInstruction(Instruction.sdivx(Register, rs1, rs2, rd)),
+            .udivx => try emit.writeInstruction(Instruction.udivx(Register, rs1, rs2, rd)),
             .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
             .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
             .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)),
src/arch/sparc64/Mir.zig
@@ -92,8 +92,9 @@ pub const Inst = struct {
 
         /// A.37 Multiply and Divide (64-bit)
         /// This uses the arithmetic_3op field.
-        // TODO add other operations.
         mulx,
+        sdivx,
+        udivx,
 
         /// A.40 No Operation
         /// This uses the nop field.