Commit 5d4288c5f6

Jacob Young <jacobly0@users.noreply.github.com>
2023-05-06 12:00:22
x86_64: fix unordered float equality
1 parent ae588a0
src/arch/x86_64/bits.zig
@@ -72,6 +72,12 @@ pub const Condition = enum(u5) {
     /// zero
     z,
 
+    // Pseudo conditions
+    /// zero and not parity
+    z_and_np,
+    /// not zero or parity
+    nz_or_p,
+
     /// Converts a std.math.CompareOperator into a condition flag,
     /// i.e. returns the condition that is true iff the result of the
     /// comparison is true. Assumes signed comparison
@@ -143,6 +149,9 @@ pub const Condition = enum(u5) {
             .po => .pe,
             .s => .ns,
             .z => .nz,
+
+            .z_and_np => .nz_or_p,
+            .nz_or_p => .z_and_np,
         };
     }
 };
src/arch/x86_64/CodeGen.zig
@@ -205,16 +205,7 @@ pub const MCValue = union(enum) {
 
     fn isMemory(mcv: MCValue) bool {
         return switch (mcv) {
-            .memory,
-            .load_direct,
-            .lea_direct,
-            .load_got,
-            .lea_got,
-            .load_tlv,
-            .lea_tlv,
-            .load_frame,
-            .lea_frame,
-            => true,
+            .memory, .indirect, .load_frame => true,
             else => false,
         };
     }
@@ -937,7 +928,7 @@ fn formatWipMir(
         .target = data.self.target,
         .src_loc = data.self.src_loc,
     };
-    for (lower.lowerMir(data.self.mir_instructions.get(data.inst)) catch |err| switch (err) {
+    for ((lower.lowerMir(data.inst) catch |err| switch (err) {
         error.LowerFail => {
             defer {
                 lower.err_msg.?.deinit(data.self.gpa);
@@ -955,7 +946,7 @@ fn formatWipMir(
             return;
         },
         else => |e| return e,
-    }) |lower_inst| try writer.print("  | {}", .{lower_inst});
+    }).insts) |lowered_inst| try writer.print("  | {}", .{lowered_inst});
 }
 fn fmtWipMir(self: *Self, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {
     return .{ .data = .{ .self = self, .inst = inst } };
@@ -1016,7 +1007,14 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void {
     _ = try self.addInst(.{
         .tag = .setcc,
         .ops = .r_cc,
-        .data = .{ .r_cc = .{ .r = reg, .cc = cc } },
+        .data = .{ .r_cc = .{
+            .r = reg,
+            .scratch = if (cc == .z_and_np or cc == .nz_or_p)
+                (try self.register_manager.allocReg(null, gp)).to8()
+            else
+                .none,
+            .cc = cc,
+        } },
     });
 }
 
@@ -1028,23 +1026,36 @@ fn asmSetccMemory(self: *Self, m: Memory, cc: bits.Condition) !void {
             .rip => .m_rip_cc,
             else => unreachable,
         },
-        .data = .{ .x_cc = .{ .cc = cc, .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
-            else => unreachable,
-        } } },
+        .data = .{ .x_cc = .{
+            .scratch = if (cc == .z_and_np or cc == .nz_or_p)
+                (try self.register_manager.allocReg(null, gp)).to8()
+            else
+                .none,
+            .cc = cc,
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
+/// A `cc` of `.z_and_np` clobbers `reg2`!
 fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void {
     _ = try self.addInst(.{
         .tag = .cmovcc,
         .ops = .rr_cc,
-        .data = .{ .rr_cc = .{ .r1 = reg1, .r2 = reg2, .cc = cc } },
+        .data = .{ .rr_cc = .{
+            .r1 = reg1,
+            .r2 = reg2,
+            .cc = cc,
+        } },
     });
 }
 
 fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condition) !void {
+    assert(cc != .z_and_np); // not supported
     _ = try self.addInst(.{
         .tag = .cmovcc,
         .ops = switch (m) {
@@ -1052,11 +1063,15 @@ fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condi
             .rip => .rm_rip_cc,
             else => unreachable,
         },
-        .data = .{ .rx_cc = .{ .r = reg, .cc = cc, .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
-            else => unreachable,
-        } } },
+        .data = .{ .rx_cc = .{
+            .r = reg,
+            .cc = cc,
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
@@ -1131,10 +1146,13 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme
         .tag = tag,
         .ops = ops,
         .data = switch (ops) {
-            .ri_s, .ri_u => .{ .ri = .{ .r = reg, .i = switch (imm) {
-                .signed => |s| @bitCast(u32, s),
-                .unsigned => |u| @intCast(u32, u),
-            } } },
+            .ri_s, .ri_u => .{ .ri = .{
+                .r = reg,
+                .i = switch (imm) {
+                    .signed => |s| @bitCast(u32, s),
+                    .unsigned => |u| @intCast(u32, u),
+                },
+            } },
             .ri64 => .{ .rx = .{
                 .r = reg,
                 .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)),
@@ -1171,10 +1189,14 @@ fn asmRegisterRegisterImmediate(
             .signed => .rri_s,
             .unsigned => .rri_u,
         },
-        .data = .{ .rri = .{ .r1 = reg1, .r2 = reg2, .i = switch (imm) {
-            .signed => |s| @bitCast(u32, s),
-            .unsigned => |u| @intCast(u32, u),
-        } } },
+        .data = .{ .rri = .{
+            .r1 = reg1,
+            .r2 = reg2,
+            .i = switch (imm) {
+                .signed => |s| @bitCast(u32, s),
+                .unsigned => |u| @intCast(u32, u),
+            },
+        } },
     });
 }
 
@@ -1202,11 +1224,14 @@ fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !
             .rip => .rm_rip,
             else => unreachable,
         },
-        .data = .{ .rx = .{ .r = reg, .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
-            else => unreachable,
-        } } },
+        .data = .{ .rx = .{
+            .r = reg,
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
@@ -1224,11 +1249,43 @@ fn asmRegisterMemoryImmediate(
             .rip => .rmi_rip,
             else => unreachable,
         },
-        .data = .{ .rix = .{ .r = reg, .i = @intCast(u8, imm.unsigned), .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+        .data = .{ .rix = .{
+            .r = reg,
+            .i = @intCast(u8, imm.unsigned),
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
+    });
+}
+
+fn asmRegisterRegisterMemoryImmediate(
+    self: *Self,
+    tag: Mir.Inst.Tag,
+    reg1: Register,
+    reg2: Register,
+    m: Memory,
+    imm: Immediate,
+) !void {
+    _ = try self.addInst(.{
+        .tag = tag,
+        .ops = switch (m) {
+            .sib => .rrmi_sib,
+            .rip => .rrmi_rip,
             else => unreachable,
-        } } },
+        },
+        .data = .{ .rrix = .{
+            .r1 = reg1,
+            .r2 = reg2,
+            .i = @intCast(u8, imm.unsigned),
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
@@ -1240,11 +1297,14 @@ fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !
             .rip => .mr_rip,
             else => unreachable,
         },
-        .data = .{ .rx = .{ .r = reg, .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
-            else => unreachable,
-        } } },
+        .data = .{ .rx = .{
+            .r = reg,
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
@@ -1262,14 +1322,17 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate)
             },
             else => unreachable,
         },
-        .data = .{ .ix = .{ .i = switch (imm) {
-            .signed => |s| @bitCast(u32, s),
-            .unsigned => |u| @intCast(u32, u),
-        }, .payload = switch (m) {
-            .sib => try self.addExtra(Mir.MemorySib.encode(m)),
-            .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
-            else => unreachable,
-        } } },
+        .data = .{ .ix = .{
+            .i = switch (imm) {
+                .signed => |s| @bitCast(u32, s),
+                .unsigned => |u| @intCast(u32, u),
+            },
+            .payload = switch (m) {
+                .sib => try self.addExtra(Mir.MemorySib.encode(m)),
+                .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
+                else => unreachable,
+            },
+        } },
     });
 }
 
@@ -6612,11 +6675,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
                 _ = try self.addInst(.{
                     .tag = .mov_linker,
                     .ops = .import_reloc,
-                    .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
-                        .reg = @enumToInt(Register.rax),
-                        .atom_index = atom_index,
-                        .sym_index = sym_index,
-                    }) },
+                    .data = .{ .rx = .{
+                        .r = .rax,
+                        .payload = try self.addExtra(Mir.Reloc{
+                            .atom_index = atom_index,
+                            .sym_index = sym_index,
+                        }),
+                    } },
                 });
                 try self.asmRegister(.call, .rax);
             } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
