Allocate a precondition pvar only for blocks that need one#91
Merged
Conversation
f92e593 to
41af426
Compare
45c2ae5 to
4c2950a
Compare
893e7d9 to
7980fbe
Compare
19bf9f1 to
24b3481
Compare
A basic block needs a precondition of its own only if it is START_BLOCK or is reached by more than one CFG edge (multiple predecessors, or multiple edges from a single predecessor, e.g. SwitchInt arms that share a target). Every other block has a unique incoming edge, so its precondition is exactly the predecessor's outgoing env state and needs no fresh predicate variable. (This is also the set of CFG cutpoints, so it still cuts every cycle.) - needs_own_precondition(bb) decides this. refine_basic_blocks builds each block's layout eagerly: blocks that need their own precondition get a pvar (build_basic_block via for_template) and has_precondition = true; the rest are built with a top precondition (no pvar, guarded by a FakeRegistry) and has_precondition = false. - At type_goto(target): if the target needs its own precondition, emit the usual subtyping clauses against its pvar. Otherwise install_inherited_bb_ty fills the target's precondition from the current env state (PrecondCapture, the focused successor of the removed UnbindAtoms) via register_basic_block_precondition, which flips has_precondition = true. No subtyping clause is emitted on the edge. - analyze_basic_blocks visits blocks in reverse postorder so each inheriting block is analyzed after the predecessor that materialized its precondition; cleanup/unwind blocks are skipped. basic_block_ty_with_precondition asserts the precondition was materialized. This shrinks the CHC system on every function with straight-line block sequences. https://claude.ai/code/session_01WB28auaD8dSQrckqBwJWBt Co-Authored-By: coord_e <me@coord-e.com>
24b3481 to
3019027
Compare
3019027 to
24b3481
Compare
24b3481 to
3019027
Compare
There was a problem hiding this comment.
Pull request overview
This PR reduces the number of predicate variables introduced into the CHC system by only allocating a fresh basic-block precondition pvar for CFG cutpoints (START block, join points, and multi-edge targets). Blocks with a unique incoming edge instead inherit their precondition from the predecessor’s outgoing environment state, materialized lazily at goto time.
Changes:
- Add
needs_own_preconditionto identify blocks that must own a precondition pvar (CFG cutpoints). - Build/register BB types in two modes: with a precondition pvar vs. pvar-free “top” precondition, and lazily install inherited preconditions via
PrecondCapture. - Update analysis to require materialized preconditions (and iterate in reverse postorder) before analyzing each block.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/refine/template.rs |
Adds a pvar-free TypeBuilder::build_basic_block path and refactors BB building to optionally accept an explicit precondition refinement. |
src/refine/env.rs |
Exposes refined vars and assumptions (vars(), assumptions()) needed to capture env state into inherited preconditions. |
src/refine/basic_block.rs |
Adds BasicBlockType::set_precondition to overwrite the last param refinement when materializing inherited preconditions. |
src/analyze/local_def.rs |
Registers BB types with/without preconditions and analyzes blocks in reverse postorder while requiring a materialized precondition. |
src/analyze/basic_block.rs |
Implements needs_own_precondition, PrecondCapture, and lazy precondition installation on goto edges for inheriting blocks. |
src/analyze.rs |
Tracks whether a BB has a materialized precondition (BasicBlockDef) and supports one-time lazy precondition registration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Owner
Author
|
it is left as TODO to remove bb0 pvar |
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.
START_BLOCKor is reached by more than one CFG edge — multiple predecessors, or multiple edges from a single predecessor (e.g.SwitchIntarms that share a target). Every other block has a unique incoming edge, so its precondition is exactly the predecessor's outgoing env state and needs no fresh predicate variable. (This is also the set of CFG cutpoints, so it still cuts every cycle — a loop header always has in-degree ≥ 2.) The predicate isneeds_own_precondition(bb).refine_basic_blocksbuilds every block's layout eagerly. Blocks that need their own precondition get a pvar viafor_template().build_basic_block(..)and are registered withregister_basic_block_ty_with_precondition(has_precondition = true). The rest are built with a top precondition (no pvar —TypeBuilder::build_basic_block, guarded by aFakeRegistrythat panics on any template/pvar registration) and registered withregister_basic_block_ty_without_precondition(has_precondition = false).type_goto(target): if the target needs its own precondition, behavior is unchanged (subtyping clauses against the target's pvar). Otherwiseinstall_inherited_bb_tyderives the target's precondition from the current env state —PrecondCapture(the focused successor of the removedUnbindAtoms) recordsparam == env-valueequations and folds in the env's refined vars + assumptions — and callsregister_basic_block_precondition, which fills the last param's refinement (set_precondition, remappingFree(last_param) → Value) and flipshas_precondition = true. No subtyping clause is emitted on the edge.analyze_basic_blocksiterates in reverse postorder so each inheriting block is analyzed after the predecessor that materialized its precondition. Cleanup/unwind blocks are skipped (data.is_cleanup).basic_block_ty_with_preconditionasserts the precondition was actually materialized, andregister_basic_block_preconditionasserts it happens exactly once.Net result: fewer predicate variables in the CHC system on every function with straight-line block sequences, with no change to what is verified.
https://claude.ai/code/session_01WB28auaD8dSQrckqBwJWBt
Generated by Claude Code