Skip to content

docs(rewrite): design the M3 analysis manager - #857

Draft
alexcgatea wants to merge 2 commits into
alex/queue-based-fixpoint-driverfrom
alex/analysis-manager
Draft

alexcgatea wants to merge 2 commits into
alex/queue-based-fixpoint-driverfrom
alex/analysis-manager

Conversation

@alexcgatea

Copy link
Copy Markdown
Collaborator

Draft: design doc only, no code yet.

What

docs/design/analysis-manager.md designs milestone M3 of rewrite-engine.md: how rewrite passes get analysis results (constprop, demand), how those results are cached, and when they are dropped.

Key decisions

  • AnalysisManager computes and caches; PassRunner runs passes. The runner owns the pipeline and the manager, so no edit goes unseen. This follows MLIR's split between the analysis manager and the pass manager.
  • Analyses run before a pass, not inside it. Interpreter analyses need the whole pipeline, which a pass doesn't have while its stage is out of the pipeline.
  • Rules declare what they read (.requires::<A>()). A registry, as in LLVM's new pass manager, lets the runner compute those analyses before the pass.
  • Any edit drops every cached result, as rewrite-engine.md requires. A pass with no edits keeps everything.
  • Facts are a snapshot for the whole pass, even after its first edit. Using them correctly is the rule author's job, as in MLIR.
  • The caller gives the roots, and results are read by key at any statement the analysis reached, following the interpreter's query-shaped API.

Interpreter change, for review

The last section, "Interpreter changes (for review)", proposes that analyze return an owned Analyzed instead of wrapping the engine. Today the result borrows the pipeline, so the manager can't keep it across passes. With owned results, one result type serves every analysis, and the three per-engine AnalysisResults impls become one. @zhenrongliew, could you review this part in particular?

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pub trait Analysis<S>: AnalysisKey {
/// What the caller chooses when registering the analysis, such as the
/// roots to analyze from and their arguments.
type Input: 'static;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

'static lifetime looks a bit strange to me, why we are requiring 'static?

@Roger-luo Roger-luo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

my main concern is whether Manager objects actually duplicates with a potential "database" storage in a query-based architecture

Comment thread docs/design/analysis-manager.md Resolved

## Goals

1. A rule can say which analyses it needs, and the manager computes them

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is where a query could enters, a rule/pass delcaration could have a corresponding query to some information, which declares its dependency. You can think a rewrite rule is a pure function f(analysis data, IR) -> IR, where analysis data also comes from IR, which will allow you to compute the dependency of

IR changes -> rewrite rule
|                    ^
|---> anaysis change |

### Example

```rust
let mut analyses = AnalysisManager::new();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the MLIR's manager object is essentially salsa's storage, I'm ok doing a first pass based on MLIR, but eventually we need to think about how does salsa play with this infrastructure instead of just reinventing MLIR in rust...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As in MLIR, where a pattern receives analysis results in its constructor, a
native rule receives them in its **recipe**: the function the rule set calls
to build a fresh rule for each sweep (or each block). The rule keeps the
`Rc`s it needs. A rule also declares what it requires:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

do we actually need Rc? and should it be an Arc?

This branch has not been deployed

No deployments
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.

2 participants