[llvm] [ConstantTime][LLVM] Add llvm.ct.select intrinsic with generic SelectionDAG lowering (PR #166702)
Akshay K via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 12 07:47:41 PST 2026
kumarak wrote:
> If metadata is allowed to be thrown away on whim, then a rigorous way to implement constant-time marking with metadata is if the metadata has the opposite sense – it represents _not_ being secret. Almost everything in every LLVM IR program would acquire a metadata attribute saying "it's ok, this doesn't need to be constant-time", except the insides of cryptography kernels. Then dropping the attribute would fail safe, and lead to only a performance problem
@statham-arm, this could be an interesting fail-safe idea: if it fails to propagate, much of the code would revert to safe code generation. My concern is that it might complicate optimization reasoning and how well it aligns with LLVM’s design.
> If that means what I understood it does, that addresses my concern and I need to go back to studying both LLVM and the PR. However, I’m not yet sure it does. One arbitrary example I might misinterpret: `visitSELECT()`, the `DAGCombiner` for non-ct select, calls `foldBoolSelectToLogic()`, which may fold `select` into (non-short-circuiting) boolean logic. From what I can tell, this is perfectly ct-safe. `visitConstantTimeSelect()` (the ct counterpart, which at the very least is a restricted duplicate of the non-ct optimization policy) on the other hand does not apply this. I suspect this is not done because the boolean instructions cannot be marked secret and we are not yet at the expansion point? Would a ct-compliant variant under this intrinsic model not require a duplication (or abstraction) of the optimization to use secret boolean intrinsics? Wouldn’t this apply to most optimizations that for any reason substitute opcodes?
@mhaeuser, regarding your specific question about disabling the `boolean` fold logic in DAGCombine: my concern was mainly around freeze and how it gets lowered on different targets. Beyond that, we have also disabled certain select simplifications such as constant folding, load merging, nested ctselect rewrites, etc. This was a cautious choice. The goal is to avoid losing the `ctselect` node before it reaches target lowering, or combining it in ways that could compromise the constant-time intent.
These restrictions apply primarily at the SelectionDAG level. Once the node reaches the target-lowering stage, the usual target-specific optimizations run as-is (particularly on x86, ARM, and AArch64, which we’ve implemented support for). Also, these broader changes are not part of the current PR; they are being introduced in follow-up PRs.
> “Expansion point” is exactly my point here. I know that there at least is some instruction relaxation going on in the x86 backend after said expansion point. I suppose it is not too hard to argue that these relaxations are safe with the opcode tables. However, I am not sure there wasn’t more that could theoretically harm DIT/DOIT-compatible selection.
The target-specific expansion is not part of the current PR. We perform the expansion much later in the pipeline, after register allocation, when resolving all pseudo-instructions. There can still be transformations after that stage that might affect DIT/DOIT-compatible lowering, so we use instruction bundling to ensure that passes running after expansion do not look through or alter the expanded sequence.
https://github.com/llvm/llvm-project/pull/166702
More information about the llvm-commits
mailing list