[llvm] [ConstantTime][LLVM] Add llvm.ct.select intrinsic with generic SelectionDAG lowering (PR #166702)
Julius Alexandre via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 20:13:13 PDT 2026
================
@@ -13209,6 +13213,67 @@ SDValue DAGCombiner::visitSELECT(SDNode *N) {
return SDValue();
}
+// Keep CT_SELECT combines deliberately conservative to preserve constant-time
+// intent across generic DAG combines. We only accept:
+// - canonicalization of negated conditions (flip true/false operands), and
+// - i1 CT_SELECT nesting merges via AND/OR that keep the result as CT_SELECT.
+// Broader rewrites should be done in target-specific lowering when stronger
+// guarantees about legality and constant-time preservation are available.
+SDValue DAGCombiner::visitCT_SELECT(SDNode *N) {
+ SDValue N0 = N->getOperand(0);
+ SDValue N1 = N->getOperand(1);
+ SDValue N2 = N->getOperand(2);
+ EVT VT = N->getValueType(0);
+ EVT VT0 = N0.getValueType();
+ SDLoc DL(N);
+ SDNodeFlags Flags = N->getFlags();
+
+ // ct_select (not Cond), N1, N2 -> ct_select 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))
+ return DAG.getCTSelect(DL, VT, F, N2, N1, Flags);
+
+ if (VT0 == MVT::i1) {
+ // Nested CT_SELECT 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 CT_SELECTs into one,
+ // reducing the total number of constant-time operations without
+ // changing semantics
+ // 3. No data-dependent branches or memory accesses are introduced
+ //
+ // ct_select C0, (ct_select C1, X, Y), Y -> ct_select (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::CT_SELECT && 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()) {
+ SDValue And = DAG.getNode(ISD::AND, DL, N0.getValueType(), N0, N1_0);
+ return DAG.getCTSelect(DL, N1.getValueType(), And, N1_1, N2, Flags);
+ }
+ }
+
+ // ct_select C0, X, (ct_select C1, X, Y) -> ct_select (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.
+ if (N2->getOpcode() == ISD::CT_SELECT && N2->hasOneUse()) {
----------------
wizardengineer wrote:
Done in 64613ed31ef6.
https://github.com/llvm/llvm-project/pull/166702
More information about the llvm-commits
mailing list