Commit e3121accac

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-02-28 19:13:15
stage2 AArch64: introduce logical immediate instructions
1 parent f2a5d0b
Changed files (4)
src/arch/aarch64/bits.zig
@@ -323,6 +323,16 @@ pub const Instruction = union(enum) {
         op: u1,
         sf: u1,
     },
+    logical_immediate: packed struct {
+        rd: u5,
+        rn: u5,
+        imms: u6,
+        immr: u6,
+        n: u1,
+        fixed: u6 = 0b100100,
+        opc: u2,
+        sf: u1,
+    },
     add_subtract_shifted_register: packed struct {
         rd: u5,
         rn: u5,
@@ -487,6 +497,7 @@ pub const Instruction = union(enum) {
             .no_operation => |v| @bitCast(u32, v),
             .logical_shifted_register => |v| @bitCast(u32, v),
             .add_subtract_immediate => |v| @bitCast(u32, v),
+            .logical_immediate => |v| @bitCast(u32, v),
             .add_subtract_shifted_register => |v| @bitCast(u32, v),
             // TODO once packed structs work, this can be refactored
             .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
@@ -900,6 +911,31 @@ pub const Instruction = union(enum) {
         };
     }
 
+    fn logicalImmediate(
+        opc: u2,
+        rd: Register,
+        rn: Register,
+        imms: u6,
+        immr: u6,
+        n: u1,
+    ) Instruction {
+        return Instruction{
+            .logical_immediate = .{
+                .rd = rd.enc(),
+                .rn = rn.enc(),
+                .imms = imms,
+                .immr = immr,
+                .n = n,
+                .opc = opc,
+                .sf = switch (rd.size()) {
+                    32 => 0b0,
+                    64 => 0b1,
+                    else => unreachable, // unexpected register size
+                },
+            },
+        };
+    }
+
     pub const AddSubtractShiftedRegisterShift = enum(u2) { lsl, lsr, asr, _ };
 
     fn addSubtractShiftedRegister(
@@ -1173,7 +1209,7 @@ pub const Instruction = union(enum) {
 
     // Logical (shifted register)
 
-    pub fn @"and"(
+    pub fn andShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1183,7 +1219,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b00, 0b0, rd, rn, rm, shift, amount);
     }
 
-    pub fn bic(
+    pub fn bicShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1193,7 +1229,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b00, 0b1, rd, rn, rm, shift, amount);
     }
 
-    pub fn orr(
+    pub fn orrShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1203,7 +1239,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b01, 0b0, rd, rn, rm, shift, amount);
     }
 
-    pub fn orn(
+    pub fn ornShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1213,7 +1249,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b01, 0b1, rd, rn, rm, shift, amount);
     }
 
-    pub fn eor(
+    pub fn eorShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1223,7 +1259,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b10, 0b0, rd, rn, rm, shift, amount);
     }
 
-    pub fn eon(
+    pub fn eonShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1233,7 +1269,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b10, 0b1, rd, rn, rm, shift, amount);
     }
 
