[X86] Lower llvm.zeroize to a late clearing sequence - #6
Draft
claude[bot] wants to merge 1 commit into
Draft
Conversation
|
|
Nothing expanded the intrinsic, so a function that used it did not build. What makes expanding it different from expanding a memset is that the write has to still be there at the end: the region is normally dead by the time the clear runs, which is exactly the shape a store-removing pass looks for. Emitting the stores at selection time and hoping they survive would rest on every later pass declining to remove them. Emit an opaque pseudo instead. ZEROIZE64 has no pattern and no memory operand, so no analysis can attribute a store to it and no pass can find a store to prove redundant, and it carries hasSideEffects and mayStore, so it is not dead either. X86ExpandPseudo turns it into "movb $0, %al; rep;stosb" in addPreSched2, after register allocation and so after every pass that deletes stores. Between selection and that point there is no store to remove, and past that point nothing is left that would remove one. Both selectors reach the pseudo through the hook each already has for an intrinsic needing custom handling rather than a generic call: LowerINTRINSIC_W_CHAIN for ISD::INTRINSIC_VOID, which X86 already marks Custom, and X86LegalizerInfo::legalizeIntrinsic, which until now accepted every intrinsic unchanged. Each copies the destination and the count into %rdi and %rcx and emits the pseudo, so the two produce the same machine code. The tests pair each clear with an ordinary store the backend does remove, one covered by a later store and one repeated, so what they pin is the difference rather than only that some instructions came out. A second test walks the three points that carry the guarantee: what each selector handed over, that it is still the pseudo once registers are allocated, and that the write exists only after the expansion. The clearing sequence is LP64 only. 32-bit x86 still reports the intrinsic as unlowered, and the sequences for other targets are separate work, on trailofbits/vspells-ct-internal-notes#11.
claude
Bot
force-pushed
the
zeroize-verifier
branch
from
August 11, 2026 19:57
2cc8417 to
53f69dc
Compare
claude
Bot
force-pushed
the
zeroize-lowering
branch
from
August 11, 2026 19:58
cf198e8 to
2812359
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Francesco Bertolaccini · Slack thread
Sixth patch in the stack, on top of #5. Closes trailofbits/vspells-ct-internal-notes#11.
llvm.zeroizehad no expansion, so a function using it did not build. This routes it through both instruction selectors to a clearing sequence on x86-64.What keeps the emitted sequence from being optimized away
Expanding the intrinsic into stores at selection time would make the guarantee depend on every later pass choosing not to remove them, and the region a clear covers is normally dead afterwards, which is exactly the shape a store-removing pass looks for.
So neither selector emits a store. Both emit
ZEROIZE64, an X86 pseudo with no pattern and no memory operand, carryinghasSideEffectsandmayStore. While it exists there is no store for anything to find: no analysis can attribute a write to it, so no pass can prove that write redundant, and the side-effect flag keeps it off the dead-code lists.X86ExpandPseudoturns it intomovb $0, %al; rep;stosbinaddPreSched2, after register allocation.That is the whole argument, and it is structural rather than a matter of pass ordering luck: before the expansion there is no store to remove, and after it nothing remains in the pipeline that removes stores.
Entry points
Both are the hook each selector already has for an intrinsic that needs custom handling rather than a generic call:
LowerINTRINSIC_W_CHAIN, reached fromLowerOperationforISD::INTRINSIC_VOID, which X86 already marksCustom.SelectionDAGBuilderbuilds that node for any intrinsic it does not handle itself.X86LegalizerInfo::legalizeIntrinsic, which until now accepted every intrinsic unchanged.Each copies the destination and the count into
%rdiand%rcxand emits the pseudo, so the two selectors produce the same machine code.Tests
llvm/test/CodeGen/X86/zeroize.llruns both selectors (-global-isel -global-isel-abort=1for the second) over dynamic, constant and narrow lengths, a clear followed by a store covering the same bytes, two identical clears, a clear of an alloca that never escapes and dies at the return, and a clear immediately beforelifetime.end. Each of the first two is paired with the equivalent plain store, which the backend does remove, so the test pins the difference rather than only that some instructions came out.llvm/test/CodeGen/X86/zeroize-expand-late.llpins the lateness directly, for both selectors: what each handed over is the pseudo, it is still the pseudo after register allocation (-stop-before=x86-expand-pseudo), and the write appears only in the expansion.Confirmed load-bearing by breaking each of the three pieces separately (SelectionDAG lowering, GlobalISel lowering, pseudo expansion) and checking the tests fail each time.
Scope
x86-64 (LP64) only. 32-bit x86 still reports the intrinsic as unlowered, as it did before this patch, and sequences for other targets are separate work.
AI tool use
This pull request contains AI-generated content. It was prepared with the assistance of Claude Code; the contributor has reviewed the generated code and text, is the author of the contribution, and is accountable for it, per the LLVM AI Tool Use Policy.
Generated by Claude Code