Commit 55864e98e0

Jacob Young <jacobly0@users.noreply.github.com>
2024-08-20 21:07:47
x86_64: support more dwarf locations
1 parent eaa2274
src/arch/x86_64/bits.zig
@@ -447,26 +447,11 @@ pub const FrameIndex = enum(u32) {
     }
 };
 
-/// A linker symbol not yet allocated in VM.
-pub const Symbol = struct {
-    /// Index of the containing atom.
-    atom_index: u32,
-    /// Index into the linker's symbol table.
-    sym_index: u32,
+pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
 
-    pub fn format(
-        sym: Symbol,
-        comptime fmt: []const u8,
-        options: std.fmt.FormatOptions,
-        writer: anytype,
-    ) @TypeOf(writer).Error!void {
-        try writer.writeAll("Symbol(");
-        try std.fmt.formatType(sym.atom_index, fmt, options, writer, 0);
-        try writer.writeAll(", ");
-        try std.fmt.formatType(sym.sym_index, fmt, options, writer, 0);
-        try writer.writeByte(')');
-    }
-};
+pub const RegisterOffset = struct { reg: Register, off: i32 = 0 };
+
+pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 };
 
 pub const Memory = struct {
     base: Base,
@@ -476,7 +461,7 @@ pub const Memory = struct {
         none,
         reg: Register,
         frame: FrameIndex,
-        reloc: Symbol,
+        reloc: u32,
 
         pub const Tag = @typeInfo(Base).Union.tag_type.?;
 
@@ -568,7 +553,7 @@ pub const Memory = struct {
 pub const Immediate = union(enum) {
     signed: i32,
     unsigned: u64,
-    reloc: Symbol,
+    reloc: SymbolOffset,
 
     pub fn u(x: u64) Immediate {
         return .{ .unsigned = x };
@@ -578,19 +563,19 @@ pub const Immediate = union(enum) {
         return .{ .signed = x };
     }
 
-    pub fn rel(symbol: Symbol) Immediate {
-        return .{ .reloc = symbol };
+    pub fn rel(sym_off: SymbolOffset) Immediate {
+        return .{ .reloc = sym_off };
     }
 
     pub fn format(
         imm: Immediate,
-        comptime fmt: []const u8,
-        options: std.fmt.FormatOptions,
+        comptime _: []const u8,
+        _: std.fmt.FormatOptions,
         writer: anytype,
     ) @TypeOf(writer).Error!void {
         switch (imm) {
-            .reloc => |x| try std.fmt.formatType(x, fmt, options, writer, 0),
-            inline else => |x| try writer.print("{d}", .{x}),
+            inline else => |int| try writer.print("{d}", .{int}),
+            .reloc => |sym_off| try writer.print("Symbol({[sym_index]d}) + {[off]d}", sym_off),
         }
     }
 };
src/arch/x86_64/CodeGen.zig
@@ -64,8 +64,8 @@ va_info: union {
     sysv: struct {
         gp_count: u32,
         fp_count: u32,
-        overflow_arg_area: FrameAddr,
-        reg_save_area: FrameAddr,
+        overflow_arg_area: bits.FrameAddr,
+        reg_save_area: bits.FrameAddr,
     },
     win64: struct {},
 },
@@ -110,10 +110,6 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
 
 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
 
-const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
-const RegisterOffset = struct { reg: Register, off: i32 = 0 };
-const SymbolOffset = struct { sym: u32, off: i32 = 0 };
-
 const Owner = union(enum) {
     nav_index: InternPool.Nav.Index,
     lazy_sym: link.File.LazySymbol,
@@ -171,7 +167,7 @@ pub const MCValue = union(enum) {
     /// The value is split across two registers.
     register_pair: [2]Register,
     /// The value is a constant offset from the value in a register.
-    register_offset: RegisterOffset,
+    register_offset: bits.RegisterOffset,
     /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
     register_overflow: struct { reg: Register, eflags: Condition },
     /// The value is in memory at a hard-coded address.
@@ -179,11 +175,11 @@ pub const MCValue = union(enum) {
     memory: u64,
     /// The value is in memory at an address not-yet-allocated by the linker.
     /// This traditionally corresponds to a relocation emitted in a relocatable object file.
-    load_symbol: SymbolOffset,
+    load_symbol: bits.SymbolOffset,
     /// The address of the memory location not-yet-allocated by the linker.
-    lea_symbol: SymbolOffset,
+    lea_symbol: bits.SymbolOffset,
     /// The value is in memory at a constant offset from the address in a register.
-    indirect: RegisterOffset,
+    indirect: bits.RegisterOffset,
     /// The value is in memory.
     /// Payload is a symbol index.
     load_direct: u32,
@@ -204,10 +200,10 @@ pub const MCValue = union(enum) {
     lea_tlv: u32,
     /// The value stored at an offset from a frame index
     /// Payload is a frame address.
-    load_frame: FrameAddr,
+    load_frame: bits.FrameAddr,
     /// The address of an offset from a frame index
     /// Payload is a frame address.
-    lea_frame: FrameAddr,
+    lea_frame: bits.FrameAddr,
     /// Supports integer_per_element abi
     elementwise_regs_then_frame: packed struct { regs: u3 = 0, frame_off: i29 = 0, frame_index: FrameIndex },
     /// This indicates that we have already allocated a frame index for this instruction,
@@ -423,10 +419,7 @@ pub const MCValue = union(enum) {
             .load_symbol => |sym_off| {
                 assert(sym_off.off == 0);
                 return .{
-                    .base = .{ .reloc = .{
-                        .atom_index = try function.owner.getSymbolIndex(function),
-                        .sym_index = sym_off.sym,
-                    } },
+                    .base = .{ .reloc = sym_off.sym_index },
                     .mod = .{ .rm = .{
                         .size = size,
                         .disp = sym_off.off,
@@ -453,8 +446,8 @@ pub const MCValue = union(enum) {
             .register_overflow => |pl| try writer.print("{s}:{s}", .{
                 @tagName(pl.eflags), @tagName(pl.reg),
             }),
-            .load_symbol => |pl| try writer.print("[{} + 0x{x}]", .{ pl.sym, pl.off }),
-            .lea_symbol => |pl| try writer.print("{} + 0x{x}", .{ pl.sym, pl.off }),
+            .load_symbol => |pl| try writer.print("[{} + 0x{x}]", .{ pl.sym_index, pl.off }),
+            .lea_symbol => |pl| try writer.print("{} + 0x{x}", .{ pl.sym_index, pl.off }),
             .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
             .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}),
             .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
@@ -921,6 +914,13 @@ pub fn generate(
             .link_mode = comp.config.link_mode,
             .pic = mod.pic,
         },
+        .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
+            error.CodegenFail => return Result{ .fail = function.err_msg.? },
+            error.OutOfRegisters => return Result{
+                .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
+            },
+            else => |e| return e,
+        },
         .debug_output = debug_output,
         .code = code,
         .prev_di_pc = 0,
@@ -1019,6 +1019,13 @@ pub fn generateLazy(
             .link_mode = comp.config.link_mode,
             .pic = mod.pic,
         },
+        .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
+            error.CodegenFail => return Result{ .fail = function.err_msg.? },
+            error.OutOfRegisters => return Result{
+                .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
+            },
+            else => |e| return e,
+        },
         .debug_output = debug_output,
         .code = code,
         .prev_di_pc = undefined, // no debug info yet
@@ -1192,6 +1199,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
         self.mir_extra.appendAssumeCapacity(switch (field.type) {
             u32 => @field(extra, field.name),
             i32, Mir.Memory.Info => @bitCast(@field(extra, field.name)),
+            bits.FrameIndex => @intFromEnum(@field(extra, field.name)),
             else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
         });
     }
@@ -1358,26 +1366,94 @@ fn asmAir(self: *Self, tag: MirTagAir, inst: Air.Inst.Index) !void {
 }
 
 fn asmAirImmediate(self: *Self, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void {
-    const ops: Mir.Inst.Ops, const i: u32 = switch (imm) {
-        .signed => |s| .{ switch (tag) {
-            .dbg_local => .pseudo_dbg_local_ai_s,
-        }, @bitCast(s) },
-        .unsigned => |u| if (math.cast(u32, u)) |small|
-            .{ switch (tag) {
+    switch (imm) {
+        .signed => |s| _ = try self.addInst(.{
+            .tag = .pseudo,
+            .ops = switch (tag) {
+                .dbg_local => .pseudo_dbg_local_ai_s,
+            },
+            .data = .{ .ai = .{
+                .air_inst = inst,
+                .i = @bitCast(s),
+            } },
+        }),
+        .unsigned => |u| _ = if (math.cast(u32, u)) |small| try self.addInst(.{
+            .tag = .pseudo,
+            .ops = switch (tag) {
                 .dbg_local => .pseudo_dbg_local_ai_u,
-            }, small }
-        else
-            .{ switch (tag) {
+            },
+            .data = .{ .ai = .{
+                .air_inst = inst,
+                .i = small,
+            } },
+        }) else try self.addInst(.{
+            .tag = .pseudo,
+            .ops = switch (tag) {
                 .dbg_local => .pseudo_dbg_local_ai_64,
-            }, try self.addExtra(Mir.Imm64.encode(u)) },
-        .reloc => unreachable,
-    };
+            },
+            .data = .{ .ai = .{
+                .air_inst = inst,
+                .i = try self.addExtra(Mir.Imm64.encode(u)),
+            } },
+        }),
+        .reloc => |sym_off| _ = if (sym_off.off == 0) try self.addInst(.{
+            .tag = .pseudo,
+            .ops = switch (tag) {
+                .dbg_local => .pseudo_dbg_local_as,
+            },
+            .data = .{ .as = .{
+                .air_inst = inst,
+                .sym_index = sym_off.sym_index,
+            } },
+        }) else try self.addInst(.{
+            .tag = .pseudo,
+            .ops = switch (tag) {
+                .dbg_local => .pseudo_dbg_local_aso,
+            },
+            .data = .{ .ax = .{
+                .air_inst = inst,
+                .payload = try self.addExtra(sym_off),
+            } },
+        }),
+    }
+}
+
+fn asmAirRegisterImmediate(
+    self: *Self,
+    tag: MirTagAir,
+    inst: Air.Inst.Index,
+    reg: Register,
+    imm: Immediate,
+) !void {
     _ = try self.addInst(.{
         .tag = .pseudo,
-        .ops = ops,
-        .data = .{ .ai = .{
+        .ops = switch (tag) {
+            .dbg_local => .pseudo_dbg_local_aro,
+        },
+        .data = .{ .rx = .{
+            .r1 = reg,
+            .payload = try self.addExtra(Mir.AirOffset{
+                .air_inst = inst,
+                .off = imm.signed,
+            }),
+        } },
+    });
+}
+
+fn asmAirFrameAddress(
+    self: *Self,
+    tag: MirTagAir,
+    inst: Air.Inst.Index,
+    frame_addr: bits.FrameAddr,
+) !void {
+    _ = try self.addInst(.{
+        .tag = .pseudo,
+        .ops = switch (tag) {
+            .dbg_local => .pseudo_dbg_local_af,
+        },
+        .data = .{ .ax = .{
             .air_inst = inst,
-            .i = i,
+            .payload = try self.addExtra(frame_addr),
         } },
     });
 }
@@ -1433,9 +1509,9 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void {
             .reloc => .rel,
         },
         .data = switch (imm) {
-            .reloc => |x| reloc: {
+            .reloc => |sym_off| reloc: {
                 assert(tag[0] == ._);
-                break :reloc .{ .reloc = x };
+                break :reloc .{ .reloc = sym_off };
             },
             .signed, .unsigned => .{ .i = .{
                 .fixes = tag[0],
@@ -2515,12 +2591,12 @@ fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayo
     };
 }
 
-fn getFrameAddrAlignment(self: *Self, frame_addr: FrameAddr) Alignment {
+fn getFrameAddrAlignment(self: *Self, frame_addr: bits.FrameAddr) Alignment {
     const alloc_align = self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_align;
     return @enumFromInt(@min(@intFromEnum(alloc_align), @ctz(frame_addr.off)));
 }
 
-fn getFrameAddrSize(self: *Self, frame_addr: FrameAddr) u32 {
+fn getFrameAddrSize(self: *Self, frame_addr: bits.FrameAddr) u32 {
     return self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_size - @as(u31, @intCast(frame_addr.off));
 }
 
@@ -11999,6 +12075,8 @@ fn genLocalDebugInfo(
                 .none => try self.asmAir(.dbg_local, inst),
                 .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable,
                 .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, Immediate.u(imm)),
+                .lea_frame => |frame_addr| try self.asmAirFrameAddress(.dbg_local, inst, frame_addr),
+                .lea_symbol => |sym_off| try self.asmAirImmediate(.dbg_local, inst, Immediate.rel(sym_off)),
                 else => {
                     const ty = switch (tag) {
                         else => unreachable,
@@ -12027,14 +12105,14 @@ fn genLocalDebugInfo(
                 } },
             }),
             .lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{
-                .base = .{ .reloc = .{ .atom_index = undefined, .sym_index = sym_off.sym } },
+                .base = .{ .reloc = sym_off.sym_index },
                 .mod = .{ .rm = .{
                     .size = .qword,
                     .disp = sym_off.off,
                 } },
             }),
-            .lea_direct, .lea_got, .lea_tlv => |sym| try self.asmAirMemory(.dbg_local, inst, .{
-                .base = .{ .reloc = .{ .atom_index = undefined, .sym_index = sym } },
+            .lea_direct, .lea_got, .lea_tlv => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{
+                .base = .{ .reloc = sym_index },
                 .mod = .{ .rm = .{ .size = .qword } },
             }),
         },
@@ -12357,10 +12435,7 @@ fn genCall(self: *Self, info: union(enum) {
                     if (self.bin_file.cast(.elf)) |elf_file| {
                         const zo = elf_file.zigObjectPtr().?;
                         const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
-                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                            .atom_index = try self.owner.getSymbolIndex(self),
-                            .sym_index = sym_index,
-                        }));
+                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index }));
                     } else if (self.bin_file.cast(.coff)) |coff_file| {
                         const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
                         const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
@@ -12370,10 +12445,7 @@ fn genCall(self: *Self, info: union(enum) {
                         const zo = macho_file.getZigObject().?;
                         const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav);
                         const sym = zo.symbols.items[sym_index];
-                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                            .atom_index = try self.owner.getSymbolIndex(self),
-                            .sym_index = sym.nlist_idx,
-                        }));
+                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym.nlist_idx }));
                     } else if (self.bin_file.cast(.plan9)) |p9| {
                         const atom_index = try p9.seeNav(pt, func.owner_nav);
                         const atom = p9.getAtom(atom_index);
@@ -12391,19 +12463,13 @@ fn genCall(self: *Self, info: union(enum) {
                         @"extern".name.toSlice(ip),
                         @"extern".lib_name.toSlice(ip),
                     );
-                    try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                        .atom_index = try self.owner.getSymbolIndex(self),
-                        .sym_index = target_sym_index,
-                    }));
+                    try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index }));
                 } else if (self.bin_file.cast(.macho)) |macho_file| {
                     const target_sym_index = try macho_file.getGlobalSymbol(
                         @"extern".name.toSlice(ip),
                         @"extern".lib_name.toSlice(ip),
                     );
-                    try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                        .atom_index = try self.owner.getSymbolIndex(self),
-                        .sym_index = target_sym_index,
-                    }));
+                    try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index }));
                 } else try self.genExternSymbolRef(
                     .call,
                     @"extern".lib_name.toSlice(ip),
@@ -12418,16 +12484,10 @@ fn genCall(self: *Self, info: union(enum) {
         },
         .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| {
             const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib);
-            try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                .atom_index = try self.owner.getSymbolIndex(self),
-                .sym_index = target_sym_index,
-            }));
+            try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index }));
         } else if (self.bin_file.cast(.macho)) |macho_file| {
             const target_sym_index = try macho_file.getGlobalSymbol(lib.callee, lib.lib);
-            try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
-                .atom_index = try self.owner.getSymbolIndex(self),
-                .sym_index = target_sym_index,
-            }));
+            try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index }));
         } else try self.genExternSymbolRef(.call, lib.lib, lib.callee),
     }
     return call_info.return_value.short;
