Commit c5ec096b2f

joachimschmidt557 <joachim.schmidt557@outlook.com>
2020-11-14 18:25:23
stage2 AArch64: add logical (shifted register) instructions
1 parent 6c0c275
Changed files (2)
src/codegen/aarch64.zig
@@ -200,17 +200,6 @@ test "FloatingPointRegister.toX" {
 
 /// Represents an instruction in the AArch64 instruction set
 pub const Instruction = union(enum) {
-    OrShiftedRegister: packed struct {
-        rd: u5,
-        rn: u5,
-        imm6: u6,
-        rm: u5,
-        n: u1,
-        shift: u2,
-        fixed: u5 = 0b01010,
-        opc: u2 = 0b01,
-        sf: u1,
-    },
     MoveWideImmediate: packed struct {
         rd: u5,
         imm16: u16,
@@ -274,10 +263,37 @@ pub const Instruction = union(enum) {
     NoOperation: packed struct {
         fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
     },
+    LogicalShiftedRegister: packed struct {
+        rd: u5,
+        rn: u5,
+        imm6: u6,
+        rm: u5,
+        n: u1,
+        shift: u2,
+        fixed: u5 = 0b01010,
+        opc: u2,
+        sf: u1,
+    },
+
+    pub const Shift = struct {
+        shift: Type = .lsl,
+        amount: u6 = 0,
+
+        pub const Type = enum(u2) {
+            lsl,
+            lsr,
+            asr,
+            ror,
+        };
+
+        pub const none = Shift{
+            .shift = .lsl,
+            .amount = 0,
+        };
+    };
 
     pub fn toU32(self: Instruction) u32 {
         return switch (self) {
-            .OrShiftedRegister => |v| @bitCast(u32, v),
             .MoveWideImmediate => |v| @bitCast(u32, v),
             .PCRelativeAddress => |v| @bitCast(u32, v),
             .LoadStoreRegister => |v| @bitCast(u32, v),
@@ -287,68 +303,10 @@ pub const Instruction = union(enum) {
             .UnconditionalBranchRegister => |v| @bitCast(u32, v),
             .UnconditionalBranchImmediate => |v| @bitCast(u32, v),
             .NoOperation => |v| @bitCast(u32, v),
+            .LogicalShiftedRegister => |v| @bitCast(u32, v),
         };
     }
 
-    pub const RegisterShift = struct {
-        rn: u5,
-        imm6: u6,
-        shift: enum(u2) {
-            Lsl = 0,
-            Lsr = 1,
-            Asr = 2,
-            Ror = 3,
-        },
-
-        pub fn none() RegisterShift {
-            return .{
-                .rn = 0b11111,
-                .imm6 = 0,
-                .shift = .Lsl,
-            };
-        }
-    };
-
-    // Helper functions for assembly syntax functions
-
-    fn orShiftedRegister(
-        rd: Register,
-        rm: Register,
-        shift: RegisterShift,
-        invert: bool,
-    ) Instruction {
-        const n: u1 = if (invert) 1 else 0;
-        switch (rd.size()) {
-            32 => {
-                return Instruction{
-                    .OrShiftedRegister = .{
-                        .rd = rd.id(),
-                        .rn = shift.rn,
-                        .imm6 = shift.imm6,
-                        .rm = rm.id(),
-                        .n = n,
-                        .shift = @enumToInt(shift.shift),
-                        .sf = 0,
-                    },
-                };
-            },
-            64 => {
-                return Instruction{
-                    .OrShiftedRegister = .{
-                        .rd = rd.id(),
-                        .rn = shift.rn,
-                        .imm6 = shift.imm6,
-                        .rm = rm.id(),
-                        .n = n,
-                        .shift = @enumToInt(shift.shift),
-                        .sf = 1,
-                    },
-                };
-            },
-            else => unreachable, // unexpected register size
-        }
-    }
-
     fn moveWideImmediate(
         opc: u2,
         rd: Register,
@@ -671,15 +629,49 @@ pub const Instruction = union(enum) {
         };
     }
 
-    // Bitwise (inclusive) OR of a register value
-
-    pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction {
-        return orShiftedRegister(rd, rm, shift, false);
+    fn logicalShiftedRegister(
+        opc: u2,
+        n: u1,
+        shift: Shift,
+        rd: Register,
+        rn: Register,
+        rm: Register,
+    ) Instruction {
+        switch (rd.size()) {
+            32 => {
+                assert(shift.amount < 32);
+                return Instruction{
+                    .LogicalShiftedRegister = .{
+                        .rd = rd.id(),
+                        .rn = rn.id(),
+                        .imm6 = shift.amount,
+                        .rm = rm.id(),
+                        .n = n,
+                        .shift = @enumToInt(shift.shift),
+                        .opc = opc,
+                        .sf = 0b0,
+                    },
+                };
+            },
+            64 => {
+                return Instruction{
+                    .LogicalShiftedRegister = .{
+                        .rd = rd.id(),
+                        .rn = rn.id(),
+                        .imm6 = shift.amount,
+                        .rm = rm.id(),
+                        .n = n,
+                        .shift = @enumToInt(shift.shift),
+                        .opc = opc,
+                        .sf = 0b1,
+                    },
+                };
+            },
+            else => unreachable, // unexpected register size
+        }
     }
 
-    pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
-        return orShiftedRegister(rd, rm, shift, true);
-    }
+    // Helper functions for assembly syntax functions
 
     // Move wide (immediate)
 
@@ -823,6 +815,40 @@ pub const Instruction = union(enum) {
     pub fn nop() Instruction {
         return Instruction{ .NoOperation = {} };
     }
+
+    // Logical (shifted register)
+
+    pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm);
+    }
+
+    pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm);
+    }
+
+    pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm);
+    }
+
+    pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm);
+    }
+
+    pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm);
+    }
+
+    pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm);
+    }
+
+    pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm);
+    }
+
+    pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
+        return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm);
+    }
 };
 
 test "" {
@@ -836,15 +862,15 @@ test "serialize instructions" {
     };
 
     const testcases = [_]Testcase{
-        .{ // orr x0 x1
-            .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
+        .{ // orr x0, xzr, x1
+            .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none),
             .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
         },
-        .{ // orn x0 x1
-            .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()),
+        .{ // orn x0, xzr, x1
+            .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none),
             .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
         },
-        .{ // movz x1 #4
+        .{ // movz x1, #4
             .inst = Instruction.movz(.x1, 4, 0),
             .expected = 0b1_10_100101_00_0000000000000100_00001,
         },
@@ -944,6 +970,14 @@ test "serialize instructions" {
             .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)),
             .expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
         },
