Commit b25cf7db02

Jakub Konka <kubkon@jakubkonka.com>
2021-01-17 11:26:02
stage2 aarch64: add basic function pro/epilogue
Fix typo in `nop` implementation. Simplify `aarch64` macOS tests.
1 parent e292f33
Changed files (3)
src
test
src/codegen/aarch64.zig
@@ -814,7 +814,7 @@ pub const Instruction = union(enum) {
     // Nop
 
     pub fn nop() Instruction {
-        return Instruction{ .NoOperation = {} };
+        return Instruction{ .NoOperation = .{} };
     }
 
     // Logical (shifted register)
src/codegen.zig
@@ -637,6 +637,67 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                         try self.dbgSetEpilogueBegin();
                     }
                 },
+                .aarch64, .aarch64_be, .aarch64_32 => {
+                    const cc = self.fn_type.fnCallingConvention();
+                    if (cc != .Naked) {
+                        // TODO Finish function prologue and epilogue for aarch64.
+                        // Reserve the stack for local variables, etc.
+
+                        // stp fp, lr, [sp, #-16]!
+                        writeInt(u32, try self.code.addManyAsArray(4), Instruction.stp(
+                            .x29,
+                            .x30,
+                            Register.sp,
+                            Instruction.LoadStorePairOffset.pre_index(-16),
+                        ).toU32());
+
+                        try self.dbgSetPrologueEnd();
+
+                        try self.genBody(self.mod_fn.body);
+
+                        try self.dbgSetEpilogueBegin();
+
+                        // exitlude jumps
+                        if (self.exitlude_jump_relocs.items.len == 1) {
+                            // There is only one relocation. Hence,
+                            // this relocation must be at the end of
+                            // the code. Therefore, we can just delete
+                            // the space initially reserved for the
+                            // jump
+                            self.code.items.len -= 4;
+                        } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
+                            const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);
+                            if (amt == -4) {
+                                // This return is at the end of the
+                                // code block. We can't just delete
+                                // the space because there may be
+                                // other jumps we already relocated to
+                                // the address. Instead, insert a nop
+                                writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
+                            } else {
+                                if (math.cast(i28, amt)) |offset| {
+                                    writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(offset).toU32());
+                                } else |err| {
+                                    return self.failSymbol("exitlude jump is too large", .{});
+                                }
+                            }
+                        }
+
+                        // ldp fp, lr, [sp], #16
+                        writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldp(
+                            .x29,
+                            .x30,
+                            Register.sp,
+                            Instruction.LoadStorePairOffset.post_index(16),
+                        ).toU32());
+                        // ret lr
+                        writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
+                    } else {
+                        try self.dbgSetPrologueEnd();
+                        try self.genBody(self.mod_fn.body);
+                        try self.dbgSetEpilogueBegin();
+                    }
+                },
                 else => {
                     try self.dbgSetPrologueEnd();
                     try self.genBody(self.mod_fn.body);
@@ -1962,8 +2023,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
                 },
                 .aarch64 => {
-                    // TODO: relocations
-                    writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
+                    // Just add space for an instruction, patch this later
+                    try self.code.resize(self.code.items.len + 4);
+                    try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
                 },
                 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
             }
test/stage2/aarch64.zig
@@ -17,97 +17,60 @@ pub fn addCases(ctx: *TestContext) !void {
 
         // Regular old hello world
         case.addCompareOutput(
+            \\extern "c" fn write(usize, usize, usize) void;
+            \\extern "c" fn exit(usize) noreturn;
+            \\
             \\export fn _start() noreturn {
             \\    print();
             \\
-            \\    exit();
+            \\    exit(0);
             \\}
             \\
             \\fn print() void {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (4),
-            \\          [arg1] "{x0}" (1),
-            \\          [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
-            \\          [arg3] "{x2}" (14)
-            \\        : "memory"
-            \\    );
-            \\    return;
-            \\}
-            \\
-            \\fn exit() noreturn {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (1),
-            \\          [arg1] "{x0}" (0)
-            \\        : "memory"
-            \\    );
-            \\    unreachable;
+            \\    const msg = @ptrToInt("Hello, World!\n");
+            \\    const len = 14;
+            \\    write(1, msg, len);
             \\}
         ,
             "Hello, World!\n",
         );
+
         // Now change the message only
         case.addCompareOutput(
+            \\extern "c" fn write(usize, usize, usize) void;
+            \\extern "c" fn exit(usize) noreturn;
+            \\
             \\export fn _start() noreturn {
             \\    print();
             \\
-            \\    exit();
+            \\    exit(0);
             \\}
             \\
             \\fn print() void {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (4),
-            \\          [arg1] "{x0}" (1),
-            \\          [arg2] "{x1}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
-            \\          [arg3] "{x2}" (104)
-            \\        : "memory"
-            \\    );
-            \\    return;
-            \\}
-            \\
-            \\fn exit() noreturn {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (1),
-            \\          [arg1] "{x0}" (0)
-            \\        : "memory"
-            \\    );
-            \\    unreachable;
+            \\    const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
+            \\    const len = 104;
+            \\    write(1, msg, len);
             \\}
         ,
             "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
         );
+
         // Now we print it twice.
         case.addCompareOutput(
+            \\extern "c" fn write(usize, usize, usize) void;
+            \\extern "c" fn exit(usize) noreturn;
+            \\
             \\export fn _start() noreturn {
             \\    print();
             \\    print();
             \\
-            \\    exit();
+            \\    exit(0);
             \\}
             \\
             \\fn print() void {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (4),
-            \\          [arg1] "{x0}" (1),
-            \\          [arg2] "{x1}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
-            \\          [arg3] "{x2}" (104)
-            \\        : "memory"
-            \\    );
-            \\    return;
-            \\}
-            \\
-            \\fn exit() noreturn {
-            \\    asm volatile ("svc #0x80"
-            \\        :
-            \\        : [number] "{x16}" (1),
-            \\          [arg1] "{x0}" (0)
-            \\        : "memory"
-            \\    );
-            \\    unreachable;
+            \\    const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
+            \\    const len = 104;
+            \\    write(1, msg, len);
             \\}
         ,
             \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
@@ -200,24 +163,6 @@ pub fn addCases(ctx: *TestContext) !void {
         );
     }
 
-    {
-        var case = ctx.exe("hello world linked to libc", macos_aarch64);
-
-        // TODO rewrite this test once we handle more int conversions and return args.
-        case.addCompareOutput(
-            \\extern "c" fn write(usize, usize, usize) void;
-            \\extern "c" fn exit(usize) noreturn;
-            \\
-            \\export fn _start() noreturn {
-            \\    write(1, @ptrToInt("Hello,"), 6);
-            \\    write(1, @ptrToInt(" World!\n,"), 8);
-            \\    exit(0);
-            \\}
-        ,
-            "Hello, World!\n",
-        );
-    }
-
     {
         var case = ctx.exe("only libc exit", macos_aarch64);