@@ -14968,10 +15028,7 @@ fn genSetReg(
                     .general_purpose => {
                         assert(sym_off.off == 0);
                         try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(dst_reg, abi_size), .{
-                            .base = .{ .reloc = .{
-                                .atom_index = try self.owner.getSymbolIndex(self),
-                                .sym_index = sym_off.sym,
-                            } },
+                            .base = .{ .reloc = sym_off.sym_index },
                             .mod = .{ .rm = .{
                                 .size = self.memSize(ty),
                                 .disp = sym_off.off,
@@ -14989,10 +15046,7 @@ fn genSetReg(
                             .ops = .direct_reloc,
                             .data = .{ .rx = .{
                                 .r1 = registerAlias(dst_reg, abi_size),
-                                .payload = try self.addExtra(bits.Symbol{
-                                    .atom_index = try self.owner.getSymbolIndex(self),
-                                    .sym_index = sym_index,
-                                }),
+                                .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }),
                             } },
                         });
                         return;
@@ -15017,52 +15071,38 @@ fn genSetReg(
                 },
             );
         },
-        .lea_symbol => |sym_index| {
-            const atom_index = try self.owner.getSymbolIndex(self);
-            switch (self.bin_file.tag) {
-                .elf, .macho => {
-                    try self.asmRegisterMemory(
-                        .{ ._, .lea },
-                        dst_reg.to64(),
-                        .{
-                            .base = .{ .reloc = .{
-                                .atom_index = atom_index,
-                                .sym_index = sym_index.sym,
-                            } },
-                            .mod = .{ .rm = .{
-                                .size = .qword,
-                                .disp = sym_index.off,
-                            } },
-                        },
-                    );
-                },
-                else => return self.fail("TODO emit symbol sequence on {s}", .{
-                    @tagName(self.bin_file.tag),
-                }),
-            }
-        },
-        .lea_direct, .lea_got => |sym_index| {
-            const atom_index = try self.owner.getSymbolIndex(self);
-            _ = try self.addInst(.{
-                .tag = switch (src_mcv) {
-                    .lea_direct => .lea,
-                    .lea_got => .mov,
-                    else => unreachable,
-                },
-                .ops = switch (src_mcv) {
-                    .lea_direct => .direct_reloc,
-                    .lea_got => .got_reloc,
-                    else => unreachable,
+        .lea_symbol => |sym_off| switch (self.bin_file.tag) {
+            .elf, .macho => try self.asmRegisterMemory(
+                .{ ._, .lea },
+                dst_reg.to64(),
+                .{
+                    .base = .{ .reloc = sym_off.sym_index },
+                    .mod = .{ .rm = .{
+                        .size = .qword,
+                        .disp = sym_off.off,
+                    } },
                 },
-                .data = .{ .rx = .{
-                    .r1 = dst_reg.to64(),
-                    .payload = try self.addExtra(bits.Symbol{
-                        .atom_index = atom_index,
-                        .sym_index = sym_index,
-                    }),
-                } },
-            });
+            ),
+            else => return self.fail("TODO emit symbol sequence on {s}", .{
+                @tagName(self.bin_file.tag),
+            }),
         },
+        .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{
+            .tag = switch (src_mcv) {
+                .lea_direct => .lea,
+                .lea_got => .mov,
+                else => unreachable,
+            },
+            .ops = switch (src_mcv) {
+                .lea_direct => .direct_reloc,
+                .lea_got => .got_reloc,
+                else => unreachable,
+            },
+            .data = .{ .rx = .{
+                .r1 = dst_reg.to64(),
+                .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }),
+            } },
+        }),
         .lea_tlv => unreachable, // TODO: remove this
         .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref), opts),
     }
