================
@@ -12592,93 +12596,57 @@ SDValue DAGCombiner::visitConstantTimeSelect(SDNode
*N) {
SDLoc DL(N);
SDNodeFlags Flags = N->getFlags();
- if (SDValue V = foldBoolSelectToLogic<EmptyMatchContext>(N, DL, DAG))
- return V;
-
// ctselect (not Cond), N1, N2 -> ctselect Cond, N2, N1
+ // This is a CT-safe canonicalization: flip negated condition by swapping
+ // arms. extractBooleanFlip only matches boolean xor-with-1, so this
preserves
+ // dataflow semantics and does not introduce data-dependent control flow.
if (SDValue F = extractBooleanFlip(N0, DAG, TLI, false)) {
SDValue SelectOp = DAG.getNode(ISD::CTSELECT, DL, VT, F, N2, N1);
SelectOp->setFlags(Flags);
return SelectOp;
}
if (VT0 == MVT::i1) {
- // The code in this block deals with the following 2 equivalences:
- // select(C0|C1, x, y) <=> select(C0, x, select(C1, x, y))
- // select(C0&C1, x, y) <=> select(C0, select(C1, x, y), y)
- // The target can specify its preferred form with the
- // shouldNormalizeToSelectSequence() callback. However we always transform
- // to the right anyway if we find the inner select exists in the DAG anyway
- // and we always transform to the left side if we know that we can further
- // optimize the combination of the conditions.
- bool normalizeToSequence =
- TLI.shouldNormalizeToSelectSequence(*DAG.getContext(), VT);
- // ctselect (and Cond0, Cond1), X, Y
- // -> ctselect Cond0, (ctselect Cond1, X, Y), Y
- if (N0->getOpcode() == ISD::AND && N0->hasOneUse()) {
- SDValue Cond0 = N0->getOperand(0);
- SDValue Cond1 = N0->getOperand(1);
- SDValue InnerSelect = DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(),
- Cond1, N1, N2, Flags);
- if (normalizeToSequence || !InnerSelect.use_empty())
- return DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(), Cond0,
- InnerSelect, N2, Flags);
- // Cleanup on failure.
- if (InnerSelect.use_empty())
- recursivelyDeleteUnusedNodes(InnerSelect.getNode());
- }
- // ctselect (or Cond0, Cond1), X, Y -> ctselect Cond0, X, (ctselect Cond1,
- // X, Y)
- if (N0->getOpcode() == ISD::OR && N0->hasOneUse()) {
- SDValue Cond0 = N0->getOperand(0);
- SDValue Cond1 = N0->getOperand(1);
- SDValue InnerSelect = DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(),
- Cond1, N1, N2, Flags);
- if (normalizeToSequence || !InnerSelect.use_empty())
- return DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(), Cond0, N1,
- InnerSelect, Flags);
- // Cleanup on failure.
- if (InnerSelect.use_empty())
- recursivelyDeleteUnusedNodes(InnerSelect.getNode());
- }
-
- // ctselect Cond0, (ctselect Cond1, X, Y), Y -> ctselect (and Cond0,
Cond1),
- // X, Y
+ // Nested CTSELECT merging optimizations for i1 conditions.
+ // These are CT-safe because:
+ // 1. AND/OR are bitwise operations that execute in constant time
+ // 2. The optimization combines two sequential CTSELECTs into one,
+ // reducing
+ // the total number of constant-time operations without changing
+ // semantics
+ // 3. No data-dependent branches or memory accesses are introduced
+ //
+ // Pattern 1: ctselect C0, (ctselect C1, X, Y), Y -> ctselect (C0 & C1), X,
+ // Y
+ // Semantic equivalence: If C0 is true, evaluate inner select (C1 ? X :
+ // Y). If C0 is false, choose Y. This is equivalent to (C0 && C1) ? X :
Y.
if (N1->getOpcode() == ISD::CTSELECT && N1->hasOneUse()) {
SDValue N1_0 = N1->getOperand(0);
SDValue N1_1 = N1->getOperand(1);
SDValue N1_2 = N1->getOperand(2);
if (N1_2 == N2 && N0.getValueType() == N1_0.getValueType()) {
- // Create the actual and node if we can generate good code for it.
- if (!normalizeToSequence) {
- SDValue And = DAG.getNode(ISD::AND, DL, N0.getValueType(), N0, N1_0);
- return DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(), And, N1_1,
- N2, Flags);
- }
- // Otherwise see if we can optimize the "and" to a better pattern.
- if (SDValue Combined = visitANDLike(N0, N1_0, N)) {
- return DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(), Combined,
- N1_1, N2, Flags);
- }
+ SDValue And = DAG.getNode(ISD::AND, DL, N0.getValueType(), N0, N1_0);
+ SDValue SelectOp =
+ DAG.getNode(ISD::CTSELECT, DL, N1.getValueType(), And, N1_1, N2);
+ SelectOp->setFlags(Flags);
+ return SelectOp;
}
}
- // ctselect Cond0, X, (ctselect Cond1, X, Y) -> ctselect (or Cond0, Cond1),
- // X, Y
+
+ // Pattern 2: ctselect C0, X, (ctselect C1, X, Y) -> ctselect (C0 | C1), X,
+ // Y
+ // Semantic equivalence: If C0 is true, choose X. If C0 is false,
evaluate
+ // inner select (C1 ? X : Y). This is equivalent to (C0 || C1) ? X : Y.
----------------
wizardengineer wrote:
same for here
https://github.com/llvm/llvm-project/pull/180883
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits