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