Skip to content

feat(verifier): check dominance#944

Open
regehr wants to merge 13 commits into
mainfrom
regehr/dominance-checker
Open

feat(verifier): check dominance#944
regehr wants to merge 13 commits into
mainfrom
regehr/dominance-checker

Conversation

@regehr

@regehr regehr commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

ok this is a substantial tooling up of our verifier. the patch got bigger and messier than I wanted, but I think it's all good changes.
the main thing this does is attempts to make our verifier return the same verdict as MLIR's verifier, for a variety of interesting cases. so there are lots of new tests! and also there's a new test predicate VEIR_MLIR_SAME_VERDICT that insists that either mlir-opt and veir-opt both accept or both reject a test case.
I had to change a bunch of unrelated test cases-- these are ones that are rejected by upstream MLIR, and now are rejected by Veir.
the other goal of this patch is to hopefully set us up to de-axiomitize Veir/Dominance.lean. our goal is that this verifier acts as a dynamic checker for freshly parsed code, and after that all transformations must prove that their output is verified if their input is.

@regehr regehr self-assigned this Jun 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

VeIR Benchmarks

Details
Benchmark suite Current: 4ee9633 Previous: a8086a5 Ratio
add-fold-worklist/create 1833000 ns (± 104438) 2191000 ns (± 116585) 0.84
add-fold-worklist/rewrite 3598000 ns (± 112494) 4025500 ns (± 99281) 0.89
add-fold-worklist-local/create 1832000 ns (± 35506) 2104000 ns (± 59879) 0.87
add-fold-worklist-local/rewrite 2914000 ns (± 68372) 3305000 ns (± 90508) 0.88
add-zero-worklist/create 1827000 ns (± 40016) 2234000 ns (± 109081) 0.82
add-zero-worklist/rewrite 2290000 ns (± 22289) 2750000 ns (± 105299) 0.83
add-zero-reuse-worklist/create 1504000 ns (± 5495) 1842000 ns (± 83972) 0.82
add-zero-reuse-worklist/rewrite 1843000 ns (± 54784) 2160000 ns (± 52555) 0.85
mul-two-worklist/create 1808000 ns (± 50525) 2231000 ns (± 55895) 0.81
mul-two-worklist/rewrite 5052000 ns (± 70031) 5644500 ns (± 283151) 0.90
add-fold-forwards/create 1869000 ns (± 72871) 2202000 ns (± 109662) 0.85
add-fold-forwards/rewrite 2664000 ns (± 39480) 3032000 ns (± 65213) 0.88
add-zero-forwards/create 1819000 ns (± 21490) 2265000 ns (± 96205) 0.80
add-zero-forwards/rewrite 1754000 ns (± 17641) 1987500 ns (± 93854) 0.88
add-zero-reuse-forwards/create 1536500 ns (± 66860) 1832000 ns (± 89057) 0.84
add-zero-reuse-forwards/rewrite 1425000 ns (± 71827) 1542000 ns (± 22638) 0.92
mul-two-forwards/create 1849000 ns (± 62588) 2198000 ns (± 41455) 0.84
mul-two-forwards/rewrite 3249000 ns (± 35550) 3767000 ns (± 172147) 0.86
add-zero-reuse-first/create 1536500 ns (± 41607) 1855500 ns (± 114257) 0.83
add-zero-reuse-first/rewrite 9000 ns (± 1965) 8500 ns (± 2543) 1.06
add-zero-lots-of-reuse-first/create 1534000 ns (± 25225) 1789000 ns (± 89329) 0.86
add-zero-lots-of-reuse-first/rewrite 759000 ns (± 11803) 803000 ns (± 57817) 0.95

This comment was automatically generated by workflow using github-action-benchmark.

@axelcool1234 axelcool1234 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 first skim of this PR. Will give another review round when I'm not busy :)

Comment thread Veir/IR/Dominance.lean
Comment thread Veir/IR/Dominance.lean
@axelcool1234

Copy link
Copy Markdown
Collaborator

For the dominance analysis changes, LGTM :)

@math-fehr math-fehr 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.

Very cool! I added a few comments, but otherwise the design seems great!
Quick question, it seems you are saying graph regions can sometimes have multiple blocks (like in a unregistered operation). Is this something from MLIR?

Also, for next time, it would probably have been easier for me if some parts of the PR (like the additions of verifiers about terminators, not necessarily dominance) would have been split. But no need for this time since I reviewed it!

