Skip to content

[DAE] Optimize callers after refining return types - #9165

Open
vouillon wants to merge 3 commits into
WebAssembly:mainfrom
vouillon:dae-opt
Open

vouillon wants to merge 3 commits into
WebAssembly:mainfrom
vouillon:dae-opt

Conversation

@vouillon

@vouillon vouillon commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

When DAE refines the result type of a function, it updates the types of the calls and refinalizes, but with dae-optimizing it only re-optimizes the functions in worthOptimizing, which does not include the callers. Optimizations opportunities in the callers, such as casts that the refined type makes statically known to succeed are therefore missed. This especially matters since dae-optimising runs among the final global passes of -O2.

For example, with --dae-optimizing:

  (module
    (type $A (struct))
    (func $make (result (ref eq))
      (struct.new $A))
    (func $use (export "use") (result (ref eq))
      (ref.cast (ref $A) (call $make))))

$make's result is refined to (ref (exact $A)), but $use kept (ref.cast (ref (exact $A)) (call $make)).

@vouillon
vouillon requested a review from a team as a code owner September 28, 2026 17:20
@vouillon
vouillon requested review from stevenfontanella and removed request for a team September 28, 2026 17:20
@kripken

kripken commented Sep 28, 2026

Copy link
Copy Markdown
Member

These changes both make sense, but please split them into separate PRs. We prefer smaller PRs when possible, which is better for later bisection if anything goes wrong.

When DAE refines the result type of a function, it updates the types of
the calls and refinalizes, but with dae-optimizing it only re-optimizes
the functions in `worthOptimizing`, which does not include the callers.
Casts in the callers that the refined type makes statically known to
succeed are therefore left in place. This matters in particular at the
end of -O2, where dae-optimizing runs among the final global passes and
no function pass runs after it (unless inlining-optimizing does).

For example, with `--dae-optimizing` (or `-O2
--skip-pass=inlining-optimizing`):

  (module
    (type $A (struct))
    (func $make (result (ref eq))
      (struct.new $A))
    (func $use (export "use") (result (ref eq))
      (ref.cast (ref $A) (call $make))))

`$make`'s result is refined to `(ref (exact $A))`, but `$use` kept
`(ref.cast (ref (exact $A)) (call $make))`.

Collect the callers of the functions whose return types were refined in
a separate set, and optimize them together with `worthOptimizing` at the
end of the iteration, after the ReFinalize. They are not added to
`worthOptimizing` itself, as that set also decides whether return values
can be removed in this iteration and whether to iterate again.
@vouillon vouillon changed the title [DAE] Small improvements [DAE] Optimize callers after refining return types Sep 28, 2026
if (refinedReturnTypes) {
// Changing a call expression's return type can propagate out to its
// parents, and so we must refinalize.
// TODO: We could track in which functions we actually make changes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems like this TODO is now almost done? refinedCallers is the set of functions we should operate on here, rather than the entire module.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If this isn't trivial to do for some reason, please only update the command to say TODO: use refinedCallers here, which is the set of the functions we need to update

;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK: (func $traps (type $1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a comment here explaining how we optimized away the return value.

;; CHECK-NEXT: )
;; CHECK-NEXT: (i31.get_s
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please also comment in the function below about the changes here.

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm otherwise

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.

3 participants