-    pub fn ands(
+    pub fn andsShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1243,7 +1279,7 @@ pub const Instruction = union(enum) {
         return logicalShiftedRegister(0b11, 0b0, rd, rn, rm, shift, amount);
     }
 
-    pub fn bics(
+    pub fn bicsShiftedRegister(
         rd: Register,
         rn: Register,
         rm: Register,
@@ -1271,6 +1307,24 @@ pub const Instruction = union(enum) {
         return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
     }
 
+    // Logical (immediate)
+
+    pub fn andImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
+        return logicalImmediate(0b00, rd, rn, imms, immr, n);
+    }
+
+    pub fn orrImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
+        return logicalImmediate(0b01, rd, rn, imms, immr, n);
+    }
+
+    pub fn eorImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
+        return logicalImmediate(0b10, rd, rn, imms, immr, n);
+    }
+
+    pub fn andsImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
+        return logicalImmediate(0b11, rd, rn, imms, immr, n);
+    }
+
     // Add/subtract (shifted register)
 
     pub fn addShiftedRegister(
@@ -1378,11 +1432,11 @@ test "serialize instructions" {
 
     const testcases = [_]Testcase{
         .{ // orr x0, xzr, x1
-            .inst = Instruction.orr(.x0, .xzr, .x1, .lsl, 0),
+            .inst = Instruction.orrShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
             .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
         },
         .{ // orn x0, xzr, x1
-            .inst = Instruction.orn(.x0, .xzr, .x1, .lsl, 0),
+            .inst = Instruction.ornShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
             .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
         },
         .{ // movz x1, #4
@@ -1502,11 +1556,11 @@ test "serialize instructions" {
             .expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
         },
         .{ // and x0, x4, x2
-            .inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0),
+            .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0),
             .expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
         },
         .{ // and x0, x4, x2, lsl #0x8
-            .inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0x8),
+            .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0x8),
             .expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
         },
         .{ // add x0, x10, #10
@@ -1537,6 +1591,10 @@ test "serialize instructions" {
             .inst = Instruction.mul(.x1, .x4, .x9),
             .expected = 0b1_00_11011_000_01001_0_11111_00100_00001,
         },
+        .{ // eor x3, x5, #1
+            .inst = Instruction.eorImmediate(.x3, .x5, 0b000000, 0b000000, 0b1),
+            .expected = 0b1_10_100100_1_000000_000000_00101_00011,
+        },
     };
 
     for (testcases) |case| {
src/arch/aarch64/CodeGen.zig
@@ -950,10 +950,69 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
                 switch (operand_ty.zigTypeTag()) {
                     .Bool => {
                         // TODO convert this to mvn + and
-                        const dest = try self.binOp(.xor, null, operand, .{ .immediate = 1 }, operand_ty, Type.bool);
-                        break :result dest;
+                        const op_reg = switch (operand) {
+                            .register => |r| r,
+                            else => try self.copyToTmpRegister(operand_ty, operand),
+                        };
+                        self.register_manager.freezeRegs(&.{op_reg});
+                        defer self.register_manager.unfreezeRegs(&.{op_reg});
+
+                        const dest_reg = blk: {
+                            if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
+                                break :blk op_reg;
+                            }
+
+                            break :blk try self.register_manager.allocReg(null);
+                        };
+
+                        _ = try self.addInst(.{
+                            .tag = .eor_immediate,
+                            .data = .{ .rr_bitmask = .{
+                                .rd = dest_reg,
+                                .rn = op_reg,
+                                .imms = 0b000000,
+                                .immr = 0b000000,
+                                .n = 0b1,
+                            } },
+                        });
+
+                        break :result MCValue{ .register = dest_reg };
+                    },
+                    .Vector => return self.fail("TODO bitwise not for vectors", .{}),
+                    .Int => {
+                        const int_info = operand_ty.intInfo(self.target.*);
+                        if (int_info.bits <= 64) {
+                            const op_reg = switch (operand) {
+                                .register => |r| r,
+                                else => try self.copyToTmpRegister(operand_ty, operand),
+                            };
+                            self.register_manager.freezeRegs(&.{op_reg});
+                            defer self.register_manager.unfreezeRegs(&.{op_reg});
+
+                            const dest_reg = blk: {
+                                if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
+                                    break :blk op_reg;
+                                }
+
+                                break :blk try self.register_manager.allocReg(null);
+                            };
+
+                            _ = try self.addInst(.{
+                                .tag = .mvn,
+                                .data = .{ .rr_imm6_shift = .{
+                                    .rd = dest_reg,
+                                    .rm = op_reg,
+                                    .imm6 = 0,
+                                    .shift = .lsl,
+                                } },
+                            });
+
+                            break :result MCValue{ .register = dest_reg };
+                        } else {
+                            return self.fail("TODO AArch64 not on integers > u64/i64", .{});
+                        }
                     },
-                    else => return self.fail("TODO bitwise not", .{}),
+                    else => unreachable,
                 }
             },
         }
@@ -1259,15 +1318,13 @@ fn binOp(
             }
         },
         // Bitwise operations on integers
