Commit d7a89f9876

joachimschmidt557 <joachim.schmidt557@outlook.com>
2021-04-08 14:20:05
stage2 AArch64: Add conditional branch instructions
1 parent ab5a445
Changed files (3)
src
codegen
link
src/codegen/aarch64.zig
@@ -200,7 +200,7 @@ test "FloatingPointRegister.toX" {
 
 /// Represents an instruction in the AArch64 instruction set
 pub const Instruction = union(enum) {
-    MoveWideImmediate: packed struct {
+    move_wide_immediate: packed struct {
         rd: u5,
         imm16: u16,
         hw: u2,
@@ -208,14 +208,14 @@ pub const Instruction = union(enum) {
         opc: u2,
         sf: u1,
     },
-    PCRelativeAddress: packed struct {
+    pc_relative_address: packed struct {
         rd: u5,
         immhi: u19,
         fixed: u5 = 0b10000,
         immlo: u2,
         op: u1,
     },
-    LoadStoreRegister: packed struct {
+    load_store_register: packed struct {
         rt: u5,
         rn: u5,
         offset: u12,
@@ -225,7 +225,7 @@ pub const Instruction = union(enum) {
         fixed: u3 = 0b111,
         size: u2,
     },
-    LoadStorePairOfRegisters: packed struct {
+    load_store_register_pair: packed struct {
         rt1: u5,
         rn: u5,
         rt2: u5,
@@ -235,20 +235,20 @@ pub const Instruction = union(enum) {
         fixed: u5 = 0b101_0_0,
         opc: u2,
     },
-    LoadLiteral: packed struct {
+    load_literal: packed struct {
         rt: u5,
         imm19: u19,
         fixed: u6 = 0b011_0_00,
         opc: u2,
     },
-    ExceptionGeneration: packed struct {
+    exception_generation: packed struct {
         ll: u2,
         op2: u3,
         imm16: u16,
         opc: u3,
         fixed: u8 = 0b1101_0100,
     },
-    UnconditionalBranchRegister: packed struct {
+    unconditional_branch_register: packed struct {
         op4: u5,
         rn: u5,
         op3: u6,
@@ -256,15 +256,15 @@ pub const Instruction = union(enum) {
         opc: u4,
         fixed: u7 = 0b1101_011,
     },
-    UnconditionalBranchImmediate: packed struct {
+    unconditional_branch_immediate: packed struct {
         imm26: u26,
         fixed: u5 = 0b00101,
         op: u1,
     },
-    NoOperation: packed struct {
+    no_operation: packed struct {
         fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
     },
-    LogicalShiftedRegister: packed struct {
+    logical_shifted_register: packed struct {
         rd: u5,
         rn: u5,
         imm6: u6,
@@ -275,7 +275,7 @@ pub const Instruction = union(enum) {
         opc: u2,
         sf: u1,
     },
-    AddSubtractImmediate: packed struct {
+    add_subtract_immediate: packed struct {
         rd: u5,
         rn: u5,
         imm12: u12,
@@ -285,6 +285,20 @@ pub const Instruction = union(enum) {
         op: u1,
         sf: u1,
     },
+    conditional_branch: struct {
+        cond: u4,
+        o0: u1,
+        imm19: u19,
+        o1: u1,
+        fixed: u7 = 0b0101010,
+    },
+    compare_and_branch: struct {
+        rt: u5,
+        imm19: u19,
+        op: u1,
+        fixed: u6 = 0b011010,
+        sf: u1,
+    },
 
     pub const Shift = struct {
         shift: Type = .lsl,
@@ -303,19 +317,73 @@ pub const Instruction = union(enum) {
         };
     };
 
+    pub const Condition = enum(u4) {
+        /// Integer: Equal
+        /// Floating point: Equal
+        eq,
+        /// Integer: Not equal
+        /// Floating point: Not equal or unordered
+        ne,
+        /// Integer: Carry set
+        /// Floating point: Greater than, equal, or unordered
+        cs,
+        /// Integer: Carry clear
+        /// Floating point: Less than
+        cc,
+        /// Integer: Minus, negative
+        /// Floating point: Less than
+        mi,
+        /// Integer: Plus, positive or zero
+        /// Floating point: Greater than, equal, or unordered
+        pl,
+        /// Integer: Overflow
+        /// Floating point: Unordered
+        vs,
+        /// Integer: No overflow
+        /// Floating point: Ordered
+        vc,
+        /// Integer: Unsigned higher
+        /// Floating point: Greater than, or unordered
+        hi,
+        /// Integer: Unsigned lower or same
+        /// Floating point: Less than or equal
+        ls,
+        /// Integer: Signed greater than or equal
+        /// Floating point: Greater than or equal
+        ge,
+        /// Integer: Signed less than
+        /// Floating point: Less than, or unordered
+        lt,
+        /// Integer: Signed greater than
+        /// Floating point: Greater than
+        gt,
+        /// Integer: Signed less than or equal
+        /// Floating point: Less than, equal, or unordered
+        le,
+        /// Integer: Always
+        /// Floating point: Always
+        al,
+        /// Integer: Always
+        /// Floating point: Always
+        nv,
+    };
+
     pub fn toU32(self: Instruction) u32 {
         return switch (self) {
-            .MoveWideImmediate => |v| @bitCast(u32, v),
-            .PCRelativeAddress => |v| @bitCast(u32, v),
-            .LoadStoreRegister => |v| @bitCast(u32, v),
-            .LoadStorePairOfRegisters => |v| @bitCast(u32, v),
-            .LoadLiteral => |v| @bitCast(u32, v),
-            .ExceptionGeneration => |v| @bitCast(u32, v),
-            .UnconditionalBranchRegister => |v| @bitCast(u32, v),
-            .UnconditionalBranchImmediate => |v| @bitCast(u32, v),
-            .NoOperation => |v| @bitCast(u32, v),
-            .LogicalShiftedRegister => |v| @bitCast(u32, v),
-            .AddSubtractImmediate => |v| @bitCast(u32, v),
+            .move_wide_immediate => |v| @bitCast(u32, v),
+            .pc_relative_address => |v| @bitCast(u32, v),
+            .load_store_register => |v| @bitCast(u32, v),
+            .load_store_register_pair => |v| @bitCast(u32, v),
+            .load_literal => |v| @bitCast(u32, v),
+            .exception_generation => |v| @bitCast(u32, v),
+            .unconditional_branch_register => |v| @bitCast(u32, v),
+            .unconditional_branch_immediate => |v| @bitCast(u32, v),
+            .no_operation => |v| @bitCast(u32, v),
+            .logical_shifted_register => |v| @bitCast(u32, v),
+            .add_subtract_immediate => |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),
+            .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
         };
     }
 
@@ -329,7 +397,7 @@ pub const Instruction = union(enum) {
             32 => {
                 assert(shift % 16 == 0 and shift <= 16);
                 return Instruction{
-                    .MoveWideImmediate = .{
+                    .move_wide_immediate = .{
                         .rd = rd.id(),
                         .imm16 = imm16,
                         .hw = @intCast(u2, shift / 16),
@@ -341,7 +409,7 @@ pub const Instruction = union(enum) {
             64 => {
                 assert(shift % 16 == 0 and shift <= 48);
                 return Instruction{
-                    .MoveWideImmediate = .{
+                    .move_wide_immediate = .{
                         .rd = rd.id(),
                         .imm16 = imm16,
                         .hw = @intCast(u2, shift / 16),
@@ -358,7 +426,7 @@ pub const Instruction = union(enum) {
         assert(rd.size() == 64);
         const imm21_u = @bitCast(u21, imm21);
         return Instruction{
-            .PCRelativeAddress = .{
+            .pc_relative_address = .{
                 .rd = rd.id(),
                 .immlo = @truncate(u2, imm21_u),
                 .immhi = @truncate(u19, imm21_u >> 2),
@@ -522,7 +590,7 @@ pub const Instruction = union(enum) {
             .str, .strh, .strb => 0b00,
         };
         return Instruction{
-            .LoadStoreRegister = .{
+            .load_store_register = .{
                 .rt = rt.id(),
                 .rn = rn.id(),
                 .offset = off,
@@ -544,7 +612,7 @@ pub const Instruction = union(enum) {
         };
     }
 
-    fn loadStorePairOfRegisters(
+    fn loadStoreRegisterPair(
         rt1: Register,
         rt2: Register,
         rn: Register,
@@ -557,7 +625,7 @@ pub const Instruction = union(enum) {
                 assert(-256 <= offset and offset <= 252);
                 const imm7 = @truncate(u7, @bitCast(u9, offset >> 2));
                 return Instruction{
-                    .LoadStorePairOfRegisters = .{
+                    .load_store_register_pair = .{
                         .rt1 = rt1.id(),
                         .rn = rn.id(),
                         .rt2 = rt2.id(),
@@ -572,7 +640,7 @@ pub const Instruction = union(enum) {
                 assert(-512 <= offset and offset <= 504);
                 const imm7 = @truncate(u7, @bitCast(u9, offset >> 3));
                 return Instruction{
-                    .LoadStorePairOfRegisters = .{
+                    .load_store_register_pair = .{
                         .rt1 = rt1.id(),
                         .rn = rn.id(),
                         .rt2 = rt2.id(),
@@ -591,7 +659,7 @@ pub const Instruction = union(enum) {
         switch (rt.size()) {
             32 => {
                 return Instruction{
-                    .LoadLiteral = .{
+                    .load_literal = .{
                         .rt = rt.id(),
                         .imm19 = imm19,
                         .opc = 0b00,
@@ -600,7 +668,7 @@ pub const Instruction = union(enum) {
             },
             64 => {
                 return Instruction{
-                    .LoadLiteral = .{
+                    .load_literal = .{
                         .rt = rt.id(),
                         .imm19 = imm19,
                         .opc = 0b01,
@@ -618,7 +686,7 @@ pub const Instruction = union(enum) {
         imm16: u16,
     ) Instruction {
         return Instruction{
-            .ExceptionGeneration = .{
+            .exception_generation = .{
                 .ll = ll,
                 .op2 = op2,
                 .imm16 = imm16,
@@ -637,7 +705,7 @@ pub const Instruction = union(enum) {
         assert(rn.size() == 64);
 
         return Instruction{
-            .UnconditionalBranchRegister = .{
+            .unconditional_branch_register = .{
                 .op4 = op4,
                 .rn = rn.id(),
                 .op3 = op3,
@@ -652,7 +720,7 @@ pub const Instruction = union(enum) {
         offset: i28,
     ) Instruction {
         return Instruction{
-            .UnconditionalBranchImmediate = .{
+            .unconditional_branch_immediate = .{
                 .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)),
                 .op = op,
             },
@@ -671,7 +739,7 @@ pub const Instruction = union(enum) {
             32 => {
                 assert(shift.amount < 32);
                 return Instruction{
-                    .LogicalShiftedRegister = .{
+                    .logical_shifted_register = .{
                         .rd = rd.id(),
                         .rn = rn.id(),
                         .imm6 = shift.amount,
@@ -685,7 +753,7 @@ pub const Instruction = union(enum) {
             },
             64 => {
                 return Instruction{
-                    .LogicalShiftedRegister = .{
+                    .logical_shifted_register = .{
                         .rd = rd.id(),
                         .rn = rn.id(),
                         .imm6 = shift.amount,
@@ -710,7 +778,7 @@ pub const Instruction = union(enum) {
         shift: bool,
     ) Instruction {
         return Instruction{
-            .AddSubtractImmediate = .{
+            .add_subtract_immediate = .{
                 .rd = rd.id(),
                 .rn = rn.id(),
                 .imm12 = imm12,
@@ -726,6 +794,43 @@ pub const Instruction = union(enum) {
         };
     }
 
+    fn conditionalBranch(
+        o0: u1,
+        o1: u1,
+        cond: Condition,
+        offset: i21,
+    ) Instruction {
+        assert(offset & 0b11 == 0b00);
+        return Instruction{
+            .conditional_branch = .{
+                .cond = @enumToInt(cond),
+                .o0 = o0,
+                .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
+                .o1 = o1,
+            },
+        };
+    }
+
+    fn compareAndBranch(
+        op: u1,
+        rt: Register,
+        offset: i21,
+    ) Instruction {
+        assert(offset & 0b11 == 0b00);
+        return Instruction{
+            .compare_and_branch = .{
+                .rt = rt.id(),
+                .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
+                .op = op,
+                .sf = switch (rt.size()) {
+                    32 => 0b0,
+                    64 => 0b1,
+                    else => unreachable, // unexpected register size
+                },
+            },
+        };
+    }
+
     // Helper functions for assembly syntax functions
 
     // Move wide (immediate)
@@ -821,19 +926,19 @@ pub const Instruction = union(enum) {
     };
 
     pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
-        return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true);
+        return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true);
     }
 
     pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
-        return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true);
+        return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true);
     }
 
     pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
-        return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false);
+        return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false);
     }
 
     pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
-        return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false);
+        return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false);
     }
 
     // Exception generation
@@ -885,7 +990,7 @@ pub const Instruction = union(enum) {
     // Nop
 
     pub fn nop() Instruction {
-        return Instruction{ .NoOperation = .{} };
+        return Instruction{ .no_operation = .{} };
     }
 
     // Logical (shifted register)
@@ -939,6 +1044,22 @@ pub const Instruction = union(enum) {
     pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
         return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
     }
+
+    // Conditional branch
+
+    pub fn bCond(cond: Condition, offset: i21) Instruction {
+        return conditionalBranch(0b0, 0b0, cond, offset);
+    }
+
+    // Compare and branch
+
+    pub fn cbz(rt: Register, offset: i21) Instruction {
+        return compareAndBranch(0b0, rt, offset);
+    }
+
+    pub fn cbnz(rt: Register, offset: i21) Instruction {
+        return compareAndBranch(0b1, rt, offset);
+    }
 };
 
 test {
@@ -1092,6 +1213,14 @@ test "serialize instructions" {
             .inst = Instruction.subs(.x0, .x5, 11, true),
             .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000,
         },
+        .{ // b.hi #-4
+            .inst = Instruction.bCond(.hi, -4),
+            .expected = 0b0101010_0_1111111111111111111_0_1000,
+        },
+        .{ // cbz x10, #40
+            .inst = Instruction.cbz(.x10, 40),
+            .expected = 0b1_011010_0_0000000000000001010_01010,
+        },
     };
 
     for (testcases) |case| {
src/link/MachO/Zld.zig
@@ -1668,7 +1668,7 @@ fn doRelocs(self: *Zld) !void {
                                 var parsed = mem.bytesAsValue(
                                     meta.TagPayload(
                                         aarch64.Instruction,
-                                        aarch64.Instruction.UnconditionalBranchImmediate,
+                                        aarch64.Instruction.unconditional_branch_immediate,
                                     ),
                                     inst,
                                 );
@@ -1688,7 +1688,7 @@ fn doRelocs(self: *Zld) !void {
                                 var parsed = mem.bytesAsValue(
                                     meta.TagPayload(
                                         aarch64.Instruction,
-                                        aarch64.Instruction.PCRelativeAddress,
+                                        aarch64.Instruction.pc_relative_address,
                                     ),
                                     inst,
                                 );
@@ -1706,7 +1706,7 @@ fn doRelocs(self: *Zld) !void {
                                     var parsed = mem.bytesAsValue(
                                         meta.TagPayload(
                                             aarch64.Instruction,
-                                            aarch64.Instruction.AddSubtractImmediate,
+                                            aarch64.Instruction.add_subtract_immediate,
                                         ),
                                         inst,
                                     );
@@ -1719,7 +1719,7 @@ fn doRelocs(self: *Zld) !void {
                                     var parsed = mem.bytesAsValue(
                                         meta.TagPayload(
                                             aarch64.Instruction,
-                                            aarch64.Instruction.LoadStoreRegister,
+                                            aarch64.Instruction.load_store_register,
                                         ),
                                         inst,
                                     );
@@ -1774,7 +1774,7 @@ fn doRelocs(self: *Zld) !void {
                                         const curr = mem.bytesAsValue(
                                             meta.TagPayload(
                                                 aarch64.Instruction,
-                                                aarch64.Instruction.AddSubtractImmediate,
+                                                aarch64.Instruction.add_subtract_immediate,
                                             ),
                                             inst,
                                         );
@@ -1783,7 +1783,7 @@ fn doRelocs(self: *Zld) !void {
                                         const curr = mem.bytesAsValue(
                                             meta.TagPayload(
                                                 aarch64.Instruction,
-                                                aarch64.Instruction.LoadStoreRegister,
+                                                aarch64.Instruction.load_store_register,
                                             ),
                                             inst,
                                         );
src/link/MachO.zig
@@ -1256,7 +1256,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
                     const inst = code_buffer.items[fixup.offset..][0..4];
                     var parsed = mem.bytesAsValue(meta.TagPayload(
                         aarch64.Instruction,
-                        aarch64.Instruction.PCRelativeAddress,
+                        aarch64.Instruction.pc_relative_address,
                     ), inst);
                     const this_page = @intCast(i32, this_addr >> 12);
                     const target_page = @intCast(i32, target_addr >> 12);
@@ -1268,7 +1268,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
                     const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
                     var parsed = mem.bytesAsValue(meta.TagPayload(
                         aarch64.Instruction,
-                        aarch64.Instruction.LoadStoreRegister,
+                        aarch64.Instruction.load_store_register,
                     ), inst);
                     const narrowed = @truncate(u12, target_addr);
                     const offset = try math.divExact(u12, narrowed, 8);