[llvm-branch-commits] [llvm] [LLVM][CodeGen] Improve CTSELECT fallback lowering and target support modeling (PR #179395)
Akshay K via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Feb 10 02:43:40 PST 2026
https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/179395
>From 7b01e6e0a3d2764cc1a16b3e6edae0a1c42ff37c Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Mon, 2 Feb 2026 23:26:31 -0500
Subject: [PATCH] [CodeGen] Improve CTSELECT fallback lowering and cleanup
target modeling
Clarify CTSELECT semantics and refine the generic fallback lowering using
a canonical bitwise formulation for improved correctness. Simplify
TargetLowering support by introducing isCtSelectSupported() and removing
CTSELECT-specific SelectSupportKind entries. Minor DAGCombiner and
documentation cleanups.
---
llvm/include/llvm/CodeGen/ISDOpcodes.h | 4 +-
llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 2 -
llvm/include/llvm/CodeGen/TargetLowering.h | 11 ++--
llvm/include/llvm/IR/Intrinsics.td | 3 +-
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 8 +--
.../SelectionDAG/SelectionDAGBuilder.cpp | 61 ++++++-------------
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 2 +
llvm/lib/Target/ARM/ARMISelLowering.h | 2 +
llvm/lib/Target/X86/X86ISelLowering.h | 2 +
9 files changed, 35 insertions(+), 60 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index e843a78549315..84f5a62cfeb08 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -783,8 +783,8 @@ enum NodeType {
/// i1 then the high bits must conform to getBooleanContents.
SELECT,
- /// Constant-time Select, implemented with CMOV instruction. This is used to
- /// implement constant-time select.
+ /// CTSELECT(Cond, TrueVal, FalseVal). Cond is i1 and the value operands must
+ /// have the same type. Used to lower the constant-time select intrinsic.
CTSELECT,
/// Select with a vector condition (op #0) and two vector operands (ops #1
diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
index 8a9e3335b2453..fdb76a93bc5bb 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
@@ -436,8 +436,6 @@ struct SDNodeFlags {
FastMathFlags = NoNaNs | NoInfs | NoSignedZeros | AllowReciprocal |
AllowContract | ApproximateFuncs | AllowReassociation,
- // Flag for disabling optimization
- NoMerge = 1 << 15,
};
/// Default constructor turns off all optimization flags.
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index c1714c3cc073f..084e08e76bd2e 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -247,10 +247,6 @@ class LLVM_ABI TargetLoweringBase {
// and vector values (ex: cmov).
VectorMaskSelect, // The target supports vector selects with a vector
// mask (ex: x86 blends).
- CtSelect, // The target implements a custom constant-time select.
- ScalarCondVectorValCtSelect, // The target supports selects with a scalar
- // condition and vector values.
- VectorMaskValCtSelect, // The target supports vector selects with a vector
};
/// Enum that specifies what an atomic load/AtomicRMWInst is expanded
@@ -481,9 +477,10 @@ class LLVM_ABI TargetLoweringBase {
MachineMemOperand::Flags
getVPIntrinsicMemOperandFlags(const VPIntrinsic &VPIntrin) const;
- virtual bool isSelectSupported(SelectSupportKind kind) const {
- return kind != CtSelect;
- }
+ virtual bool isSelectSupported(SelectSupportKind kind) const { return true; }
+
+ /// Return true if the target has custom lowering for constant-time select.
+ virtual bool isCtSelectSupported(EVT VT) const { return false; }
/// Return true if the @llvm.get.active.lane.mask intrinsic should be expanded
/// using generic code in SelectionDAGBuilder.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 4fe2736b78023..abfa5bc279918 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1863,8 +1863,7 @@ def int_coro_subfn_addr : DefaultAttrsIntrinsic<
[IntrReadMem, IntrArgMemOnly, ReadOnly<ArgIndex<0>>,
NoCapture<ArgIndex<0>>]>;
-///===-------------------------- Constant Time Intrinsics
-///--------------------------===//
+///===---------------------- Constant Time Intrinsics ----------------------===//
//
// Intrinsic to support constant time select
def int_ct_select
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 7cec739015756..646bc5e78c051 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -484,7 +484,8 @@ namespace {
SDValue visitCTTZ_ZERO_UNDEF(SDNode *N);
SDValue visitCTPOP(SDNode *N);
SDValue visitSELECT(SDNode *N);
- SDValue visitCTSELECT(SDNode *N);
+ // visit CTSELECT Node
+ SDValue visitConstantTimeSelect(SDNode *N);
SDValue visitVSELECT(SDNode *N);
SDValue visitVP_SELECT(SDNode *N);
SDValue visitSELECT_CC(SDNode *N);
@@ -1905,7 +1906,6 @@ void DAGCombiner::Run(CombineLevel AtLevel) {
}
SDValue DAGCombiner::visit(SDNode *N) {
-
// clang-format off
switch (N->getOpcode()) {
default: break;
@@ -1976,7 +1976,7 @@ SDValue DAGCombiner::visit(SDNode *N) {
case ISD::CTTZ_ZERO_UNDEF: return visitCTTZ_ZERO_UNDEF(N);
case ISD::CTPOP: return visitCTPOP(N);
case ISD::SELECT: return visitSELECT(N);
- case ISD::CTSELECT: return visitCTSELECT(N);
+ case ISD::CTSELECT: return visitConstantTimeSelect(N);
case ISD::VSELECT: return visitVSELECT(N);
case ISD::SELECT_CC: return visitSELECT_CC(N);
case ISD::SETCC: return visitSETCC(N);
@@ -12583,7 +12583,7 @@ SDValue DAGCombiner::visitSELECT(SDNode *N) {
return SDValue();
}
-SDValue DAGCombiner::visitCTSELECT(SDNode *N) {
+SDValue DAGCombiner::visitConstantTimeSelect(SDNode *N) {
SDValue N0 = N->getOperand(0);
SDValue N1 = N->getOperand(1);
SDValue N2 = N->getOperand(2);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index eaecb87979648..49e38f56d30ad 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -6582,7 +6582,10 @@ void SelectionDAGBuilder::visitVectorExtractLastActive(const CallInst &I,
/// Fallback implementation for constant-time select using DAG chaining.
/// This implementation uses data dependencies through virtual registers to
-/// prevent optimizations from breaking the constant-time property.
+/// prevent optimizations from breaking the constant-time property. It is a
+/// best-effort safeguard; for stronger guarantees we prefer target-specific
+/// lowering pipelines that preserve the select pattern by construction.
+///
/// It handles scalars, vectors (fixed and scalable), and floating-point types.
SDValue SelectionDAGBuilder::createProtectedCtSelectFallback(
SelectionDAG &DAG, const SDLoc &DL, SDValue Cond, SDValue T, SDValue F,
@@ -6599,28 +6602,12 @@ SDValue SelectionDAGBuilder::createProtectedCtSelectFallback(
if (VT.isVector() && !Cond.getValueType().isVector()) {
ElementCount NumElems = VT.getVectorElementCount();
EVT CondVT = EVT::getVectorVT(*DAG.getContext(), MVT::i1, NumElems);
-
- if (VT.isScalableVector()) {
- Cond = DAG.getSplatVector(CondVT, DL, Cond);
- } else {
- Cond = DAG.getSplatBuildVector(CondVT, DL, Cond);
- }
+ Cond = DAG.getSplat(CondVT, DL, Cond);
}
// Handle floating-point types: bitcast to integer for bitwise operations
if (VT.isFloatingPoint()) {
- if (VT.isVector()) {
- // float vector -> int vector
- EVT ElemVT = VT.getVectorElementType();
- unsigned int ElemBitWidth = ElemVT.getScalarSizeInBits();
- EVT IntElemVT = EVT::getIntegerVT(*DAG.getContext(), ElemBitWidth);
-
- WorkingVT = EVT::getVectorVT(*DAG.getContext(), IntElemVT,
- VT.getVectorElementCount());
- } else {
- WorkingVT = EVT::getIntegerVT(*DAG.getContext(), VT.getSizeInBits());
- }
-
+ WorkingVT = VT.changeTypeToInteger();
WorkingT = DAG.getBitcast(WorkingVT, T);
WorkingF = DAG.getBitcast(WorkingVT, F);
}
@@ -6628,24 +6615,10 @@ SDValue SelectionDAGBuilder::createProtectedCtSelectFallback(
// Create mask: sign-extend condition to all bits
SDValue Mask = DAG.getSExtOrTrunc(Cond, DL, WorkingVT);
- // Create all-ones constant for inversion
- SDValue AllOnes;
- if (WorkingVT.isScalableVector()) {
- unsigned BitWidth = WorkingVT.getScalarSizeInBits();
- APInt AllOnesVal = APInt::getAllOnes(BitWidth);
- SDValue ScalarAllOnes =
- DAG.getConstant(AllOnesVal, DL, WorkingVT.getScalarType());
- AllOnes = DAG.getSplatVector(WorkingVT, DL, ScalarAllOnes);
- } else {
- AllOnes = DAG.getAllOnesConstant(DL, WorkingVT);
- }
-
- // Invert mask for false value
- SDValue Invert = DAG.getNode(ISD::XOR, DL, WorkingVT, Mask, AllOnes);
-
- // Compute: (T & Mask) | (F & ~Mask)
+ // Compute: F ^ ((T ^ F) & Mask)
// This is constant-time because both branches are always computed
- SDValue TM = DAG.getNode(ISD::AND, DL, WorkingVT, Mask, WorkingT);
+ SDValue XorTF = DAG.getNode(ISD::XOR, DL, WorkingVT, WorkingT, WorkingF);
+ SDValue TM = DAG.getNode(ISD::AND, DL, WorkingVT, XorTF, Mask);
// DAG chaining: create data dependency through virtual register
// This prevents optimizations from reordering or eliminating operations
@@ -6653,10 +6626,15 @@ SDValue SelectionDAGBuilder::createProtectedCtSelectFallback(
bool CanUseChaining = false;
if (!WorkingVT.isScalableVector()) {
- // For fixed-size vectors and scalars, check if type is legal
+ // For fixed-size vectors and scalars, chaining is a best-effort hardening
+ // step. The CT guarantee comes from the dataflow-only select
+ // pattern (both sides computed, no control-flow). Chaining only adds an
+ // extra dependency to discourage later combines.
CanUseChaining = TLI.isTypeLegal(WorkingVT.getSimpleVT());
} else {
- // For scalable vectors, disable chaining (conservative approach)
+ // For scalable vectors, skip chaining because there is no stable register
+ // class to copy through. CT behavior still relies on the masking/select
+ // pattern above.
CanUseChaining = false;
}
@@ -6668,8 +6646,7 @@ SDValue SelectionDAGBuilder::createProtectedCtSelectFallback(
TM = DAG.getCopyFromReg(Chain, DL, TMReg, WorkingVT);
}
- SDValue FM = DAG.getNode(ISD::AND, DL, WorkingVT, Invert, WorkingF);
- SDValue Result = DAG.getNode(ISD::OR, DL, WorkingVT, TM, FM);
+ SDValue Result = DAG.getNode(ISD::XOR, DL, WorkingVT, WorkingF, TM);
// Convert back to original type if needed
if (WorkingVT != VT) {
@@ -6878,9 +6855,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
assert(!CondVT.isVector() && "Vector type cond not supported yet");
// Handle scalar types
- if (TLI.isSelectSupported(
- TargetLoweringBase::SelectSupportKind::CtSelect) &&
- !CondVT.isVector()) {
+ if (TLI.isCtSelectSupported(VT) && !CondVT.isVector()) {
SDValue Result = DAG.getNode(ISD::CTSELECT, DL, VT, Cond, A, B);
setValue(&I, Result);
return;
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index e8c026d989eb8..292caf16a97e2 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -157,6 +157,8 @@ class AArch64TargetLowering : public TargetLowering {
EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
EVT VT) const override;
+ bool isCtSelectSupported(EVT VT) const override { return false; }
+
SDValue ReconstructShuffle(SDValue Op, SelectionDAG &DAG) const;
MachineBasicBlock *EmitF128CSEL(MachineInstr &MI,
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h
index d0fb58c764edd..faa12aba61ed3 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.h
+++ b/llvm/lib/Target/ARM/ARMISelLowering.h
@@ -119,6 +119,8 @@ class VectorType;
return (Kind != ScalarCondVectorVal);
}
+ bool isCtSelectSupported(EVT VT) const override { return false; }
+
bool isReadOnly(const GlobalValue *GV) const;
/// getSetCCResultType - Return the value type to use for ISD::SETCC.
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index 848fe4bf86d2c..4100ae9925781 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -1078,6 +1078,8 @@ namespace llvm {
unsigned getJumpTableEncoding() const override;
bool useSoftFloat() const override;
+ bool isCtSelectSupported(EVT VT) const override { return false; }
+
void markLibCallAttributes(MachineFunction *MF, unsigned CC,
ArgListTy &Args) const override;
More information about the llvm-branch-commits
mailing list