@@ -6695,8 +6760,6 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
     const ty = self.air.typeOf(bin_op.lhs);
-    const ty_abi_size = ty.abiSize(self.target.*);
-    const can_reuse = ty_abi_size <= 8;
 
     try self.spillEflagsIfOccupied();
     self.eflags_inst = inst;
@@ -6715,69 +6778,93 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
     };
     defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
 
-    const dst_mem_ok = !ty.isRuntimeFloat();
-    var flipped = false;
-    const dst_mcv: MCValue = if (can_reuse and !lhs_mcv.isImmediate() and
-        (dst_mem_ok or lhs_mcv.isRegister()) and self.liveness.operandDies(inst, 0))
-        lhs_mcv
-    else if (can_reuse and !rhs_mcv.isImmediate() and
-        (dst_mem_ok or rhs_mcv.isRegister()) and self.liveness.operandDies(inst, 1))
-    dst: {
-        flipped = true;
-        break :dst rhs_mcv;
-    } else if (dst_mem_ok) dst: {
-        const dst_mcv = try self.allocTempRegOrMem(ty, true);
-        try self.genCopy(ty, dst_mcv, lhs_mcv);
-        break :dst dst_mcv;
-    } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) };
-    const dst_lock = switch (dst_mcv) {
-        .register => |reg| self.register_manager.lockReg(reg),
-        else => null,
-    };
-    defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
-
-    const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
-    switch (ty.zigTypeTag()) {
-        else => try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv),
-        .Float => switch (ty.floatBits(self.target.*)) {
-            16 => if (self.hasFeature(.f16c)) {
-                const dst_reg = dst_mcv.getReg().?.to128();
+    const result = MCValue{
+        .eflags = switch (ty.zigTypeTag()) {
+            else => result: {
+                var flipped = false;
+                const dst_mcv: MCValue = if (lhs_mcv.isRegister() or lhs_mcv.isMemory())
+                    lhs_mcv
+                else if (rhs_mcv.isRegister() or rhs_mcv.isMemory()) dst: {
+                    flipped = true;
+                    break :dst rhs_mcv;
+                } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) };
+                const dst_lock = switch (dst_mcv) {
+                    .register => |reg| self.register_manager.lockReg(reg),
+                    else => null,
+                };
+                defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
+                const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
 
-                const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128();
-                const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
-                defer self.register_manager.unlockReg(tmp_lock);
+                try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv);
+                break :result Condition.fromCompareOperator(
+                    if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned,
+                    if (flipped) op.reverse() else op,
+                );
+            },
+            .Float => result: {
+                const flipped = switch (op) {
+                    .lt, .lte => true,
+                    .eq, .gte, .gt, .neq => false,
+                };
 
-                if (src_mcv.isRegister())
-                    try self.asmRegisterRegisterRegister(
-                        .vpunpcklwd,
-                        dst_reg,
-                        dst_reg,
-                        src_mcv.getReg().?.to128(),
-                    )
+                const dst_mcv = if (flipped) rhs_mcv else lhs_mcv;
+                const dst_reg = if (dst_mcv.isRegister())
+                    dst_mcv.getReg().?
                 else
-                    try self.asmRegisterMemoryImmediate(
-                        .vpinsrw,
-                        dst_reg,
-                        src_mcv.mem(.word),
-                        Immediate.u(1),
-                    );
-                try self.asmRegisterRegister(.vcvtph2ps, dst_reg, dst_reg);
-                try self.asmRegisterRegister(.vmovshdup, tmp_reg, dst_reg);
-                try self.genBinOpMir(.ucomiss, ty, dst_mcv, .{ .register = tmp_reg });
-            } else return self.fail("TODO implement airCmp for {}", .{
-                ty.fmt(self.bin_file.options.module.?),
-            }),
-            32 => try self.genBinOpMir(.ucomiss, ty, dst_mcv, src_mcv),
-            64 => try self.genBinOpMir(.ucomisd, ty, dst_mcv, src_mcv),
-            else => return self.fail("TODO implement airCmp for {}", .{
-                ty.fmt(self.bin_file.options.module.?),
-            }),
-        },
-    }
+                    try self.copyToTmpRegister(ty, dst_mcv);
+                const dst_lock = self.register_manager.lockReg(dst_reg);
+                defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
+                const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
+
+                switch (ty.floatBits(self.target.*)) {
+                    16 => if (self.hasFeature(.f16c)) {
+                        const tmp1_reg = (try self.register_manager.allocReg(null, sse)).to128();
+                        const tmp1_mcv = MCValue{ .register = tmp1_reg };
+                        const tmp1_lock = self.register_manager.lockRegAssumeUnused(tmp1_reg);
+                        defer self.register_manager.unlockReg(tmp1_lock);
+
+                        const tmp2_reg = (try self.register_manager.allocReg(null, sse)).to128();
+                        const tmp2_mcv = MCValue{ .register = tmp2_reg };
+                        const tmp2_lock = self.register_manager.lockRegAssumeUnused(tmp2_reg);
+                        defer self.register_manager.unlockReg(tmp2_lock);
+
+                        if (src_mcv.isRegister())
+                            try self.asmRegisterRegisterRegister(
+                                .vpunpcklwd,
+                                tmp1_reg,
+                                dst_reg.to128(),
+                                src_mcv.getReg().?.to128(),
+                            )
+                        else
+                            try self.asmRegisterRegisterMemoryImmediate(
+                                .vpinsrw,
+                                tmp1_reg,
+                                dst_reg.to128(),
+                                src_mcv.mem(.word),
+                                Immediate.u(1),
+                            );
+                        try self.asmRegisterRegister(.vcvtph2ps, tmp1_reg, tmp1_reg);
+                        try self.asmRegisterRegister(.vmovshdup, tmp2_reg, tmp1_reg);
+                        try self.genBinOpMir(.ucomiss, ty, tmp1_mcv, tmp2_mcv);
+                    } else return self.fail("TODO implement airCmp for {}", .{
+                        ty.fmt(self.bin_file.options.module.?),
+                    }),
+                    32 => try self.genBinOpMir(.ucomiss, ty, .{ .register = dst_reg }, src_mcv),
+                    64 => try self.genBinOpMir(.ucomisd, ty, .{ .register = dst_reg }, src_mcv),
+                    else => return self.fail("TODO implement airCmp for {}", .{
+                        ty.fmt(self.bin_file.options.module.?),
+                    }),
+                }
 