@@ -15083,7 +15123,7 @@ fn genSetMem(
         .none => .{ .immediate = @bitCast(@as(i64, disp)) },
         .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } },
         .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } },
-        .reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } },
+        .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } },
     };
     switch (src_mcv) {
         .none,
@@ -15326,7 +15366,6 @@ fn genExternSymbolRef(
     lib: ?[]const u8,
     callee: []const u8,
 ) InnerError!void {
-    const atom_index = try self.owner.getSymbolIndex(self);
     if (self.bin_file.cast(.coff)) |coff_file| {
         const global_index = try coff_file.getGlobalSymbol(callee, lib);
         _ = try self.addInst(.{
@@ -15334,8 +15373,7 @@ fn genExternSymbolRef(
             .ops = .import_reloc,
             .data = .{ .rx = .{
                 .r1 = .rax,
-                .payload = try self.addExtra(bits.Symbol{
-                    .atom_index = atom_index,
+                .payload = try self.addExtra(bits.SymbolOffset{
                     .sym_index = link.File.Coff.global_symbol_bit | global_index,
                 }),
             } },
@@ -15362,10 +15400,10 @@ fn genLazySymbolRef(
         if (self.mod.pic) {
             switch (tag) {
                 .lea, .call => try self.genSetReg(reg, Type.usize, .{
-                    .lea_symbol = .{ .sym = sym_index },
+                    .lea_symbol = .{ .sym_index = sym_index },
                 }, .{}),
                 .mov => try self.genSetReg(reg, Type.usize, .{
-                    .load_symbol = .{ .sym = sym_index },
+                    .load_symbol = .{ .sym_index = sym_index },
                 }, .{}),
                 else => unreachable,
             }
@@ -15374,19 +15412,13 @@ fn genLazySymbolRef(
                 .call => try self.asmRegister(.{ ._, .call }, reg),
                 else => unreachable,
             }
-        } else {
-            const reloc = bits.Symbol{
-                .atom_index = try self.owner.getSymbolIndex(self),
-                .sym_index = sym_index,
-            };
-            switch (tag) {
-                .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{
-                    .base = .{ .reloc = reloc },
-                    .mod = .{ .rm = .{ .size = .qword } },
-                }),
-                .call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(reloc)),
-                else => unreachable,
-            }
+        } else switch (tag) {
+            .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{
+                .base = .{ .reloc = sym_index },
+                .mod = .{ .rm = .{ .size = .qword } },
+            }),
+            .call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index })),
+            else => unreachable,
         }
     } else if (self.bin_file.cast(.plan9)) |p9_file| {
         const atom_index = p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
@@ -15436,10 +15468,10 @@ fn genLazySymbolRef(
         const sym = zo.symbols.items[sym_index];
         switch (tag) {
             .lea, .call => try self.genSetReg(reg, Type.usize, .{
-                .lea_symbol = .{ .sym = sym.nlist_idx },
+                .lea_symbol = .{ .sym_index = sym.nlist_idx },
             }, .{}),
             .mov => try self.genSetReg(reg, Type.usize, .{
-                .load_symbol = .{ .sym = sym.nlist_idx },
+                .load_symbol = .{ .sym_index = sym.nlist_idx },
             }, .{}),
             else => unreachable,
         }
@@ -18784,7 +18816,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
                             .{ .frame = frame_index },
                             0,
                             Type.usize,
-                            .{ .lea_symbol = .{ .sym = tlv_sym } },
+                            .{ .lea_symbol = .{ .sym_index = tlv_sym } },
                             .{},
                         );
                         break :init .{ .load_frame = .{ .index = frame_index } };
@@ -18840,8 +18872,8 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
             .undef => .undef,
             .immediate => |imm| .{ .immediate = imm },
             .memory => |addr| .{ .memory = addr },
-            .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
-            .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } },
+            .load_symbol => |sym_index| .{ .load_symbol = .{ .sym_index = sym_index } },
+            .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index } },
             .load_direct => |sym_index| .{ .load_direct = sym_index },
             .lea_direct => |sym_index| .{ .lea_direct = sym_index },
             .load_got => |sym_index| .{ .lea_got = sym_index },
