Commit d25c93a868

Jakub Konka <kubkon@jakubkonka.com>
2024-08-13 09:56:17
x86_64: emit call rel32 for near calls with linker reloc
1 parent ffcf047
src/arch/x86_64/bits.zig
@@ -569,6 +569,7 @@ pub const Memory = struct {
 pub const Immediate = union(enum) {
     signed: i32,
     unsigned: u64,
+    reloc: Symbol,
 
     pub fn u(x: u64) Immediate {
         return .{ .unsigned = x };
@@ -578,39 +579,19 @@ pub const Immediate = union(enum) {
         return .{ .signed = x };
     }
 
-    pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
-        return switch (imm) {
-            .signed => |x| switch (bit_size) {
-                1, 8 => @as(i8, @intCast(x)),
-                16 => @as(i16, @intCast(x)),
-                32, 64 => x,
-                else => unreachable,
-            },
-            .unsigned => |x| switch (bit_size) {
-                1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
-                16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
-                32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
-                64 => @bitCast(x),
-                else => unreachable,
-            },
-        };
+    pub fn rel(symbol: Symbol) Immediate {
+        return .{ .reloc = symbol };
     }
 
-    pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
-        return switch (imm) {
-            .signed => |x| switch (bit_size) {
-                1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))),
-                16 => @as(u16, @bitCast(@as(i16, @intCast(x)))),
-                32, 64 => @as(u32, @bitCast(x)),
-                else => unreachable,
-            },
-            .unsigned => |x| switch (bit_size) {
-                1, 8 => @as(u8, @intCast(x)),
-                16 => @as(u16, @intCast(x)),
-                32 => @as(u32, @intCast(x)),
-                64 => x,
-                else => unreachable,
-            },
-        };
+    pub fn format(
+        imm: Immediate,
+        comptime fmt: []const u8,
+        options: 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}),
+        }
     }
 };
src/arch/x86_64/CodeGen.zig
@@ -1379,14 +1379,19 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void {
         .ops = switch (imm) {
             .signed => .i_s,
             .unsigned => .i_u,
+            .reloc => .rel,
+        },
+        .data = switch (imm) {
+            .reloc => |x| .{ .reloc = x },
+            .signed, .unsigned => .{ .i = .{
+                .fixes = tag[0],
+                .i = switch (imm) {
+                    .signed => |s| @bitCast(s),
+                    .unsigned => |u| @intCast(u),
+                    .reloc => unreachable,
+                },
+            } },
         },
-        .data = .{ .i = .{
-            .fixes = tag[0],
-            .i = switch (imm) {
-                .signed => |s| @bitCast(s),
-                .unsigned => |u| @intCast(u),
-            },
-        } },
     });
 }
 
@@ -1406,6 +1411,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm:
     const ops: Mir.Inst.Ops = switch (imm) {
         .signed => .ri_s,
         .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64,
+        .reloc => unreachable,
     };
     _ = try self.addInst(.{
         .tag = tag[1],
@@ -1417,6 +1423,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm:
                 .i = switch (imm) {
                     .signed => |s| @bitCast(s),
                     .unsigned => |u| @intCast(u),
+                    .reloc => unreachable,
                 },
             } },
             .ri64 => .{ .rx = .{
@@ -1488,6 +1495,7 @@ fn asmRegisterRegisterRegisterImmediate(
             .i = switch (imm) {
                 .signed => |s| @bitCast(@as(i8, @intCast(s))),
                 .unsigned => |u| @intCast(u),
+                .reloc => unreachable,
             },
         } },
     });
@@ -1505,6 +1513,7 @@ fn asmRegisterRegisterImmediate(
         .ops = switch (imm) {
             .signed => .rri_s,
             .unsigned => .rri_u,
+            .reloc => unreachable,
         },
         .data = .{ .rri = .{
             .fixes = tag[0],
@@ -1513,6 +1522,7 @@ fn asmRegisterRegisterImmediate(
             .i = switch (imm) {
                 .signed => |s| @bitCast(s),
                 .unsigned => |u| @intCast(u),
+                .reloc => unreachable,
             },
         } },
     });
