Skip to content

[CodeGen] Fall back to clearing more when the analysis is incomplete - #23

Merged
frabert merged 8 commits into
enforced_secrecy_mainfrom
zeroize-fallback-rebased
Sep 15, 2026
Merged

frabert merged 8 commits into
enforced_secrecy_mainfrom
zeroize-fallback-rebased

Conversation

@kumarak

@kumarak kumarak commented Sep 10, 2026

Copy link
Copy Markdown
Member

Count implicit machine-register operands when selecting registers for the used variants of zero-call-used-regs. This includes inline-assembly clobbers and helper-call arguments that the previous scan skipped. Preserve exit operands, callee-saved registers, and the target's return-address register, including under RISC-V ghccc, which has no callee-saved registers.

Treat empty, valueless, and unrecognized register-clearing modes as all; an absent attribute or explicit skip requests no clearing. Document these semantics in LangRef and test both successful clearing on X86 and the unsupported-target diagnostic on ARM.

Include otherwise unclassified terminal instructions in exit clearing, while retaining explicit exclusions for traps, non-returning calls, and non-local jumps. Look past fallback traps and non-returning calls for an earlier opaque assembly exit. Place clearing before the instruction that can transfer control, even when an x87 pop follows it. Record live x87 stack entries at inline assembly and clear only free slots so clearing does not overwrite live values.

The RISC-V floating-point expectations include these additional dead helper-call argument clears:

Function Configuration Additional clears
used RV32/RV64 +F fa0
used_arg_double RV32 +F a2, a3
used_arg_double RV64 +F a1

Validation

Local Release build with assertions enabled and X86, ARM, AArch64, and RISC-V targets:

  • All 37 zeroize* and zero-call-used-regs* test files in those four CodeGen directories pass, with no skipped tests. This includes the ARM fallback test, x87 opaque-exit cases, all RISC-V floating-point configurations, and the RV32/RV64 return-address regression.
  • The new opaque-assembly-plus-trap and opaque-assembly-plus-noreturn cases fail before the exit-scan fix and pass after it. The tests also check preservation of the fallback call's argument and exclusion of standalone trap/noreturn exits.
  • The x87 fallback test also passes with -verify-machineinstrs.
  • LLVM HTML and man documentation builds pass with warnings treated as errors. Changed C++ lines pass the LLVM clang-format check, and git diff --check is clean.

These are focused local results, not a full CodeGen-suite run.

Diagnostic policy and remaining checks

An in-scope exit where a requested clearing sequence cannot be placed produces an error. The defensive X86 fallback for unknown x87 stack depth retains its warning, per Akshay's review decision. The normal return, unwind-resume call, and inline-assembly exit paths provide the stack-depth information used by the clearer; the x87 tests exercise those reachable clearing behaviors rather than claiming direct coverage of the defensive warning.

The inherited premerge build jobs are restricted to repositories owned by llvm and are skipped in this fork. Local results above are not GitHub CI results. The CLA check remains pending for the existing claude contributor entry; this follow-up preserves published commit history and attribution.

History

Supersedes #12, which GitHub closed when #11 merged and its base branch was deleted. The initial changes were rebased onto enforced_secrecy_main; this PR also includes the x87 live-entry fix, return-address preservation, and review follow-ups. Earlier review discussion remains on #12.

Addresses trailofbits/vspells-ct-internal-notes#24 under trailofbits/vspells-ct-internal-notes#17.

AI tool use

This pull request contains AI-generated content and was prepared with assistance from Claude Code and Codex. See the LLVM AI Tool Use Policy.

claude and others added 2 commits September 10, 2026 15:24
Count implicit register operands when selecting registers for the used
zero-call-used-regs modes. This includes inline-assembly clobbers and
operands of target instructions and call pseudos that the previous scan
ignored.

Treat unrecognized zero-call-used-regs modes as all. Include otherwise
unclassified terminal instructions in exit clearing, while retaining the
explicit exclusions for traps, non-returning calls, and non-local jumps.
Diagnose an in-scope exit if a requested clearing sequence cannot be
placed there.

Add X86 and ARM tests for implicit register operands, unknown modes, and
opaque exits.

Counting implicit call operands also changes existing RISC-V output.
The RV32 and RV64 +F runs of zero-call-used-regs-fp.ll fail their unchanged
checks: used now clears fa0, and used_arg_double clears a2/a3 on RV32 or
a1 on RV64. These are dead helper-call arguments; return values and ra
remain intact. The four +D/+Q runs and the three adjacent scalar/vector
runs pass. A separate test commit updates the five affected check lines.
The parent passes all six original floating-point configurations. These
are focused local results, not a full CodeGen validation.

