[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:12 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()) {
----------------
wizardengineer wrote:

Done in 64613ed31ef6.

https://github.com/llvm/llvm-project/pull/166702


More information about the llvm-commits mailing list