src/arch/x86_64/Emit.zig
@@ -2,6 +2,7 @@
 
 air: Air,
 lower: Lower,
+atom_index: u32,
 debug_output: DebugInfoOutput,
 code: *std.ArrayList(u8),
 
@@ -37,83 +38,84 @@ pub fn emitMir(emit: *Emit) Error!void {
             }) switch (lowered_relocs[0].target) {
                 .inst => |target| try emit.relocs.append(emit.lower.allocator, .{
                     .source = start_offset,
+                    .source_offset = end_offset - 4,
                     .target = target,
-                    .offset = end_offset - 4,
+                    .target_offset = lowered_relocs[0].off,
                     .length = @intCast(end_offset - start_offset),
                 }),
-                .linker_extern_fn => |symbol| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
+                .linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
                     // Add relocation to the decl.
                     const zo = elf_file.zigObjectPtr().?;
-                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
+                    const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);
                     try atom_ptr.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
-                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
-                        .r_addend = -4,
+                        .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
+                        .r_addend = lowered_relocs[0].off - 4,
                     });
                 } else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
                     // Add relocation to the decl.
                     const zo = macho_file.getZigObject().?;
-                    const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?;
+                    const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
                     try atom.addReloc(macho_file, .{
                         .tag = .@"extern",
                         .offset = end_offset - 4,
-                        .target = symbol.sym_index,
-                        .addend = 0,
+                        .target = sym_index,
+                        .addend = lowered_relocs[0].off,
                         .type = .branch,
                         .meta = .{
                             .pcrel = true,
                             .has_subtractor = false,
                             .length = 2,
-                            .symbolnum = @intCast(symbol.sym_index),
+                            .symbolnum = @intCast(sym_index),
                         },
                     });
                 } else if (emit.lower.bin_file.cast(.coff)) |coff_file| {
                     // Add relocation to the decl.
                     const atom_index = coff_file.getAtomIndexForSymbol(
-                        .{ .sym_index = symbol.atom_index, .file = null },
+                        .{ .sym_index = emit.atom_index, .file = null },
                     ).?;
-                    const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0)
-                        coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index)
+                    const target = if (link.File.Coff.global_symbol_bit & sym_index != 0)
+                        coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index)
                     else
