[llvm] [X86] Fix TC_RETURN operand type for the x32 ABI (PR #228281)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 16:59:41 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
Author: Craig Topper (topperc)
<details>
<summary>Changes</summary>
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@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/228281.diff
3 Files Affected:
- (modified) llvm/lib/Target/X86/X86InstrCompiler.td (+3-3)
- (modified) llvm/lib/Target/X86/X86InstrFragments.td (+4-1)
- (modified) llvm/lib/Target/X86/X86SelectionDAGInfo.cpp (+34)
``````````diff
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 45619663c45cb6..0965a011e38164 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 383e713f93810d..11fd86f1f55f21 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 8b9782be0ea41a..00e214f521622e 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.
``````````
</details>
https://github.com/llvm/llvm-project/pull/228281
More information about the llvm-commits
mailing list