@@ -1610,6 +1620,7 @@ fn asmRegisterMemoryImmediate(
     if (switch (imm) {
         .signed => |s| if (math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null,
         .unsigned => |u| math.cast(u16, u),
+        .reloc => unreachable,
     }) |small_imm| {
         _ = try self.addInst(.{
             .tag = tag[1],
@@ -1625,6 +1636,7 @@ fn asmRegisterMemoryImmediate(
         const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) {
             .signed => |s| @bitCast(s),
             .unsigned => unreachable,
+            .reloc => unreachable,
         } });
         assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
         _ = try self.addInst(.{
@@ -1632,6 +1644,7 @@ fn asmRegisterMemoryImmediate(
             .ops = switch (imm) {
                 .signed => .rmi_s,
                 .unsigned => .rmi_u,
+                .reloc => unreachable,
             },
             .data = .{ .rx = .{
                 .fixes = tag[0],
@@ -1679,6 +1692,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed
     const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) {
         .signed => |s| @bitCast(s),
         .unsigned => |u| @intCast(u),
+        .reloc => unreachable,
     } });
     assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
     _ = try self.addInst(.{
@@ -1686,6 +1700,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed
         .ops = switch (imm) {
             .signed => .mi_s,
             .unsigned => .mi_u,
+            .reloc => unreachable,
         },
         .data = .{ .x = .{
             .fixes = tag[0],
@@ -12310,33 +12325,10 @@ 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);
-                        if (self.mod.pic) {
-                            const callee_reg: Register = switch (resolved_cc) {
-                                .SysV => callee: {
-                                    if (!fn_info.is_var_args) break :callee .rax;
-                                    const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
-                                    break :callee if (call_info.gp_count < param_regs.len)
-                                        param_regs[call_info.gp_count]
-                                    else
-                                        .r10;
-                                },
-                                .Win64 => .rax,
-                                else => unreachable,
-                            };
-                            try self.genSetReg(
-                                callee_reg,
-                                Type.usize,
-                                .{ .lea_symbol = .{ .sym = sym_index } },
-                                .{},
-                            );
-                            try self.asmRegister(.{ ._, .call }, callee_reg);
-                        } else try self.asmMemory(.{ ._, .call }, .{
-                            .base = .{ .reloc = .{
-                                .atom_index = try self.owner.getSymbolIndex(self),
-                                .sym_index = sym_index,
-                            } },
-                            .mod = .{ .rm = .{ .size = .qword } },
-                        });
+                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
+                            .atom_index = try self.owner.getSymbolIndex(self),
+                            .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().?;
src/arch/x86_64/Disassembler.zig
@@ -8,7 +8,7 @@ const bits = @import("bits.zig");
 const encoder = @import("encoder.zig");
 
 const Encoding = @import("Encoding.zig");
-const Immediate = bits.Immediate;
+const Immediate = Instruction.Immediate;
 const Instruction = encoder.Instruction;
 const LegacyPrefixes = encoder.LegacyPrefixes;
 const Memory = Instruction.Memory;
src/arch/x86_64/encoder.zig
@@ -7,7 +7,6 @@ const testing = std.testing;
 const bits = @import("bits.zig");
 const Encoding = @import("Encoding.zig");
 const FrameIndex = bits.FrameIndex;
-const Immediate = bits.Immediate;
 const Register = bits.Register;
 const Symbol = bits.Symbol;
 
@@ -28,6 +27,55 @@ pub const Instruction = struct {
         repnz,
     };
 
+    pub const Immediate = union(enum) {
+        signed: i32,
+        unsigned: u64,
+
+        pub fn u(x: u64) Immediate {
+            return .{ .unsigned = x };
+        }
+
+        pub fn s(x: i32) Immediate {
+            return .{ .signed = x };
+        }
+
+        pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
+            return switch (imm) {
+                .signed => |x| switch (bit_size) {
+                    1, 8 => @as(i8, @intCast(x)),
+                    16 => @as(i16, @intCast(x)),
+                    32, 64 => x,
+                    else => unreachable,
+                },
+                .unsigned => |x| switch (bit_size) {
+                    1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
+                    16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
+                    32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
+                    64 => @bitCast(x),
+                    else => unreachable,
+                },
+            };
+        }
+
+        pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
+            return switch (imm) {
+                .signed => |x| switch (bit_size) {
+                    1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))),
+                    16 => @as(u16, @bitCast(@as(i16, @intCast(x)))),
+                    32, 64 => @as(u32, @bitCast(x)),
+                    else => unreachable,
+                },
+                .unsigned => |x| switch (bit_size) {
+                    1, 8 => @as(u8, @intCast(x)),
+                    16 => @as(u16, @intCast(x)),
+                    32 => @as(u32, @intCast(x)),
+                    64 => x,
+                    else => unreachable,
+                },
+            };
+        }
+    };
+
     pub const Memory = union(enum) {
         sib: Sib,
         rip: Rip,
@@ -1119,7 +1167,7 @@ test "encode" {
 
     const inst = try Instruction.new(.none, .mov, &.{
         .{ .reg = .rbx },
-        .{ .imm = Immediate.u(4) },
+        .{ .imm = Instruction.Immediate.u(4) },
     });
     try inst.encode(buf.writer(), .{});
     try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
@@ -1129,47 +1177,47 @@ test "lower I encoding" {
     var enc = TestEncode{};
 
     try enc.encode(.push, &.{
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
 
     try enc.encode(.push, &.{
-        .{ .imm = Immediate.u(0x1000) },
+        .{ .imm = Instruction.Immediate.u(0x1000) },
     });
     try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
 
     try enc.encode(.push, &.{
-        .{ .imm = Immediate.u(0x10000000) },
+        .{ .imm = Instruction.Immediate.u(0x10000000) },
     });
     try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
 
     try enc.encode(.adc, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x10000000) },
+        .{ .imm = Instruction.Immediate.u(0x10000000) },
     });
     try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
 
     try enc.encode(.add, &.{
         .{ .reg = .al },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
 
     try enc.encode(.add, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
 
     try enc.encode(.sbb, &.{
         .{ .reg = .ax },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
 
     try enc.encode(.xor, &.{
         .{ .reg = .al },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
 }
@@ -1179,43 +1227,43 @@ test "lower MI encoding" {
 
     try enc.encode(.mov, &.{
         .{ .reg = .r12 },
-        .{ .imm = Immediate.u(0x1000) },
+        .{ .imm = Instruction.Immediate.u(0x1000) },
     });
     try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
 
     try enc.encode(.mov, &.{
         .{ .reg = .r12 },
-        .{ .imm = Immediate.u(0x1000) },
+        .{ .imm = Instruction.Immediate.u(0x1000) },
     });
     try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
 
     try enc.encode(.mov, &.{
         .{ .reg = .r12 },
-        .{ .imm = Immediate.u(0x1000) },
+        .{ .imm = Instruction.Immediate.u(0x1000) },
     });
     try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
 
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.rip(.qword, 0x10) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
@@ -1225,19 +1273,19 @@ test "lower MI encoding" {
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
-        .{ .imm = Immediate.s(-16) },
+        .{ .imm = Instruction.Immediate.s(-16) },
     });
     try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
 
@@ -1247,7 +1295,7 @@ test "lower MI encoding" {
             .disp = 0x10000000,
             .scale_index = .{ .scale = 2, .index = .rcx },
         }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
@@ -1257,43 +1305,43 @@ test "lower MI encoding" {
 
     try enc.encode(.adc, &.{
         .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
 
     try enc.encode(.adc, &.{
         .{ .mem = Instruction.Memory.rip(.qword, 0) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
 
     try enc.encode(.adc, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
 
     try enc.encode(.add, &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
 
     try enc.encode(.add, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
 
     try enc.encode(.add, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
-        .{ .imm = Immediate.s(-0x10) },
+        .{ .imm = Instruction.Immediate.s(-0x10) },
     });
     try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
 
     try enc.encode(.@"and", &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x83\x24\x25\x00\x00\x00\x10\x10",
@@ -1303,7 +1351,7 @@ test "lower MI encoding" {
 
     try enc.encode(.@"and", &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x26\x83\x24\x25\x00\x00\x00\x10\x10",
@@ -1313,7 +1361,7 @@ test "lower MI encoding" {
 
     try enc.encode(.@"and", &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
@@ -1323,7 +1371,7 @@ test "lower MI encoding" {
 
     try enc.encode(.sub, &.{
         .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
         "\x41\x83\xAB\x00\x00\x00\x10\x10",
@@ -1542,14 +1590,14 @@ test "lower RMI encoding" {
     try enc.encode(.imul, &.{
         .{ .reg = .r11 },
         .{ .reg = .r12 },
-        .{ .imm = Immediate.s(-2) },
+        .{ .imm = Instruction.Immediate.s(-2) },
     });
     try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
 
     try enc.encode(.imul, &.{
         .{ .reg = .r11 },
         .{ .mem = Instruction.Memory.rip(.qword, -16) },
-        .{ .imm = Immediate.s(-1024) },
+        .{ .imm = Instruction.Immediate.s(-1024) },
     });
     try expectEqualHexStrings(
         "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
@@ -1560,7 +1608,7 @@ test "lower RMI encoding" {
     try enc.encode(.imul, &.{
         .{ .reg = .bx },
         .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
-        .{ .imm = Immediate.s(-1024) },
+        .{ .imm = Instruction.Immediate.s(-1024) },
     });
     try expectEqualHexStrings(
         "\x66\x69\x5D\xF0\x00\xFC",
@@ -1571,7 +1619,7 @@ test "lower RMI encoding" {
     try enc.encode(.imul, &.{
         .{ .reg = .bx },
         .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
-        .{ .imm = Immediate.u(1024) },
+        .{ .imm = Instruction.Immediate.u(1024) },
     });
     try expectEqualHexStrings(
         "\x66\x69\x5D\xF0\x00\x04",
@@ -1687,7 +1735,7 @@ test "lower M encoding" {
     try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
 
     try enc.encode(.call, &.{
-        .{ .imm = Immediate.s(0) },
+        .{ .imm = Instruction.Immediate.s(0) },
     });
     try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
 
@@ -1746,7 +1794,7 @@ test "lower OI encoding" {
 
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
-        .{ .imm = Immediate.u(0x1000000000000000) },
+        .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
     });
     try expectEqualHexStrings(
         "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
@@ -1756,7 +1804,7 @@ test "lower OI encoding" {
 
     try enc.encode(.mov, &.{
         .{ .reg = .r11 },
-        .{ .imm = Immediate.u(0x1000000000000000) },
+        .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
     });
     try expectEqualHexStrings(
         "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
@@ -1766,19 +1814,19 @@ test "lower OI encoding" {
 
     try enc.encode(.mov, &.{
         .{ .reg = .r11d },
-        .{ .imm = Immediate.u(0x10000000) },
+        .{ .imm = Instruction.Immediate.u(0x10000000) },
     });
     try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
 
     try enc.encode(.mov, &.{
         .{ .reg = .r11w },
-        .{ .imm = Immediate.u(0x1000) },
+        .{ .imm = Instruction.Immediate.u(0x1000) },
     });
     try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
 
     try enc.encode(.mov, &.{
         .{ .reg = .r11b },
-        .{ .imm = Immediate.u(0x10) },
+        .{ .imm = Instruction.Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
 }
@@ -1900,7 +1948,7 @@ test "invalid instruction" {
         .{ .reg = .r12d },
     });
     try invalidInstruction(.push, &.{
-        .{ .imm = Immediate.u(0x1000000000000000) },
+        .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
     });
 }
 
@@ -2213,7 +2261,7 @@ const Assembler = struct {
                 .immediate => {
                     const is_neg = if (as.expect(.minus)) |_| true else |_| false;
                     const imm_tok = try as.expect(.numeral);
-                    const imm: Immediate = if (is_neg) blk: {
+                    const imm: Instruction.Immediate = if (is_neg) blk: {
                         const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0);
                         break :blk .{ .signed = imm * -1 };
                     } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };
src/arch/x86_64/Lower.zig
@@ -475,7 +475,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
         .rrmi => inst.data.rrix.fixes,
         .mi_u, .mi_s => inst.data.x.fixes,
         .m => inst.data.x.fixes,
-        .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._,
+        .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc, .rel => ._,
         else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
     };
     try lower.emit(switch (fixes) {
@@ -607,7 +607,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .mem = lower.mem(inst.data.rrix.payload) },
             .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
         },
-        .extern_fn_reloc => &.{
+        .extern_fn_reloc, .rel => &.{
             .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
         },
         .got_reloc, .direct_reloc, .import_reloc => ops: {
@@ -650,7 +650,7 @@ const std = @import("std");
 const Air = @import("../../Air.zig");
 const Allocator = std.mem.Allocator;
 const ErrorMsg = Zcu.ErrorMsg;
-const Immediate = bits.Immediate;
+const Immediate = Instruction.Immediate;
 const Instruction = encoder.Instruction;
 const Lower = @This();
 const Memory = Instruction.Memory;
src/arch/x86_64/Mir.zig
@@ -769,7 +769,7 @@ pub const Inst = struct {
         /// Uses `imm` payload.
         i_u,
         /// Relative displacement operand.
-        /// Uses `imm` payload.
+        /// Uses `reloc` payload.
         rel,
         /// Register, memory operands.
         /// Uses `rx` payload with extra data of type `Memory`.
src/link/Elf/Atom.zig
@@ -1215,12 +1215,11 @@ const x86_64 = struct {
                 );
             },
 
-            .PLT32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
-
-            .PC32 => {
+            .PLT32 => {
                 const S_ = if (target.flags.zig_jump_table) ZJT else S;
                 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
             },
+            .PC32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
 
             .GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little),
             .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little),
@@ -1620,7 +1619,7 @@ const x86_64 = struct {
     const bits = @import("../../arch/x86_64/bits.zig");
     const encoder = @import("../../arch/x86_64/encoder.zig");
     const Disassembler = @import("../../arch/x86_64/Disassembler.zig");
-    const Immediate = bits.Immediate;
+    const Immediate = Instruction.Immediate;
     const Instruction = encoder.Instruction;
 };