-                        link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null };
+                        link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null };
                     try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
                         .type = .direct,
                         .target = target,
                         .offset = end_offset - 4,
-                        .addend = 0,
+                        .addend = @intCast(lowered_relocs[0].off),
                         .pcrel = true,
                         .length = 2,
                     });
                 } else return emit.fail("TODO implement extern reloc for {s}", .{
                     @tagName(emit.lower.bin_file.tag),
                 }),
-                .linker_tlsld => |data| {
+                .linker_tlsld => |sym_index| {
                     const elf_file = emit.lower.bin_file.cast(.elf).?;
                     const zo = elf_file.zigObjectPtr().?;
-                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
+                    const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
                     try atom.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
-                        .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
-                        .r_addend = -4,
+                        .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
+                        .r_addend = lowered_relocs[0].off - 4,
                     });
                 },
-                .linker_dtpoff => |data| {
+                .linker_dtpoff => |sym_index| {
                     const elf_file = emit.lower.bin_file.cast(.elf).?;
                     const zo = elf_file.zigObjectPtr().?;
-                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
+                    const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
                     try atom.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
-                        .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
-                        .r_addend = 0,
+                        .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
+                        .r_addend = lowered_relocs[0].off,
                     });
                 },
-                .linker_reloc => |data| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
+                .linker_reloc => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
                     const zo = elf_file.zigObjectPtr().?;
-                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
-                    const sym = zo.symbol(data.sym_index);
+                    const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+                    const sym = zo.symbol(sym_index);
                     if (emit.lower.pic) {
                         const r_type: u32 = if (sym.flags.is_extern_ptr)
                             @intFromEnum(std.elf.R_X86_64.GOTPCREL)
@@ -121,8 +123,8 @@ pub fn emitMir(emit: *Emit) Error!void {
                             @intFromEnum(std.elf.R_X86_64.PC32);
                         try atom.addReloc(elf_file, .{
                             .r_offset = end_offset - 4,
-                            .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
-                            .r_addend = -4,
+                            .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
+                            .r_addend = lowered_relocs[0].off - 4,
                         });
                     } else {
                         const r_type: u32 = if (sym.flags.is_tls)
@@ -131,14 +133,14 @@ pub fn emitMir(emit: *Emit) Error!void {
                             @intFromEnum(std.elf.R_X86_64.@"32");
                         try atom.addReloc(elf_file, .{
                             .r_offset = end_offset - 4,
-                            .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
-                            .r_addend = 0,
+                            .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
+                            .r_addend = lowered_relocs[0].off,
                         });
                     }
                 } else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
                     const zo = macho_file.getZigObject().?;
-                    const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
-                    const sym = &zo.symbols.items[data.sym_index];
+                    const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
+                    const sym = &zo.symbols.items[sym_index];
                     const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr)
                         .got_load
                     else if (sym.flags.tlv)
@@ -148,33 +150,33 @@ pub fn emitMir(emit: *Emit) Error!void {
                     try atom.addReloc(macho_file, .{
                         .tag = .@"extern",
                         .offset = @intCast(end_offset - 4),
-                        .target = data.sym_index,
-                        .addend = 0,
+                        .target = sym_index,
+                        .addend = lowered_relocs[0].off,
                         .type = @"type",
                         .meta = .{
                             .pcrel = true,
                             .has_subtractor = false,
                             .length = 2,
-                            .symbolnum = @intCast(data.sym_index),
+                            .symbolnum = @intCast(sym_index),
                         },
                     });
                 } else unreachable,
                 .linker_got,
                 .linker_direct,
                 .linker_import,
-                => |symbol| if (emit.lower.bin_file.cast(.elf)) |_| {
+                => |sym_index| if (emit.lower.bin_file.cast(.elf)) |_| {
                     unreachable;
                 } else if (emit.lower.bin_file.cast(.macho)) |_| {
                     unreachable;
                 } else if (emit.lower.bin_file.cast(.coff)) |coff_file| {
                     const atom_index = coff_file.getAtomIndexForSymbol(.{
-                        .sym_index = symbol.atom_index,
+                        .sym_index = emit.atom_index,
                         .file = null,
                     }).?;
-                    const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0)
-                        coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index)
+                    const target = if (link.File.Coff.global_symbol_bit & sym_index != 0)
+                        coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index)
                     else
