[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:15 PDT 2026
================
@@ -4329,6 +4330,159 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
}
Results.push_back(Tmp1);
break;
+ case ISD::CT_SELECT: {
+ // Constant-time select: F ^ ((T ^ F) & Mask), Mask = 0 - (cond & 1).
+ // Bitwise-only — no select/cmov SDNode is constructed, so the CT property
+ // holds against any combiner that targets those opcodes. FP types operate
+ // on the same-size integer; vectors build the mask as a scalar then splat
+ // (avoids illegal vNi1).
+ //
+ // The masked-diff is routed through a virtual register (CopyToReg /
+ // CopyFromReg) below as a forward-looking DAGCombine barrier. This is
+ // *not* required for correctness against any combiner in tree today —
+ // DAGCombiner has no rewrite that recognizes XOR/AND/XOR-with-sext-mask
+ // and reconstructs a SELECT. The chain edge is defense-in-depth against
+ // a hypothetical future fold of that form: the dependency partitions the
+ // bitwise sequence into a region the combiner can't see through. Cost is
+ // at most a coalesce-able MOV per call. Swap for ARITH_FENCE when its
+ // int/vector extension lands.
+ Tmp1 = Node->getOperand(0); // cond
+ Tmp2 = Node->getOperand(1); // T
+ Tmp3 = Node->getOperand(2); // F
+ EVT VT = Tmp2.getValueType();
+
+ // Memory-blend for FP scalars whose same-size integer isn't legal (f64 on
+ // i386 no-SSE, x86_fp80, fp128). The bitcast-to-int expansion below can't
+ // run since LegalizeDAG is the last legalization stage. Spill T/F, blend
+ // chunk-by-chunk at a legal int width, reload as the FP type. Fixed and
+ // scalable FP vectors fall through to the unified path below.
+ if (VT.isFloatingPoint() && !VT.isVector() &&
+ !TLI.isTypeLegal(VT.changeTypeToInteger())) {
+ const DataLayout &DL = DAG.getDataLayout();
+ Type *VTTy = VT.getTypeForEVT(*DAG.getContext());
+ unsigned StorageBytes = DL.getTypeStoreSize(VTTy);
+ assert(StorageBytes > 0 && "FP type with zero storage size");
+
+ // Pick the largest legal scalar integer chunk that divides StorageBytes
+ // evenly. i8 is the universal fallback (legal on every target).
----------------
wizardengineer wrote:
Reworked it in 64613ed31ef6 to blend at the widest legal integer instead of i8.
https://github.com/llvm/llvm-project/pull/166702
More information about the llvm-commits
mailing list