Commit d9d9fea6ae

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-04-16 23:03:11
stage2 AArch64: Add ldrsb, ldrsh instructions
1 parent f95a8dd
Changed files (6)
src/arch/aarch64/bits.zig
@@ -665,18 +665,24 @@ pub const Instruction = union(enum) {
 
     /// Which kind of load/store to perform
     const LoadStoreVariant = enum {
-        /// 32-bit or 64-bit
+        /// 32 bits or 64 bits
         str,
-        /// 16-bit, zero-extended
-        strh,
-        /// 8-bit, zero-extended
+        /// 8 bits, zero-extended
         strb,
-        /// 32-bit or 64-bit
+        /// 16 bits, zero-extended
+        strh,
+        /// 32 bits or 64 bits
         ldr,
-        /// 16-bit, zero-extended
-        ldrh,
-        /// 8-bit, zero-extended
+        /// 8 bits, zero-extended
         ldrb,
+        /// 16 bits, zero-extended
+        ldrh,
+        /// 8 bits, sign extended
+        ldrsb,
+        /// 16 bits, sign extended
+        ldrsh,
+        /// 32 bits, sign extended
+        ldrsw,
     };
 
     fn loadStoreRegister(
@@ -689,6 +695,7 @@ pub const Instruction = union(enum) {
         assert(rn.id() != Register.xzr.id());
 
         const off = offset.toU12();
+
         const op1: u2 = blk: {
             switch (offset) {
                 .immediate => |imm| switch (imm) {
@@ -699,10 +706,35 @@ pub const Instruction = union(enum) {
             }
             break :blk 0b00;
         };
-        const opc: u2 = switch (variant) {
-            .ldr, .ldrh, .ldrb => 0b01,
-            .str, .strh, .strb => 0b00,
+
+        const opc: u2 = blk: {
+            switch (variant) {
+                .ldr, .ldrh, .ldrb => break :blk 0b01,
+                .str, .strh, .strb => break :blk 0b00,
+                .ldrsb,
+                .ldrsh,
+                => switch (rt.size()) {
+                    32 => break :blk 0b11,
+                    64 => break :blk 0b10,
+                    else => unreachable, // unexpected register size
+                },
+                .ldrsw => break :blk 0b10,
+            }
+        };
+
+        const size: u2 = blk: {
+            switch (variant) {
+                .ldr, .str => switch (rt.size()) {
+                    32 => break :blk 0b10,
+                    64 => break :blk 0b11,
+                    else => unreachable, // unexpected register size
+                },
+                .ldrsw => break :blk 0b10,
+                .ldrh, .ldrsh, .strh => break :blk 0b01,
+                .ldrb, .ldrsb, .strb => break :blk 0b00,
+            }
         };
+
         return Instruction{
             .load_store_register = .{
                 .rt = rt.enc(),
@@ -711,17 +743,7 @@ pub const Instruction = union(enum) {
                 .opc = opc,
                 .op1 = op1,
                 .v = 0,
-                .size = blk: {
-                    switch (variant) {
-                        .ldr, .str => switch (rt.size()) {
-                            32 => break :blk 0b10,
-                            64 => break :blk 0b11,
-                            else => unreachable, // unexpected register size
-                        },
-                        .ldrh, .strh => break :blk 0b01,
-                        .ldrb, .strb => break :blk 0b00,
-                    }
-                },
+                .size = size,
             },
         };
     }
@@ -1150,6 +1172,18 @@ pub const Instruction = union(enum) {
         return loadStoreRegister(rt, rn, offset, .ldrb);
     }
 
+    pub fn ldrsb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
+        return loadStoreRegister(rt, rn, offset, .ldrsb);
+    }
+
+    pub fn ldrsh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
+        return loadStoreRegister(rt, rn, offset, .ldrsh);
+    }
+
+    pub fn ldrsw(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
+        return loadStoreRegister(rt, rn, offset, .ldrsw);
+    }
+
     pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
         return loadStoreRegister(rt, rn, offset, .str);
     }
src/arch/aarch64/CodeGen.zig
@@ -2056,7 +2056,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
                 .undef => unreachable,
                 .compare_flags_signed, .compare_flags_unsigned => unreachable,
                 .register => |dst_reg| {
-                    try self.genLdrRegister(dst_reg, addr_reg, elem_size);
+                    try self.genLdrRegister(dst_reg, addr_reg, elem_ty);
                 },
                 .stack_offset => |off| {
                     if (elem_size <= 8) {
@@ -2210,98 +2210,47 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
-fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void {
-    switch (abi_size) {
-        1 => {
-            _ = try self.addInst(.{
-                .tag = .ldrb_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        2 => {
-            _ = try self.addInst(.{
-                .tag = .ldrh_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        4 => {
-            _ = try self.addInst(.{
-                .tag = .ldr_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        8 => {
-            _ = try self.addInst(.{
-                .tag = .ldr_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to64(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
+fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
+    const abi_size = ty.abiSize(self.target.*);
+
+    const tag: Mir.Inst.Tag = switch (abi_size) {
+        1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate,
+        2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate,
+        4 => .ldr_immediate,
+        8 => .ldr_immediate,
         3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}),
         else => unreachable,
-    }
+    };
+
+    _ = try self.addInst(.{
+        .tag = tag,
+        .data = .{ .load_store_register_immediate = .{
+            .rt = value_reg,
+            .rn = addr_reg,
+            .offset = Instruction.LoadStoreOffset.none.immediate,
+        } },
+    });
 }
 
-fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void {
-    switch (abi_size) {
-        1 => {
-            _ = try self.addInst(.{
-                .tag = .strb_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        2 => {
-            _ = try self.addInst(.{
-                .tag = .strh_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        4 => {
-            _ = try self.addInst(.{
-                .tag = .str_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to32(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
-        8 => {
-            _ = try self.addInst(.{
-                .tag = .str_immediate,
-                .data = .{ .load_store_register_immediate = .{
-                    .rt = value_reg.to64(),
-                    .rn = addr_reg,
-                    .offset = Instruction.LoadStoreOffset.none.immediate,
-                } },
-            });
-        },
+fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
+    const abi_size = ty.abiSize(self.target.*);
+
+    const tag: Mir.Inst.Tag = switch (abi_size) {
+        1 => .strb_immediate,
+        2 => .strh_immediate,
+        4, 8 => .str_immediate,
         3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}),
         else => unreachable,
-    }
+    };
+
+    _ = try self.addInst(.{
+        .tag = tag,
+        .data = .{ .load_store_register_immediate = .{
+            .rt = value_reg,
+            .rn = addr_reg,
+            .offset = Instruction.LoadStoreOffset.none.immediate,
+        } },
+    });
 }
 
 fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
@@ -2326,7 +2275,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
 
             switch (value) {
                 .register => |value_reg| {
-                    try self.genStrRegister(value_reg, addr_reg, abi_size);
+                    try self.genStrRegister(value_reg, addr_reg, value_ty);
                 },
                 else => {
                     if (abi_size <= 8) {
@@ -3624,7 +3573,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             // The value is in memory at a hard-coded address.
             // If the type is a pointer, it means the pointer address is at this memory location.
             try self.genSetReg(ty, reg.to64(), .{ .immediate = addr });
-            try self.genLdrRegister(reg, reg.to64(), ty.abiSize(self.target.*));
+            try self.genLdrRegister(reg, reg.to64(), ty);
         },
         .stack_offset => |off| {
             const abi_size = ty.abiSize(self.target.*);
@@ -3632,21 +3581,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             switch (abi_size) {
                 1, 2, 4, 8 => {
                     const tag: Mir.Inst.Tag = switch (abi_size) {
-                        1 => .ldrb_stack,
-                        2 => .ldrh_stack,
+                        1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack,
+                        2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack,
                         4, 8 => .ldr_stack,
                         else => unreachable, // unexpected abi size
                     };
-                    const rt: Register = switch (abi_size) {
-                        1, 2, 4 => reg.to32(),
-                        8 => reg.to64(),
-                        else => unreachable, // unexpected abi size
-                    };
 
                     _ = try self.addInst(.{
                         .tag = tag,
                         .data = .{ .load_store_stack = .{
-                            .rt = rt,
+                            .rt = reg,
                             .offset = @intCast(u32, off),
                         } },
                     });
src/arch/aarch64/Emit.zig
@@ -131,6 +131,8 @@ pub fn emitMir(
             .ldr_stack => try emit.mirLoadStoreStack(inst),
             .ldrb_stack => try emit.mirLoadStoreStack(inst),
             .ldrh_stack => try emit.mirLoadStoreStack(inst),
+            .ldrsb_stack => try emit.mirLoadStoreStack(inst),
+            .ldrsh_stack => try emit.mirLoadStoreStack(inst),
             .str_stack => try emit.mirLoadStoreStack(inst),
             .strb_stack => try emit.mirLoadStoreStack(inst),
             .strh_stack => try emit.mirLoadStoreStack(inst),
@@ -145,6 +147,9 @@ pub fn emitMir(
             .ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
             .ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
             .ldrh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
+            .ldrsb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
+            .ldrsh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
+            .ldrsw_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
             .str_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
             .strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
             .strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
@@ -847,14 +852,14 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
 
     const raw_offset = emit.stack_size - load_store_stack.offset;
     const offset = switch (tag) {
-        .ldrb_stack, .strb_stack => blk: {
+        .ldrb_stack, .ldrsb_stack, .strb_stack => blk: {
             if (math.cast(u12, raw_offset)) |imm| {
                 break :blk Instruction.LoadStoreOffset.imm(imm);
             } else |_| {
                 return emit.fail("TODO load/store stack byte with larger offset", .{});
             }
         },
-        .ldrh_stack, .strh_stack => blk: {
+        .ldrh_stack, .ldrsh_stack, .strh_stack => blk: {
             assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
             if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {
                 break :blk Instruction.LoadStoreOffset.imm(imm);
@@ -883,6 +888,8 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
         .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
         .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
         .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
+        .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
+        .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
         .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
         .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
         .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
@@ -901,6 +908,9 @@ fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
         .ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
         .ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
         .ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)),
+        .ldrsb_immediate => try emit.writeInstruction(Instruction.ldrsb(rt, rn, offset)),
+        .ldrsh_immediate => try emit.writeInstruction(Instruction.ldrsh(rt, rn, offset)),
+        .ldrsw_immediate => try emit.writeInstruction(Instruction.ldrsw(rt, rn, offset)),
         .str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)),
         .strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
         .strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),
src/arch/aarch64/Mir.zig
@@ -100,6 +100,16 @@ pub const Inst = struct {
         ldrh_immediate,
         /// Load Register Halfword (register)
         ldrh_register,
+        /// Load Register Signed Byte (immediate)
+        ldrsb_immediate,
+        /// Pseudo-instruction: Load signed byte from stack
+        ldrsb_stack,
+        /// Load Register Signed Halfword (immediate)
+        ldrsh_immediate,
+        /// Pseudo-instruction: Load signed halfword from stack
+        ldrsh_stack,
+        /// Load Register Signed Word (immediate)
+        ldrsw_immediate,
         /// Logical Shift Left (immediate)
         lsl_immediate,
         /// Logical Shift Left (register)
test/behavior/basic.zig
@@ -23,8 +23,6 @@ fn testTruncate(x: u32) u8 {
 }
 
 test "truncate to non-power-of-two integers" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
     try testTrunc(u32, u1, 0b10101, 0b1);
     try testTrunc(u32, u1, 0b10110, 0b0);
     try testTrunc(u32, u2, 0b10101, 0b01);
test/behavior/truncate.zig
@@ -49,8 +49,6 @@ test "truncate.i0.var" {
 }
 
 test "truncate on comptime integer" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
     var x = @truncate(u16, 9999);
     try expect(x == 9999);
     var y = @truncate(u16, -21555);