Commit c749b78df5

Jakub Konka <kubkon@jakubkonka.com>
2020-11-25 19:58:15
stage2 macho: add orr and orn instructions
1 parent 10942e3
Changed files (3)
src/codegen/aarch64.zig
@@ -19,6 +19,8 @@ pub const Register = enum(u6) {
     w16, w17, w18, w19, w20, w21, w22, w23,
     w24, w25, w26, w27, w28, w29, w30, wzr,
 
+    pub const sp = .xzr;
+
     pub fn id(self: Register) u5 {
         return @truncate(u5, @enumToInt(self));
     }
@@ -195,6 +197,17 @@ 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,
@@ -251,6 +264,7 @@ pub const Instruction = union(enum) {
 
     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),
@@ -379,8 +393,65 @@ pub const Instruction = union(enum) {
         }
     };
 
+    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,
@@ -543,6 +614,16 @@ 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);
+    }
+
+    pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
+        return orShiftedRegister(rd, rm, shift, true);
+    }
+
     // Move wide (immediate)
 
     pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
@@ -653,6 +734,14 @@ test "serialize instructions" {
     };
 
     const testcases = [_]Testcase{
+        .{ // orr x0 x1
+            .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
+            .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
+        },
+        .{ // orn x0 x1
+            .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()),
+            .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
+        },
         .{ // movz x1 #4
             .inst = Instruction.movz(.x1, 4, 0),
             .expected = 0b1_10_100101_00_0000000000000100_00001,
src/link/MachO.zig
@@ -1028,7 +1028,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
         } else {
             const displacement = @intCast(u27, target_addr - this_addr);
             var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
-            mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
+            mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
         }
     }
 
@@ -1670,10 +1670,10 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
     } else {
         const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]);
         const symbol_off = @intCast(i21, pos_symbol_off) * -1;
-        // adr .x0 [-disp]
-        mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x1, symbol_off).toU32());
-        // ret
-        mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(null).toU32());
+        // adr x0, #-disp
+        mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
+        // ret x28
+        mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
     }
     log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
     try self.base.file.?.pwriteAll(&code, off);
src/codegen.zig
@@ -2588,17 +2588,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             // For MachO, the binary, with the exception of object files, has to be a PIE.
                             // Therefore we cannot load an absolute address.
                             // Instead, we need to make use of PC-relative addressing.
-                            // if (reg.id() == 0) { // x0 is special-cased
+                            // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
+                            // like described here:
+                            // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
+                            // TODO As far as branching is concerned, instead of saving the return address
+                            // in a register, I'm thinking here of immitating x86_64, and having the address
+                            // passed on the stack.
+                            if (reg.id() == 0) { // x0 is special-cased
+                                // str x28, [sp, #-16]
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
+                                    .offset = Instruction.Offset.imm_pre_index(-16),
+                                }).toU32());
+                                // adr x28, #8
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
                                 try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{
                                     .address = addr,
                                     .start = self.code.items.len,
                                     .len = 4,
                                 });
-                                // bl [label]
-                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
-                            // } else {
-                            //     unreachable; // TODO
-                            // }
+                                // b [label]
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
+                                // mov r, x0
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32());
+                                // ldr x28, [sp], #16
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
+                                    .rn = Register.sp,
+                                    .offset = Instruction.Offset.imm_post_index(16),
+                                }).toU32());
+                            } else {
+                                // str x28, [sp, #-16]
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
+                                    .offset = Instruction.Offset.imm_pre_index(-16),
+                                }).toU32());
+                                // str x0, [sp, #-16]
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{
+                                    .offset = Instruction.Offset.imm_pre_index(-16),
+                                }).toU32());
+                                // adr x28, #8
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
+                                try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{
+                                    .address = addr,
+                                    .start = self.code.items.len,
+                                    .len = 4,
+                                });
+                                // b [label]
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
+                                // mov r, x0
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32());
+                                // ldr x0, [sp], #16
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{
+                                    .rn = Register.sp,
+                                    .offset = Instruction.Offset.imm_post_index(16),
+                                }).toU32());
+                                // ldr x28, [sp], #16
+                                mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
+                                    .rn = Register.sp,
+                                    .offset = Instruction.Offset.imm_post_index(16),
+                                }).toU32());
+                            }
                         } else {
                             // The value is in memory at a hard-coded address.
                             // If the type is a pointer, it means the pointer address is at this memory location.