Skip to content

Allocate a precondition pvar only for blocks that need one#91

Merged
coord-e merged 1 commit into
mainfrom
claude/reduce-pvar-allocation-XFxwT
May 21, 2026
Merged

Allocate a precondition pvar only for blocks that need one#91
coord-e merged 1 commit into
mainfrom
claude/reduce-pvar-allocation-XFxwT

Conversation

@coord-e
Copy link
Copy Markdown
Owner

@coord-e coord-e commented May 19, 2026

  • 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 — a loop header always has in-degree ≥ 2.) The predicate is needs_own_precondition(bb).
  • refine_basic_blocks builds every block's layout eagerly. Blocks that need their own precondition get a pvar via for_template().build_basic_block(..) and are registered with register_basic_block_ty_with_precondition (has_precondition = true). The rest are built with a top precondition (no pvar — TypeBuilder::build_basic_block, guarded by a FakeRegistry that panics on any template/pvar registration) and registered with register_basic_block_ty_without_precondition (has_precondition = false).
  • At each type_goto(target): if the target needs its own precondition, behavior is unchanged (subtyping clauses against the target's pvar). Otherwise install_inherited_bb_ty derives the target's precondition from the current env state — PrecondCapture (the focused successor of the removed UnbindAtoms) records param == env-value equations and folds in the env's refined vars + assumptions — and calls register_basic_block_precondition, which fills the last param's refinement (set_precondition, remapping Free(last_param) → Value) and flips has_precondition = true. No subtyping clause is emitted on the edge.
  • analyze_basic_blocks iterates 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_precondition asserts the precondition was actually materialized, and register_basic_block_precondition asserts 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

@coord-e coord-e force-pushed the coord-e/drop-post-pvar-of-bb branch 5 times, most recently from f92e593 to 41af426 Compare May 20, 2026 16:43
@coord-e coord-e force-pushed the claude/reduce-pvar-allocation-XFxwT branch from 45c2ae5 to 4c2950a Compare May 20, 2026 17:08
@coord-e coord-e force-pushed the coord-e/drop-post-pvar-of-bb branch from 893e7d9 to 7980fbe Compare May 21, 2026 04:15
Base automatically changed from coord-e/drop-post-pvar-of-bb to main May 21, 2026 04:16
@coord-e coord-e force-pushed the claude/reduce-pvar-allocation-XFxwT branch 5 times, most recently from 19bf9f1 to 24b3481 Compare May 21, 2026 16:05
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>
@coord-e coord-e force-pushed the claude/reduce-pvar-allocation-XFxwT branch from 24b3481 to 3019027 Compare May 21, 2026 16:11
@coord-e coord-e changed the title Allocate BB pre pvar only at cutpoints Allocate a precondition pvar only for blocks that need one May 21, 2026
@coord-e coord-e force-pushed the claude/reduce-pvar-allocation-XFxwT branch from 3019027 to 24b3481 Compare May 21, 2026 16:15
@coord-e coord-e marked this pull request as ready for review May 21, 2026 16:16
@coord-e coord-e requested a review from Copilot May 21, 2026 16:16
@coord-e coord-e force-pushed the claude/reduce-pvar-allocation-XFxwT branch from 24b3481 to 3019027 Compare May 21, 2026 16:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_precondition to 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.

@coord-e
Copy link
Copy Markdown
Owner Author

coord-e commented May 21, 2026

it is left as TODO to remove bb0 pvar

@coord-e coord-e merged commit 2a5ce31 into main May 21, 2026
12 checks passed
@coord-e coord-e deleted the claude/reduce-pvar-allocation-XFxwT branch May 21, 2026 16:37
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.

3 participants