mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
stage2 AArch64 Emit: implement call_extern and load_memory
This commit is contained in:
parent
7fc89f64b4
commit
9471e3da35
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user