This is trailofbits/vspells-ct-internal-notes#24, under the umbrella
trailofbits/vspells-ct-internal-notes#17.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Update zero-call-used-regs-fp.ll for the implicit operands now counted by
the used-register scan. With +F and no +D, helper calls consume fa0 in
used and a2/a3 (RV32) or a1 (RV64) in used_arg_double. Expect those dead
argument registers to be cleared before returning.

All six floating-point configurations pass with the updated checks, as do
the two scalar configurations and the vector configuration. The new +F
checks fail on the parent implementation, confirming they detect the
additional clears. All invocations use -verify-machineinstrs.

The assembly comparison confirms that return-value registers and ra are
preserved. No production-code change is required for this test update.
The complete CodeGen suite was not rerun.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ frabert
❌ claude
You have signed the CLA already but the status is still pending? Let us recheck it.

Shorten the comment blocks that PR #23 added to PrologEpilogInserter.cpp
to a statement of each rule, drop the blank lines they introduced, and cut
the test commentary to one sentence per case. No RUN, CHECK or IR line
changes.
The X86 clearer fills the x87 stack with eight pushes and subtracts the
implicit ST0/ST1 uses X86FloatingPoint puts on a return. At an inline
asm exit a live input is an explicit operand and a value live across the
asm is no operand at all, so the count was eight, the stack overflowed,
and the input became NaN.

Have X86FloatingPoint record every entry live across an inline asm as an
ST use, and derive the depth at any exit from the ST uses on the exit
instruction: returned values on a return, nothing at a call, and the
recorded entries at an asm. Push zeros into the free slots only, so an
asm with an empty stack still gets the full clear.

X86FloatingPoint pops a dead asm input after the asm, and that pop was
being taken as the exit, putting the whole sequence after the asm. Skip
instructions that cannot transfer control when choosing the exit.
@kumarak
kumarak force-pushed the zeroize-fallback-rebased branch from c914d64 to f9a141a Compare September 11, 2026 16:28
// at a call. Push zeros into the free slots only; pushing over a live
// entry overflows the stack and turns the entry into NaN.
if (!MBBI->isReturn() && !MBBI->isCall() && !MBBI->isInlineAsm()) {
MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it as a warning seems fine. We will review the policy later if it should be converted to an error when x87 clearing cannot be performed.

kumarak and others added 4 commits September 11, 2026 15:28
Keep scanning past trap and noreturn fallbacks for an earlier opaque
assembly exit. Regression cases fail before this fix and pass afterward;
the fallback call argument and standalone exclusions remain intact.

Split ARM fallback inputs so successful cases cannot borrow an expected
failure from an unrelated function. Document and test empty, valueless,
and absent register-clearing modes, and remove the vacuous def/use check.
Rename the RISC-V return-address test and explain the ghccc trigger.

Retain the defensive x87 warning per Akshay's explicit review decision.
Do not rewrite published commits to change attribution or commit bodies.

Validation: assertions-enabled X86/ARM/AArch64/RISCV build; all 37 focused
zeroization tests pass; x87 test passes with the machine verifier; HTML
and man docs build with warnings as errors; clang-format and whitespace
checks pass. No repository prek configuration is present.

Addresses trailofbits/vspells-ct-internal-notes#24, under #17.
Assisted-by: Codex
Run the machine verifier through the checked-in RUN line so the test retains the validation documented in PR #23. The focused lit test passes.
@frabert
frabert merged commit 5638db2 into enforced_secrecy_main Sep 15, 2026
9 of 10 checks passed
frabert pushed a commit that referenced this pull request Sep 15, 2026
Shorten the comment blocks that PR #23 added to PrologEpilogInserter.cpp
to a statement of each rule, drop the blank lines they introduced, and cut
the test commentary to one sentence per case. No RUN, CHECK or IR line
changes.
frabert added a commit that referenced this pull request Sep 15, 2026
Keep scanning past trap and noreturn fallbacks for an earlier opaque
assembly exit. Regression cases fail before this fix and pass afterward;
the fallback call argument and standalone exclusions remain intact.

Split ARM fallback inputs so successful cases cannot borrow an expected
failure from an unrelated function. Document and test empty, valueless,
and absent register-clearing modes, and remove the vacuous def/use check.
Rename the RISC-V return-address test and explain the ghccc trigger.

Retain the defensive x87 warning per Akshay's explicit review decision.
Do not rewrite published commits to change attribution or commit bodies.

Validation: assertions-enabled X86/ARM/AArch64/RISCV build; all 37 focused
zeroization tests pass; x87 test passes with the machine verifier; HTML
and man docs build with warnings as errors; clang-format and whitespace
checks pass. No repository prek configuration is present.

Addresses trailofbits/vspells-ct-internal-notes#24, under #17.
Assisted-by: Codex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants