Commit f95a8ddafa

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-04-16 18:58:48
stage2 AArch64: Implement basic truncate functionality
1 parent c78daeb
Changed files (5)
src
test
behavior
src/arch/aarch64/bits.zig
@@ -510,33 +510,23 @@ pub const Instruction = union(enum) {
         imm16: u16,
         shift: u6,
     ) Instruction {
-        switch (rd.size()) {
-            32 => {
-                assert(shift % 16 == 0 and shift <= 16);
-                return Instruction{
-                    .move_wide_immediate = .{
-                        .rd = rd.enc(),
-                        .imm16 = imm16,
-                        .hw = @intCast(u2, shift / 16),
-                        .opc = opc,
-                        .sf = 0,
-                    },
-                };
-            },
-            64 => {
-                assert(shift % 16 == 0 and shift <= 48);
-                return Instruction{
-                    .move_wide_immediate = .{
-                        .rd = rd.enc(),
-                        .imm16 = imm16,
-                        .hw = @intCast(u2, shift / 16),
-                        .opc = opc,
-                        .sf = 1,
-                    },
-                };
+        assert(shift % 16 == 0);
+        assert(!(rd.size() == 32 and shift > 16));
+        assert(!(rd.size() == 64 and shift > 48));
+
+        return Instruction{
+            .move_wide_immediate = .{
+                .rd = rd.enc(),
+                .imm16 = imm16,
+                .hw = @intCast(u2, shift / 16),
+                .opc = opc,
+                .sf = switch (rd.size()) {
+                    32 => 0,
+                    64 => 1,
+                    else => unreachable, // unexpected register size
+                },
             },
-            else => unreachable, // unexpected register size
-        }
+        };
     }
 
     fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
@@ -914,7 +904,7 @@ pub const Instruction = union(enum) {
         n: u1,
     ) Instruction {
         assert(rd.size() == rn.size());
-        assert(!(rd.size() == 32 and n == 1));
+        assert(!(rd.size() == 32 and n != 0));
 
         return Instruction{
             .logical_immediate = .{
@@ -942,6 +932,8 @@ pub const Instruction = union(enum) {
         imms: u6,
     ) Instruction {
         assert(rd.size() == rn.size());
+        assert(!(rd.size() == 64 and n != 1));
+        assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0)));
 
         return Instruction{
             .bitfield = .{
@@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) {
         return sbfm(rd, rn, shift, imms);
     }
 
+    pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
+        return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1));
+    }
+
+    pub fn sxtb(rd: Register, rn: Register) Instruction {
+        return sbfm(rd, rn, 0, 7);
+    }
+
+    pub fn sxth(rd: Register, rn: Register) Instruction {
+        return sbfm(rd, rn, 0, 15);
+    }
+
+    pub fn sxtw(rd: Register, rn: Register) Instruction {
+        assert(rd.size() == 64);
+        return sbfm(rd, rn, 0, 31);
+    }
+
     pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
         const size = @intCast(u6, rd.size() - 1);
         return ubfm(rd, rn, size - shift + 1, size - shift);
@@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) {
         return ubfm(rd, rn, shift, imms);
     }
 
+    pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
+        return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1));
+    }
+
+    pub fn uxtb(rd: Register, rn: Register) Instruction {
+        return ubfm(rd, rn, 0, 7);
+    }
+
+    pub fn uxth(rd: Register, rn: Register) Instruction {
+        return ubfm(rd, rn, 0, 15);
+    }
+
     // Add/subtract (shifted register)
 
     pub fn addShiftedRegister(
src/arch/aarch64/CodeGen.zig
@@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
 }
 
+fn truncRegister(
+    self: *Self,
+    operand_reg: Register,
+    dest_reg: Register,
+    int_signedness: std.builtin.Signedness,
+    int_bits: u16,
+) !void {
+    switch (int_bits) {
+        1...31, 33...63 => {
+            _ = try self.addInst(.{
+                .tag = switch (int_signedness) {
+                    .signed => .sbfx,
+                    .unsigned => .ubfx,
+                },
+                .data = .{ .rr_lsb_width = .{
+                    .rd = dest_reg,
+                    .rn = operand_reg,
+                    .lsb = 0,
+                    .width = @intCast(u6, int_bits),
+                } },
+            });
+        },
+        32, 64 => {
+            _ = try self.addInst(.{
+                .tag = .mov_register,
+                .data = .{ .rr = .{
+                    .rd = dest_reg,
+                    .rn = operand_reg,
+                } },
+            });
+        },
+        else => unreachable,
+    }
+}
+
+fn trunc(
+    self: *Self,
+    maybe_inst: ?Air.Inst.Index,
+    operand: MCValue,
+    operand_ty: Type,
+    dest_ty: Type,
+) !MCValue {
+    const info_a = operand_ty.intInfo(self.target.*);
+    const info_b = dest_ty.intInfo(self.target.*);
+
+    if (info_b.bits <= 64) {
+        const operand_reg = switch (operand) {
+            .register => |r| r,
+            else => operand_reg: {
+                if (info_a.bits <= 64) {
+                    const raw_reg = try self.copyToTmpRegister(operand_ty, operand);
+                    break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*));
+                } else {
+                    return self.fail("TODO load least significant word into register", .{});
+                }
+            },
+        };
+        self.register_manager.freezeRegs(&.{operand_reg});
+        defer self.register_manager.unfreezeRegs(&.{operand_reg});
+
+        const dest_reg = if (maybe_inst) |inst| blk: {
+            const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+
+            if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
+                break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*));
+            } else {
+                const raw_reg = try self.register_manager.allocReg(inst);
+                break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
+            }
+        } else blk: {
+            const raw_reg = try self.register_manager.allocReg(null);
+            break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
+        };
+
+        try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
+
+        return MCValue{ .register = dest_reg };
+    } else {
+        return self.fail("TODO: truncate to ints > 32 bits", .{});
+    }
+}
+
 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    if (self.liveness.isUnused(inst))