-        .xor => {
+        .bit_and,
+        .bit_or,
+        .xor,
+        => {
             switch (lhs_ty.zigTypeTag()) {
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
-                .Int => return self.fail("TODO binary operations on vectors", .{}),
-                .Bool => {
-                    assert(lhs_ty.eql(rhs_ty));
-                    // TODO boolean operations with immediates
-                    return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
-                },
+                .Int => return self.fail("TODO binary operations on integers", .{}),
                 else => unreachable,
             }
         },
@@ -3136,11 +3193,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                         4, 8 => .str_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
-                    };
+                    const rt = registerAlias(reg, abi_size);
 
                     _ = try self.addInst(.{
                         .tag = tag,
@@ -3622,7 +3675,6 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
     if (typed_value.val.isUndef())
         return MCValue{ .undef = {} };
-    const ptr_bits = self.target.cpu.arch.ptrBitWidth();
 
     if (typed_value.val.castTag(.decl_ref)) |payload| {
         return self.lowerDeclRef(typed_value, payload.data);
@@ -3652,13 +3704,19 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         },
         .Int => {
             const info = typed_value.ty.intInfo(self.target.*);
-            if (info.bits <= ptr_bits and info.signedness == .signed) {
-                return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) };
-            }
-            if (info.bits > ptr_bits or info.signedness == .signed) {
-                return self.fail("TODO const int bigger than ptr and signed int", .{});
+            if (info.bits <= 64) {
+                const unsigned = switch (info.signedness) {
+                    .signed => blk: {
+                        const signed = typed_value.val.toSignedInt();
+                        break :blk @bitCast(u64, signed);
+                    },
+                    .unsigned => typed_value.val.toUnsignedInt(),
+                };
+
+                return MCValue{ .immediate = unsigned };
+            } else {
+                return self.lowerUnnamedConst(typed_value);
             }
-            return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
         },
         .Bool => {
             return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
@@ -3875,7 +3933,7 @@ fn parseRegName(name: []const u8) ?Register {
     return std.meta.stringToEnum(Register, name);
 }
 
-fn registerAlias(reg: Register, size_bytes: u32) Register {
+fn registerAlias(reg: Register, size_bytes: u64) Register {
     if (size_bytes == 0) {
         unreachable; // should be comptime known
     } else if (size_bytes <= 4) {
src/arch/aarch64/Emit.zig
@@ -95,6 +95,8 @@ pub fn emitMir(
 
             .call_extern => try emit.mirCallExtern(inst),
 
+            .eor_immediate => try emit.mirLogicalImmediate(inst),
+
             .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
             .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
             .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
@@ -605,6 +607,21 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
     }
 }
 
+fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const rr_bitmask = emit.mir.instructions.items(.data)[inst].rr_bitmask;
+    const rd = rr_bitmask.rd;
+    const rn = rr_bitmask.rn;
+    const imms = rr_bitmask.imms;
+    const immr = rr_bitmask.immr;
+    const n = rr_bitmask.n;
+
+    switch (tag) {
+        .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)),
+        else => unreachable,
+    }
+}
+
 fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift;
@@ -643,7 +660,7 @@ fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
     const imm6 = rrr_imm6_logical_shift.imm6;
 
     switch (tag) {
-        .eor_shifted_register => try emit.writeInstruction(Instruction.eor(rd, rn, rm, shift, imm6)),
+        .eor_shifted_register => try emit.writeInstruction(Instruction.eorShiftedRegister(rd, rn, rm, shift, imm6)),
         else => unreachable,
     }
 }
@@ -844,7 +861,7 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
     switch (tag) {
         .mov_register => {
             const rr = emit.mir.instructions.items(.data)[inst].rr;
-            try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, .lsl, 0));
+            try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, .xzr, rr.rn, .lsl, 0));
         },
         .mov_to_from_sp => {
             const rr = emit.mir.instructions.items(.data)[inst].rr;
@@ -852,7 +869,7 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
         },
         .mvn => {
             const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift;
-            try emit.writeInstruction(Instruction.orn(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, .lsl, 0));
+            try emit.writeInstruction(Instruction.ornShiftedRegister(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, rr_imm6_shift.shift, rr_imm6_shift.imm6));
         },
         else => unreachable,
     }
src/arch/aarch64/Mir.zig
@@ -54,6 +54,8 @@ pub const Inst = struct {
         dbg_epilogue_begin,
         /// Pseudo-instruction: Update debug line
         dbg_line,
+        /// Bitwise Exclusive OR (immediate)
+        eor_immediate,
         /// Bitwise Exclusive OR (shifted register)
         eor_shifted_register,
         /// Loads the contents into a register
@@ -231,14 +233,25 @@ pub const Inst = struct {
             imm12: u12,
             sh: u1 = 0,
         },
-        /// Two registers and a shift (shift type and 6-bit amount)
+        /// Two registers and a shift (logical instruction version)
+        /// (shift type and 6-bit amount)
         ///
         /// Used by e.g. mvn
         rr_imm6_shift: struct {
             rd: Register,
             rm: Register,
             imm6: u6,
-            shift: bits.Instruction.AddSubtractShiftedRegisterShift,
+            shift: bits.Instruction.LogicalShiftedRegisterShift,
+        },
+        /// Two registers and a bitmask immediate
+        ///
+        /// Used by e.g. eor_immediate
+        rr_bitmask: struct {
+            rd: Register,
+            rn: Register,
+            imms: u6,
+            immr: u6,
+            n: u1,
         },
         /// Two registers
         ///