authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-10-30 16:16:22+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-10-31 12:32:07+01:00
log9471e3da353baef541e620ac6853a8dcc1160614
tree7f257f2cbc63ad3adfa854bda8d5cf8279dd6ec5
parent7fc89f64b46aa2c1430b3098281ec29d22419ee7
signaturelock-open Commit is signed but in an unrecognized format.

stage2 AArch64 Emit: implement call_extern and load_memory


3 files changed, 175 insertions(+), 84 deletions(-)

src/arch/aarch64/CodeGen.zig+31-84
......@@ -315,6 +315,7 @@ pub fn generate(
315315
316316 var emit = Emit{
317317 .mir = mir,
318 .bin_file = bin_file,
318319 .target = &bin_file.options.target,
319320 .code = code,
320321 };
......@@ -337,6 +338,25 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
337338 return result_index;
338339}
339340
341pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
342 const fields = std.meta.fields(@TypeOf(extra));
343 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
344 return self.addExtraAssumeCapacity(extra);
345}
346
347pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
348 const fields = std.meta.fields(@TypeOf(extra));
349 const result = @intCast(u32, self.mir_extra.items.len);
350 inline for (fields) |field| {
351 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
352 u32 => @field(extra, field.name),
353 i32 => @bitCast(u32, @field(extra, field.name)),
354 else => @compileError("bad field type"),
355 });
356 }
357 return result;
358}
359
340360fn gen(self: *Self) !void {
341361 const cc = self.fn_type.fnCallingConvention();
342362 if (cc != .Naked) {
......@@ -1537,26 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
15371557 } else if (func_value.castTag(.extern_fn)) |func_payload| {
15381558 const decl = func_payload.data;
15391559 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
1540 const offset = blk: {
1541 // TODO add a pseudo-instruction
1542 const offset = @intCast(u32, self.mir_instructions.len);
1543 // bl
1544 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
1545 _ = try self.addInst(.{
1546 .tag = .bl,
1547 .data = .{ .nop = {} },
1548 });
1549 break :blk offset;
1550 };
1551 // Add relocation to the decl.
1552 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
1553 .offset = offset,
1554 .target = .{ .global = n_strx },
1555 .addend = 0,
1556 .subtractor = null,
1557 .pcrel = true,
1558 .length = 2,
1559 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1560
1561 _ = try self.addInst(.{
1562 .tag = .call_extern,
1563 .data = .{ .extern_fn = n_strx },
15601564 });
15611565 } else {
15621566 return self.fail("TODO implement calling bitcasted functions", .{});
......@@ -2185,70 +2189,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
21852189 });
21862190 },
21872191 .memory => |addr| {
2188 if (self.bin_file.options.pie) {
2189 // PC-relative displacement to the entry in the GOT table.
2190 // adrp
2191 // TODO add a pseudo instruction
2192 const offset = @intCast(u32, self.mir_instructions.len);
2193 // mem.writeIntLittle(
2194 // u32,
2195 // try self.code.addManyAsArray(4),
2196 // Instruction.adrp(reg, 0).toU32(),
2197 // );
2198 _ = try self.addInst(.{
2199 .tag = .nop,
2200 .data = .{ .nop = {} },
2201 });
2202
2203 // ldr reg, reg, offset
2204 _ = try self.addInst(.{
2205 .tag = .ldr,
2206 .data = .{ .load_store_register = .{
2207 .rt = reg,
2208 .rn = reg,
2209 .offset = Instruction.LoadStoreOffset.imm(0),
2210 } },
2211 });
2212
2213 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2214 // TODO I think the reloc might be in the wrong place.
2215 const decl = macho_file.active_decl.?;
2216 // Page reloc for adrp instruction.
2217 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2218 .offset = offset,
2219 .target = .{ .local = @intCast(u32, addr) },
2220 .addend = 0,
2221 .subtractor = null,
2222 .pcrel = true,
2223 .length = 2,
2224 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
2225 });
2226 // Pageoff reloc for adrp instruction.
2227 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2228 .offset = offset + 4,
2229 .target = .{ .local = @intCast(u32, addr) },
2230 .addend = 0,
2231 .subtractor = null,
2232 .pcrel = false,
2233 .length = 2,
2234 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
2235 });
2236 } else {
2237 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
2238 }
2239 } else {
2240 // The value is in memory at a hard-coded address.
2241 // If the type is a pointer, it means the pointer address is at this memory location.
2242 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
2243 _ = try self.addInst(.{
2244 .tag = .ldr,
2245 .data = .{ .load_store_register = .{
2246 .rt = reg,
2247 .rn = reg,
2248 .offset = Instruction.LoadStoreOffset.none,
2249 } },
2250 });
2251 }
2192 _ = try self.addInst(.{
2193 .tag = .load_memory,
2194 .data = .{ .payload = try self.addExtra(Mir.LoadMemory{
2195 .register = @enumToInt(reg),
2196 .addr = @intCast(u32, addr),
2197 }) },
2198 });
22522199 },
22532200 .stack_offset => |unadjusted_off| {
22542201 // TODO: maybe addressing from sp instead of fp
src/arch/aarch64/Emit.zig+107
......@@ -3,11 +3,16 @@
33
44const Emit = @This();
55const std = @import("std");
6const math = std.math;
67const Mir = @import("Mir.zig");
78const bits = @import("bits.zig");
9const link = @import("../../link.zig");
10const assert = std.debug.assert;
811const Instruction = bits.Instruction;
12const Register = bits.Register;
913
1014mir: Mir,
15bin_file: *link.File,
1116target: *const std.Target,
1217code: *std.ArrayList(u8),
1318
......@@ -31,6 +36,10 @@ pub fn emitMir(
3136 .brk => try emit.mirExceptionGeneration(inst),
3237 .svc => try emit.mirExceptionGeneration(inst),
3338
39 .call_extern => try emit.mirCallExtern(inst),
40
41 .load_memory => try emit.mirLoadMemory(inst),
42
3443 .ldp => try emit.mirLoadStoreRegisterPair(inst),
3544 .stp => try emit.mirLoadStoreRegisterPair(inst),
3645
......@@ -57,6 +66,20 @@ fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
5766 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
5867}
5968
69fn moveImmediate(emit: *Emit, reg: Register, imm64: u64) !void {
70 try emit.writeInstruction(Instruction.movz(reg, @truncate(u16, imm64), 0));
71
72 if (imm64 > math.maxInt(u16)) {
73 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 16), 16));
74 }
75 if (imm64 > math.maxInt(u32)) {
76 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 32), 32));
77 }
78 if (imm64 > math.maxInt(u48)) {
79 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 48), 48));
80 }
81}
82
6083fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
6184 const tag = emit.mir.instructions.items(.tag)[inst];
6285 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 {
113136 }
114137}
115138
139fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
140 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
141 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
142
143 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
144 const offset = blk: {
145 const offset = @intCast(u32, emit.code.items.len);
146 // bl
147 try emit.writeInstruction(Instruction.bl(0));
148 break :blk offset;
149 };
150 // Add relocation to the decl.
151 try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{
152 .offset = offset,
153 .target = .{ .global = n_strx },
154 .addend = 0,
155 .subtractor = null,
156 .pcrel = true,
157 .length = 2,
158 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
159 });
160 } else {
161 @panic("Implement call_extern for linking backends != MachO");
162 }
163}
164
165fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void {
166 assert(emit.mir.instructions.items(.tag)[inst] == .load_memory);
167 const payload = emit.mir.instructions.items(.data)[inst].payload;
168 const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data;
169 const reg = @intToEnum(Register, load_memory.register);
170 const addr = load_memory.addr;
171
172 if (emit.bin_file.options.pie) {
173 // PC-relative displacement to the entry in the GOT table.
174 // adrp
175 const offset = @intCast(u32, emit.code.items.len);
176 try emit.writeInstruction(Instruction.adrp(reg, 0));
177
178 // ldr reg, reg, offset
179 try emit.writeInstruction(Instruction.ldr(reg, .{
180 .register = .{
181 .rn = reg,
182 .offset = Instruction.LoadStoreOffset.imm(0),
183 },
184 }));
185
186 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
187 // TODO I think the reloc might be in the wrong place.
188 const decl = macho_file.active_decl.?;
189 // Page reloc for adrp instruction.
190 try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
191 .offset = offset,
192 .target = .{ .local = addr },
193 .addend = 0,
194 .subtractor = null,
195 .pcrel = true,
196 .length = 2,
197 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
198 });
199 // Pageoff reloc for adrp instruction.
200 try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
201 .offset = offset + 4,
202 .target = .{ .local = addr },
203 .addend = 0,
204 .subtractor = null,
205 .pcrel = false,
206 .length = 2,
207 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
208 });
209 } else {
210 return @panic("TODO implement load_memory for PIE GOT indirection on this platform");
211 }
212 } else {
213 // The value is in memory at a hard-coded address.
214 // If the type is a pointer, it means the pointer address is at this memory location.
215 try emit.moveImmediate(reg, addr);
216 try emit.writeInstruction(Instruction.ldr(
217 reg,
218 .{ .register = .{ .rn = reg, .offset = Instruction.LoadStoreOffset.none } },
219 ));
220 }
221}
222
116223fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void {
117224 const tag = emit.mir.instructions.items(.tag)[inst];
118225 const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair;
src/arch/aarch64/Mir.zig+37
......@@ -34,6 +34,12 @@ pub const Inst = struct {
3434 blr,
3535 /// Breakpoint
3636 brk,
37 /// Pseudo-instruction: Call extern
38 call_extern,
39 /// Psuedo-instruction: Load memory
40 ///
41 /// Payload is `LoadMemory`
42 load_memory,
3743 /// Load Pair of Registers
3844 ldp,
3945 /// Load Register
......@@ -89,11 +95,17 @@ pub const Inst = struct {
8995 ///
9096 /// Used by e.g. b
9197 inst: Index,
98 /// An extern function
99 ///
100 /// Used by e.g. call_extern
101 extern_fn: u32,
92102 /// A 16-bit immediate value.
93103 ///
94104 /// Used by e.g. svc
95105 imm16: u16,
96106 /// Index into `extra`. Meaning of what can be found there is context-dependent.
107 ///
108 /// Used by e.g. load_memory
97109 payload: u32,
98110 /// A register
99111 ///
......@@ -156,3 +168,28 @@ pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
156168 gpa.free(mir.extra);
157169 mir.* = undefined;
158170}
171
172/// Returns the requested data, as well as the new index which is at the start of the
173/// trailers for the object.
174pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
175 const fields = std.meta.fields(T);
176 var i: usize = index;
177 var result: T = undefined;
178 inline for (fields) |field| {
179 @field(result, field.name) = switch (field.field_type) {
180 u32 => mir.extra[i],
181 i32 => @bitCast(i32, mir.extra[i]),
182 else => @compileError("bad field type"),
183 };
184 i += 1;
185 }
186 return .{
187 .data = result,
188 .end = i,
189 };
190}
191
192pub const LoadMemory = struct {
193 register: u32,
194 addr: u32,
195};