| author | |
| committer | |
| log | c749b78df50160bedae40f90765442dd1f49de3a |
| tree | 18db7b29753f67f787ff2c3de696af673ce0b5cf |
| parent | 10942e3f86c03d30833cc221371f30b78a4bd710 |
3 files changed, 147 insertions(+), 11 deletions(-)
src/codegen.zig+53-6| ... | @@ -2588,17 +2588,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2588,17 +2588,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2588 | // For MachO, the binary, with the exception of object files, has to be a PIE. | 2588 | // For MachO, the binary, with the exception of object files, has to be a PIE. |
| 2589 | // Therefore we cannot load an absolute address. | 2589 | // Therefore we cannot load an absolute address. |
| 2590 | // Instead, we need to make use of PC-relative addressing. | 2590 | // Instead, we need to make use of PC-relative addressing. |
| 2591 | // if (reg.id() == 0) { // x0 is special-cased | 2591 | // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack |
| 2592 | // like described here: | ||
| 2593 | // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) | ||
| 2594 | // TODO As far as branching is concerned, instead of saving the return address | ||
| 2595 | // in a register, I'm thinking here of immitating x86_64, and having the address | ||
| 2596 | // passed on the stack. | ||
| 2597 | if (reg.id() == 0) { // x0 is special-cased | ||
| 2598 | // str x28, [sp, #-16] | ||
| 2599 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ | ||
| 2600 | .offset = Instruction.Offset.imm_pre_index(-16), | ||
| 2601 | }).toU32()); | ||
| 2602 | // adr x28, #8 | ||
| 2603 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); | ||
| 2592 | try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ | 2604 | try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ |
| 2593 | .address = addr, | 2605 | .address = addr, |
| 2594 | .start = self.code.items.len, | 2606 | .start = self.code.items.len, |
| 2595 | .len = 4, | 2607 | .len = 4, |
| 2596 | }); | 2608 | }); |
| 2597 | // bl [label] | 2609 | // b [label] |
| 2598 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); | 2610 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); |
| 2599 | // } else { | 2611 | // mov r, x0 |
| 2600 | // unreachable; // TODO | 2612 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); |
| 2601 | // } | 2613 | // ldr x28, [sp], #16 |
| 2614 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ | ||
| 2615 | .rn = Register.sp, | ||
| 2616 | .offset = Instruction.Offset.imm_post_index(16), | ||
| 2617 | }).toU32()); | ||
| 2618 | } else { | ||
| 2619 | // str x28, [sp, #-16] | ||
| 2620 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ | ||
| 2621 | .offset = Instruction.Offset.imm_pre_index(-16), | ||
| 2622 | }).toU32()); | ||
| 2623 | // str x0, [sp, #-16] | ||
| 2624 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{ | ||
| 2625 | .offset = Instruction.Offset.imm_pre_index(-16), | ||
| 2626 | }).toU32()); | ||
| 2627 | // adr x28, #8 | ||
| 2628 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); | ||
| 2629 | try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ | ||
| 2630 | .address = addr, | ||
| 2631 | .start = self.code.items.len, | ||
| 2632 | .len = 4, | ||
| 2633 | }); | ||
| 2634 | // b [label] | ||
| 2635 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); | ||
| 2636 | // mov r, x0 | ||
| 2637 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); | ||
| 2638 | // ldr x0, [sp], #16 | ||
| 2639 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{ | ||
| 2640 | .rn = Register.sp, | ||
| 2641 | .offset = Instruction.Offset.imm_post_index(16), | ||
| 2642 | }).toU32()); | ||
| 2643 | // ldr x28, [sp], #16 | ||
| 2644 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ | ||
| 2645 | .rn = Register.sp, | ||
| 2646 | .offset = Instruction.Offset.imm_post_index(16), | ||
| 2647 | }).toU32()); | ||
| 2648 | } | ||
| 2602 | } else { | 2649 | } else { |
| 2603 | // The value is in memory at a hard-coded address. | 2650 | // The value is in memory at a hard-coded address. |
| 2604 | // If the type is a pointer, it means the pointer address is at this memory location. | 2651 | // If the type is a pointer, it means the pointer address is at this memory location. |
src/codegen/aarch64.zig+89| ... | @@ -19,6 +19,8 @@ pub const Register = enum(u6) { | ... | @@ -19,6 +19,8 @@ pub const Register = enum(u6) { |
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | 19 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | 20 | w24, w25, w26, w27, w28, w29, w30, wzr, |
| 21 | 21 | ||
| 22 | pub const sp = .xzr; | ||
| 23 | |||
| 22 | pub fn id(self: Register) u5 { | 24 | pub fn id(self: Register) u5 { |
| 23 | return @truncate(u5, @enumToInt(self)); | 25 | return @truncate(u5, @enumToInt(self)); |
| 24 | } | 26 | } |
| ... | @@ -195,6 +197,17 @@ test "FloatingPointRegister.toX" { | ... | @@ -195,6 +197,17 @@ test "FloatingPointRegister.toX" { |
| 195 | 197 | ||
| 196 | /// Represents an instruction in the AArch64 instruction set | 198 | /// Represents an instruction in the AArch64 instruction set |
| 197 | pub const Instruction = union(enum) { | 199 | pub const Instruction = union(enum) { |
| 200 | OrShiftedRegister: packed struct { | ||
| 201 | rd: u5, | ||
| 202 | rn: u5, | ||
| 203 | imm6: u6, | ||
| 204 | rm: u5, | ||
| 205 | n: u1, | ||
| 206 | shift: u2, | ||
| 207 | fixed: u5 = 0b01010, | ||
| 208 | opc: u2 = 0b01, | ||
| 209 | sf: u1, | ||
| 210 | }, | ||
| 198 | MoveWideImmediate: packed struct { | 211 | MoveWideImmediate: packed struct { |
| 199 | rd: u5, | 212 | rd: u5, |
| 200 | imm16: u16, | 213 | imm16: u16, |
| ... | @@ -251,6 +264,7 @@ pub const Instruction = union(enum) { | ... | @@ -251,6 +264,7 @@ pub const Instruction = union(enum) { |
| 251 | 264 | ||
| 252 | pub fn toU32(self: Instruction) u32 { | 265 | pub fn toU32(self: Instruction) u32 { |
| 253 | return switch (self) { | 266 | return switch (self) { |
| 267 | .OrShiftedRegister => |v| @bitCast(u32, v), | ||
| 254 | .MoveWideImmediate => |v| @bitCast(u32, v), | 268 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 255 | .PCRelativeAddress => |v| @bitCast(u32, v), | 269 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 256 | .LoadStoreRegister => |v| @bitCast(u32, v), | 270 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| ... | @@ -379,8 +393,65 @@ pub const Instruction = union(enum) { | ... | @@ -379,8 +393,65 @@ pub const Instruction = union(enum) { |
| 379 | } | 393 | } |
| 380 | }; | 394 | }; |
| 381 | 395 | ||
| 396 | pub const RegisterShift = struct { | ||
| 397 | rn: u5, | ||
| 398 | imm6: u6, | ||
| 399 | shift: enum(u2) { | ||
| 400 | Lsl = 0, | ||
| 401 | Lsr = 1, | ||
| 402 | Asr = 2, | ||
| 403 | Ror = 3, | ||
| 404 | }, | ||
| 405 | |||
| 406 | pub fn none() RegisterShift { | ||
| 407 | return .{ | ||
| 408 | .rn = 0b11111, | ||
| 409 | .imm6 = 0, | ||
| 410 | .shift = .Lsl, | ||
| 411 | }; | ||
| 412 | } | ||
| 413 | }; | ||
| 414 | |||
| 382 | // Helper functions for assembly syntax functions | 415 | // Helper functions for assembly syntax functions |
| 383 | 416 | ||
| 417 | fn orShiftedRegister( | ||
| 418 | rd: Register, | ||
| 419 | rm: Register, | ||
| 420 | shift: RegisterShift, | ||
| 421 | invert: bool, | ||
| 422 | ) Instruction { | ||
| 423 | const n: u1 = if (invert) 1 else 0; | ||
| 424 | switch (rd.size()) { | ||
| 425 | 32 => { | ||
| 426 | return Instruction{ | ||
| 427 | .OrShiftedRegister = .{ | ||
| 428 | .rd = rd.id(), | ||
| 429 | .rn = shift.rn, | ||
| 430 | .imm6 = shift.imm6, | ||
| 431 | .rm = rm.id(), | ||
| 432 | .n = n, | ||
| 433 | .shift = @enumToInt(shift.shift), | ||
| 434 | .sf = 0, | ||
| 435 | }, | ||
| 436 | }; | ||
| 437 | }, | ||
| 438 | 64 => { | ||
| 439 | return Instruction{ | ||
| 440 | .OrShiftedRegister = .{ | ||
| 441 | .rd = rd.id(), | ||
| 442 | .rn = shift.rn, | ||
| 443 | .imm6 = shift.imm6, | ||
| 444 | .rm = rm.id(), | ||
| 445 | .n = n, | ||
| 446 | .shift = @enumToInt(shift.shift), | ||
| 447 | .sf = 1, | ||
| 448 | }, | ||
| 449 | }; | ||
| 450 | }, | ||
| 451 | else => unreachable, // unexpected register size | ||
| 452 | } | ||
| 453 | } | ||
| 454 | |||
| 384 | fn moveWideImmediate( | 455 | fn moveWideImmediate( |
| 385 | opc: u2, | 456 | opc: u2, |
| 386 | rd: Register, | 457 | rd: Register, |
| ... | @@ -543,6 +614,16 @@ pub const Instruction = union(enum) { | ... | @@ -543,6 +614,16 @@ pub const Instruction = union(enum) { |
| 543 | }; | 614 | }; |
| 544 | } | 615 | } |
| 545 | 616 | ||
| 617 | // Bitwise (inclusive) OR of a register value | ||
| 618 | |||
| 619 | pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction { | ||
| 620 | return orShiftedRegister(rd, rm, shift, false); | ||
| 621 | } | ||
| 622 | |||
| 623 | pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction { | ||
| 624 | return orShiftedRegister(rd, rm, shift, true); | ||
| 625 | } | ||
| 626 | |||
| 546 | // Move wide (immediate) | 627 | // Move wide (immediate) |
| 547 | 628 | ||
| 548 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { | 629 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { |
| ... | @@ -653,6 +734,14 @@ test "serialize instructions" { | ... | @@ -653,6 +734,14 @@ test "serialize instructions" { |
| 653 | }; | 734 | }; |
| 654 | 735 | ||
| 655 | const testcases = [_]Testcase{ | 736 | const testcases = [_]Testcase{ |
| 737 | .{ // orr x0 x1 | ||
| 738 | .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()), | ||
| 739 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | ||
| 740 | }, | ||
| 741 | .{ // orn x0 x1 | ||
| 742 | .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()), | ||
| 743 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | ||
| 744 | }, | ||
| 656 | .{ // movz x1 #4 | 745 | .{ // movz x1 #4 |
| 657 | .inst = Instruction.movz(.x1, 4, 0), | 746 | .inst = Instruction.movz(.x1, 4, 0), |
| 658 | .expected = 0b1_10_100101_00_0000000000000100_00001, | 747 | .expected = 0b1_10_100101_00_0000000000000100_00001, |
src/link/MachO.zig+5-5| ... | @@ -1028,7 +1028,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1028,7 +1028,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1028 | } else { | 1028 | } else { |
| 1029 | const displacement = @intCast(u27, target_addr - this_addr); | 1029 | const displacement = @intCast(u27, target_addr - this_addr); |
| 1030 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | 1030 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| 1031 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); | 1031 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); |
| 1032 | } | 1032 | } |
| 1033 | } | 1033 | } |
| 1034 | 1034 | ||
| ... | @@ -1670,10 +1670,10 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -1670,10 +1670,10 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1670 | } else { | 1670 | } else { |
| 1671 | const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); | 1671 | const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); |
| 1672 | const symbol_off = @intCast(i21, pos_symbol_off) * -1; | 1672 | const symbol_off = @intCast(i21, pos_symbol_off) * -1; |
| 1673 | // adr .x0 [-disp] | 1673 | // adr x0, #-disp |
| 1674 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x1, symbol_off).toU32()); | 1674 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); |
| 1675 | // ret | 1675 | // ret x28 |
| 1676 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(null).toU32()); | 1676 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); |
| 1677 | } | 1677 | } |
| 1678 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); | 1678 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); |
| 1679 | try self.base.file.?.pwriteAll(&code, off); | 1679 | try self.base.file.?.pwriteAll(&code, off); |