Conversation
|
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.
| 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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: ) |
There was a problem hiding this comment.
Please also comment in the function below about the changes here.
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:$make's result is refined to(ref (exact $A)), but$usekept(ref.cast (ref (exact $A)) (call $make)).