Commit 8ef80cfaab

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-02-24 22:18:21
stage2 ARM: implement truncate to ints with bits <= 32
1 parent db82c1b
Changed files (6)
src/arch/arm/bits.zig
@@ -1,5 +1,6 @@
 const std = @import("std");
 const DW = std.dwarf;
+const assert = std.debug.assert;
 const testing = std.testing;
 
 /// The condition field specifies the flags necessary for an
@@ -237,6 +238,17 @@ pub const Instruction = union(enum) {
         fixed_3: u5 = 0b00010,
         cond: u4,
     },
+    bit_field_extract: packed struct {
+        rn: u4,
+        fixed_1: u3 = 0b101,
+        lsb: u5,
+        rd: u4,
+        widthm1: u5,
+        fixed_2: u1 = 0b1,
+        unsigned: u1,
+        fixed_3: u5 = 0b01111,
+        cond: u4,
+    },
     single_data_transfer: packed struct {
         offset: u12,
         rd: u4,
@@ -576,6 +588,7 @@ pub const Instruction = union(enum) {
             .multiply => |v| @bitCast(u32, v),
             .multiply_long => |v| @bitCast(u32, v),
             .integer_saturating_arithmetic => |v| @bitCast(u32, v),
+            .bit_field_extract => |v| @bitCast(u32, v),
             .single_data_transfer => |v| @bitCast(u32, v),
             .extra_load_store => |v| @bitCast(u32, v),
             .block_data_transfer => |v| @bitCast(u32, v),
@@ -691,6 +704,27 @@ pub const Instruction = union(enum) {
         };
     }
 
+    fn bitFieldExtract(
+        unsigned: u1,
+        cond: Condition,
+        rd: Register,
+        rn: Register,
+        lsb: u5,
+        width: u6,
+    ) Instruction {
+        assert(width > 0 and width <= 32);
+        return Instruction{
+            .bit_field_extract = .{
+                .rn = rn.id(),
+                .lsb = lsb,
+                .rd = rd.id(),
+                .widthm1 = @intCast(u5, width - 1),
+                .unsigned = unsigned,
+                .cond = @enumToInt(cond),
+            },
+        };
+    }
+
     fn singleDataTransfer(
         cond: Condition,
         rd: Register,
@@ -1044,6 +1078,16 @@ pub const Instruction = union(enum) {
         return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
     }
 
+    // Bit field extract
+
+    pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
+        return bitFieldExtract(0b1, cond, rd, rn, lsb, width);
+    }
+
+    pub fn sbfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
+        return bitFieldExtract(0b0, cond, rd, rn, lsb, width);
+    }
+
     // Single data transfer
 
     pub const OffsetArgs = struct {
src/arch/arm/CodeGen.zig
@@ -439,7 +439,7 @@ fn gen(self: *Self) !void {
             // the code. Therefore, we can just delete
             // the space initially reserved for the
             // jump
-            self.mir_instructions.len -= 1;
+            self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.items[0]);
         } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
             self.mir_instructions.set(jmp_reloc, .{
                 .tag = .b,
@@ -749,6 +749,17 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
 /// Use a pointer instruction as the basis for allocating stack memory.
 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
     const elem_ty = self.air.typeOfIndex(inst).elemType();
+
+    if (!elem_ty.hasRuntimeBits()) {
+        // As this stack item will never be dereferenced at runtime,
+        // return the current stack offset
+        try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{
+            .inst = inst,
+            .size = 0,
+        });
+        return self.next_stack_offset;
+    }
+
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
     };
@@ -872,11 +883,61 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
     if (self.liveness.isUnused(inst))
         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
 
+    const operand_ty = self.air.typeOf(ty_op.operand);
     const operand = try self.resolveInst(ty_op.operand);
-    _ = operand;
+    const info_a = operand_ty.intInfo(self.target.*);
+    const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
 
-    return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
-    // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+    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;
+                }
+
+                break :dest_reg try self.register_manager.allocReg(null);
+            };
+
+            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 };
+                },
+            }
+        } else {
+            return self.fail("TODO: truncate to ints > 32 bits", .{});
+        }
+    };
+
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
src/arch/arm/Emit.zig
@@ -130,6 +130,9 @@ pub fn emitMir(
             .push => try emit.mirBlockDataTransfer(inst),
 
             .svc => try emit.mirSupervisorCall(inst),
+
+            .sbfx => try emit.mirBitFieldExtract(inst),
+            .ubfx => try emit.mirBitFieldExtract(inst),
         }
     }
 }
@@ -691,3 +694,19 @@ fn mirSupervisorCall(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 cond = emit.mir.instructions.items(.cond)[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(cond, rd, rn, lsb, width)),
+        .ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)),
+        else => unreachable,
+    }
+}
src/arch/arm/Mir.zig
@@ -88,6 +88,8 @@ pub const Inst = struct {
         push,
         /// Reverse Subtract
         rsb,
+        /// Signed Bit Field Extract
+        sbfx,
         /// Store Register
         str,
         /// Store Register Byte
@@ -98,6 +100,8 @@ pub const Inst = struct {
         sub,
         /// Supervisor Call
         svc,
+        /// Unsigned Bit Field Extract
+        ubfx,
     };
 
     /// The position of an MIR instruction within the `Mir` instructions array.
@@ -179,6 +183,16 @@ pub const Inst = struct {
             rn: Register,
             offset: bits.Instruction.ExtraLoadStoreOffsetArgs,
         },
+        /// Two registers and a lsb (range 0-31) and a width (range
+        /// 1-32)
+        ///
+        /// Used by e.g. sbfx
+        rr_lsb_width: struct {
+            rd: Register,
+            rn: Register,
+            lsb: u5,
+            width: u6,
+        },
         /// Three registers
         ///
         /// Used by e.g. mul
test/behavior/basic.zig
@@ -16,7 +16,6 @@ test "empty function with comments" {
 
 test "truncate" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     try expect(testTruncate(0x10fd) == 0xfd);
     comptime try expect(testTruncate(0x10fd) == 0xfd);
test/behavior/truncate.zig
@@ -4,7 +4,6 @@ const expect = std.testing.expect;
 
 test "truncate u0 to larger integer allowed and has comptime known result" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var x: u0 = 0;
     const y = @truncate(u8, x);
@@ -13,7 +12,6 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
 
 test "truncate.u0.literal" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var z = @truncate(u0, 0);
     try expect(z == 0);
@@ -21,7 +19,6 @@ test "truncate.u0.literal" {
 
 test "truncate.u0.const" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     const c0: usize = 0;
     var z = @truncate(u0, c0);
@@ -30,7 +27,6 @@ test "truncate.u0.const" {
 
 test "truncate.u0.var" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var d: u8 = 2;
     var z = @truncate(u0, d);
@@ -39,7 +35,6 @@ test "truncate.u0.var" {
 
 test "truncate i0 to larger integer allowed and has comptime known result" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var x: i0 = 0;
     const y = @truncate(i8, x);
@@ -48,7 +43,6 @@ test "truncate i0 to larger integer allowed and has comptime known result" {
 
 test "truncate.i0.literal" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var z = @truncate(i0, 0);
     try expect(z == 0);
@@ -56,7 +50,6 @@ test "truncate.i0.literal" {
 
 test "truncate.i0.const" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     const c0: isize = 0;
     var z = @truncate(i0, c0);
@@ -65,7 +58,6 @@ test "truncate.i0.const" {
 
 test "truncate.i0.var" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
     var d: i8 = 2;
     var z = @truncate(i0, d);