| 1 | //! This file contains the functionality for emitting x86_64 MIR as machine code |
| 2 | |
| 3 | lower: Lower, |
| 4 | bin_file: *link.File, |
| 5 | pt: Zcu.PerThread, |
| 6 | pic: bool, |
| 7 | atom_id: link.File.AtomId, |
| 8 | debug_output: link.File.DebugInfoOutput, |
| 9 | w: *std.Io.Writer, |
| 10 | |
| 11 | prev_di_loc: Loc, |
| 12 | /// Relative to the beginning of `code`. |
| 13 | prev_di_pc: usize, |
| 14 | |
| 15 | code_offset_mapping: std.ArrayList(u32), |
| 16 | relocs: std.ArrayList(Reloc), |
| 17 | table_relocs: std.ArrayList(TableReloc), |
| 18 | |
| 19 | pub const Error = Lower.Error || error{ |
| 20 | AlreadyReported, |
| 21 | EmitFail, |
| 22 | NotFile, |
| 23 | } || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError; |
| 24 | |
| 25 | pub 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 | |
| 722 | pub 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 | |
| 730 | const 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 | |
| 752 | fn 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 | |
| 942 | fn 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 | |
| 949 | const 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 | |
| 964 | const 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 | |
| 971 | const Loc = struct { |
| 972 | line: u32, |
| 973 | column: u32, |
| 974 | is_stmt: bool, |
| 975 | }; |
| 976 | |
| 977 | fn 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 | |
| 993 | const assert = std.debug.assert; |
| 994 | const bits = @import("bits.zig"); |
| 995 | const codegen = @import("../../codegen.zig"); |
| 996 | const Emit = @This(); |
| 997 | const encoder = @import("encoder.zig"); |
| 998 | const Instruction = encoder.Instruction; |
| 999 | const InternPool = @import("../../InternPool.zig"); |
| 1000 | const link = @import("../../link.zig"); |
| 1001 | const log = std.log.scoped(.emit); |
| 1002 | const Lower = @import("Lower.zig"); |
| 1003 | const Mir = @import("Mir.zig"); |
| 1004 | const std = @import("std"); |
| 1005 | const Type = @import("../../Type.zig"); |
| 1006 | const Zcu = @import("../../Zcu.zig"); |