@@ -0,0 +1,24 @@
// RUN: not veir-opt %s 2>&1 | filecheck %s
// RUN: VEIR_MLIR_SAME_VERDICT

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.

Gavin added MLIR_INVALID, maybe we should use that instead of adding VEIR_MLIR_SAME_VERDICT?

Comment on lines +12 to +17
"test.test"() ({
^g0():
%a = "test.test"() : () -> i64
^g1():
"test.test"(%a, %v) : (i64, i64) -> ()
}) : () -> ()

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 do not understand why this test actually passes?
You mention in the comment that test.test is a graph region, but in that case graph regions only have a single block!
In general, any block that is part of a multi-block region should have a terminator

Comment on lines +4 to +5
// Dominance is only required in SSACFG regions. The body of an unregistered op
// is a graph region, where a value defined in one block may be used in another

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.

It is not actually true that the body of an unregistered op is a graph region.
Because graph regions have only a single block, this means that whenever any operation has a region with multiple blocks, the region needs to be a SSACFG region.

Comment on lines +55 to +57
-- In a graph region every dominator (including the block itself) properly
-- dominates the block; in an SSACFG region a block does not properly dominate
-- itself.

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.

Where does this rule come from?
My understanding is that "properly dominates" means dominates and not equal?

Comment thread Veir/IR/Dominance.lean
Comment on lines -41 to +62
true
else
let some idom := block.getIDom? dfCtx irCtx | return false
idom ≠ block && dominatesWithinRegion dominator idom dfCtx irCtx
return true
-- In a graph region there is no block ordering, so every block dominates every
-- other block in the region.
if let some region := (block.get! irCtx).parent then
if !region.hasSSADominanceByKind irCtx then
return true
let some idom := block.getIDom? dfCtx irCtx | return false
return idom ≠ block && dominatesWithinRegion dominator idom dfCtx irCtx

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.

There can only be a single block in a graph region, so this function should probably be unchanged?

Comment thread Veir/IR/Dominance.lean
Comment on lines +156 to +161
match dominator.block! irCtx with
| some block =>
match (block.get! irCtx).parent with
| some region => !region.hasSSADominanceByKind irCtx
| none => false
| none => false

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.

Should this code be factored somewhere? You also use the exact same one earlier

Comment thread Veir/Verifier.lean
Comment on lines +968 to +969
if op.getNumSuccessors ctx.raw opIn ≠ 0 && operation.next.isSome then
throw "operation with block successors must terminate its parent block"

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.

Only terminators can have successors. So maybe there is a stronger check to be made somewhere about this?

Comment thread Veir/Verifier.lean
| none => pure ()
| some useRegion =>
let instrName := String.fromUTF8! (op.getOpType ctx.raw opIn).name
for i in [0:op.getNumOperands ctx.raw opIn] do

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.

Suggested change
for i in [0:op.getNumOperands ctx.raw opIn] do
for i in 0...op.getNumOperands ctx.raw opIn do

I believe this is the standard notation for ranges (unless it is ..).
This avoids allocating a list

Comment thread Veir/Verifier.lean
Comment on lines +1134 to +1138
-- A consequence, shared with MLIR, is that a use hidden in a *non-entry* block
-- of a graph region with no intra-region control-flow edges is unreachable and
-- therefore not checked, even if it captures a value that does not dominate the
-- graph-owning operation. (A capture used in the graph region's *entry* block
-- is still checked, because the entry block is always reachable.)

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 would argue that if an operation is unreachable, then its operands are dominated by the value definitions. This is because dominance is defined as "all path that goes to A goes first to B", but here since there is no path, it is trivially correct!

This has the same effect (that we don't need to check unreachable blocks), but the reasoning is different (and will be reflected in the proof).

Comment thread Veir/Verifier.lean
Comment on lines +1184 to +1187
let some dfCtx := fixpointSolve top #[DominanceAnalysis] ctx.raw
| throw "dominance analysis did not terminate"
ctx.raw.forOpsDepM fun op opIn =>
op.verifyOperandDominance ctx dfCtx opIn

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.

This will actually break if we have any operation that wasn't deallocated, nor inserted in the module.
We can keep it as-is here, but we'll have to fix it at some point (hopefully after I add walkers to walk the IR a bit more nicely).

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