Commit f48f4baf67

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-02-26 10:23:25
stage2 ARM: generate correct variants of ldr instruction
When loading an i16 for example, generate ldrsh instead of ldrh
1 parent 8ef80cf
Changed files (6)
src/arch/arm/bits.zig
@@ -1123,11 +1123,19 @@ pub const Instruction = union(enum) {
     };
 
     pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
-        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset);
+        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b0, 0b01, rn, rt, args.offset);
     }
 
     pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
-        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset);
+        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b01, rn, rt, args.offset);
+    }
+
+    pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
+        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b11, rn, rt, args.offset);
+    }
+
+    pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
+        return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b10, rn, rt, args.offset);
     }
 
     // Block data transfer
src/arch/arm/CodeGen.zig
@@ -1575,7 +1575,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
                 .compare_flags_signed, .compare_flags_unsigned => unreachable,
                 .embedded_in_code => unreachable,
                 .register => |dst_reg| {
-                    try self.genLdrRegister(dst_reg, reg, elem_size);
+                    try self.genLdrRegister(dst_reg, reg, elem_ty);
                 },
                 .stack_offset => |off| {
                     if (elem_size <= 4) {
@@ -1676,7 +1676,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, @intCast(u32, value_ty.abiSize(self.target.*)));
+                    try self.genStrRegister(value_reg, addr_reg, value_ty);
                 },
                 else => {
                     if (value_ty.abiSize(self.target.*) <= 4) {
@@ -2241,68 +2241,71 @@ fn binOp(
     }
 }
 
-fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void {
-    switch (abi_size) {
-        1, 3, 4 => {
-            const tag: Mir.Inst.Tag = switch (abi_size) {
-                1 => .ldrb,
-                3, 4 => .ldr,
-                else => unreachable,
-            };
+fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void {
+    const abi_size = ty.abiSize(self.target.*);
 
-            _ = try self.addInst(.{
-                .tag = tag,
-                .data = .{ .rr_offset = .{
-                    .rt = dest_reg,
-                    .rn = addr_reg,
-                    .offset = .{ .offset = Instruction.Offset.none },
-                } },
-            });
-        },
-        2 => {
-            _ = try self.addInst(.{
-                .tag = .ldrh,
-                .data = .{ .rr_extra_offset = .{
-                    .rt = dest_reg,
-                    .rn = addr_reg,
-                    .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
-                } },
-            });
-        },
-        else => unreachable, // invalid abi_size for a register
-    }
+    const tag: Mir.Inst.Tag = switch (abi_size) {
+        1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb,
+        2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh,
+        3, 4 => .ldr,
+        else => unreachable,
+    };
+
+    const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
+        .rt = dest_reg,
+        .rn = addr_reg,
+        .offset = .{ .offset = Instruction.Offset.none },
+    } };
+    const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
+        .rt = dest_reg,
+        .rn = addr_reg,
+        .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
+    } };
+
+    const data: Mir.Inst.Data = switch (abi_size) {
+        1 => if (ty.isSignedInt()) rr_extra_offset else rr_offset,
+        2 => rr_extra_offset,
+        3, 4 => rr_offset,
+        else => unreachable,
+    };
+
+    _ = try self.addInst(.{
+        .tag = tag,
+        .data = data,
+    });
 }
 
-fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, abi_size: u32) !void {
-    switch (abi_size) {
-        1, 3, 4 => {
-            const tag: Mir.Inst.Tag = switch (abi_size) {
-                1 => .strb,
-                3, 4 => .str,
-                else => unreachable,
-            };
+fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void {
+    const abi_size = ty.abiSize(self.target.*);
 
-            _ = try self.addInst(.{
-                .tag = tag,
-                .data = .{ .rr_offset = .{
-                    .rt = source_reg,
-                    .rn = addr_reg,
-                    .offset = .{ .offset = Instruction.Offset.none },
-                } },
-            });
-        },
-        2 => {
-            _ = try self.addInst(.{
-                .tag = .strh,
-                .data = .{ .rr_extra_offset = .{
-                    .rt = source_reg,
-                    .rn = addr_reg,
-                    .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
-                } },
-            });
-        },
-        else => unreachable, // invalid abi_size for a register
-    }
+    const tag: Mir.Inst.Tag = switch (abi_size) {
+        1 => .strb,
+        2 => .strh,
+        3, 4 => .str,
+        else => unreachable,
+    };
+
+    const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
+        .rt = source_reg,
+        .rn = addr_reg,
+        .offset = .{ .offset = Instruction.Offset.none },
+    } };
+    const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
+        .rt = source_reg,
+        .rn = addr_reg,
+        .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
+    } };
+
+    const data: Mir.Inst.Data = switch (abi_size) {
+        1, 3, 4 => rr_offset,
+        2 => rr_extra_offset,
+        else => unreachable,
+    };
+
+    _ = try self.addInst(.{
+        .tag = tag,
+        .data = data,
+    });
 }
 
 fn genInlineMemcpy(
@@ -2895,8 +2898,6 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
 }
 
 fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
