1//! This file contains the functionality for emitting x86_64 MIR as machine code
2
3lower: Lower,
4bin_file: *link.File,
5pt: Zcu.PerThread,
6pic: bool,
7atom_id: link.File.AtomId,
8debug_output: link.File.DebugInfoOutput,
9w: *std.Io.Writer,
10
11prev_di_loc: Loc,
12/// Relative to the beginning of `code`.
13prev_di_pc: usize,
14
15code_offset_mapping: std.ArrayList(u32),
16relocs: std.ArrayList(Reloc),
17table_relocs: std.ArrayList(TableReloc),
18
19pub const Error = Lower.Error || error{
20 AlreadyReported,
21 EmitFail,
22 NotFile,
23} || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError;
24
25pub fn emitMir(emit: *Emit) Error!void {
26 const comp = emit.bin_file.comp;
27 const gpa = comp.gpa;
28 try emit.code_offset_mapping.resize(gpa, emit.lower.mir.instructions.len);
29 emit.relocs.clearRetainingCapacity();
30 emit.table_relocs.clearRetainingCapacity();
31 var local_index: usize = 0;
32 for (0..emit.lower.mir.instructions.len) |mir_i| {
33 const mir_index: Mir.Inst.Index = @intCast(mir_i);
34 emit.code_offset_mapping.items[mir_index] = @intCast(emit.w.end);
35 const lowered = try emit.lower.lowerMir(mir_index);
36 var lowered_relocs = lowered.relocs;
37 lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| {
38 if (lowered_inst.prefix == .directive) {
39 const start_offset: u32 = @intCast(emit.w.end);
40 switch (emit.debug_output) {
41 .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) {
42 .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{
43 .reg = lowered_inst.ops[0].reg.dwarfNum(),
44 .off = lowered_inst.ops[1].imm.signed,
45 } }),
46 .@".cfi_def_cfa_register" => try dwarf.genDebugFrame(start_offset, .{
47 .def_cfa_register = lowered_inst.ops[0].reg.dwarfNum(),
48 }),
49 .@".cfi_def_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{
50 .def_cfa_offset = lowered_inst.ops[0].imm.signed,
51 }),
52 .@".cfi_adjust_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{
53 .adjust_cfa_offset = lowered_inst.ops[0].imm.signed,
54 }),
55 .@".cfi_offset" => try dwarf.genDebugFrame(start_offset, .{ .offset = .{
56 .reg = lowered_inst.ops[0].reg.dwarfNum(),
57 .off = lowered_inst.ops[1].imm.signed,
58 } }),
59 .@".cfi_val_offset" => try dwarf.genDebugFrame(start_offset, .{ .val_offset = .{
60 .reg = lowered_inst.ops[0].reg.dwarfNum(),
61 .off = lowered_inst.ops[1].imm.signed,
62 } }),
63 .@".cfi_rel_offset" => try dwarf.genDebugFrame(start_offset, .{ .rel_offset = .{
64 .reg = lowered_inst.ops[0].reg.dwarfNum(),
65 .off = lowered_inst.ops[1].imm.signed,
66 } }),
67 .@".cfi_register" => try dwarf.genDebugFrame(start_offset, .{ .register = .{
68 lowered_inst.ops[0].reg.dwarfNum(),
69 lowered_inst.ops[1].reg.dwarfNum(),
70 } }),
71 .@".cfi_restore" => try dwarf.genDebugFrame(start_offset, .{
72 .restore = lowered_inst.ops[0].reg.dwarfNum(),
73 }),
74 .@".cfi_undefined" => try dwarf.genDebugFrame(start_offset, .{
75 .undefined = lowered_inst.ops[0].reg.dwarfNum(),
76 }),
77 .@".cfi_same_value" => try dwarf.genDebugFrame(start_offset, .{
78 .same_value = lowered_inst.ops[0].reg.dwarfNum(),
79 }),
80 .@".cfi_remember_state" => try dwarf.genDebugFrame(start_offset, .remember_state),
81 .@".cfi_restore_state" => try dwarf.genDebugFrame(start_offset, .restore_state),
82 .@".cfi_escape" => try dwarf.genDebugFrame(start_offset, .{
83 .escape = lowered_inst.ops[0].bytes,
84 }),
85 else => unreachable,
86 },
87 .none => {},
88 }
89 continue;
90 }
91 var reloc_info_buf: [2]RelocInfo = undefined;
92 var reloc_info_index: usize = 0;
93 const ip = &emit.pt.zcu.intern_pool;
94 while (lowered_relocs.len > 0 and
95 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
96 lowered_relocs = lowered_relocs[1..];
97 reloc_info_index += 1;
98 }) reloc_info_buf[reloc_info_index] = .{
99 .op_index = lowered_relocs[0].op_index,
100 .off = lowered_relocs[0].off,
101 .target = target: switch (lowered_relocs[0].target) {
102 .inst => |inst| .{ .inst = inst },
103 .table => .table,
104 .nav => |nav| {
105 const symbol_id = try codegen.genNavRef(
106 emit.bin_file,
107 emit.pt,
108 nav,
109 );
110 const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{
111 .symbol = symbol_id,
112 .is_extern = switch (@"extern".visibility) {
113 .default => true,
114 .hidden, .protected => false,
115 },
116 .is_dll_import = @"extern".is_dll_import,
117 .force_pcrel_direct = switch (@"extern".relocation) {
118 .any => false,
119 .pcrel => true,
120 },
121 } else .{ .symbol = symbol_id, .is_extern = false };
122 if (ip.getNav(nav).resolved.?.@"threadlocal" and comp.config.any_non_single_threaded) {
123 break :target .{ .tlv = target_symbol };
124 } else {
125 break :target .{ .symbol = target_symbol };
126 }
127 },
128 .uav => |uav| .{ .symbol = .{
129 .symbol = try emit.bin_file.lowerUav(
130 emit.pt,
131 uav.val,
132 Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
133 ),
134 .is_extern = false,
135 } },
136 .lazy_sym => |lazy_sym| .{ .symbol = .{
137 .symbol = if (emit.bin_file.cast(.elf)) |elf_file|
138 @fromBackingInt(@intCast(
139 elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
140 return emit.fail("{s} creating lazy symbol", .{@errorName(err)}),
141 ))
142 else if (emit.bin_file.cast(.elf2)) |elf|
143 try elf.lazySymbol(lazy_sym)
144 else if (emit.bin_file.cast(.macho)) |macho_file|
145 @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
146 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})))
147 else if (emit.bin_file.cast(.coff2)) |coff|
148 @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym))))
149 else
150 return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
151 .is_extern = false,
152 } },
153 .extern_func => |extern_func| .{ .symbol = .{
154 .symbol = if (emit.bin_file.cast(.elf)) |elf_file|
155 @fromBackingInt(@intCast(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)))
156 else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{
157 .name = extern_func.toSlice(&emit.lower.mir).?,
158 .lib_name = null,
159 .type = .FUNC,
160 }) else if (emit.bin_file.cast(.macho)) |macho_file|
161 @fromBackingInt(@intCast(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)))
162 else if (emit.bin_file.cast(.coff2)) |coff| @fromBackingInt(@intCast(@backingInt(try coff.globalSymbol(.{
163 .name = extern_func.toSlice(&emit.lower.mir).?,
164 })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
165 .is_extern = true,
166 } },
167 },
168 };
169 const reloc_info = reloc_info_buf[0..reloc_info_index];
170 for (reloc_info) |*reloc| switch (reloc.target) {
171 .inst, .table => {},
172 .symbol => |target| {
173 switch (lowered_inst.encoding.mnemonic) {
174 .call => {
175 reloc.target = .{ .branch = target };
176 if (target.is_dll_import and emit.bin_file.cast(.coff2) != null) {
177 try emit.encodeInst(try .new(.none, .call, &.{
178 .{ .mem = .initRip(.ptr, 0) },
179 }, emit.lower.target), reloc_info);
180 } else {
181 try emit.encodeInst(lowered_inst, reloc_info);
182 }
183 continue :lowered_inst;
184 },
185 else => {},
186 }
187 if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
188 if (!emit.pic) switch (lowered_inst.encoding.mnemonic) {
189 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
190 lowered_inst.ops[0],
191 .{ .imm = .s(0) },
192 }, emit.lower.target), reloc_info),
193 .mov => try emit.encodeInst(try .new(.none, .mov, &.{
194 lowered_inst.ops[0],
195 .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{}) },
196 }, emit.lower.target), reloc_info),
197 else => unreachable,
198 } else if (target.is_extern) switch (lowered_inst.encoding.mnemonic) {
199 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
200 lowered_inst.ops[0],
201 .{ .mem = .initRip(.ptr, 0) },
202 }, emit.lower.target), reloc_info),
203 .mov => {
204 try emit.encodeInst(try .new(.none, .mov, &.{
205 lowered_inst.ops[0],
206 .{ .mem = .initRip(.ptr, 0) },
207 }, emit.lower.target), reloc_info);
208 try emit.encodeInst(try .new(.none, .mov, &.{
209 lowered_inst.ops[0],
210 .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
211 .reg = lowered_inst.ops[0].reg.to64(),
212 } }) },
213 }, emit.lower.target), &.{});
214 },
215 else => unreachable,
216 } else switch (lowered_inst.encoding.mnemonic) {
217 .lea => try emit.encodeInst(try .new(.none, .lea, &.{
218 lowered_inst.ops[0],
219 .{ .mem = .initRip(.none, 0) },
220 }, emit.lower.target), reloc_info),
221 .mov => try emit.encodeInst(try .new(.none, .mov, &.{
222 lowered_inst.ops[0],
223 .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) },
224 }, emit.lower.target), reloc_info),
225 else => unreachable,
226 }
227 } else if (emit.bin_file.cast(.macho)) |_| {
228 if (target.is_extern) switch (lowered_inst.encoding.mnemonic) {
229 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
230 lowered_inst.ops[0],
231 .{ .mem = .initRip(.ptr, 0) },
232 }, emit.lower.target), reloc_info),
233 .mov => {
234 try emit.encodeInst(try .new(.none, .mov, &.{
235 lowered_inst.ops[0],
236 .{ .mem = .initRip(.ptr, 0) },
237 }, emit.lower.target), reloc_info);
238 try emit.encodeInst(try .new(.none, .mov, &.{
239 lowered_inst.ops[0],
240 .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
241 .reg = lowered_inst.ops[0].reg.to64(),
242 } }) },
243 }, emit.lower.target), &.{});
244 },
245 else => unreachable,
246 } else switch (lowered_inst.encoding.mnemonic) {
247 .lea => try emit.encodeInst(try .new(.none, .lea, &.{
248 lowered_inst.ops[0],
249 .{ .mem = .initRip(.none, 0) },
250 }, emit.lower.target), reloc_info),
251 .mov => try emit.encodeInst(try .new(.none, .mov, &.{
252 lowered_inst.ops[0],
253 .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) },
254 }, emit.lower.target), reloc_info),
255 else => unreachable,
256 }
257 } else if (emit.bin_file.cast(.coff2)) |_| {
258 if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {
259 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
260 lowered_inst.ops[0],
261 .{ .mem = .initRip(.ptr, 0) },
262 }, emit.lower.target), reloc_info),
263 .mov => {
264 try emit.encodeInst(try .new(.none, .mov, &.{
265 lowered_inst.ops[0],
266 .{ .mem = .initRip(.ptr, 0) },
267 }, emit.lower.target), reloc_info);
268 try emit.encodeInst(try .new(.none, .mov, &.{
269 lowered_inst.ops[0],
270 .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
271 .reg = lowered_inst.ops[0].reg.to64(),
272 } }) },
273 }, emit.lower.target), &.{});
274 },
275 else => unreachable,
276 } else switch (lowered_inst.encoding.mnemonic) {
277 .lea => try emit.encodeInst(try .new(.none, .lea, &.{
278 lowered_inst.ops[0],
279 .{ .mem = .initRip(.none, 0) },
280 }, emit.lower.target), reloc_info),
281 .mov => try emit.encodeInst(try .new(.none, .mov, &.{
282 lowered_inst.ops[0],
283 .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) },
284 }, emit.lower.target), reloc_info),
285 else => unreachable,
286 }
287 } else return emit.fail("TODO implement relocs for {s}", .{
288 @tagName(emit.bin_file.tag),
289 });
290 continue :lowered_inst;
291 },
292 .branch, .tls => unreachable,
293 .tlv => |target| {
294 if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
295 // TODO handle extern TLS vars, i.e., emit GD model
296 if (emit.pic) switch (lowered_inst.encoding.mnemonic) {
297 .lea, .mov => {
298 // Here, we currently assume local dynamic TLS vars, and so
299 // we emit LD model.
300 try emit.encodeInst(try .new(.none, .lea, &.{
301 .{ .reg = .rdi },
302 .{ .mem = .initRip(.none, 0) },
303 }, emit.lower.target), &.{.{
304 .op_index = 1,
305 .target = .{ .tls = target.symbol },
306 }});
307 try emit.encodeInst(try .new(.none, .call, &.{
308 .{ .imm = .s(0) },
309 }, emit.lower.target), &.{.{
310 .op_index = 0,
311 .target = .{ .branch = .{
312 .symbol = if (emit.bin_file.cast(.elf)) |elf_file| @fromBackingInt(@intCast(try elf_file.getGlobalSymbol(
313 "__tls_get_addr",
314 if (comp.config.link_libc) "c" else null,
315 ))) else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{
316 .name = "__tls_get_addr",
317 .lib_name = if (comp.config.link_libc) "c" else null,
318 .type = .FUNC,
319 }) else unreachable,
320 .is_extern = true,
321 } },
322 }});
323 try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
324 lowered_inst.ops[0],
325 .{ .mem = .initSib(.none, .{
326 .base = .{ .reg = .rax },
327 .disp = std.math.minInt(i32),
328 }) },
329 }, emit.lower.target), reloc_info);
330 },
331 else => unreachable,
332 } else switch (lowered_inst.encoding.mnemonic) {
333 .lea, .mov => {
334 // Since we are linking statically, we emit LE model directly.
335 try emit.encodeInst(try .new(.none, .mov, &.{
336 .{ .reg = .rax },
337 .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .fs } }) },
338 }, emit.lower.target), &.{});
339 try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
340 lowered_inst.ops[0],
341 .{ .mem = .initSib(.none, .{
342 .base = .{ .reg = .rax },
343 .disp = std.math.minInt(i32),
344 }) },
345 }, emit.lower.target), reloc_info);
346 },
347 else => unreachable,
348 }
349 } else if (emit.bin_file.cast(.macho)) |_| switch (lowered_inst.encoding.mnemonic) {
350 .lea => {
351 try emit.encodeInst(try .new(.none, .mov, &.{
352 .{ .reg = .rdi },
353 .{ .mem = .initRip(.ptr, 0) },
354 }, emit.lower.target), reloc_info);
355 try emit.encodeInst(try .new(.none, .call, &.{
356 .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
357 }, emit.lower.target), &.{});
358 try emit.encodeInst(try .new(.none, .mov, &.{
359 lowered_inst.ops[0],
360 .{ .reg = .rax },
361 }, emit.lower.target), &.{});
362 },
363 .mov => {
364 try emit.encodeInst(try .new(.none, .mov, &.{
365 .{ .reg = .rdi },
366 .{ .mem = .initRip(.ptr, 0) },
367 }, emit.lower.target), reloc_info);
368 try emit.encodeInst(try .new(.none, .call, &.{
369 .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
370 }, emit.lower.target), &.{});
371 try emit.encodeInst(try .new(.none, .mov, &.{
372 lowered_inst.ops[0],
373 .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rax } }) },
374 }, emit.lower.target), &.{});
375 },
376 else => unreachable,
377 } else if (emit.bin_file.cast(.coff2)) |coff| {
378 switch (emit.lower.target.cpu.arch) {
379 else => unreachable,
380 .x86 => {
381 try emit.encodeInst(try .new(.none, .mov, &.{
382 .{ .reg = .eax },
383 .{ .mem = .initSib(.qword, .{
384 .base = .{ .reg = .fs },
385 .disp = 4 * 11,
386 }) },
387 }, emit.lower.target), &.{});
388 try emit.encodeInst(try .new(.none, .mov, &.{
389 .{ .reg = .edi },
390 .{ .mem = .initSib(.dword, .{}) },
391 }, emit.lower.target), &.{.{
392 .op_index = 1,
393 .target = .{ .symbol = .{
394 .symbol = @fromBackingInt(@intCast(@backingInt(
395 try coff.globalSymbol(.{ .name = "__tls_index" }),
396 ))),
397 .is_extern = false,
398 } },
399 }});
400 try emit.encodeInst(try .new(.none, .mov, &.{
401 .{ .reg = .eax },
402 .{ .mem = .initSib(.dword, .{
403 .base = .{ .reg = .eax },
404 .scale_index = .{ .index = .edi, .scale = 4 },
405 }) },
406 }, emit.lower.target), &.{});
407 try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
408 lowered_inst.ops[0],
409 .{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{
410 .base = .{ .reg = .eax },
411 .disp = std.math.minInt(i32),
412 }) },
413 }, emit.lower.target), reloc_info);
414 },
415 .x86_64 => {
416 try emit.encodeInst(try .new(.none, .mov, &.{
417 .{ .reg = .rax },
418 .{ .mem = .initSib(.qword, .{
419 .base = .{ .reg = .gs },
420 .disp = 8 * 11,
421 }) },
422 }, emit.lower.target), &.{});
423 try emit.encodeInst(try .new(.none, .mov, &.{
424 .{ .reg = .edi },
425 .{ .mem = .initRip(.dword, 0) },
426 }, emit.lower.target), &.{.{
427 .op_index = 1,
428 .target = .{ .symbol = .{
429 .symbol = @fromBackingInt(@intCast(@backingInt(
430 try coff.globalSymbol(.{ .name = "_tls_index" }),
431 ))),
432 .is_extern = false,
433 } },
434 }});
435 try emit.encodeInst(try .new(.none, .mov, &.{
436 .{ .reg = .rax },
437 .{ .mem = .initSib(.qword, .{
438 .base = .{ .reg = .rax },
439 .scale_index = .{ .index = .rdi, .scale = 8 },
440 }) },
441 }, emit.lower.target), &.{});
442 try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
443 lowered_inst.ops[0],
444 .{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{
445 .base = .{ .reg = .rax },
446 .disp = std.math.minInt(i32),
447 }) },
448 }, emit.lower.target), reloc_info);
449 },
450 }
451 } else return emit.fail("TODO implement relocs for {s}", .{
452 @tagName(emit.bin_file.tag),
453 });
454 continue :lowered_inst;
455 },
456 };
457 try emit.encodeInst(lowered_inst, reloc_info);
458 }
459 assert(lowered_relocs.len == 0);
460
461 if (lowered.insts.len == 0) {
462 const mir_inst = emit.lower.mir.instructions.get(mir_index);
463 switch (mir_inst.tag) {
464 else => unreachable,
465 .pseudo => switch (mir_inst.ops) {
466 else => unreachable,
467 .pseudo_dbg_prologue_end_none => switch (emit.debug_output) {
468 .dwarf => |dwarf| try dwarf.setPrologueEnd(),
469 .none => {},
470 },
471 .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePCAndLine(.{
472 .line = mir_inst.data.line_column.line,
473 .column = mir_inst.data.line_column.column,
474 .is_stmt = true,
475 }),
476 .pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(.{
477 .line = mir_inst.data.line_column.line,
478 .column = mir_inst.data.line_column.column,
479 .is_stmt = false,
480 }),
481 .pseudo_dbg_epilogue_begin_none => switch (emit.debug_output) {
482 .dwarf => |dwarf| {
483 try dwarf.setEpilogueBegin();
484 log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
485 emit.prev_di_loc.line, emit.prev_di_loc.column,
486 });
487 try emit.dbgAdvancePCAndLine(emit.prev_di_loc);
488 },
489 .none => {},
490 },
491 .pseudo_dbg_enter_block_none => switch (emit.debug_output) {
492 .dwarf => |dwarf| {
493 log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
494 emit.prev_di_loc.line, emit.prev_di_loc.column,
495 });
496 try dwarf.enterBlock(emit.w.end);
497 },
498 .none => {},
499 },
500 .pseudo_dbg_leave_block_none => switch (emit.debug_output) {
501 .dwarf => |dwarf| {
502 log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
503 emit.prev_di_loc.line, emit.prev_di_loc.column,
504 });
505 try dwarf.leaveBlock(emit.w.end);
506 },
507 .none => {},
508 },
509 .pseudo_dbg_enter_inline_func => switch (emit.debug_output) {
510 .dwarf => |dwarf| {
511 log.debug("mirDbgEnterInline (line={d}, col={d})", .{
512 emit.prev_di_loc.line, emit.prev_di_loc.column,
513 });
514 try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column);
515 },
516 .none => {},
517 },
518 .pseudo_dbg_leave_inline_func => switch (emit.debug_output) {
519 .dwarf => |dwarf| {
520 log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
521 emit.prev_di_loc.line, emit.prev_di_loc.column,
522 });
523 try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end);
524 },
525 .none => {},
526 },
527 .pseudo_dbg_arg_none,
528 .pseudo_dbg_arg_i_s,
529 .pseudo_dbg_arg_i_u,
530 .pseudo_dbg_arg_i_64,
531 .pseudo_dbg_arg_ro,
532 .pseudo_dbg_arg_fa,
533 .pseudo_dbg_arg_m,
534 .pseudo_dbg_var_none,
535 .pseudo_dbg_var_i_s,
536 .pseudo_dbg_var_i_u,
537 .pseudo_dbg_var_i_64,
538 .pseudo_dbg_var_ro,
539 .pseudo_dbg_var_fa,
540 .pseudo_dbg_var_m,
541 => switch (emit.debug_output) {
542 .dwarf => |dwarf| {
543 var loc_buf: [2]link.File.Dwarf.Loc = undefined;
544 const loc: link.File.Dwarf.Loc = loc: switch (mir_inst.ops) {
545 else => unreachable,
546 .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty,
547 .pseudo_dbg_arg_i_s,
548 .pseudo_dbg_arg_i_u,
549 .pseudo_dbg_var_i_s,
550 .pseudo_dbg_var_i_u,
551 => .{ .stack_value = stack_value: {
552 loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) {
553 .signed => |s| .{ .consts = s },
554 .unsigned => |u| .{ .constu = u },
555 };
556 break :stack_value &loc_buf[0];
557 } },
558 .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: {
559 loc_buf[0] = .{ .constu = mir_inst.data.i64 };
560 break :stack_value &loc_buf[0];
561 } },
562 .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
563 const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa);
564 break :loc .{ .plus = .{
565 reg: {
566 loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() };
567 break :reg &loc_buf[0];
568 },
569 off: {
570 loc_buf[1] = .{ .consts = reg_off.off };
571 break :off &loc_buf[1];
572 },
573 } };
574 },
575 .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
576 const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode();
577 break :loc .{ .plus = .{
578 base: {
579 loc_buf[0] = switch (mem.base()) {
580 .none => .{ .constu = 0 },
581 .reg => |reg| .{ .breg = reg.dwarfNum() },
582 .frame, .table, .rip_inst => unreachable,
583 .nav => |nav| .{ .addr_reloc = try codegen.genNavRef(
584 emit.bin_file,
585 emit.pt,
586 nav,
587 ) },
588 .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav(
589 emit.pt,
590 uav.val,
591 Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
592 ) },
593 .lazy_sym, .extern_func => unreachable,
594 };
595 break :base &loc_buf[0];
596 },
597 disp: {
598 loc_buf[1] = switch (mem.disp()) {
599 .signed => |s| .{ .consts = s },
600 .unsigned => |u| .{ .constu = u },
601 };
602 break :disp &loc_buf[1];
603 },
604 } };
605 },
606 };
607
608 const local = &emit.lower.mir.locals[local_index];
609 local_index += 1;
610 try dwarf.genLocalVarDebugInfo(
611 switch (mir_inst.ops) {
612 else => unreachable,
613 .pseudo_dbg_arg_none,
614 .pseudo_dbg_arg_i_s,
615 .pseudo_dbg_arg_i_u,
616 .pseudo_dbg_arg_i_64,
617 .pseudo_dbg_arg_ro,
618 .pseudo_dbg_arg_fa,
619 .pseudo_dbg_arg_m,
620 .pseudo_dbg_arg_val,
621 => .arg,
622 .pseudo_dbg_var_none,
623 .pseudo_dbg_var_i_s,
624 .pseudo_dbg_var_i_u,
625 .pseudo_dbg_var_i_64,
626 .pseudo_dbg_var_ro,
627 .pseudo_dbg_var_fa,
628 .pseudo_dbg_var_m,
629 .pseudo_dbg_var_val,
630 => .local_var,
631 },
632 local.name.toSlice(&emit.lower.mir),
633 .fromInterned(local.type),
634 loc,
635 );
636 },
637 .none => local_index += 1,
638 },
639 .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) {
640 .dwarf => |dwarf| {
641 const local = &emit.lower.mir.locals[local_index];
642 local_index += 1;
643 try dwarf.genLocalConstDebugInfo(
644 switch (mir_inst.ops) {
645 else => unreachable,
646 .pseudo_dbg_arg_val => .comptime_arg,
647 .pseudo_dbg_var_val => .local_const,
648 },
649 local.name.toSlice(&emit.lower.mir),
650 .fromInterned(mir_inst.data.ip_index),
651 );
652 },
653 .none => local_index += 1,
654 },
655 .pseudo_dbg_var_args_none => switch (emit.debug_output) {
656 .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(),
657 .none => {},
658 },
659 .pseudo_dead_none => {},
660 },
661 }
662 }
663 }
664 for (emit.relocs.items) |reloc| {
665 const target = emit.code_offset_mapping.items[reloc.target];
666 const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset;
667 const inst_bytes = emit.w.buffered()[reloc.inst_offset..][0..reloc.inst_length];
668 switch (reloc.source_length) {
669 else => unreachable,
670 inline 1, 4 => |source_length| std.mem.writeInt(
671 @Int(.signed, @as(u16, 8) * source_length),
672 inst_bytes[reloc.source_offset..][0..source_length],
673 @intCast(disp),
674 .little,
675 ),
676 }
677 }
678 if (emit.lower.mir.table.len > 0) {
679 const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
680 var table_offset = std.mem.alignForward(u32, @intCast(emit.w.end), ptr_size);
681 if (emit.bin_file.cast(.elf)) |elf_file| {
682 const zo = elf_file.zigObjectPtr().?;
683 const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?;
684
685 for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
686 .r_offset = table_reloc.source_offset,
687 .r_info = @as(u64, @backingInt(emit.atom_id)) << 32 | @backingInt(std.elf.R_X86_64.@"32S"),
688 .r_addend = @as(i64, table_offset) + table_reloc.target_offset,
689 }, zo);
690 for (emit.lower.mir.table) |entry| {
691 try atom.addReloc(gpa, .{
692 .r_offset = table_offset,
693 .r_info = @as(u64, @backingInt(emit.atom_id)) << 32 | @backingInt(std.elf.R_X86_64.@"64"),
694 .r_addend = emit.code_offset_mapping.items[entry],
695 }, zo);
696 table_offset += ptr_size;
697 }
698 try emit.w.splatByteAll(0, table_offset - emit.w.end);
699 } else if (emit.bin_file.cast(.elf2)) |elf| {
700 for (emit.table_relocs.items) |table_reloc| try elf.addReloc(
701 emit.atom_id,
702 table_reloc.source_offset,
703 elf.symbolForAtom(emit.atom_id),
704 @as(i64, table_offset) + table_reloc.target_offset,
705 .{ .X86_64 = .@"32S" },
706 );
707 for (emit.lower.mir.table) |entry| {
708 try elf.addReloc(
709 emit.atom_id,
710 table_offset,
711 elf.symbolForAtom(emit.atom_id),
712 emit.code_offset_mapping.items[entry],
713 .{ .X86_64 = .@"64" },
714 );
715 table_offset += ptr_size;
716 }
717 try emit.w.splatByteAll(0, table_offset - emit.w.end);
718 } else unreachable;
719 }
720}
721
722pub fn deinit(emit: *Emit) void {
723 const gpa = emit.bin_file.comp.gpa;
724 emit.code_offset_mapping.deinit(gpa);
725 emit.relocs.deinit(gpa);
726 emit.table_relocs.deinit(gpa);
727 emit.* = undefined;
728}
729
730const RelocInfo = struct {
731 op_index: Lower.InstOpIndex,
732 off: i32 = 0,
733 target: Target,
734
735 const Target = union(enum) {
736 inst: Mir.Inst.Index,
737 table,
738 branch: Symbol,
739 symbol: Symbol,
740 tlv: Symbol,
741 tls: link.File.SymbolId,
742
743 const Symbol = struct {
744 symbol: link.File.SymbolId,
745 is_extern: bool,
746 is_dll_import: bool = false,
747 force_pcrel_direct: bool = false,
748 };
749 };
750};
751
752fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void {
753 const comp = emit.bin_file.comp;
754 const gpa = comp.gpa;
755 const start_offset: u32 = @intCast(emit.w.end);
756 lowered_inst.encode(emit.w, .{}) catch |err| switch (err) {
757 error.WriteFailed => return error.OutOfMemory,
758 else => |e| return e,
759 };
760 const end_offset: u32 = @intCast(emit.w.end);
761 for (reloc_info) |reloc| switch (reloc.target) {
762 .inst => |target_inst| {
763 const inst_length: u4 = @intCast(end_offset - start_offset);
764 const reloc_offset, const reloc_length = reloc_offset_length: {
765 var reloc_offset = inst_length;
766 var op_index: usize = lowered_inst.ops.len;
767 while (true) {
768 op_index -= 1;
769 const op = lowered_inst.encoding.data.ops[op_index];
770 if (op == .none) continue;
771 const is_mem = op.isMemory();
772 const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
773 .rip_inst => 4,
774 else => unreachable,
775 } else @intCast(@divCeil(op.immBitSize(), 8));
776 reloc_offset -= enc_length;
777 if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
778 assert(!is_mem);
779 }
780 };
781 try emit.relocs.append(emit.lower.allocator, .{
782 .inst_offset = start_offset,
783 .inst_length = inst_length,
784 .source_offset = reloc_offset,
785 .source_length = reloc_length,
786 .target = target_inst,
787 .target_offset = reloc.off,
788 });
789 },
790 .table => try emit.table_relocs.append(emit.lower.allocator, .{
791 .source_offset = end_offset - 4,
792 .target_offset = reloc.off,
793 }),
794 .symbol => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
795 const zo = elf_file.zigObjectPtr().?;
796 const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?;
797 const r_type: std.elf.R_X86_64 = if (!emit.pic)
798 .@"32S"
799 else if (target.is_extern and !target.force_pcrel_direct)
800 .GOTPCREL
801 else
802 .PC32;
803 try atom.addReloc(gpa, .{
804 .r_offset = end_offset - 4,
805 .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type),
806 .r_addend = if (emit.pic) reloc.off - 4 else reloc.off,
807 }, zo);
808 } else if (emit.bin_file.cast(.macho)) |macho_file| {
809 const zo = macho_file.getZigObject().?;
810 const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?;
811 try atom.addReloc(macho_file, .{
812 .tag = .@"extern",
813 .offset = end_offset - 4,
814 .target = @backingInt(target.symbol),
815 .addend = reloc.off,
816 .type = if (target.is_extern and !target.force_pcrel_direct) .got_load else .signed,
817 .meta = .{
818 .pcrel = true,
819 .has_subtractor = false,
820 .length = 2,
821 .symbolnum = @intCast(@backingInt(target.symbol)),
822 },
823 });
824 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
825 emit.atom_id,
826 end_offset - 4,
827 target.symbol,
828 if (emit.pic) reloc.off - 4 else reloc.off,
829 .{ .X86_64 = rt: {
830 if (!emit.pic) break :rt .@"32S";
831 if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL;
832 break :rt .PC32;
833 } },
834 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
835 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
836 end_offset - 4,
837 @fromBackingInt(@intCast(@backingInt(target.symbol))),
838 .{ .known = reloc.off },
839 .{ .AMD64 = .REL32 },
840 ) else unreachable,
841 .branch => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
842 const zo = elf_file.zigObjectPtr().?;
843 const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?;
844 const r_type: std.elf.R_X86_64 = .PLT32;
845 try atom.addReloc(gpa, .{
846 .r_offset = end_offset - 4,
847 .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type),
848 .r_addend = reloc.off - 4,
849 }, zo);
850 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
851 emit.atom_id,
852 end_offset - 4,
853 target.symbol,
854 reloc.off - 4,
855 .{ .X86_64 = .PLT32 },
856 ) else if (emit.bin_file.cast(.macho)) |macho_file| {
857 const zo = macho_file.getZigObject().?;
858 const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?;
859 try atom.addReloc(macho_file, .{
860 .tag = .@"extern",
861 .offset = end_offset - 4,
862 .target = @backingInt(target.symbol),
863 .addend = reloc.off,
864 .type = .branch,
865 .meta = .{
866 .pcrel = true,
867 .has_subtractor = false,
868 .length = 2,
869 .symbolnum = @intCast(@backingInt(target.symbol)),
870 },
871 });
872 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
873 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
874 end_offset - 4,
875 @fromBackingInt(@intCast(@backingInt(target.symbol))),
876 .{ .known = reloc.off },
877 .{ .AMD64 = .REL32 },
878 ) else return emit.fail("TODO implement {s} reloc for {s}", .{
879 @tagName(reloc.target), @tagName(emit.bin_file.tag),
880 }),
881 .tls => |target_symbol| if (emit.bin_file.cast(.elf)) |elf_file| {
882 const zo = elf_file.zigObjectPtr().?;
883 const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?;
884 const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable;
885 try atom.addReloc(gpa, .{
886 .r_offset = end_offset - 4,
887 .r_info = @as(u64, @backingInt(target_symbol)) << 32 | @backingInt(r_type),
888 .r_addend = reloc.off - 4,
889 }, zo);
890 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
891 emit.atom_id,
892 end_offset - 4,
893 target_symbol,
894 reloc.off - 4,
895 .{ .X86_64 = if (emit.pic) .TLSLD else unreachable },
896 ) else return emit.fail("TODO implement {s} reloc for {s}", .{
897 @tagName(reloc.target), @tagName(emit.bin_file.tag),
898 }),
899 .tlv => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
900 const zo = elf_file.zigObjectPtr().?;
901 const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?;
902 const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32;
903 try atom.addReloc(gpa, .{
904 .r_offset = end_offset - 4,
905 .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type),
906 .r_addend = reloc.off,
907 }, zo);
908 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
909 emit.atom_id,
910 end_offset - 4,
911 target.symbol,
912 reloc.off,
913 .{ .X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32 },
914 ) else if (emit.bin_file.cast(.macho)) |macho_file| {
915 const zo = macho_file.getZigObject().?;
916 const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?;
917 try atom.addReloc(macho_file, .{
918 .tag = .@"extern",
919 .offset = end_offset - 4,
920 .target = @backingInt(target.symbol),
921 .addend = reloc.off,
922 .type = .tlv,
923 .meta = .{
924 .pcrel = true,
925 .has_subtractor = false,
926 .length = 2,
927 .symbolnum = @intCast(@backingInt(target.symbol)),
928 },
929 });
930 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
931 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
932 end_offset - 4,
933 @fromBackingInt(@intCast(@backingInt(target.symbol))),
934 .{ .known = reloc.off },
935 .{ .AMD64 = .SECREL },
936 ) else return emit.fail("TODO implement {s} reloc for {s}", .{
937 @tagName(reloc.target), @tagName(emit.bin_file.tag),
938 }),
939 };
940}
941
942fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
943 return switch (emit.lower.fail(format, args)) {
944 error.LowerFail => error.EmitFail,
945 else => |e| e,
946 };
947}
948
949const Reloc = struct {
950 /// Offset of the instruction.
951 inst_offset: u32,
952 /// Length of the instruction.
953 inst_length: u4,
954 /// Offset of the relocation within the instruction.
955 source_offset: u4,
956 /// Length of the relocation.
957 source_length: u4,
958 /// Target of the relocation.
959 target: Mir.Inst.Index,
960 /// Offset from the target.
961 target_offset: i32,
962};
963
964const TableReloc = struct {
965 /// Offset of the relocation.
966 source_offset: u32,
967 /// Offset from the start of the table.
968 target_offset: i32,
969};
970
971const Loc = struct {
972 line: u32,
973 column: u32,
974 is_stmt: bool,
975};
976
977fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
978 const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line);
979 const delta_pc: usize = emit.w.end - emit.prev_di_pc;
980 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
981 switch (emit.debug_output) {
982 .dwarf => |dwarf| {
983 if (loc.is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt();
984 if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column);
985 try dwarf.advancePCAndLine(delta_line, delta_pc);
986 emit.prev_di_loc = loc;
987 emit.prev_di_pc = emit.w.end;
988 },
989 .none => {},
990 }
991}
992
993const assert = std.debug.assert;
994const bits = @import("bits.zig");
995const codegen = @import("../../codegen.zig");
996const Emit = @This();
997const encoder = @import("encoder.zig");
998const Instruction = encoder.Instruction;
999const InternPool = @import("../../InternPool.zig");
1000const link = @import("../../link.zig");
1001const log = std.log.scoped(.emit);
1002const Lower = @import("Lower.zig");
1003const Mir = @import("Mir.zig");
1004const std = @import("std");
1005const Type = @import("../../Type.zig");
1006const Zcu = @import("../../Zcu.zig");