[llvm] [ConstantTime][LLVM] Add llvm.ct.select intrinsic with generic SelectionDAG lowering (PR #166702)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 15 13:39:02 PDT 2026


================
@@ -13625,6 +13627,57 @@ 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);
+
+  // 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);
+
+  if (VT0 == MVT::i1) {
+    // Merge nested i1 ct_selects. The AND/OR of the two conditions is itself
+    // constant-time and the result remains a CT_SELECT.
+
+    // ct_select C0, (ct_select C1, X, Y), Y -> ct_select (C0 & C1), X, Y
+    if (N1.getOpcode() == ISD::CT_SELECT && N1.hasOneUse()) {
----------------
dtcxzyw wrote:

See my previous comment in InstSimplify.


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


More information about the llvm-commits mailing list