[llvm] [X86] Fix operand register set TAILJMPm64_REX (PR #225080)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 06:18:26 PDT 2026
https://github.com/eleviant created https://github.com/llvm/llvm-project/pull/225080
The TAILJMPm64_REX pseudo-instruction is emitted by the x86-expand-pseudo pass after the register allocator, replacing TCRETURN_WINmi64 or TCRETURNmi64 depending on the target ABI. The problem is that the former uses the GR64_TC register class for its memory address operand, while the latter uses GR64_TCW64. This breaks MachineVerifier, triggering an assertion in EXPENSIVE_CHECKS builds.
>From 3171d11aefa0094f0f0f36d615e7ea96b9dbccc2 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Mon, 21 Sep 2026 15:06:33 +0200
Subject: [PATCH] [X86] Fix operand register set TAILJMPm64_REX
The TAILJMPm64_REX pseudo-instruction is emitted by the x86-expand-pseudo
pass after the register allocator, replacing TCRETURN_WINmi64 or TCRETURNmi64
depending on the target ABI. The problem is that the former uses the GR64_TC
register class for its memory address operand, while the latter uses GR64_TCW64.
This breaks MachineVerifier, triggering an assertion in EXPENSIVE_CHECKS builds.
---
llvm/lib/Target/X86/X86InstrControl.td | 2 +-
llvm/lib/Target/X86/X86InstrOperands.td | 9 ++
llvm/lib/Target/X86/X86RegisterInfo.td | 1 +
llvm/test/CodeGen/X86/pseudo-expand-tc.mir | 100 +++++++++++++++++++++
4 files changed, 111 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/X86/pseudo-expand-tc.mir
diff --git a/llvm/lib/Target/X86/X86InstrControl.td b/llvm/lib/Target/X86/X86InstrControl.td
index 1b2dbefce6f1c..3d0a1726b9d89 100644
--- a/llvm/lib/Target/X86/X86InstrControl.td
+++ b/llvm/lib/Target/X86/X86InstrControl.td
@@ -397,7 +397,7 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
[]>, Sched<[WriteJump]>;
let mayLoad = 1 in
- def TAILJMPm64_REX : PseudoI<(outs), (ins i64mem_TC:$dst),
+ def TAILJMPm64_REX : PseudoI<(outs), (ins i64mem_TC_or_w64TC:$dst),
[]>, Sched<[WriteJumpLd]>;
}
}
diff --git a/llvm/lib/Target/X86/X86InstrOperands.td b/llvm/lib/Target/X86/X86InstrOperands.td
index 4373f39c360a8..0b470e0015e2c 100644
--- a/llvm/lib/Target/X86/X86InstrOperands.td
+++ b/llvm/lib/Target/X86/X86InstrOperands.td
@@ -139,6 +139,10 @@ def ptr_rc_tailcall : RegClassByHwMode<
[X86_32, X86_64, X86_64_X32],
[GR32_TC, GR64_TC, GR64_TC]>;
+def ptr_rc_tc_or_tcw64 : RegClassByHwMode<
+ [X86_32, X86_64, X86_64_X32],
+ [GR32_TC, GR64_TC_or_TCW64, GR64_TC_or_TCW64]>;
+
// Special i32mem for addresses of load folding tail calls. These are not
// allowed to use callee-saved registers since they must be scheduled
// after callee-saved register are popped.
@@ -155,6 +159,11 @@ def i64mem_TC : X86MemOperand<"printqwordmem", X86Mem64AsmOperand, 64> {
ptr_rc_tailcall, i32imm, SEGMENT_REG);
}
+def i64mem_TC_or_w64TC : X86MemOperand<"printqwordmem", X86Mem64AsmOperand, 64> {
+ let MIOperandInfo = (ops ptr_rc_tc_or_tcw64, i8imm,
+ ptr_rc_tc_or_tcw64, i32imm, SEGMENT_REG);
+}
+
def i64mem_w64TC : X86MemOperand<"printqwordmem", X86Mem64AsmOperand, 64> {
let MIOperandInfo = (ops GR64_TCW64, i8imm,
GR64_TCW64, i32imm, SEGMENT_REG);
diff --git a/llvm/lib/Target/X86/X86RegisterInfo.td b/llvm/lib/Target/X86/X86RegisterInfo.td
index 63ad4b2bae5ac..33de3cdcfbb04 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.td
+++ b/llvm/lib/Target/X86/X86RegisterInfo.td
@@ -633,6 +633,7 @@ def GR64_TC : RegisterClass<"X86", [i64], 64, (add RAX, RCX, RDX, RSI, RDI,
def GR64_TCW64 : RegisterClass<"X86", [i64], 64, (add RAX, RCX, RDX,
R8, R9, R10, R11,
RIP, RSP)>;
+def GR64_TC_or_TCW64 : RegisterClass<"X86", [i64], 64, (add GR64_TC, GR64_TCW64)>;
// GR8_NOREX - GR8 registers which do not require a REX prefix.
def GR8_NOREX : RegisterClass<"X86", [i8], 8,
diff --git a/llvm/test/CodeGen/X86/pseudo-expand-tc.mir b/llvm/test/CodeGen/X86/pseudo-expand-tc.mir
new file mode 100644
index 0000000000000..f270e4e543e9c
--- /dev/null
+++ b/llvm/test/CodeGen/X86/pseudo-expand-tc.mir
@@ -0,0 +1,100 @@
+# REQUIRES: asserts
+# RUN: llc --run-pass=x86-expand-pseudo --verify-machineinstrs %s -o /dev/null
+
+--- |
+ target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+ target triple = "x86_64-pc-windows-msvc19.33.0"
+
+ define dso_local void @"?dump at FileSystem@@QEBAXXZ"(ptr noundef nonnull align 8 dereferenceable(16) %this) local_unnamed_addr align 2 {
+ entry:
+ %call = tail call noundef nonnull align 4 dereferenceable(4) ptr @"?dbgs@@YAAEAHXZ"()
+ %print_Type = getelementptr inbounds nuw i8, ptr %this, i64 8
+ %0 = load i32, ptr %print_Type, align 8
+ %vtable = load ptr, ptr %this, align 8
+ %1 = load ptr, ptr %vtable, align 8
+ tail call void %1(ptr noundef nonnull align 8 dereferenceable(16) %this, ptr noundef nonnull align 4 dereferenceable(4) %call, i32 noundef %0, i32 noundef 0)
+ ret void
+ }
+
+ declare dso_local noundef nonnull align 4 dereferenceable(4) ptr @"?dbgs@@YAAEAHXZ"() local_unnamed_addr
+...
+---
+name: '?dump at FileSystem@@QEBAXXZ'
+alignment: 1
+exposesReturnsTwice: false
+legalized: false
+regBankSelected: false
+selected: false
+failedISel: false
+tracksRegLiveness: true
+hasWinCFI: true
+noPhis: true
+isSSA: false
+noVRegs: true
+hasFakeUses: false
+callsEHReturn: false
+callsUnwindInit: false
+hasEHContTarget: false
+hasEHScopes: false
+hasEHFunclets: false
+isOutlined: false
+debugInstrRef: true
+failsVerification: false
+tracksDebugUserValues: true
+registers: []
+liveins:
+ - { reg: '$rcx', virtual-reg: '' }
+frameInfo:
+ isFrameAddressTaken: false
+ isReturnAddressTaken: false
+ hasStackMap: false
+ hasPatchPoint: false
+ stackSize: 40
+ offsetAdjustment: -40
+ maxAlignment: 8
+ adjustsStack: true
+ hasCalls: true
+ framePointerPolicy: none
+ stackProtector: ''
+ functionContext: ''
+ maxCallFrameSize: 32
+ cvBytesOfCalleeSavedRegisters: 8
+ hasOpaqueSPAdjustment: false
+ hasVAStart: false
+ hasMustTailInVarArgFunc: false
+ hasTailCall: true
+ isCalleeSavedInfoValid: true
+ localFrameSize: 0
+fixedStack:
+ - { id: 0, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default,
+ callee-saved-register: '$rsi', callee-saved-restored: true, debug-info-variable: '',
+ debug-info-expression: '', debug-info-location: '' }
+stack: []
+entry_values: []
+callSites: []
+debugValueSubstitutions: []
+constants: []
+machineFunctionInfo:
+ amxProgModel: None
+body: |
+ bb.0.entry:
+ liveins: $rcx, $rsi
+
+ frame-setup PUSH64r killed $rsi, implicit-def $rsp, implicit $rsp
+ frame-setup SEH_PushReg 60
+ $rsp = frame-setup SUB64ri32 $rsp, 32, implicit-def dead $eflags
+ frame-setup SEH_StackAlloc 32
+ frame-setup SEH_EndPrologue
+ $rsi = MOV64rr $rcx
+ CALL64pcrel32 @"?dbgs@@YAAEAHXZ", csr_win64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax
+ renamable $r8d = MOV32rm renamable $rsi, 1, $noreg, 8, $noreg :: (load (s32) from %ir.print_Type, align 8)
+ renamable $r10 = MOV64rm renamable $rsi, 1, $noreg, 0, $noreg :: (load (s64) from %ir.this)
+ $rcx = MOV64rr killed $rsi
+ $rdx = MOV64rr killed $rax
+ $r9d = XOR32rr undef $r9d, undef $r9d, implicit-def dead $eflags
+ SEH_BeginEpilogue
+ $rsp = frame-destroy ADD64ri32 $rsp, 32, implicit-def dead $eflags
+ $rsi = frame-destroy POP64r implicit-def $rsp, implicit $rsp
+ SEH_EndEpilogue
+ TCRETURN_WINmi64 killed renamable $r10, 1, $noreg, 0, $noreg, 0, csr_win64, implicit $rsp, implicit $ssp, implicit $rcx, implicit $rdx, implicit $r8d, implicit $r9d :: (load (s64) from %ir.vtable)
+...
More information about the llvm-commits
mailing list