+        .{ // and x0, x4, x2
+            .inst = Instruction.@"and"(.x0, .x4, .x2, .{}),
+            .expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
+        },
+        .{ // and x0, x4, x2, lsl #0x8
+            .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }),
+            .expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
+        },
     };
 
     for (testcases) |case| {
src/codegen.zig
@@ -2415,19 +2415,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                         try self.genSetReg(inst.base.src, reg, arg);
                     }
 
-                    // TODO move this to lib/std/{elf, macho}.zig, etc.
-                    const is_syscall_inst = switch (self.bin_file.tag) {
-                        .macho => mem.eql(u8, inst.asm_source, "svc #0x80"),
-                        .elf => mem.eql(u8, inst.asm_source, "svc #0"),
-                        else => |tag| return self.fail(inst.base.src, "TODO implement aarch64 support for other syscall instructions for file format: '{}'", .{tag}),
-                    };
-                    if (is_syscall_inst) {
-                        const imm16: u16 = switch (self.bin_file.tag) {
-                            .macho => 0x80,
-                            .elf => 0,
-                            else => unreachable,
-                        };
-                        mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(imm16).toU32());
+                    if (mem.eql(u8, inst.asm_source, "svc #0")) {
+                        mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
+                    } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {
+                        mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
                     } else {
                         return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
                     }
@@ -2876,8 +2867,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                                 // mov r, x0
                                 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
                                     reg,
+                                    .xzr,
                                     .x0,
-                                    Instruction.RegisterShift.none(),
+                                    Instruction.Shift.none,
                                 ).toU32());
                                 // ldr x28, [sp], #16
                                 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
@@ -2908,8 +2900,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                                 // mov r, x0
                                 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
                                     reg,
+                                    .xzr,
                                     .x0,
-                                    Instruction.RegisterShift.none(),
+                                    Instruction.Shift.none,
                                 ).toU32());
                                 // ldp x0, x28, [sp, #16]
                                 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(