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