Skip to content

TCB: AArch64 CLZ and RBIT - #2713

Merged
alex merged 1 commit into
mainfrom
claude/a64-clz-rbit
Oct 18, 2026
Merged

alex merged 1 commit into
mainfrom
claude/a64-clz-rbit

Conversation

@alex

@alex alex commented Oct 18, 2026

Copy link
Copy Markdown
Member

Part of #991.

This is a trust change (TCB/AArch64/Isa.lean, TCB/AArch64/Print.lean). It adds two instructions to the AArch64 model and nothing else in it; no Spec/, Impl/ or artifact changes.

The instructions

  • clz d, n (CLZ, 32- and 64-bit): the number of zero bits above the highest one bit of n, the register's width if n is zero. DDI 0487 C6.2, CLZ: result = CountLeadingZeroBits(X[n, datasize]); X[d, datasize] = result<datasize-1:0>, with CountLeadingZeroBits(x) = N - (HighestSetBit(x) + 1) (shared pseudocode). Modelled with core's BitVec.clz.
  • rbit d, n (RBIT, 32- and 64-bit): the bits of n in reverse order. DDI 0487 C6.2, RBIT: for i = 0 to datasize-1: result<datasize-1-i> = operand<i>. Modelled with core's BitVec.reverse.

As for every other modelled W-form instruction, the 32-bit forms read the low half and zero the upper half (State.read/State.write).

Requirements: both are baseline A64 (ARMv8.0-A, no FEAT_* condition in their decode), so Instr.requires gives [] for them, and the test checks that.

Timing: Arm lists CLZ and RBIT among the data-independent-time instructions (DDI 0487, "About PSTATE.DIT"). The model's note listing the data-processing instructions with that property now names them, so the rule against operand-dependent timing is kept.

Why

Together they count trailing zeros (RBIT then CLZ). The next PR uses that for a variable-time inverse of the public scalar s in AArch64 P-256 verification. Constant-time paths stay for anything secret.

Framework and tests

  • Proof/Framework/AArch64: Inline.dstOf, TaintMono.gprDst and the taint step (Taint.stepFn and its soundness cases) handle them. Their result is as public as their operand.
  • VerifiedGarbageTest/AArch64Bits.lean: CLZ of zero, one, a middle bit and the top bit; the W forms, including that they ignore and clear the upper half; RBIT on both widths; RBIT then CLZ as a trailing-zero count; printing; empty requirements; taint. The RBIT values use decide +kernel, because elaboration-time decide exceeds the recursion limit on the 64-bit reversal.

lake build, Emit.lean --check (its audits accept BitVec.clz and BitVec.reverse) and the ci/check_*.py scripts pass. No generated code changes.


Generated by Claude Code

CLZ counts the zero bits above the highest one bit of a register (the
register's width if it is zero), and RBIT reverses its bits (DDI 0487 C6.2,
CLZ and RBIT, with CountLeadingZeroBits from the shared pseudocode), in both
the 32- and 64-bit forms. They are baseline A64, so they require no CPU
feature, and Arm lists both among the data-independent-time instructions
(DDI 0487, About PSTATE.DIT). The model uses core's BitVec.clz and
BitVec.reverse.

Together they count trailing zeros (RBIT then CLZ), which a variable-time
inverse of a public scalar needs.

Framework: Inline.dstOf, TaintMono.gprDst and the taint step (Taint.stepFn,
with its soundness cases) handle them. VerifiedGarbageTest.AArch64Bits
checks their semantics, printing, requirements and taint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ma2fA9rUTPXEEQjYyCMCRw
@alex
alex enabled auto-merge October 18, 2026 01:39
@alex
alex added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 5215640 Oct 18, 2026
18 checks passed
@alex
alex deleted the claude/a64-clz-rbit branch October 18, 2026 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants