feat(ssa): remove range checks and unsigned lt comparisons implied by a dominating bound - #13266
Draft
jeswr wants to merge 3 commits into
Draft
feat(ssa): remove range checks and unsigned lt comparisons implied by a dominating bound#13266jeswr wants to merge 3 commits into
jeswr wants to merge 3 commits into
Conversation
Contributor
|
Thank you for your contribution to the Noir language. Please do not force push to this branch after the Noir team have started review of this PR. Doing so will only delay us merging your PR as we will need to start the review process from scratch. Thanks for your understanding. |
added 3 commits
July 10, 2026 04:28
… a dominating bound Addresses noir-lang#9463
…narrowing-cast validation contract); add execution fixture
jeswr
force-pushed
the
feat/remove-redundant-range-checks
branch
from
July 10, 2026 04:30
4fd3ade to
cebd23f
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.
Description
This change was developed with support from generative AI; I am reviewing it as the PR author before requesting maintainer review.
Summary
Adds a
remove_redundant_range_checksSSA pass (issue #9463): it tracks, per value, the smallest exclusive upper bound proven by a dominating fact — arange_check(value < 2^N), aconstrain (lt value, c) == u1 1(value < c), or an unsignedmodby a non-zero constant (result < c, recorded only under a constant-true side-effects condition since ACIR-gen predicates the division) — and removesrange_checks and unsignedlt-against-constant comparisons that a dominating fact already implies. Onlyrange_check-derived facts may elide anotherrange_check, so the narrowing-cast validation rule keeps a visible justification in the SSA. Runs right afterremove_truncate_after_range_check, reusing its dominance/map-clearing discipline.Measured (
bb 5.0.0-nightly.20260522,bb gates -s ultra_honk; ACIR opcodes vianargo info --force):circuit_sizevector_dynamic_index(existing test)redundant_range_check_elision(new fixture)Across the full
execution_successsuite + benchmarks (555 programs): 3 improve in ACIR (vector_dynamic_index−117,lambda_from_array−8,bit_shifts_runtime−5), 3 improve in Brillig opcodes (uhashmap−18,brillig_nested_arrays−9,array_sort−3), none regress. Caveat: an external 39-program corpus of real ZK circuits (fixed-point/IEEE-754/XPath kernels) is unchanged — the win concentrates in code with repeated dynamic-index bounds checks on the same value, where the first check dominates and implies the rest.Fact sources, guard conditions and measurement notes
Fact sources (each with an SSA-level test)
range_check value to N bitsprovesvalue < 2^N;constrain (lt value, c) == u1 1provesvalue < c. Both are enforced unconditionally at ACIR generation (flattening bakes the predicate into their operands), so their bounds hold regardless of the side-effects condition.result = mod value, c(unsigned, constantc != 0) provesresult < c, but ACIR generation predicates the euclidean division — under a false predicate the remainder is unconstrained — so this bound is only recorded while the side-effects condition is the constanttrue(tested non-firing under a non-constant predicate).Guard conditions
remove_truncate_after_range_check; a fact only elides an instruction it dominates (tested non-firing across non-dominated blocks).ltandrange_check).range_checkelision is restricted torange_check-derived facts: the narrowing-cast validation rule justifies casts by the range checks that remain in the SSA, so a removed check's justification must stay visible as arange_checkon the same value; anlt-constraint-derived bound must not elide arange_check(tested non-firing).range_checkcan itself be the instruction that establishes a value's type invariant, so bounds are never derived from types; a fact is learned only when its enforcing instruction is visited, so it can never elide that instruction itself. (Checks implied by a value's statically-known bit width are already removed on insertion viaget_value_max_num_bits.)ltorders by signed value, not the representation the bound map tracks, so it is neither learned from nor elided (tested non-firing).Measurement notes
vector_dynamic_indexrepeatedly indexes the same vector with the same witness indices, so each access's bounds comparison after the first is implied by a dominating one; the backend win is real (−275 UltraHonk gates) because the removed comparisons carried gate cost beyond the shared range-table floor.execution_successprograms + the 9test_programs/benchmarksprograms (sha512_100_bytes13173 → 13173,semaphore_depth_105699 → 5699,bench_eddsa_poseidon4147 → 4147,bench_poseidon2_hash_100202 → 202), plus an external corpus of 31 real ZK circuit binaries and 8 library-kernel probes (IEEE-754 double arithmetic, XPath string/numeric kernels, 34–22313 opcodes): all unchanged apart from the 6 programs listed above.circuit_sizebarely moves because its removed checks ride on already-present range tables; its win is ACIR opcodes and witness work.Validation
ltconstraint removal (keeping the enforcing one), impliedltacross a dominated block, tighter-bound non-firing, non-dominating-constraint non-firing, signed non-firing,range_check-implied-by-range_checkremoval,lt-constraint-must-not-elide-range_check,mod-derivedltremoval,modunder a non-constant predicate non-firing, constraint-derived facts under a non-constant predicate.cargo nextest run -p noirc_evaluator: 1841 passed (the pre-existingssa::interpreter::tests::infinite_recursionSIGABRT reproduces identically on unpatched master on this machine, so it is environmental).execution_success/redundant_range_check_elisionfixture: all 16 generated harness variants pass (ACIR +--force-brillig× inliner settings, minimal, interpreter, comptime, expand); it keeps a semantically-active reversed-direction control assert.cargo fmt/cargo clippyclean; no existing SSA snapshots changed.Documentation
cc @jeswr for author review.