aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjoachimschmidt557 <joachim.schmidt557@outlook.com>2021-10-30 16:16:22 +0200
committerjoachimschmidt557 <joachim.schmidt557@outlook.com>2021-10-31 12:32:07 +0100
commit9471e3da353baef541e620ac6853a8dcc1160614 (patch)
tree7f257f2cbc63ad3adfa854bda8d5cf8279dd6ec5 /src
parent7fc89f64b46aa2c1430b3098281ec29d22419ee7 (diff)
downloadzig-9471e3da353baef541e620ac6853a8dcc1160614.tar.gz
zig-9471e3da353baef541e620ac6853a8dcc1160614.zip
stage2 AArch64 Emit: implement call_extern and load_memory
Diffstat (limited to 'src')
-rw-r--r--src/arch/aarch64/CodeGen.zig115
-rw-r--r--src/arch/aarch64/Emit.zig107
-rw-r--r--src/arch/aarch64/Mir.zig37
3 files changed, 175 insertions, 84 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index 1b68a938b2..9174c70ca7 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -315,6 +315,7 @@ pub fn generate(
var emit = Emit{
.mir = mir,
+ .bin_file = bin_file,
.target = &bin_file.options.target,
.code = code,
};
@@ -337,6 +338,25 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
return result_index;
}
+pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
+ const fields = std.meta.fields(@TypeOf(extra));
+ try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
+ return self.addExtraAssumeCapacity(extra);
+}
+
+pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
+ const fields = std.meta.fields(@TypeOf(extra));
+ const result = @intCast(u32, self.mir_extra.items.len);
+ inline for (fields) |field| {
+ self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
+ u32 => @field(extra, field.name),
+ i32 => @bitCast(u32, @field(extra, field.name)),
+ else => @compileError("bad field type"),
+ });
+ }
+ return result;
+}
+
fn gen(self: *Self) !void {
const cc = self.fn_type.fnCallingConvention();
if (cc != .Naked) {
@@ -1537,26 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
} else if (func_value.castTag(.extern_fn)) |func_payload| {
const decl = func_payload.data;
const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
- const offset = blk: {
- // TODO add a pseudo-instruction
- const offset = @intCast(u32, self.mir_instructions.len);
- // bl
- // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
- _ = try self.addInst(.{
- .tag = .bl,
- .data = .{ .nop = {} },
- });
- break :blk offset;
- };
- // Add relocation to the decl.
- try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
- .offset = offset,
- .target = .{ .global = n_strx },
- .addend = 0,
- .subtractor = null,
- .pcrel = true,
- .length = 2,
- .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
+
+ _ = try self.addInst(.{
+ .tag = .call_extern,
+ .data = .{ .extern_fn = n_strx },
});
} else {
return self.fail("TODO implement calling bitcasted functions", .{});
@@ -2185,70 +2189,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
});
},
.memory => |addr| {
- if (self.bin_file.options.pie) {
- // PC-relative displacement to the entry in the GOT table.
- // adrp
- // TODO add a pseudo instruction
- const offset = @intCast(u32, self.mir_instructions.len);
- // mem.writeIntLittle(
- // u32,
- // try self.code.addManyAsArray(4),
- // Instruction.adrp(reg, 0).toU32(),
- // );
- _ = try self.addInst(.{
- .tag = .nop,
- .data = .{ .nop = {} },
- });
-
- // ldr reg, reg, offset
- _ = try self.addInst(.{
- .tag = .ldr,
- .data = .{ .load_store_register = .{
- .rt = reg,
- .rn = reg,
- .offset = Instruction.LoadStoreOffset.imm(0),
- } },
- });
-
- if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- // TODO I think the reloc might be in the wrong place.
- const decl = macho_file.active_decl.?;
- // Page reloc for adrp instruction.
- try decl.link.macho.relocs.append(self.bin_file.allocator, .{
- .offset = offset,
- .target = .{ .local = @intCast(u32, addr) },
- .addend = 0,
- .subtractor = null,
- .pcrel = true,
- .length = 2,
- .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
- });
- // Pageoff reloc for adrp instruction.
- try decl.link.macho.relocs.append(self.bin_file.allocator, .{
- .offset = offset + 4,
- .target = .{ .local = @intCast(u32, addr) },
- .addend = 0,
- .subtractor = null,
- .pcrel = false,
- .length = 2,
- .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
- });
- } else {
- return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
- }
- } 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.
- try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
- _ = try self.addInst(.{
- .tag = .ldr,
- .data = .{ .load_store_register = .{
- .rt = reg,
- .rn = reg,
- .offset = Instruction.LoadStoreOffset.none,
- } },
- });
- }
+ _ = try self.addInst(.{
+ .tag = .load_memory,
+ .data = .{ .payload = try self.addExtra(Mir.LoadMemory{
+ .register = @enumToInt(reg),
+ .addr = @intCast(u32, addr),
+ }) },
+ });
},
.stack_offset => |unadjusted_off| {
// TODO: maybe addressing from sp instead of fp
diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig
index 7d399d3b07..5d9fc1f210 100644
--- a/src/arch/aarch64/Emit.zig
+++ b/src/arch/aarch64/Emit.zig
@@ -3,11 +3,16 @@
const Emit = @This();
const std = @import("std");
+const math = std.math;
const Mir = @import("Mir.zig");
const bits = @import("bits.zig");
+const link = @import("../../link.zig");
+const assert = std.debug.assert;
const Instruction = bits.Instruction;
+const Register = bits.Register;
mir: Mir,
+bin_file: *link.File,
target: *const std.Target,
code: *std.ArrayList(u8),
@@ -31,6 +36,10 @@ pub fn emitMir(
.brk => try emit.mirExceptionGeneration(inst),
.svc => try emit.mirExceptionGeneration(inst),
+ .call_extern => try emit.mirCallExtern(inst),
+
+ .load_memory => try emit.mirLoadMemory(inst),
+
.ldp => try emit.mirLoadStoreRegisterPair(inst),
.stp => try emit.mirLoadStoreRegisterPair(inst),
@@ -57,6 +66,20 @@ fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
}
+fn moveImmediate(emit: *Emit, reg: Register, imm64: u64) !void {
+ try emit.writeInstruction(Instruction.movz(reg, @truncate(u16, imm64), 0));
+
+ if (imm64 > math.maxInt(u16)) {
+ try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 16), 16));
+ }
+ if (imm64 > math.maxInt(u32)) {
+ try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 32), 32));
+ }
+ if (imm64 > math.maxInt(u48)) {
+ try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 48), 48));
+ }
+}
+
fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh;
@@ -113,6 +136,90 @@ fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
}
}
+fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
+ assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
+ const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
+
+ if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
+ const offset = blk: {
+ const offset = @intCast(u32, emit.code.items.len);
+ // bl
+ try emit.writeInstruction(Instruction.bl(0));
+ break :blk offset;
+ };
+ // Add relocation to the decl.
+ try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{
+ .offset = offset,
+ .target = .{ .global = n_strx },
+ .addend = 0,
+ .subtractor = null,
+ .pcrel = true,
+ .length = 2,
+ .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
+ });
+ } else {
+ @panic("Implement call_extern for linking backends != MachO");
+ }
+}
+
+fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void {
+ assert(emit.mir.instructions.items(.tag)[inst] == .load_memory);
+ const payload = emit.mir.instructions.items(.data)[inst].payload;
+ const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data;
+ const reg = @intToEnum(Register, load_memory.register);
+ const addr = load_memory.addr;
+
+ if (emit.bin_file.options.pie) {
+ // PC-relative displacement to the entry in the GOT table.
+ // adrp
+ const offset = @intCast(u32, emit.code.items.len);
+ try emit.writeInstruction(Instruction.adrp(reg, 0));
+
+ // ldr reg, reg, offset
+ try emit.writeInstruction(Instruction.ldr(reg, .{
+ .register = .{
+ .rn = reg,
+ .offset = Instruction.LoadStoreOffset.imm(0),
+ },
+ }));
+
+ if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
+ // TODO I think the reloc might be in the wrong place.
+ const decl = macho_file.active_decl.?;
+ // Page reloc for adrp instruction.
+ try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
+ .offset = offset,
+ .target = .{ .local = addr },
+ .addend = 0,
+ .subtractor = null,
+ .pcrel = true,
+ .length = 2,
+ .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
+ });
+ // Pageoff reloc for adrp instruction.
+ try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
+ .offset = offset + 4,
+ .target = .{ .local = addr },
+ .addend = 0,
+ .subtractor = null,
+ .pcrel = false,
+ .length = 2,
+ .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
+ });
+ } else {
+ return @panic("TODO implement load_memory for PIE GOT indirection on this platform");
+ }
+ } 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.
+ try emit.moveImmediate(reg, addr);
+ try emit.writeInstruction(Instruction.ldr(
+ reg,
+ .{ .register = .{ .rn = reg, .offset = Instruction.LoadStoreOffset.none } },
+ ));
+ }
+}
+
fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair;
diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig
index 0f83850940..765f279eb4 100644
--- a/src/arch/aarch64/Mir.zig
+++ b/src/arch/aarch64/Mir.zig
@@ -34,6 +34,12 @@ pub const Inst = struct {
blr,
/// Breakpoint
brk,
+ /// Pseudo-instruction: Call extern
+ call_extern,
+ /// Psuedo-instruction: Load memory
+ ///
+ /// Payload is `LoadMemory`
+ load_memory,
/// Load Pair of Registers
ldp,
/// Load Register
@@ -89,11 +95,17 @@ pub const Inst = struct {
///
/// Used by e.g. b
inst: Index,
+ /// An extern function
+ ///
+ /// Used by e.g. call_extern
+ extern_fn: u32,
/// A 16-bit immediate value.
///
/// Used by e.g. svc
imm16: u16,
/// Index into `extra`. Meaning of what can be found there is context-dependent.
+ ///
+ /// Used by e.g. load_memory
payload: u32,
/// A register
///
@@ -156,3 +168,28 @@ pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
gpa.free(mir.extra);
mir.* = undefined;
}
+
+/// Returns the requested data, as well as the new index which is at the start of the
+/// trailers for the object.
+pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
+ const fields = std.meta.fields(T);
+ var i: usize = index;
+ var result: T = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.field_type) {
+ u32 => mir.extra[i],
+ i32 => @bitCast(i32, mir.extra[i]),
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return .{
+ .data = result,
+ .end = i,
+ };
+}
+
+pub const LoadMemory = struct {
+ register: u32,
+ addr: u32,
+};