[llvm] [X86] Fix TC_RETURN operand type for the x32 ABI (PR #228281)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 17:05:30 PDT 2026
https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/228281
>From 2472b79254593791856aa89700883fd8aad1f351 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 1 Oct 2026 16:41:57 -0700
Subject: [PATCH 1/2] [X86] Fix TC_RETURN operand type for the x32 ABI
SDT_X86TCRET constrained the tail-call target operand with SDTCisPtrTy<0>,
which resolves to the data layout's 32-bit pointer type on the x32 ABI.
However the operand is actually a GPR value that is zero-extended to 64
bits on x32, since calls still execute in 64-bit mode even though pointers
are 32-bit there; this mismatch crashed instruction selection for tail
calls through a register or folded load on x32 (e.g. call-structfp.ll).
Relax SDT_X86TCRET to SDTCisInt<0>, and update the two load-folding
patterns in X86InstrCompiler.td whose inferred type depended on the old
constraint to explicitly annotate i32/i64.
Add an X86-specific verifyTargetNode check for TC_RETURN/
TC_RETURN_GLOBALADDR that reinstates the precise expected width: i32 for a
direct call to a GlobalAddress/ExternalSymbol under the x32 ABI (and in
plain 32-bit mode), i64 for the same under LP64, and otherwise i64 iff the
subtarget runs in 64-bit mode (including x32) or i32 otherwise.
Co-Authored-By: Claude Sonnet 5.5 <noreply at anthropic.com>
---
llvm/lib/Target/X86/X86InstrCompiler.td | 6 ++--
llvm/lib/Target/X86/X86InstrFragments.td | 5 ++-
llvm/lib/Target/X86/X86SelectionDAGInfo.cpp | 34 +++++++++++++++++++++
3 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 45619663c45cb61..0965a011e381646 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1410,7 +1410,7 @@ def : Pat<(X86tcret GR32:$dst, timm:$off),
// FIXME: This is disabled for 32-bit PIC mode because the global base
// register which is part of the address mode may be assigned a
// callee-saved register.
-def : Pat<(X86tcret_enough_regs (load addr:$dst), timm:$off),
+def : Pat<(X86tcret_enough_regs (i32 (load addr:$dst)), timm:$off),
(TCRETURNmi addr:$dst, timm:$off)>,
Requires<[Not64BitMode, IsNotPIC, NotUseIndirectThunkCalls]>;
@@ -1436,11 +1436,11 @@ def : Pat<(X86tcret ptr_rc_tailcall:$dst, timm:$off),
// Don't fold loads into X86tcret requiring too many regs.
// There wouldn't be enough scratch registers for base+index.
-def : Pat<(X86tcret_enough_regs (load addr:$dst), timm:$off),
+def : Pat<(X86tcret_enough_regs (i64 (load addr:$dst)), timm:$off),
(TCRETURNmi64 addr:$dst, timm:$off)>,
Requires<[In64BitMode, IsNotWin64CCFunc, NotUseIndirectThunkCalls]>;
-def : Pat<(X86tcret_enough_regs (load addr:$dst), timm:$off),
+def : Pat<(X86tcret_enough_regs (i64 (load addr:$dst)), timm:$off),
(TCRETURN_WINmi64 addr:$dst, timm:$off)>,
Requires<[IsWin64CCFunc, NotUseIndirectThunkCalls]>;
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index 383e713f93810d3..11fd86f1f55f218 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -120,7 +120,10 @@ def SDT_X86PROBED_ALLOCA : SDTypeProfile<1, 1, [SDTCisVT<0, iPTR>, SDTCisVT<1, i
def SDT_X86EHRET : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
-def SDT_X86TCRET : SDTypeProfile<0, 2, [SDTCisPtrTy<0>, SDTCisVT<1, i32>]>;
+// The tail-call target is an integer, not necessarily a pointer: on the
+// x32 ABI it is a 64-bit zero-extension of a 32-bit pointer, since calls
+// still execute in 64-bit mode even though pointers are 32-bit.
+def SDT_X86TCRET : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisVT<1, i32>]>;
def SDT_X86ENQCMD : SDTypeProfile<1, 2, [SDTCisVT<0, i32>,
SDTCisPtrTy<1>, SDTCisSameAs<1, 2>]>;
diff --git a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
index 8b9782be0ea41ae..00e214f521622e0 100644
--- a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
+++ b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
@@ -90,6 +90,40 @@ void X86SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
}
SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+
+ switch (N->getOpcode()) {
+ default:
+ break;
+ case X86ISD::TC_RETURN:
+ case X86ISD::TC_RETURN_GLOBALADDR: {
+ // The tail-call target is an integer whose width depends on both the
+ // subtarget and on how the callee is addressed:
+ // * A direct call to a GlobalAddress/ExternalSymbol is i32 on the
+ // x32 ABI (as well as plain 32-bit mode) and i64 under LP64, since
+ // TCRETURNdi/TCRETURNdi64 are selected based on IsLP64/NotLP64.
+ // * Anything else (register, folded load, or TC_RETURN_GLOBALADDR's
+ // RIP-relative CFGuard call) uses the register width the subtarget
+ // executes in, i.e. i64 whenever the subtarget runs in 64-bit mode
+ // (including x32) and i32 otherwise.
+ const X86Subtarget &Subtarget =
+ DAG.getMachineFunction().getSubtarget<X86Subtarget>();
+ SDValue Target = N->getOperand(1);
+ bool IsDirect = Target.getOpcode() == ISD::GlobalAddress ||
+ Target.getOpcode() == ISD::TargetGlobalAddress ||
+ Target.getOpcode() == ISD::ExternalSymbol ||
+ Target.getOpcode() == ISD::TargetExternalSymbol;
+ bool WantI64 = IsDirect ? Subtarget.isTarget64BitLP64()
+ : Subtarget.is64Bit();
+ EVT ExpectedVT = WantI64 ? MVT::i64 : MVT::i32;
+ EVT VT = Target.getValueType();
+ if (VT != ExpectedVT)
+ report_fatal_error("invalid node: " + Twine(N->getOperationName(&DAG)) +
+ " operand #1 must have type " +
+ ExpectedVT.getEVTString() + ", but has type " +
+ VT.getEVTString());
+ break;
+ }
+ }
}
/// Returns the best type to use with repmovs/repstos depending on alignment.
>From a207f048804e7f82a53c0564053808ca026182e3 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 1 Oct 2026 17:05:03 -0700
Subject: [PATCH 2/2] fixup! clang-format
---
llvm/lib/Target/X86/X86SelectionDAGInfo.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
index 00e214f521622e0..0002d93d82ab42b 100644
--- a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
+++ b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
@@ -112,8 +112,8 @@ void X86SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
Target.getOpcode() == ISD::TargetGlobalAddress ||
Target.getOpcode() == ISD::ExternalSymbol ||
Target.getOpcode() == ISD::TargetExternalSymbol;
- bool WantI64 = IsDirect ? Subtarget.isTarget64BitLP64()
- : Subtarget.is64Bit();
+ bool WantI64 =
+ IsDirect ? Subtarget.isTarget64BitLP64() : Subtarget.is64Bit();
EVT ExpectedVT = WantI64 ? MVT::i64 : MVT::i32;
EVT VT = Target.getValueType();
if (VT != ExpectedVT)
More information about the llvm-commits
mailing list