-    const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned;
-    const result = MCValue{
-        .eflags = Condition.fromCompareOperator(signedness, if (flipped) op.reverse() else op),
+                break :result switch (if (flipped) op.reverse() else op) {
+                    .lt, .lte => unreachable, // required to have been canonicalized to gt(e)
+                    .gt => .a,
+                    .gte => .ae,
+                    .eq => .z_and_np,
+                    .neq => .nz_or_p,
+                };
+            },
+        },
     };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
@@ -7929,11 +8016,13 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                     _ = try self.addInst(.{
                         .tag = .mov_linker,
                         .ops = .direct_reloc,
-                        .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
-                            .reg = @enumToInt(dst_reg.to64()),
-                            .atom_index = atom_index,
-                            .sym_index = sym_index,
-                        }) },
+                        .data = .{ .rx = .{
+                            .r = dst_reg.to64(),
+                            .payload = try self.addExtra(Mir.Reloc{
+                                .atom_index = atom_index,
+                                .sym_index = sym_index,
+                            }),
+                        } },
                     });
                     return;
                 },
@@ -7975,11 +8064,13 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                     .lea_got => .got_reloc,
                     else => unreachable,
                 },
-                .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
-                    .reg = @enumToInt(dst_reg.to64()),
-                    .atom_index = atom_index,
-                    .sym_index = sym_index,
-                }) },
+                .data = .{ .rx = .{
+                    .r = dst_reg.to64(),
+                    .payload = try self.addExtra(Mir.Reloc{
+                        .atom_index = atom_index,
+                        .sym_index = sym_index,
+                    }),
+                } },
             });
         },
         .lea_tlv => |sym_index| {
@@ -7988,11 +8079,13 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                 _ = try self.addInst(.{
                     .tag = .lea_linker,
                     .ops = .tlv_reloc,
-                    .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
-                        .reg = @enumToInt(Register.rdi),
-                        .atom_index = atom_index,
-                        .sym_index = sym_index,
-                    }) },
+                    .data = .{ .rx = .{
+                        .r = .rdi,
+                        .payload = try self.addExtra(Mir.Reloc{
+                            .atom_index = atom_index,
+                            .sym_index = sym_index,
+                        }),
+                    } },
                 });
                 // TODO: spill registers before calling
                 try self.asmMemory(.call, Memory.sib(.qword, .{ .base = .{ .reg = .rdi } }));
@@ -8463,14 +8556,20 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
 
     try self.spillEflagsIfOccupied();
     if (val_abi_size <= 8) {
-        _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{
-            .r = registerAlias(new_reg.?, val_abi_size),
-            .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
-        } } });
+        _ = try self.addInst(.{
+            .tag = .cmpxchg,
+            .ops = .lock_mr_sib,
+            .data = .{ .rx = .{
+                .r = registerAlias(new_reg.?, val_abi_size),
+                .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
+            } },
+        });
     } else {
-        _ = try self.addInst(.{ .tag = .cmpxchgb, .ops = .lock_m_sib, .data = .{
-            .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
-        } });
+        _ = try self.addInst(.{
+            .tag = .cmpxchgb,
+            .ops = .lock_m_sib,
+            .data = .{ .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)) },
+        });
     }
 
     const result: MCValue = result: {
@@ -8571,14 +8670,18 @@ fn atomicOp(
             if (rmw_op == std.builtin.AtomicRmwOp.Sub and tag == .xadd) {
                 try self.genUnOpMir(.neg, val_ty, dst_mcv);
             }
-            _ = try self.addInst(.{ .tag = tag, .ops = switch (tag) {
-                .mov, .xchg => .mr_sib,
-                .xadd, .add, .sub, .@"and", .@"or", .xor => .lock_mr_sib,
-                else => unreachable,
-            }, .data = .{ .rx = .{
-                .r = registerAlias(dst_reg, val_abi_size),
-                .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
-            } } });
+            _ = try self.addInst(.{
+                .tag = tag,
+                .ops = switch (tag) {
+                    .mov, .xchg => .mr_sib,
+                    .xadd, .add, .sub, .@"and", .@"or", .xor => .lock_mr_sib,
+                    else => unreachable,
+                },
+                .data = .{ .rx = .{
+                    .r = registerAlias(dst_reg, val_abi_size),
+                    .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
+                } },
+            });
 
             return if (unused) .unreach else dst_mcv;
         },
@@ -8645,10 +8748,14 @@ fn atomicOp(
                     }
                 },
             };