-        return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
-
     const operand = try self.resolveInst(ty_op.operand);
-    _ = operand;
-    return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
+    const operand_ty = self.air.typeOf(ty_op.operand);
+    const dest_ty = self.air.typeOfIndex(inst);
+
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
+        break :blk try self.trunc(inst, operand, operand_ty, dest_ty);
+    };
+
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
@@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
             });
 
-            if (x > math.maxInt(u16)) {
+            if (x & 0x0000_0000_ffff_0000 != 0) {
                 _ = try self.addInst(.{
                     .tag = .movk,
                     .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
                 });
             }
-            if (x > math.maxInt(u32)) {
-                _ = try self.addInst(.{
-                    .tag = .movk,
-                    .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
-                });
-            }
-            if (x > math.maxInt(u48)) {
-                _ = try self.addInst(.{
-                    .tag = .movk,
-                    .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
-                });
+
+            if (reg.size() == 64) {
+                if (x & 0x0000_ffff_0000_0000 != 0) {
+                    _ = try self.addInst(.{
+                        .tag = .movk,
+                        .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
+                    });
+                }
+                if (x & 0xffff_0000_0000_0000 != 0) {
+                    _ = try self.addInst(.{
+                        .tag = .movk,
+                        .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
+                    });
+                }
             }
         },
         .register => |src_reg| {
src/arch/aarch64/Emit.zig
@@ -162,6 +162,17 @@ pub fn emitMir(
 
             .push_regs => try emit.mirPushPopRegs(inst),
             .pop_regs => try emit.mirPushPopRegs(inst),
+
+            .sbfx,
+            .ubfx,
+            => try emit.mirBitfieldExtract(inst),
+
+            .sxtb,
+            .sxth,
+            .sxtw,
+            .uxtb,
+            .uxth,
+            => try emit.mirExtend(inst),
         }
     }
 }
@@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
         else => unreachable,
     }
 }
+
+fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width;
+    const rd = rr_lsb_width.rd;
+    const rn = rr_lsb_width.rn;
+    const lsb = rr_lsb_width.lsb;
+    const width = rr_lsb_width.width;
+
+    switch (tag) {
+        .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)),
+        .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)),
+        else => unreachable,
+    }
+}
+
+fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const rr = emit.mir.instructions.items(.data)[inst].rr;
+
+    switch (tag) {
+        .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)),
+        .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)),
+        .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)),
+        .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)),
+        .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)),
+        else => unreachable,
+    }
+}
src/arch/aarch64/Mir.zig
@@ -130,6 +130,14 @@ pub const Inst = struct {
         push_regs,
         /// Return from subroutine
         ret,
+        /// Signed bitfield extract
+        sbfx,
+        /// Signed extend byte
+        sxtb,
+        /// Signed extend halfword
+        sxth,
+        /// Signed extend word
+        sxtw,
         /// Store Pair of Registers
         stp,
         /// Pseudo-instruction: Store to stack
@@ -156,6 +164,12 @@ pub const Inst = struct {
         sub_shifted_register,
         /// Supervisor Call
         svc,
+        /// Unsigned bitfield extract
+        ubfx,
+        /// Unsigned extend byte
+        uxtb,
+        /// Unsigned extend halfword
+        uxth,
     };
 
     /// The position of an MIR instruction within the `Mir` instructions array.
@@ -225,13 +239,6 @@ pub const Inst = struct {
             rt: Register,
             inst: Index,
         },
-        /// Two registers
-        ///
-        /// Used by e.g. mov_register
-        rr: struct {
-            rd: Register,
-            rn: Register,
-        },
         /// A register, an unsigned 12-bit immediate, and an optional shift
         ///
         /// Used by e.g. cmp_immediate
@@ -240,6 +247,13 @@ pub const Inst = struct {
             imm12: u12,
             sh: u1 = 0,
         },
+        /// Two registers
+        ///
+        /// Used by e.g. mov_register
+        rr: struct {
+            rd: Register,
+            rn: Register,
+        },
         /// Two registers, an unsigned 12-bit immediate, and an optional shift
         ///
         /// Used by e.g. sub_immediate
@@ -268,6 +282,16 @@ pub const Inst = struct {
             imm6: u6,
             shift: bits.Instruction.LogicalShiftedRegisterShift,
         },
+        /// Two registers and a lsb (range 0-63) and a width (range
+        /// 1-64)
+        ///
+        /// Used by e.g. ubfx
+        rr_lsb_width: struct {
+            rd: Register,
+            rn: Register,
+            lsb: u6,
+            width: u7,
+        },
         /// Two registers and a bitmask immediate
         ///
         /// Used by e.g. eor_immediate
test/behavior/basic.zig
@@ -15,8 +15,6 @@ test "empty function with comments" {
 }
 
 test "truncate" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
     try expect(testTruncate(0x10fd) == 0xfd);
     comptime try expect(testTruncate(0x10fd) == 0xfd);
 }