Commit c749b78df5
Changed files (3)
src
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.