diff options
| author | joachimschmidt557 <joachim.schmidt557@outlook.com> | 2021-10-30 16:16:22 +0200 |
|---|---|---|
| committer | joachimschmidt557 <joachim.schmidt557@outlook.com> | 2021-10-31 12:32:07 +0100 |
| commit | 9471e3da353baef541e620ac6853a8dcc1160614 (patch) | |
| tree | 7f257f2cbc63ad3adfa854bda8d5cf8279dd6ec5 /src | |
| parent | 7fc89f64b46aa2c1430b3098281ec29d22419ee7 (diff) | |
| download | zig-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.zig | 115 | ||||
| -rw-r--r-- | src/arch/aarch64/Emit.zig | 107 | ||||
| -rw-r--r-- | src/arch/aarch64/Mir.zig | 37 |
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, +}; |
