[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