-                        link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null };
+                        link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null };
                     try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
                         .type = switch (lowered_relocs[0].target) {
                             .linker_got => .got,
@@ -184,16 +186,15 @@ pub fn emitMir(emit: *Emit) Error!void {
                         },
                         .target = target,
                         .offset = @intCast(end_offset - 4),
-                        .addend = 0,
+                        .addend = @intCast(lowered_relocs[0].off),
                         .pcrel = true,
                         .length = 2,
                     });
                 } else if (emit.lower.bin_file.cast(.plan9)) |p9_file| {
-                    const atom_index = symbol.atom_index;
-                    try p9_file.addReloc(atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct
-                        .target = symbol.sym_index, // we set sym_index to just be the atom index
+                    try p9_file.addReloc(emit.atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct
+                        .target = sym_index, // we set sym_index to just be the atom index
                         .offset = @intCast(end_offset - 4),
-                        .addend = 0,
+                        .addend = @intCast(lowered_relocs[0].off),
                         .type = .pcrel,
                     });
                 } else return emit.fail("TODO implement linker reloc for {s}", .{
@@ -261,6 +262,10 @@ pub fn emitMir(emit: *Emit) Error!void {
                     .pseudo_dbg_local_ai_s,
                     .pseudo_dbg_local_ai_u,
                     .pseudo_dbg_local_ai_64,
+                    .pseudo_dbg_local_as,
+                    .pseudo_dbg_local_aso,
+                    .pseudo_dbg_local_aro,
+                    .pseudo_dbg_local_af,
                     .pseudo_dbg_local_am,
                     => {
                         switch (emit.debug_output) {
@@ -279,6 +284,57 @@ pub fn emitMir(emit: *Emit) Error!void {
                                         };
                                         break :stack_value &loc_buf[0];
                                     } } },
+                                    .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ .addr = .{
+                                        .sym = mir_inst.data.as.sym_index,
+                                    } } },
+                                    .pseudo_dbg_local_aso => loc: {
+                                        const sym_off = emit.lower.mir.extraData(
+                                            bits.SymbolOffset,
+                                            mir_inst.data.ax.payload,
+                                        ).data;
+                                        break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
+                                            sym: {
+                                                loc_buf[0] = .{ .addr = .{ .sym = sym_off.sym_index } };
+                                                break :sym &loc_buf[0];
+                                            },
+                                            off: {
+                                                loc_buf[1] = .{ .consts = sym_off.off };
+                                                break :off &loc_buf[1];
+                                            },
+                                        } } };
+                                    },
+                                    .pseudo_dbg_local_aro => loc: {
+                                        const air_off = emit.lower.mir.extraData(
+                                            Mir.AirOffset,
+                                            mir_inst.data.rx.payload,
+                                        ).data;
+                                        break :loc .{ air_off.air_inst, .{ .plus = .{
+                                            reg: {
+                                                loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() };
+                                                break :reg &loc_buf[0];
+                                            },
+                                            off: {
+                                                loc_buf[1] = .{ .consts = air_off.off };
+                                                break :off &loc_buf[1];
+                                            },
+                                        } } };
+                                    },
+                                    .pseudo_dbg_local_af => loc: {
+                                        const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData(
+                                            bits.FrameAddr,
+                                            mir_inst.data.ax.payload,
+                                        ).data);
+                                        break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
+                                            reg: {
+                                                loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() };
+                                                break :reg &loc_buf[0];
+                                            },
+                                            off: {
+                                                loc_buf[1] = .{ .consts = reg_off.off };
+                                                break :off &loc_buf[1];
+                                            },
+                                        } } };
+                                    },
                                     .pseudo_dbg_local_am => loc: {
                                         const mem = emit.lower.mem(mir_inst.data.ax.payload);
                                         break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
@@ -287,7 +343,7 @@ pub fn emitMir(emit: *Emit) Error!void {
                                                     .none => .{ .constu = 0 },
                                                     .reg => |reg| .{ .breg = reg.dwarfNum() },
                                                     .frame => unreachable,
-                                                    .reloc => |reloc| .{ .addr = .{ .sym = reloc.sym_index } },
+                                                    .reloc => |sym_index| .{ .addr = .{ .sym = sym_index } },
                                                 };
                                                 break :base &loc_buf[0];
                                             },
@@ -352,10 +408,12 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
 const Reloc = struct {
     /// Offset of the instruction.
     source: usize,
+    /// Offset of the relocation within the instruction.
+    source_offset: u32,
     /// Target of the relocation.
     target: Mir.Inst.Index,
-    /// Offset of the relocation within the instruction.
-    offset: u32,
+    /// Offset from the target instruction.
+    target_offset: i32,
     /// Length of the instruction.
     length: u5,
 };
@@ -368,8 +426,8 @@ fn fixupRelocs(emit: *Emit) Error!void {
     for (emit.relocs.items) |reloc| {
         const target = emit.code_offset_mapping.get(reloc.target) orelse
             return emit.fail("JMP/CALL relocation target not found!", .{});
-        const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length));
-        std.mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], @intCast(disp), .little);
+        const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset;
+        std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little);
     }
 }
 
@@ -422,6 +480,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
     }
 }
 
+const bits = @import("bits.zig");
 const link = @import("../../link.zig");
 const log = std.log.scoped(.emit);
 const std = @import("std");
src/arch/x86_64/encoder.zig
@@ -266,17 +266,12 @@ pub const Instruction = struct {
 
                         try writer.writeByte('[');
 
-                        var any = false;
+                        var any = true;
                         switch (sib.base) {
-                            .none => {},
-                            .reg => |reg| {
-                                try writer.print("{s}", .{@tagName(reg)});
-                                any = true;
-                            },
-                            inline .frame, .reloc => |payload| {
-                                try writer.print("{}", .{payload});
-                                any = true;
-                            },
+                            .none => any = false,
+                            .reg => |reg| try writer.print("{s}", .{@tagName(reg)}),
+                            .frame => |frame_index| try writer.print("{}", .{frame_index}),
+                            .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}),
                         }
                         if (mem.scaleIndex()) |si| {
                             if (any) try writer.writeAll(" + ");
src/arch/x86_64/Lower.zig
@@ -52,16 +52,17 @@ pub const Error = error{
 pub const Reloc = struct {
     lowered_inst_index: u8,
     target: Target,
+    off: i32,
 
     const Target = union(enum) {
         inst: Mir.Inst.Index,
-        linker_reloc: bits.Symbol,
-        linker_tlsld: bits.Symbol,
-        linker_dtpoff: bits.Symbol,
-        linker_extern_fn: bits.Symbol,
-        linker_got: bits.Symbol,
-        linker_direct: bits.Symbol,
-        linker_import: bits.Symbol,
+        linker_reloc: u32,
+        linker_tlsld: u32,
+        linker_dtpoff: u32,
+        linker_extern_fn: u32,
+        linker_got: u32,
+        linker_direct: u32,
+        linker_import: u32,
     };
 };
 
@@ -173,19 +174,19 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_j_z_and_np_inst => {
                 assert(inst.data.inst.fixes == ._);
                 try lower.emit(.none, .jnz, &.{
-                    .{ .imm = lower.reloc(.{ .inst = index + 1 }) },
+                    .{ .imm = lower.reloc(.{ .inst = index + 1 }, 0) },
                 });
                 try lower.emit(.none, .jnp, &.{
-                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
+                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) },
                 });
             },
             .pseudo_j_nz_or_p_inst => {
                 assert(inst.data.inst.fixes == ._);
                 try lower.emit(.none, .jnz, &.{
-                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
+                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) },
                 });
                 try lower.emit(.none, .jp, &.{
-                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
+                    .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) },
                 });
             },
 