-            _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{
-                .r = registerAlias(tmp_reg, val_abi_size),
-                .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
-            } } });
+            _ = try self.addInst(.{
+                .tag = .cmpxchg,
+                .ops = .lock_mr_sib,
+                .data = .{ .rx = .{
+                    .r = registerAlias(tmp_reg, val_abi_size),
+                    .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
+                } },
+            });
             _ = try self.asmJccReloc(loop, .ne);
             return if (unused) .unreach else .{ .register = .rax };
         } else {
src/arch/x86_64/Emit.zig
@@ -18,142 +18,149 @@ pub const Error = Lower.Error || error{
 };
 
 pub fn emitMir(emit: *Emit) Error!void {
-    for (0..emit.lower.mir.instructions.len) |i| {
-        const index = @intCast(Mir.Inst.Index, i);
-        const inst = emit.lower.mir.instructions.get(index);
-
-        const start_offset = @intCast(u32, emit.code.items.len);
-        try emit.code_offset_mapping.putNoClobber(emit.lower.allocator, index, start_offset);
-        for (try emit.lower.lowerMir(inst)) |lower_inst| try lower_inst.encode(emit.code.writer(), .{});
-        const end_offset = @intCast(u32, emit.code.items.len);
-
-        switch (inst.tag) {
-            else => {},
-
-            .jmp_reloc => try emit.relocs.append(emit.lower.allocator, .{
-                .source = start_offset,
-                .target = inst.data.inst,
-                .offset = end_offset - 4,
-                .length = 5,
-            }),
-
-            .call_extern => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
-                // Add relocation to the decl.
-                const atom_index = macho_file.getAtomIndexForSymbol(
-                    .{ .sym_index = inst.data.relocation.atom_index, .file = null },
-                ).?;
-                const target = macho_file.getGlobalByIndex(inst.data.relocation.sym_index);
-                try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
-                    .type = .branch,
+    for (0..emit.lower.mir.instructions.len) |mir_i| {
+        const mir_index = @intCast(Mir.Inst.Index, mir_i);
+        try emit.code_offset_mapping.putNoClobber(
+            emit.lower.allocator,
+            mir_index,
+            @intCast(u32, emit.code.items.len),
+        );
+        const lowered = try emit.lower.lowerMir(mir_index);
+        var lowered_relocs = lowered.relocs;
+        for (lowered.insts, 0..) |lowered_inst, lowered_index| {
+            const start_offset = @intCast(u32, emit.code.items.len);
+            try lowered_inst.encode(emit.code.writer(), .{});
+            const end_offset = @intCast(u32, emit.code.items.len);
+            while (lowered_relocs.len > 0 and
+                lowered_relocs[0].lowered_inst_index == lowered_index) : ({
+                lowered_relocs = lowered_relocs[1..];
+            }) switch (lowered_relocs[0].target) {
+                .inst => |target| try emit.relocs.append(emit.lower.allocator, .{
+                    .source = start_offset,
                     .target = target,
                     .offset = end_offset - 4,
-                    .addend = 0,
-                    .pcrel = true,
-                    .length = 2,
-                });
-            } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
-                // Add relocation to the decl.
-                const atom_index = coff_file.getAtomIndexForSymbol(
-                    .{ .sym_index = inst.data.relocation.atom_index, .file = null },
-                ).?;
-                const target = coff_file.getGlobalByIndex(inst.data.relocation.sym_index);
-                try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
-                    .type = .direct,
-                    .target = target,
-                    .offset = end_offset - 4,
-                    .addend = 0,
-                    .pcrel = true,
-                    .length = 2,
-                });
-            } else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
-
-            .mov_linker, .lea_linker => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
-                const metadata =
-                    emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
-                const atom_index = macho_file.getAtomIndexForSymbol(.{
-                    .sym_index = metadata.atom_index,
-                    .file = null,
-                }).?;
-                try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
-                    .type = switch (inst.ops) {
-                        .got_reloc => .got,
-                        .direct_reloc => .signed,
-                        .tlv_reloc => .tlv,
-                        else => unreachable,
-                    },
-                    .target = .{ .sym_index = metadata.sym_index, .file = null },
-                    .offset = @intCast(u32, end_offset - 4),
-                    .addend = 0,
-                    .pcrel = true,
-                    .length = 2,
-                });
-            } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
-                const metadata =
-                    emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
-                const atom_index = coff_file.getAtomIndexForSymbol(.{
-                    .sym_index = metadata.atom_index,
-                    .file = null,
-                }).?;
-                try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
-                    .type = switch (inst.ops) {
-                        .got_reloc => .got,
-                        .direct_reloc => .direct,
-                        .import_reloc => .import,
-                        else => unreachable,
-                    },
-                    .target = switch (inst.ops) {
-                        .got_reloc,
-                        .direct_reloc,
-                        => .{ .sym_index = metadata.sym_index, .file = null },
-                        .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
-                        else => unreachable,
-                    },
-                    .offset = @intCast(u32, end_offset - 4),
-                    .addend = 0,
-                    .pcrel = true,
-                    .length = 2,
-                });
-            } else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
-
-            .jcc => try emit.relocs.append(emit.lower.allocator, .{
-                .source = start_offset,
-                .target = inst.data.inst_cc.inst,
-                .offset = end_offset - 4,
-                .length = 6,
-            }),
-
-            .dbg_line => try emit.dbgAdvancePCAndLine(
-                inst.data.line_column.line,
-                inst.data.line_column.column,
-            ),
-
-            .dbg_prologue_end => {
-                switch (emit.debug_output) {
-                    .dwarf => |dw| {
-                        try dw.setPrologueEnd();
-                        log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
-                            emit.prev_di_line, emit.prev_di_column,
-                        });
-                        try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
-                    },
-                    .plan9 => {},
-                    .none => {},
-                }
-            },
+                    .length = @intCast(u5, end_offset - start_offset),
+                }),
+                .@"extern" => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
+                    // Add relocation to the decl.
+                    const atom_index = macho_file.getAtomIndexForSymbol(
+                        .{ .sym_index = symbol.atom_index, .file = null },
+                    ).?;
+                    const target = macho_file.getGlobalByIndex(symbol.sym_index);
+                    try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
+                        .type = .branch,
+                        .target = target,
+                        .offset = end_offset - 4,
+                        .addend = 0,
+                        .pcrel = true,
+                        .length = 2,
+                    });
+                } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
+                    // Add relocation to the decl.
+                    const atom_index = coff_file.getAtomIndexForSymbol(
+                        .{ .sym_index = symbol.atom_index, .file = null },
+                    ).?;
+                    const target = coff_file.getGlobalByIndex(symbol.sym_index);
+                    try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
+                        .type = .direct,
+                        .target = target,
+                        .offset = end_offset - 4,
+                        .addend = 0,
+                        .pcrel = true,
+                        .length = 2,
+                    });
+                } else return emit.fail("TODO implement extern reloc for {s}", .{
+                    @tagName(emit.bin_file.tag),
+                }),
+                .linker_got,
+                .linker_direct,
+                .linker_import,
+                .linker_tlv,
+                => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
+                    const atom_index = macho_file.getAtomIndexForSymbol(.{
+                        .sym_index = symbol.atom_index,
+                        .file = null,
+                    }).?;
+                    try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
+                        .type = switch (lowered_relocs[0].target) {
+                            .linker_got => .got,
+                            .linker_direct => .signed,
+                            .linker_tlv => .tlv,
+                            else => unreachable,
+                        },
+                        .target = .{ .sym_index = symbol.sym_index, .file = null },
+                        .offset = @intCast(u32, end_offset - 4),
+                        .addend = 0,
+                        .pcrel = true,
+                        .length = 2,
+                    });
+                } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
+                    const atom_index = coff_file.getAtomIndexForSymbol(.{
+                        .sym_index = symbol.atom_index,
+                        .file = null,
+                    }).?;
+                    try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
+                        .type = switch (lowered_relocs[0].target) {
+                            .linker_got => .got,
+                            .linker_direct => .direct,
+                            .linker_import => .import,
+                            else => unreachable,
+                        },
+                        .target = switch (lowered_relocs[0].target) {
+                            .linker_got,
+                            .linker_direct,
+                            => .{ .sym_index = symbol.sym_index, .file = null },
+                            .linker_import => coff_file.getGlobalByIndex(symbol.sym_index),
+                            else => unreachable,
+                        },
+                        .offset = @intCast(u32, end_offset - 4),
+                        .addend = 0,
+                        .pcrel = true,
+                        .length = 2,
+                    });
+                } else return emit.fail("TODO implement linker reloc for {s}", .{
+                    @tagName(emit.bin_file.tag),
+                }),
+            };
+        }
+        std.debug.assert(lowered_relocs.len == 0);
 
