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");