-    _ = operand;
-
     const error_type = ty.errorUnionSet();
     const payload_type = ty.errorUnionPayload();
 
@@ -3630,55 +3631,59 @@ 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, .{ .immediate = @intCast(u32, addr) });
-            try self.genLdrRegister(reg, reg, @intCast(u32, ty.abiSize(self.target.*)));
+            try self.genLdrRegister(reg, reg, ty);
         },
         .stack_offset => |unadjusted_off| {
             // TODO: maybe addressing from sp instead of fp
             const abi_size = @intCast(u32, ty.abiSize(self.target.*));
             const adj_off = unadjusted_off + abi_size;
 
-            switch (abi_size) {
-                1, 4 => {
-                    const offset = if (adj_off <= math.maxInt(u12)) blk: {
-                        break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
-                    } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
+            const tag: Mir.Inst.Tag = switch (abi_size) {
+                1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb,
+                2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh,
+                3, 4 => .ldr,
+                else => unreachable,
+            };
 
-                    const tag: Mir.Inst.Tag = switch (abi_size) {
-                        1 => .ldrb,
-                        4 => .ldr,
-                        else => unreachable,
-                    };
+            const extra_offset = switch (abi_size) {
+                1 => ty.isSignedInt(),
+                2 => true,
+                3, 4 => false,
+                else => unreachable,
+            };
 
-                    _ = try self.addInst(.{
-                        .tag = tag,
-                        .data = .{ .rr_offset = .{
-                            .rt = reg,
-                            .rn = .fp,
-                            .offset = .{
-                                .offset = offset,
-                                .positive = false,
-                            },
-                        } },
-                    });
-                },
-                2 => {
-                    const offset = if (adj_off <= math.maxInt(u8)) blk: {
-                        break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
-                    } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
+            if (extra_offset) {
+                const offset = if (adj_off <= math.maxInt(u8)) blk: {
+                    break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
+                } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
 
-                    _ = try self.addInst(.{
-                        .tag = .ldrh,
-                        .data = .{ .rr_extra_offset = .{
-                            .rt = reg,
-                            .rn = .fp,
-                            .offset = .{
-                                .offset = offset,
-                                .positive = false,
-                            },
-                        } },
-                    });
-                },
-                else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
+                _ = try self.addInst(.{
+                    .tag = tag,
+                    .data = .{ .rr_extra_offset = .{
+                        .rt = reg,
+                        .rn = .fp,
+                        .offset = .{
+                            .offset = offset,
+                            .positive = false,
+                        },
+                    } },
+                });
+            } else {
+                const offset = if (adj_off <= math.maxInt(u12)) blk: {
+                    break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
+                } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
+
+                _ = try self.addInst(.{
+                    .tag = tag,
+                    .data = .{ .rr_offset = .{
+                        .rt = reg,
+                        .rn = .fp,
+                        .offset = .{
+                            .offset = offset,
+                            .positive = false,
+                        },
+                    } },
+                });
             }
         },
         .stack_argument_offset => |unadjusted_off| {
@@ -3686,9 +3691,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             const adj_off = unadjusted_off + abi_size;
 
             const tag: Mir.Inst.Tag = switch (abi_size) {
-                1 => .ldrb_stack_argument,
-                2 => .ldrh_stack_argument,
-                4 => .ldr_stack_argument,
+                1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument,
+                2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument,
+                3, 4 => .ldr_stack_argument,
                 else => unreachable,
             };
 
src/arch/arm/Emit.zig
@@ -115,8 +115,12 @@ pub fn emitMir(
             .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
             .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),
             .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),
+            .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),
+            .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
 
             .ldrh => try emit.mirLoadStoreExtra(inst),