@@ -195,7 +196,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                     .{ .imm = Immediate.s(@bitCast(inst.data.ri.i)) },
                 });
                 try lower.emit(.none, .jz, &.{
-                    .{ .imm = lower.reloc(.{ .inst = index + 1 }) },
+                    .{ .imm = lower.reloc(.{ .inst = index + 1 }, 0) },
                 });
                 try lower.emit(.none, .lea, &.{
                     .{ .reg = inst.data.ri.r1 },
@@ -211,7 +212,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                     .{ .reg = inst.data.ri.r1.to32() },
                 });
                 try lower.emit(.none, .jmp, &.{
-                    .{ .imm = lower.reloc(.{ .inst = index }) },
+                    .{ .imm = lower.reloc(.{ .inst = index }, 0) },
                 });
                 assert(lower.result_insts_len == pseudo_probe_align_insts);
             },
@@ -257,7 +258,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                     .{ .imm = Immediate.s(page_size) },
                 });
                 try lower.emit(.none, .jae, &.{
-                    .{ .imm = lower.reloc(.{ .inst = index }) },
+                    .{ .imm = lower.reloc(.{ .inst = index }, 0) },
                 });
                 assert(lower.result_insts_len == pseudo_probe_adjust_loop_insts);
             },
@@ -273,6 +274,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_dbg_local_ai_s,
             .pseudo_dbg_local_ai_u,
             .pseudo_dbg_local_ai_64,
+            .pseudo_dbg_local_as,
+            .pseudo_dbg_local_aso,
+            .pseudo_dbg_local_aro,
+            .pseudo_dbg_local_af,
             .pseudo_dbg_local_am,
             .pseudo_dead_none,
             => {},
@@ -328,10 +333,11 @@ pub fn mem(lower: Lower, payload: u32) Memory {
     return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
 }
 
-fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
+fn reloc(lower: *Lower, target: Reloc.Target, off: i32) Immediate {
     lower.result_relocs[lower.result_relocs_len] = .{
         .lowered_inst_index = lower.result_insts_len,
         .target = target,
+        .off = off,
     };
     lower.result_relocs_len += 1;
     return Immediate.s(0);
@@ -347,37 +353,36 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
             else => op,
             .mem => |mem_op| switch (mem_op.base()) {
                 else => op,
-                .reloc => |sym| op: {
+                .reloc => |sym_index| op: {
                     assert(prefix == .none);
                     assert(mem_op.sib.disp == 0);
                     assert(mem_op.sib.scale_index.scale == 0);
 
                     if (lower.bin_file.cast(.elf)) |elf_file| {
                         const zo = elf_file.zigObjectPtr().?;
-                        const elf_sym = zo.symbol(sym.sym_index);
+                        const elf_sym = zo.symbol(sym_index);
 
                         if (elf_sym.flags.is_tls) {
                             // TODO handle extern TLS vars, i.e., emit GD model
                             if (lower.pic) {
                                 // Here, we currently assume local dynamic TLS vars, and so
                                 // we emit LD model.
-                                _ = lower.reloc(.{ .linker_tlsld = sym });
+                                _ = lower.reloc(.{ .linker_tlsld = sym_index }, 0);
                                 lower.result_insts[lower.result_insts_len] =
                                     try Instruction.new(.none, .lea, &[_]Operand{
                                     .{ .reg = .rdi },
                                     .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
                                 });
                                 lower.result_insts_len += 1;
-                                _ = lower.reloc(.{ .linker_extern_fn = .{
-                                    .atom_index = sym.atom_index,
-                                    .sym_index = try elf_file.getGlobalSymbol("__tls_get_addr", null),
-                                } });
+                                _ = lower.reloc(.{
+                                    .linker_extern_fn = try elf_file.getGlobalSymbol("__tls_get_addr", null),
+                                }, 0);
                                 lower.result_insts[lower.result_insts_len] =
                                     try Instruction.new(.none, .call, &[_]Operand{
                                     .{ .imm = Immediate.s(0) },
                                 });
                                 lower.result_insts_len += 1;
-                                _ = lower.reloc(.{ .linker_dtpoff = sym });
+                                _ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0);
                                 emit_mnemonic = .lea;
                                 break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
                                     .base = .{ .reg = .rax },
@@ -391,7 +396,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                                     .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) },
                                 });
                                 lower.result_insts_len += 1;
-                                _ = lower.reloc(.{ .linker_reloc = sym });
+                                _ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
                                 emit_mnemonic = .lea;
                                 break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
                                     .base = .{ .reg = .rax },
@@ -400,7 +405,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                             }
                         }
 
