Skip to content

(feat): dominator analysis using forward dataflow#9871

Open
eytan-starkware wants to merge 1 commit intomainfrom
eytan_graphite/_feat_dominator_analysis_using_forward_dataflow
Open

(feat): dominator analysis using forward dataflow#9871
eytan-starkware wants to merge 1 commit intomainfrom
eytan_graphite/_feat_dominator_analysis_using_forward_dataflow

Conversation

@eytan-starkware
Copy link
Copy Markdown
Contributor

@eytan-starkware eytan-starkware commented Apr 23, 2026

Summary

Adds a block dominator analysis pass to the lowering IR. The analysis computes, for each block, the set of all blocks that dominate it (i.e., every path from the entry block to that block must pass through each dominator). The implementation uses the existing forward dataflow framework, with set intersection as the merge operation and self-insertion as the transfer function.

A Dominators struct is exposed with dominates(a, b) and dominators_of(block) query methods. File-based tests cover linear functions, if-else without a merge block, diamond CFGs, nested conditionals, and multi-arm matches.


Type of change

Please check one:

  • Bug fix (fixes incorrect behavior)
  • New feature
  • Performance improvement
  • Documentation change with concrete technical impact
  • Style, wording, formatting, or typo-only change

Why is this change needed?

Dominator information is a foundational building block for many compiler analyses and optimizations (e.g., loop detection, SSA construction, code motion). Without a dedicated dominator pass, downstream analyses must either recompute dominance ad hoc or forgo dominator-dependent optimizations entirely.


What was the behavior or documentation before?

No dominator analysis existed in the lowering analysis crate.


What is the behavior or documentation after?

Callers can run Dominators::analyze(&lowered) on a lowered function body and query whether one block dominates another, or retrieve the full dominator set for any block.


Related issue or discussion (if any)

N/A


Additional context

The implementation piggybacks on the existing ForwardDataflowAnalysis infrastructure. The merge step performs set intersection across predecessor dominator sets, and the transfer step adds the current block to its own dominator set, correctly encoding the standard dominator lattice.

@reviewable-StarkWare
Copy link
Copy Markdown

This change is Reviewable

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 23, 2026

PR Summary

Low Risk
Additive analysis code and tests only; no existing lowering or optimization behavior is modified, and current usage is confined to tests.

Overview
Introduces a new analysis::dominator pass that computes per-block dominator sets for lowered IR using the existing forward dataflow framework, and exposes a Dominators API for dominates(a, b) and dominators_of(block) queries.

Adds unit and file-based tests (new test_data/dominator) covering linear flows, branching with/without merge blocks, nested conditionals, and multi-arm matches, and wires the module/tests into analysis::mod.

Reviewed by Cursor Bugbot for commit d3a2d1c. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

@orizi made 2 comments.
Reviewable status: 0 of 4 files reviewed, 2 unresolved discussions (waiting on eytan-starkware and TomerStarkware).


crates/cairo-lang-lowering/src/analysis/dom.rs line 0 at r1 (raw file):
dominator - use full name.


crates/cairo-lang-lowering/src/analysis/dom.rs line 16 at r1 (raw file):

pub struct Dominators {
    /// dom_sets for a block_id = set of blocks that dominate block_id.
    dom_sets: Vec<Option<OrderedHashSet<BlockId>>>,

why Option?

Code quote:

    dom_sets: Vec<Option<OrderedHashSet<BlockId>>>,

@eytan-starkware eytan-starkware changed the base branch from eytan_graphite/_feat_def_site_analysis_based_on_forward_dataflow to graphite-base/9871 April 28, 2026 15:01
@eytan-starkware eytan-starkware force-pushed the eytan_graphite/_feat_dominator_analysis_using_forward_dataflow branch from 89d473e to d3a2d1c Compare April 30, 2026 13:20
Copy link
Copy Markdown
Contributor Author

@eytan-starkware eytan-starkware left a comment

Choose a reason for hiding this comment

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

@eytan-starkware made 2 comments.
Reviewable status: 0 of 4 files reviewed, 2 unresolved discussions (waiting on orizi and TomerStarkware).


crates/cairo-lang-lowering/src/analysis/dom.rs line 16 at r1 (raw file):

Previously, orizi wrote…

why Option?

No good enough reason. Changed.


crates/cairo-lang-lowering/src/analysis/dom.rs line at r1 (raw file):

Previously, orizi wrote…

dominator - use full name.

Done.

@eytan-starkware eytan-starkware changed the base branch from graphite-base/9871 to main April 30, 2026 13:20
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3a2d1c. Configure here.

/// Runs dominator analysis on a lowered function.
pub fn analyze(lowered: &Lowered<'_>) -> Self {
let mut fwd = ForwardDataflowAnalysis::new(lowered, DominatorAnalyzer);
let per_block = fwd.run().into_iter().map(|opt| opt.unwrap_or_default()).collect();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unreachable blocks get empty set, not {root}

Medium Severity

The doc comment on per_block states unreachable blocks default to {root}, but unwrap_or_default() produces an empty OrderedHashSet. This means dominates(BlockId::root(), unreachable_block) returns false, violating both the documented contract and the standard dominator invariant that the entry block dominates all blocks. Downstream analyses relying on this invariant will behave incorrectly for unreachable blocks.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d3a2d1c. Configure here.

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

@orizi partially reviewed 5 files and all commit messages, made 3 comments, and resolved 2 discussions.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on eytan-starkware and TomerStarkware).


crates/cairo-lang-lowering/src/analysis/dominator.rs line 70 at r2 (raw file):

}

#[cfg(test)]

move to test file.


crates/cairo-lang-lowering/src/analysis/dominator_test.rs line 46 at r2 (raw file):

                dom_list.sort();
                let dom_strs: Vec<String> = dom_list.iter().map(|d| format!("blk{d}")).collect();
                format!("blk{}: {{{}}}", block_id.0, dom_strs.join(", "))

Suggestion:

                let doms = dominators.dominators_of(block_id);
                let dom_strs = doms.iter().map(|d| d.0).sorted().map(|d| format!("blk{d}");
                format!("blk{}: {{{}}}", block_id.0, dom_strs.join(", "))

crates/cairo-lang-lowering/src/analysis/dominator_test.rs line 48 at r2 (raw file):

                format!("blk{}: {{{}}}", block_id.0, dom_strs.join(", "))
            })
            .collect::<Vec<_>>()

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