-            .dbg_epilogue_begin => {
-                switch (emit.debug_output) {
-                    .dwarf => |dw| {
-                        try dw.setEpilogueBegin();
-                        log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
-                            emit.prev_di_line, emit.prev_di_column,
-                        });
-                        try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
-                    },
-                    .plan9 => {},
-                    .none => {},
-                }
-            },
+        if (lowered.insts.len == 0) {
+            const mir_inst = emit.lower.mir.instructions.get(mir_index);
+            switch (mir_inst.tag) {
+                else => unreachable,
+                .dead => {},
+                .dbg_line => try emit.dbgAdvancePCAndLine(
+                    mir_inst.data.line_column.line,
+                    mir_inst.data.line_column.column,
+                ),
+                .dbg_prologue_end => {
+                    switch (emit.debug_output) {
+                        .dwarf => |dw| {
+                            try dw.setPrologueEnd();
+                            log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
+                                emit.prev_di_line, emit.prev_di_column,
+                            });
+                            try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
+                        },
+                        .plan9 => {},
+                        .none => {},
+                    }
+                },
+                .dbg_epilogue_begin => {
+                    switch (emit.debug_output) {
+                        .dwarf => |dw| {
+                            try dw.setEpilogueBegin();
+                            log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
+                                emit.prev_di_line, emit.prev_di_column,
+                            });
+                            try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
+                        },
+                        .plan9 => {},
+                        .none => {},
+                    }
+                },
+            }
         }
     }
     try emit.fixupRelocs();
