Skip to content

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

Open
kumarak wants to merge 5 commits into
enforced_secrecy_mainfrom
zeroize-fallback-rebased
Open

[CodeGen] Fall back to clearing more when the analysis is incomplete#23
kumarak wants to merge 5 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. Return operands and the target's callee-saved register list still exclude registers that must remain intact.

The change also treats unknown register-clearing modes as all, includes otherwise unclassified terminal instructions in exit clearing, and diagnoses an in-scope exit when a requested clearing sequence cannot be placed. Explicit exclusions for traps, non-returning calls, and non-local jumps remain.

X86 and ARM tests cover implicit operands, unknown modes, and opaque exits. The existing RISC-V floating-point test gains five check lines for the intentional change to implicit helper-call arguments:

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

These registers are dead at return. The return-value registers and ra remain intact, and no production-code change was needed for these expectations.

Validation

Local build with assertions enabled and X86, ARM and AArch64 as targets, on the rebased branch:

  • The CodeGen/X86, CodeGen/ARM, CodeGen/AArch64, CodeGen/Generic and CodeGen/MIR suites pass (12038 tests; the single failure was an untracked local file that is not part of this branch).
  • The four new test files and every zeroize* and zero-call-used-regs* test under CodeGen/X86, CodeGen/ARM and CodeGen/AArch64 pass.

RISC-V is not in that build. The RISC-V expectations were validated on a RISC-V build with -verify-machineinstrs when #12 was prepared:

  • zero-call-used-regs-fp.ll: all six RV32/RV64 +F/+D/+Q configurations pass with the updated expectations. The new +F checks fail on the parent commit, confirming that they detect the added clears.
  • zero-call-used-regs.ll (RV32, RV64) and zero-call-used-regs-v.ll (RV64 +V) pass.

History

Supersedes #12, which GitHub closed automatically when #11 merged and deleted its base branch, and which it would not reopen once the branch had been rebased. The two commits are rebased onto enforced_secrecy_main; the diff is byte-identical to #12 and authorship is unchanged. Review discussion is 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. 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.

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.

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