+            .ldrsb => try emit.mirLoadStore(inst),
+            .ldrsh => try emit.mirLoadStoreExtra(inst),
             .strh => try emit.mirLoadStoreExtra(inst),
 
             .movw => try emit.mirSpecialMove(inst),
@@ -593,36 +597,42 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void {
 
     const raw_offset = emit.prologue_stack_space - r_stack_offset.stack_offset;
     switch (tag) {
-        .ldr_stack_argument => {
+        .ldr_stack_argument,
+        .ldrb_stack_argument,
+        => {
             const offset = if (raw_offset <= math.maxInt(u12)) blk: {
                 break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
             } else return emit.fail("TODO mirLoadStack larger offsets", .{});
 
-            try emit.writeInstruction(Instruction.ldr(
-                cond,
-                r_stack_offset.rt,
-                .fp,
-                .{ .offset = offset },
-            ));
-        },
-        .ldrb_stack_argument => {
-            const offset = if (raw_offset <= math.maxInt(u12)) blk: {
-                break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
-            } else return emit.fail("TODO mirLoadStack larger offsets", .{});
+            const ldr = switch (tag) {
+                .ldr_stack_argument => Instruction.ldr,
+                .ldrb_stack_argument => Instruction.ldrb,
+                else => unreachable,
+            };
 
-            try emit.writeInstruction(Instruction.ldrb(
+            try emit.writeInstruction(ldr(
                 cond,
                 r_stack_offset.rt,
                 .fp,
                 .{ .offset = offset },
             ));
         },
-        .ldrh_stack_argument => {
+        .ldrh_stack_argument,
+        .ldrsb_stack_argument,
+        .ldrsh_stack_argument,
+        => {
             const offset = if (raw_offset <= math.maxInt(u8)) blk: {
                 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, raw_offset));
             } else return emit.fail("TODO mirLoadStack larger offsets", .{});
 
-            try emit.writeInstruction(Instruction.ldrh(
+            const ldr = switch (tag) {
+                .ldrh_stack_argument => Instruction.ldrh,
+                .ldrsb_stack_argument => Instruction.ldrsb,
+                .ldrsh_stack_argument => Instruction.ldrsh,
+                else => unreachable,
+            };
+
+            try emit.writeInstruction(ldr(
                 cond,
                 r_stack_offset.rt,
                 .fp,
@@ -640,6 +650,8 @@ fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {
 
     switch (tag) {
         .ldrh => try emit.writeInstruction(Instruction.ldrh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
+        .ldrsb => try emit.writeInstruction(Instruction.ldrsb(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
+        .ldrsh => try emit.writeInstruction(Instruction.ldrsh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
         .strh => try emit.writeInstruction(Instruction.strh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
         else => unreachable,
     }
src/arch/arm/Mir.zig
@@ -64,6 +64,14 @@ pub const Inst = struct {
         ldrh,
         /// Load Register Halfword
         ldrh_stack_argument,
+        /// Load Register Signed Byte
+        ldrsb,
+        /// Load Register Signed Byte
+        ldrsb_stack_argument,
+        /// Load Register Signed Halfword
+        ldrsh,
+        /// Load Register Signed Halfword
+        ldrsh_stack_argument,
         /// Logical Shift Left
         lsl,
         /// Logical Shift Right
test/behavior/basic.zig
@@ -26,7 +26,6 @@ fn testTruncate(x: u32) u8 {
 
 test "truncate to non-power-of-two integers" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     try testTrunc(u32, u1, 0b10101, 0b1);
     try testTrunc(u32, u1, 0b10110, 0b0);
test/behavior/truncate.zig
@@ -66,7 +66,6 @@ test "truncate.i0.var" {
 
 test "truncate on comptime integer" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var x = @truncate(u16, 9999);
     try expect(x == 9999);