src/arch/x86_64/encoder.zig
@@ -245,9 +245,9 @@ pub const Instruction = struct {
                     },
                     .mem => |mem| {
                         const op = switch (data.op_en) {
-                            .m, .mi, .m1, .mc => .none,
+                            .m, .mi, .m1, .mc, .vmi => .none,
                             .mr, .mri, .mrc => inst.ops[1],
-                            .rm, .rmi => inst.ops[0],
+                            .rm, .rmi, .rvm, .rvmi => inst.ops[0],
                             else => unreachable,
                         };
                         try encodeMemory(enc, mem, op, encoder);
src/arch/x86_64/Lower.zig
@@ -5,13 +5,22 @@ mir: Mir,
 target: *const std.Target,
 err_msg: ?*ErrorMsg = null,
 src_loc: Module.SrcLoc,
-result: [
+result_insts_len: u8 = undefined,
+result_relocs_len: u8 = undefined,
+result_insts: [
     std.mem.max(usize, &.{
-        abi.Win64.callee_preserved_regs.len,
-        abi.SysV.callee_preserved_regs.len,
+        2, // cmovcc: cmovcc \ cmovcc
+        3, // setcc: setcc \ setcc \ logicop
+        2, // jcc: jcc \ jcc
+        abi.Win64.callee_preserved_regs.len, // push_regs/pop_regs
+        abi.SysV.callee_preserved_regs.len, // push_regs/pop_regs
     })
 ]Instruction = undefined,
-result_len: usize = undefined,
+result_relocs: [
+    std.mem.max(usize, &.{
+        2, // jcc: jcc \ jcc
+    })
+]Reloc = undefined,
 
 pub const Error = error{
     OutOfMemory,
@@ -20,13 +29,35 @@ pub const Error = error{
     CannotEncode,
 };
 
-/// The returned slice is overwritten by the next call to lowerMir.
-pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
-    lower.result = undefined;
-    errdefer lower.result = undefined;
-    lower.result_len = 0;
-    defer lower.result_len = undefined;
+pub const Reloc = struct {
+    lowered_inst_index: u8,
+    target: Target,
+
+    const Target = union(enum) {
+        inst: Mir.Inst.Index,
+        @"extern": Mir.Reloc,
+        linker_got: Mir.Reloc,
+        linker_direct: Mir.Reloc,
+        linker_import: Mir.Reloc,
+        linker_tlv: Mir.Reloc,
+    };
+};
 
+/// The returned slice is overwritten by the next call to lowerMir.
+pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
+    insts: []const Instruction,
+    relocs: []const Reloc,
+} {
+    lower.result_insts = undefined;
+    lower.result_relocs = undefined;
+    errdefer lower.result_insts = undefined;
+    errdefer lower.result_relocs = undefined;
+    lower.result_insts_len = 0;
+    lower.result_relocs_len = 0;
+    defer lower.result_insts_len = undefined;
+    defer lower.result_relocs_len = undefined;
+
+    const inst = lower.mir.instructions.get(index);
     switch (inst.tag) {
         .adc,
         .add,
@@ -185,22 +216,26 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
 
         .cmpxchgb => try lower.mirCmpxchgBytes(inst),
 
-        .jmp_reloc => try lower.emit(.none, .jmp, &.{.{ .imm = Immediate.s(0) }}),
+        .jmp_reloc => try lower.emitInstWithReloc(.none, .jmp, &.{
+            .{ .imm = Immediate.s(0) },
+        }, .{ .inst = inst.data.inst }),
 
-        .call_extern => try lower.emit(.none, .call, &.{.{ .imm = Immediate.s(0) }}),
+        .call_extern => try lower.emitInstWithReloc(.none, .call, &.{
+            .{ .imm = Immediate.s(0) },
+        }, .{ .@"extern" = inst.data.relocation }),
 
-        .lea_linker => try lower.mirLeaLinker(inst),
-        .mov_linker => try lower.mirMovLinker(inst),
+        .lea_linker => try lower.mirLinker(.lea, inst),
+        .mov_linker => try lower.mirLinker(.mov, inst),
 
         .mov_moffs => try lower.mirMovMoffs(inst),
 
         .movsx => try lower.mirMovsx(inst),
         .cmovcc => try lower.mirCmovcc(inst),
         .setcc => try lower.mirSetcc(inst),
-        .jcc => try lower.emit(.none, mnem_cc(.j, inst.data.inst_cc.cc), &.{.{ .imm = Immediate.s(0) }}),
+        .jcc => try lower.mirJcc(index, inst),
 
-        .push_regs => try lower.mirPushPopRegisterList(inst, .push),
-        .pop_regs => try lower.mirPushPopRegisterList(inst, .pop),
+        .push_regs => try lower.mirRegisterList(.push, inst),
+        .pop_regs => try lower.mirRegisterList(.pop, inst),
 
         .dbg_line,
         .dbg_prologue_end,
@@ -209,7 +244,10 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
         => {},
     }
 
-    return lower.result[0..lower.result_len];
+    return .{
+        .insts = lower.result_insts[0..lower.result_insts_len],
+        .relocs = lower.result_relocs[0..lower.result_relocs_len],
+    };
 }
 
 pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
@@ -221,7 +259,10 @@ pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
 
 fn mnem_cc(comptime base: @Type(.EnumLiteral), cc: bits.Condition) Mnemonic {
     return switch (cc) {
-        inline else => |c| @field(Mnemonic, @tagName(base) ++ @tagName(c)),
+        inline else => |c| if (@hasField(Mnemonic, @tagName(base) ++ @tagName(c)))
+            @field(Mnemonic, @tagName(base) ++ @tagName(c))
+        else
+            unreachable,
     };
 }
 
@@ -247,6 +288,8 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
         .rmi_rip,
         .mri_sib,
         .mri_rip,
+        .rrmi_sib,
+        .rrmi_rip,
         => Immediate.u(i),
 
         .ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
@@ -267,6 +310,7 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
         .mr_sib,
         .mrr_sib,
         .mri_sib,
+        .rrmi_sib,
         .lock_m_sib,
         .lock_mi_sib_u,
         .lock_mi_sib_s,
@@ -283,6 +327,7 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
         .mr_rip,
         .mrr_rip,
         .mri_rip,
+        .rrmi_rip,
         .lock_m_rip,
         .lock_mi_rip_u,
         .lock_mi_rip_s,
@@ -298,13 +343,28 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
     });
 }
 
-fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
-    lower.result[lower.result_len] = try Instruction.new(prefix, mnemonic, ops);
-    lower.result_len += 1;
+fn emitInst(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
+    lower.result_insts[lower.result_insts_len] = try Instruction.new(prefix, mnemonic, ops);
+    lower.result_insts_len += 1;
+}
+
+fn emitInstWithReloc(
+    lower: *Lower,
+    prefix: Prefix,
+    mnemonic: Mnemonic,
+    ops: []const Operand,
+    target: Reloc.Target,
+) Error!void {
+    lower.result_relocs[lower.result_relocs_len] = .{
+        .lowered_inst_index = lower.result_insts_len,
+        .target = target,
+    };
+    lower.result_relocs_len += 1;
+    try lower.emitInst(prefix, mnemonic, ops);
 }
 
 fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
-    try lower.emit(switch (inst.ops) {
+    try lower.emitInst(switch (inst.ops) {
         else => .none,
         .lock_m_sib,
         .lock_m_rip,
@@ -389,13 +449,19 @@ fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .reg = inst.data.rix.r },
             .{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
         },
+        .rrmi_sib, .rrmi_rip => &.{
+            .{ .reg = inst.data.rrix.r1 },
+            .{ .reg = inst.data.rrix.r2 },
+            .{ .mem = lower.mem(inst.ops, inst.data.rrix.payload) },
+            .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
+        },
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
     });
 }
 
 fn mirString(lower: *Lower, inst: Mir.Inst) Error!void {
     switch (inst.ops) {
-        .string => try lower.emit(switch (inst.data.string.repeat) {
+        .string => try lower.emitInst(switch (inst.data.string.repeat) {
             inline else => |repeat| @field(Prefix, @tagName(repeat)),
         }, switch (inst.tag) {
             inline .cmps, .lods, .movs, .scas, .stos => |tag| switch (inst.data.string.width) {
@@ -414,7 +480,7 @@ fn mirCmpxchgBytes(lower: *Lower, inst: Mir.Inst) Error!void {
         },
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
     };
-    try lower.emit(switch (inst.ops) {
+    try lower.emitInst(switch (inst.ops) {
         .m_sib, .m_rip => .none,
         .lock_m_sib, .lock_m_rip => .lock,
         else => unreachable,
@@ -426,7 +492,7 @@ fn mirCmpxchgBytes(lower: *Lower, inst: Mir.Inst) Error!void {
 }
 
 fn mirMovMoffs(lower: *Lower, inst: Mir.Inst) Error!void {
-    try lower.emit(switch (inst.ops) {
+    try lower.emitInst(switch (inst.ops) {
         .rax_moffs, .moffs_rax => .none,
         .lock_moffs_rax => .lock,
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
@@ -455,7 +521,7 @@ fn mirMovsx(lower: *Lower, inst: Mir.Inst) Error!void {
         },
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
     };
-    try lower.emit(.none, switch (ops[0].bitSize()) {
+    try lower.emitInst(.none, switch (ops[0].bitSize()) {
         32, 64 => switch (ops[1].bitSize()) {
             32 => .movsxd,
             else => .movsx,
@@ -465,32 +531,82 @@ fn mirMovsx(lower: *Lower, inst: Mir.Inst) Error!void {
 }
 
 fn mirCmovcc(lower: *Lower, inst: Mir.Inst) Error!void {
-    switch (inst.ops) {
-        .rr_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rr_cc.cc), &.{
+    const data: struct { cc: bits.Condition, ops: [2]Operand } = switch (inst.ops) {
+        .rr_cc => .{ .cc = inst.data.rr_cc.cc, .ops = .{
             .{ .reg = inst.data.rr_cc.r1 },
             .{ .reg = inst.data.rr_cc.r2 },
-        }),
-        .rm_sib_cc, .rm_rip_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rx_cc.cc), &.{
+        } },
+        .rm_sib_cc, .rm_rip_cc => .{ .cc = inst.data.rx_cc.cc, .ops = .{
             .{ .reg = inst.data.rx_cc.r },
             .{ .mem = lower.mem(inst.ops, inst.data.rx_cc.payload) },
-        }),
+        } },
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
+    };
+    switch (data.cc) {
+        else => |cc| try lower.emitInst(.none, mnem_cc(.cmov, cc), &data.ops),
+        .z_and_np => {
+            try lower.emitInst(.none, mnem_cc(.cmov, .nz), &.{ data.ops[1], data.ops[0] });
+            try lower.emitInst(.none, mnem_cc(.cmov, .np), &data.ops);
+        },
+        .nz_or_p => {
+            try lower.emitInst(.none, mnem_cc(.cmov, .nz), &data.ops);
+            try lower.emitInst(.none, mnem_cc(.cmov, .p), &data.ops);
+        },
     }
 }
 
 fn mirSetcc(lower: *Lower, inst: Mir.Inst) Error!void {
-    switch (inst.ops) {
-        .r_cc => try lower.emit(.none, mnem_cc(.set, inst.data.r_cc.cc), &.{
+    const data: struct { cc: bits.Condition, ops: [2]Operand } = switch (inst.ops) {
+        .r_cc => .{ .cc = inst.data.r_cc.cc, .ops = .{
             .{ .reg = inst.data.r_cc.r },
-        }),
-        .m_sib_cc, .m_rip_cc => try lower.emit(.none, mnem_cc(.set, inst.data.x_cc.cc), &.{
+            .{ .reg = inst.data.r_cc.scratch },
+        } },
+        .m_sib_cc, .m_rip_cc => .{ .cc = inst.data.x_cc.cc, .ops = .{
             .{ .mem = lower.mem(inst.ops, inst.data.x_cc.payload) },
-        }),
+            .{ .reg = inst.data.x_cc.scratch },
+        } },
         else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
+    };
+    switch (data.cc) {
+        else => |cc| try lower.emitInst(.none, mnem_cc(.set, cc), data.ops[0..1]),
+        .z_and_np => {
+            try lower.emitInst(.none, mnem_cc(.set, .z), data.ops[0..1]);
+            try lower.emitInst(.none, mnem_cc(.set, .np), data.ops[1..2]);
+            try lower.emitInst(.none, .@"and", data.ops[0..2]);
+        },
+        .nz_or_p => {
+            try lower.emitInst(.none, mnem_cc(.set, .nz), data.ops[0..1]);
+            try lower.emitInst(.none, mnem_cc(.set, .p), data.ops[1..2]);
+            try lower.emitInst(.none, .@"or", data.ops[0..2]);
+        },
     }
 }
 
-fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst, comptime mnemonic: Mnemonic) Error!void {
+fn mirJcc(lower: *Lower, index: Mir.Inst.Index, inst: Mir.Inst) Error!void {
+    switch (inst.data.inst_cc.cc) {
+        else => |cc| try lower.emitInstWithReloc(.none, mnem_cc(.j, cc), &.{
+            .{ .imm = Immediate.s(0) },
+        }, .{ .inst = inst.data.inst_cc.inst }),
+        .z_and_np => {
+            try lower.emitInstWithReloc(.none, mnem_cc(.j, .nz), &.{
+                .{ .imm = Immediate.s(0) },
+            }, .{ .inst = index + 1 });
+            try lower.emitInstWithReloc(.none, mnem_cc(.j, .np), &.{
+                .{ .imm = Immediate.s(0) },
+            }, .{ .inst = inst.data.inst_cc.inst });
+        },
+        .nz_or_p => {
+            try lower.emitInstWithReloc(.none, mnem_cc(.j, .nz), &.{
+                .{ .imm = Immediate.s(0) },
+            }, .{ .inst = inst.data.inst_cc.inst });
+            try lower.emitInstWithReloc(.none, mnem_cc(.j, .p), &.{
+                .{ .imm = Immediate.s(0) },
+            }, .{ .inst = inst.data.inst_cc.inst });
+        },
+    }
+}
+
+fn mirRegisterList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
     const reg_list = Mir.RegisterList.fromInt(inst.data.payload);
     const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*);
     var it = reg_list.iterator(.{ .direction = switch (mnemonic) {
@@ -498,24 +614,20 @@ fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst, comptime mnemonic: Mnem
         .pop => .forward,
         else => unreachable,
     } });
-    while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
+    while (it.next()) |i| try lower.emitInst(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
 }
 
-fn mirLeaLinker(lower: *Lower, inst: Mir.Inst) Error!void {
-    const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
-    const reg = @intToEnum(Register, metadata.reg);
-    try lower.emit(.none, .lea, &.{
-        .{ .reg = reg },
-        .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
-    });
-}
-
-fn mirMovLinker(lower: *Lower, inst: Mir.Inst) Error!void {
-    const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
-    const reg = @intToEnum(Register, metadata.reg);
-    try lower.emit(.none, .mov, &.{
-        .{ .reg = reg },
-        .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
+fn mirLinker(lower: *Lower, mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
+    const reloc = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
+    try lower.emitInstWithReloc(.none, mnemonic, &.{
+        .{ .reg = inst.data.rx.r },
+        .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(inst.data.rx.r.bitSize()), 0) },
+    }, switch (inst.ops) {
+        .got_reloc => .{ .linker_got = reloc },
+        .direct_reloc => .{ .linker_direct = reloc },
+        .import_reloc => .{ .linker_import = reloc },
+        .tlv_reloc => .{ .linker_tlv = reloc },
+        else => unreachable,
     });
 }
 
src/arch/x86_64/Mir.zig
@@ -434,6 +434,12 @@ pub const Inst = struct {
         /// Register, memory (SIB), immediate (byte) operands.
         /// Uses `rix` payload with extra data of type `MemorySib`.
         rmi_sib,
+        /// Register, register, memory (RIP), immediate (byte) operands.
+        /// Uses `rrix` payload with extra data of type `MemoryRip`.
+        rrmi_rip,
+        /// Register, register, memory (SIB), immediate (byte) operands.
+        /// Uses `rrix` payload with extra data of type `MemorySib`.
+        rrmi_sib,
         /// Register, memory (RIP), immediate (byte) operands.
         /// Uses `rix` payload with extra data of type `MemoryRip`.
         rmi_rip,
@@ -524,16 +530,16 @@ pub const Inst = struct {
         /// Uses `reloc` payload.
         reloc,
         /// Linker relocation - GOT indirection.
-        /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
+        /// Uses `rx` payload with extra data of type `Reloc`.
         got_reloc,
         /// Linker relocation - direct reference.
-        /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
+        /// Uses `rx` payload with extra data of type `Reloc`.
         direct_reloc,
         /// Linker relocation - imports table indirection (binding).
-        /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
+        /// Uses `rx` payload with extra data of type `Reloc`.
         import_reloc,
         /// Linker relocation - threadlocal variable via GOT indirection.
-        /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
+        /// Uses `rx` payload with extra data of type `Reloc`.
         tlv_reloc,
     };
 
@@ -567,12 +573,14 @@ pub const Inst = struct {
         },
         /// Condition code (CC), followed by custom payload found in extra.
         x_cc: struct {
+            scratch: Register,
             cc: bits.Condition,
             payload: u32,
         },
         /// Register with condition code (CC).
         r_cc: struct {
             r: Register,
+            scratch: Register,
             cc: bits.Condition,
         },
         /// Register, register with condition code (CC).
@@ -614,6 +622,13 @@ pub const Inst = struct {
             i: u8,
             payload: u32,
         },
+        /// Register, register, byte immediate, followed by Custom payload found in extra.
+        rrix: struct {
+            r1: Register,
+            r2: Register,
+            i: u8,
+            payload: u32,
+        },
         /// String instruction prefix and width.
         string: struct {
             repeat: bits.StringRepeat,
@@ -622,12 +637,7 @@ pub const Inst = struct {
         /// Relocation for the linker where:
         /// * `atom_index` is the index of the source
         /// * `sym_index` is the index of the target
-        relocation: struct {
-            /// Index of the containing atom.
-            atom_index: u32,
-            /// Index into the linker's symbol table.
-            sym_index: u32,
-        },
+        relocation: Reloc,
         /// Debug line and column position
         line_column: struct {
             line: u32,
@@ -646,9 +656,7 @@ pub const Inst = struct {
     }
 };
 
-pub const LeaRegisterReloc = struct {
-    /// Destination register.
-    reg: u32,
+pub const Reloc = struct {
     /// Index of the containing atom.
     atom_index: u32,
     /// Index into the linker's symbol table.
test/behavior/bugs/12891.zig
@@ -29,7 +29,6 @@ test "inf >= 1" {
 test "isNan(nan * 1)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const nan_times_one = comptime std.math.nan(f64) * 1;
     try std.testing.expect(std.math.isNan(nan_times_one));
@@ -37,7 +36,6 @@ test "isNan(nan * 1)" {
 test "runtime isNan(nan * 1)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const nan_times_one = std.math.nan(f64) * 1;
     try std.testing.expect(std.math.isNan(nan_times_one));
@@ -45,7 +43,6 @@ test "runtime isNan(nan * 1)" {
 test "isNan(nan * 0)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const nan_times_zero = comptime std.math.nan(f64) * 0;
     try std.testing.expect(std.math.isNan(nan_times_zero));
@@ -55,7 +52,6 @@ test "isNan(nan * 0)" {
 test "isNan(inf * 0)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const inf_times_zero = comptime std.math.inf(f64) * 0;
     try std.testing.expect(std.math.isNan(inf_times_zero));
@@ -65,7 +61,6 @@ test "isNan(inf * 0)" {
 test "runtime isNan(nan * 0)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const nan_times_zero = std.math.nan(f64) * 0;
     try std.testing.expect(std.math.isNan(nan_times_zero));
@@ -75,7 +70,6 @@ test "runtime isNan(nan * 0)" {
 test "runtime isNan(inf * 0)" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const inf_times_zero = std.math.inf(f64) * 0;
     try std.testing.expect(std.math.isNan(inf_times_zero));
test/behavior/field_parent_ptr.zig
@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
 const builtin = @import("builtin");
 
 test "@fieldParentPtr non-first field" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
     try testParentFieldPtr(&foo.c);