Repository navigation
t27o: a declaration index, a line cache and a bounded for-search make tri t27o linear in the file; compiler.rs finishes in 1.3 s (Closes #10544) - #10631
Merged
Conversation
This was referenced Oct 18, 2026
Open
gHashTag
enabled auto-merge (squash)
October 18, 2026 20:16
gHashTag
force-pushed
the
t27o/linear-lookups-10544
branch
from
October 18, 2026 20:17
62561ed to
77b82ad
Compare
Owner
Author
|
Review (orchestrator, Opus):
|
This was referenced Oct 18, 2026
This was referenced Oct 18, 2026
Contributor
…sh built once per conversion (Closes #10544) find_struct, const_type and fn_ret_type each walked the file token by token from byte 0 on every call, once per expression that needs a type: quadratic in the file. prescan now builds one index (index_decls) at the top of the state vector -- buckets first, entries below, the name table stops where it begins -- holding the first declaration of every struct X {, const|static X : and fn X(..). The three lookups read it (ix_get); the scans stay as *_scan, the answer when the file has more declarations than the vector has room for, and the check the index is built against. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng, an overflow and a spoiled-entry control (Closes #10544) lookups_agree / all_names_agree compare find_struct, const_type and fn_ret_type with their *_scan versions for every name a text spells. Six tests: the answers on a text with shadowing (first struct A, the free fn f, the impl's g without a return type), names that only look declared (unit and tuple structs, comments, texts, fn pointer types), a spoiled entry that the check catches, a file with more declarations than fit (index off, scans answer), the name table stopping at the index, and a conversion that reads the same with the index on and off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, method_owner, impl_around, struct_open and the declared_* checks read it too (Closes #10544) The profile after the first index (gates.rs): callee_class -> ret_class 27 %, let_owner 17 %, method_owner / method_kind (inlined into postfix) the rest -- each a walk of the file from byte 0 per call, the same kind of rescan. The index now stores, per name, the position of every keyword token that has the name next to it (struct enum type trait union const static; fn; typed lets; the impls in file order, with the end of each inherent block), and every lookup runs the check of its old walk on those tokens only: const_hit, fn_hit, struct_hit, item_named, let_at, kind_at, impl_owner_at. With the index off the same checks run over every token of the file, so the answer cannot differ. bootstrap/src/compiler.rs (1.9 MB), which did not finish in 9 minutes, now finishes in 16 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… looks for its for only before its brace (Closes #10544) After the index, the profile of compiler.rs was two more rescans: put_refusal / put_reason counted the newlines of the file from byte 0 for every refusal (line_of, twice a refusal, in each of up to 16 passes) and impl_item / impl_header searched for `for` with find_tok, which ran on to the next top-level `for` of the file once per impl. line_at keeps the last position and its newline count in the state (the slots a pass clears) and counts on from there; find_tok_to stops at a bound, and the two impl headers bound it at their `{`. compiler.rs 15.8 s -> 2.1 s, output byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ypes a name, index on and off (Closes #10544) method_owner picks the first inherent impl with a method taking self (P), or the receiver's own (Q); let_owner gives the last `let p` before the use (Q, not P); impl_around gives the type after `for`. all_names_agree runs the same text with the index on and off for every name and every method-name / receiver-type pair. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…10544) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag
force-pushed
the
t27o/linear-lookups-10544
branch
from
October 18, 2026 20:47
77b82ad to
079522e
Compare
Contributor
PR DashboardGenerated at: 2026-10-18 20:55:15 UTC
Summary
Seal Status
|
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.
Stacked on #10611 (branch t27o/check-defects-10534), which is itself on #10593: the diff below holds their commits until they land, then only the six commits of this lane. Lane P of the epic #10522.
What
tri t27ofound a struct, a const or a fn by walking the file token by token from byte 0, once per expression that needs a type, in each of up to 16 passes: quadratic in the file. This change builds one index per conversion and reads it.prescancallsindex_decls, which walks the file once and enters, per name, the position of every keyword token that has that name next to it, in file order:struct enum type trait union const static(also by the name aftermut),fn, the typedlets, and theimplblocks (with the end of each inherent block). The index lives at the top of the state vector (buckets first, entries below, four slots each); the name table stops where it begins (add_name). The gluecli/tri/src/main.rsis not touched: its 65536-slot vector holds the index of compiler.rs (about 4400 entries; with the index off that file would not have finished in seconds).find_struct,const_type,fn_ret_typeread it, so do the same-kind rescans the profile showed next:ret_class,let_owner,method_kind/method_owner,impl_around,struct_open,declared_fn,declared_type. Each runs the very check its old walk made (struct_hit,const_hit,fn_hit,let_at,kind_at,impl_owner_at,item_named, ...) on the entries of the name only, in file order, so the first match wins exactly as before (afn gin an impl that shadows the freefn g, the lastlet pbefore the use, the firstimplthat has the method).IXON0: a file with more declarations than the vector has room for, or a vector of fewer than 120 slots) the same checks run over every token of the file, as before. The index takes at most half of the free slots.put_refusal/put_reasoncounted newlines from byte 0 for every refusal (line_atkeeps the last position in the slots a pass clears), andimpl_item/impl_headersearched forforpast the impl's{to the next top-levelforof the file (find_tok_tostops at the brace).Only
specs/tools/t27o/rust_convert.t27, its generatedgen/rust/tools/t27o/rust_convert.rs(regenerated) and.trinity/seals/t27o_T27oRust.jsonchange.Profile (macOS
sample, 1 ms, top of stack, top 8)Before,
tri t27o cli/tri/src/skillnum.rson the base (#10611), 367 samples:tok_end147,fn_ret_type39,const_type37,op325,quoted_end22,op221,callee_class20,postfix16. Inclusive,const_type+fn_ret_type+find_structare 214 of 367 (58 %).After,
bootstrap/src/compiler.rs(skillnum.rs now ends in 0.02 s, too short to sample), 816 samples, 2.1 s:tok_end228,deps141,empty_arg61,named59,strloc_in44,op334,op233,block_end22. No declaration lookup is left in the top 8;deps,namedandstrloc_inscan the name table, which is linear in the names, not in the file.How the stages moved
bootstrap/src/compiler.rs(1.9 MB):let_owner,callee_class/ret_class,method_ownerinlined inpostfixline_offind_tokfromimpl_itemfind_tok_tocompiler.rsfinishes. Its count line:// t27o: 1766 fn seen, 109 converted (39 of 702 tests), 1657 refused (21 kept as stubs); coverage 6%.Timing (wall, same machine, base and new run back to back, best of two, load average 4.6 to 6.7)
The base is faster here than the figures in the issue (dlc10 5.9 s there, 0.59 s here), so the ratios, not the seconds, are what carries over.
Output is byte-identical
tri t27o <file>andtri t27o --oracle <file>on the base binary and on this one,cmpon stdout and the exit code (60 s cap, 240 s for the five files above 300 KB):.rsfiles (every one except compiler.rs): 962 comparisons, all identical, all exit 0, none timed out.--oraclemode (8 comparisons, the base finished each within 240 s).Tests (
t27b test: 101 of 101;t27c test-report: 101 pass, 0 fail, vacuous passes 0 of 101; seal verify: all hashes MATCH)the_declaration_index_answers_the_three_lookups_as_the_scans_do: a text with a shadowed struct, a shadowed fn (the impl'sghas no->, the freegdoes),static mut,const fn:all_names_agreeruns every lookup for every name of the text with the index on and off, and the answers are pinned (firststruct Ahasx, the freefreturnsu32).negative_control_names_that_only_look_declared_are_not_in_the_index(unit and tuple structs, a struct in a comment or a text,fn(u32) -> u32, a const asked for as a fn),negative_control_a_wrong_entry_makes_the_lookup_disagree_with_its_scan(point an entry into the middle of its keyword and the check fails, so it can fail).a_file_with_more_declarations_than_fit_leaves_the_index_off_and_the_scans_answer,the_name_table_stops_where_the_index_begins(300 names pushed at a table that stops at the index, the index stays whole),a_conversion_reads_the_same_with_the_index_on_and_off.the_index_names_the_impl_that_owns_a_method_and_the_last_let_that_types_a_name(method in two impls with and without a receiver type,let pshadowed, the type afterfor), plusline_at(agrees withline_ofgoing forward, back and past the end),find_tok_toand a negative control for each.Not done here
deps,named,strloc_inscan the name table per identifier (linear in the names; next in the profile after this change).find_enum/enum_variant(the oracle's sampling),slice_param_elemand threeline_ofcallers outside the refusal path still walk the file; none is in the profile of a conversion.Closes #10544
🤖 Generated with Claude Code