Commit 0eebdfcad3

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-03-13 20:23:20
stage2 ARM: fix bitwise negation of ints with bits < 32
1 parent ca1ffb0
Changed files (2)
src
arch
test
behavior
src/arch/arm/CodeGen.zig
@@ -939,63 +939,85 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
-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 });
+fn truncRegister(
+    self: *Self,
+    operand_reg: Register,
+    dest_reg: Register,
+    int_signedness: std.builtin.Signedness,
+    int_bits: u16,
+) !void {
+    // TODO check if sxtb/uxtb/sxth/uxth are more efficient
+    _ = 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),
+        } },
+    });
+}
 
-    const operand_ty = self.air.typeOf(ty_op.operand);
-    const operand = try self.resolveInst(ty_op.operand);
+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 = self.air.typeOfIndex(inst).intInfo(self.target.*);
-
-    const result: MCValue = blk: {
-        if (info_b.bits <= 32) {
-            const operand_reg = switch (operand) {
-                .register => |r| r,
-                else => operand_reg: {
-                    if (info_a.bits <= 32) {
-                        break :operand_reg try self.copyToTmpRegister(operand_ty, operand);
-                    } 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 = dest_reg: {
-                if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
-                    break :dest_reg operand_reg;
+    const info_b = dest_ty.intInfo(self.target.*);
+
+    if (info_b.bits <= 32) {
+        const operand_reg = switch (operand) {
+            .register => |r| r,
+            else => operand_reg: {
+                if (info_a.bits <= 32) {
+                    break :operand_reg try self.copyToTmpRegister(operand_ty, operand);
+                } else {
+                    return self.fail("TODO load least significant word into register", .{});
                 }
+            },
+        };
+        self.register_manager.freezeRegs(&.{operand_reg});
+        defer self.register_manager.unfreezeRegs(&.{operand_reg});
 
-                break :dest_reg try self.register_manager.allocReg(null);
-            };
+        const dest_reg = if (maybe_inst) |inst| blk: {
+            const ty_op = self.air.instructions.items(.data)[inst].ty_op;
 
-            switch (info_b.bits) {
-                32 => {
-                    try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
-                    break :blk MCValue{ .register = dest_reg };
-                },
-                else => {
-                    _ = try self.addInst(.{
-                        .tag = switch (info_b.signedness) {
-                            .signed => .sbfx,
-                            .unsigned => .ubfx,
-                        },
-                        .data = .{ .rr_lsb_width = .{
-                            .rd = dest_reg,
-                            .rn = operand_reg,
-                            .lsb = 0,
-                            .width = @intCast(u6, info_b.bits),
-                        } },
-                    });
-                    break :blk MCValue{ .register = dest_reg };
-                },
+            if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
+                break :blk operand_reg;
+            } else {
+                break :blk try self.register_manager.allocReg(inst);
             }
-        } else {
-            return self.fail("TODO: truncate to ints > 32 bits", .{});
+        } else try self.register_manager.allocReg(null);
+
+        switch (info_b.bits) {
+            32 => {
+                try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
+                return MCValue{ .register = dest_reg };
+            },
+            else => {
+                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;
+    const operand = try self.resolveInst(ty_op.operand);
+    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 });
@@ -1099,6 +1121,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
                                 } },
                             });
 
+                            if (int_info.bits < 32) {
+                                try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
+                            }
+
                             break :result MCValue{ .register = dest_reg };
                         } else {
                             return self.fail("TODO ARM not on integers > u32/i32", .{});
test/behavior/math.zig
@@ -367,7 +367,6 @@ test "binary not" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 
     try expect(comptime x: {
         break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;