-                        _ = lower.reloc(.{ .linker_reloc = sym });
+                        _ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
                         if (lower.pic) switch (mnemonic) {
                             .lea => {
                                 if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
@@ -437,10 +442,10 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                         }
                     } else if (lower.bin_file.cast(.macho)) |macho_file| {
                         const zo = macho_file.getZigObject().?;
-                        const macho_sym = zo.symbols.items[sym.sym_index];
+                        const macho_sym = zo.symbols.items[sym_index];
 
                         if (macho_sym.flags.tlv) {
-                            _ = lower.reloc(.{ .linker_reloc = sym });
+                            _ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
                             lower.result_insts[lower.result_insts_len] =
                                 try Instruction.new(.none, .mov, &[_]Operand{
                                 .{ .reg = .rdi },
@@ -456,7 +461,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                             break :op .{ .reg = .rax };
                         }
 
-                        _ = lower.reloc(.{ .linker_reloc = sym });
+                        _ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
                         break :op switch (mnemonic) {
                             .lea => {
                                 if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
@@ -535,7 +540,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
     }, switch (inst.ops) {
         .none => &.{},
         .inst => &.{
-            .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
+            .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) },
         },
         .i_s, .i_u => &.{
             .{ .imm = lower.imm(inst.ops, inst.data.i.i) },
@@ -637,17 +642,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
         },
         .extern_fn_reloc, .rel => &.{
-            .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
+            .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc.sym_index }, inst.data.reloc.off) },
         },
         .got_reloc, .direct_reloc, .import_reloc => ops: {
             const reg = inst.data.rx.r1;
-            const extra = lower.mir.extraData(bits.Symbol, inst.data.rx.payload).data;
+            const extra = lower.mir.extraData(bits.SymbolOffset, inst.data.rx.payload).data;
             _ = lower.reloc(switch (inst.ops) {
-                .got_reloc => .{ .linker_got = extra },
-                .direct_reloc => .{ .linker_direct = extra },
-                .import_reloc => .{ .linker_import = extra },
+                .got_reloc => .{ .linker_got = extra.sym_index },
+                .direct_reloc => .{ .linker_direct = extra.sym_index },
+                .import_reloc => .{ .linker_import = extra.sym_index },
                 else => unreachable,
-            });
+            }, extra.off);
             break :ops &.{
                 .{ .reg = reg },
                 .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
src/arch/x86_64/Mir.zig
@@ -820,16 +820,16 @@ pub const Inst = struct {
         /// Uses `reloc` payload.
         extern_fn_reloc,
         /// Linker relocation - GOT indirection.
-        /// Uses `rx` payload with extra data of type `bits.Symbol`.
+        /// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
         got_reloc,
         /// Linker relocation - direct reference.
-        /// Uses `rx` payload with extra data of type `bits.Symbol`.
+        /// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
         direct_reloc,
         /// Linker relocation - imports table indirection (binding).
-        /// Uses `rx` payload with extra data of type `bits.Symbol`.
+        /// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
         import_reloc,
         /// Linker relocation - threadlocal variable via GOT indirection.
-        /// Uses `rx` payload with extra data of type `bits.Symbol`.
+        /// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
         tlv_reloc,
 
         // Pseudo instructions:
@@ -907,9 +907,21 @@ pub const Inst = struct {
         /// Uses `ai` payload.
         pseudo_dbg_local_ai_u,
         /// Local argument or variable.
-        /// Uses `ax` payload with extra data of type `Imm64`.
+        /// Uses `ai` payload with extra data of type `Imm64`.
         pseudo_dbg_local_ai_64,
         /// Local argument or variable.
+        /// Uses `as` payload.
+        pseudo_dbg_local_as,
+        /// Local argument or variable.
+        /// Uses `ax` payload with extra data of type `bits.SymbolOffset`.
+        pseudo_dbg_local_aso,
+        /// Local argument or variable.
+        /// Uses `rx` payload with extra data of type `AirOffset`.
+        pseudo_dbg_local_aro,
+        /// Local argument or variable.
+        /// Uses `ax` payload with extra data of type `bits.FrameAddr`.
+        pseudo_dbg_local_af,
+        /// Local argument or variable.
         /// Uses `ax` payload with extra data of type `Memory`.
         pseudo_dbg_local_am,
 
@@ -1014,6 +1026,9 @@ pub const Inst = struct {
             fixes: Fixes = ._,
             payload: u32,
         },
+        ix: struct {
+            payload: u32,
+        },
         a: struct {
             air_inst: Air.Inst.Index,
         },
@@ -1021,14 +1036,18 @@ pub const Inst = struct {
             air_inst: Air.Inst.Index,
             i: u32,
         },
+        as: struct {
+            air_inst: Air.Inst.Index,
+            sym_index: u32,
+        },
         ax: struct {
             air_inst: Air.Inst.Index,
             payload: u32,
         },
         /// Relocation for the linker where:
-        /// * `atom_index` is the index of the source
         /// * `sym_index` is the index of the target
-        reloc: bits.Symbol,
+        /// * `off` is the offset from the target
+        reloc: bits.SymbolOffset,
         /// Debug line and column position
         line_column: struct {
             line: u32,
@@ -1048,6 +1067,8 @@ pub const Inst = struct {
     }
 };
 
+pub const AirOffset = struct { air_inst: Air.Inst.Index, off: i32 };
+
 /// Used in conjunction with payload to transfer a list of used registers in a compact manner.
 pub const RegisterList = struct {
     bitset: BitSet = BitSet.initEmpty(),
@@ -1146,15 +1167,13 @@ pub const Memory = struct {
                 .none => undefined,
                 .reg => |reg| @intFromEnum(reg),
                 .frame => |frame_index| @intFromEnum(frame_index),
-                .reloc => |symbol| symbol.sym_index,
+                .reloc => |sym_index| sym_index,
             },
             .off = switch (mem.mod) {
                 .rm => |rm| @bitCast(rm.disp),
                 .off => |off| @truncate(off),
             },
-            .extra = if (mem.base == .reloc)
-                mem.base.reloc.atom_index
-            else if (mem.mod == .off)
+            .extra = if (mem.mod == .off)
                 @intCast(mem.mod.off >> 32)
             else
                 undefined,
@@ -1174,7 +1193,7 @@ pub const Memory = struct {
                         .none => .none,
                         .reg => .{ .reg = @enumFromInt(mem.base) },
                         .frame => .{ .frame = @enumFromInt(mem.base) },
-                        .reloc => .{ .reloc = .{ .atom_index = mem.extra, .sym_index = mem.base } },
+                        .reloc => .{ .reloc = mem.base },
                     },
                     .scale_index = switch (mem.info.index) {
                         .none => null,
@@ -1214,6 +1233,7 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end:
         @field(result, field.name) = switch (field.type) {
             u32 => mir.extra[i],
             i32, Memory.Info => @bitCast(mir.extra[i]),
+            bits.FrameIndex, Air.Inst.Index => @enumFromInt(mir.extra[i]),
             else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
         };
         i += 1;
@@ -1229,6 +1249,11 @@ pub const FrameLoc = struct {
     disp: i32,
 };
 
+pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffset {
+    const frame_loc = mir.frame_locs.get(@intFromEnum(frame_addr.index));
+    return .{ .reg = frame_loc.base, .off = frame_loc.disp + frame_addr.off };
+}
+
 pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
     return switch (mem.info.base) {
         .none, .reg, .reloc => mem,