Skip to content

ConstraintAnalysis: Optimize local.get - #9136

Merged
kripken merged 39 commits into
WebAssembly:mainfrom
kripken:c.get
Sep 24, 2026
Merged

kripken merged 39 commits into
WebAssembly:mainfrom
kripken:c.get

Conversation

@kripken

@kripken kripken commented Sep 22, 2026

Copy link
Copy Markdown
Member

E.g.

(if
  (local.get $x)
  (then ..)
  (else
    ..(local.get $x)..   ;; this must be 0.
  )
)

This removes the "markRelevant" logic that tried to only track locals
that have interesting operations on them that we can optimize,
because now anything with a local.get might get optimized - which
is pretty much anything.

This does make the pass significantly slower, unfortunately, around
30% or so, and this was one of our slower passes. I'm looking into
speedups to land after this to mitigate that, but this does look useful
enough: this increases the amount of things we optimize by a
substantial amount. On one large Dart testcase I saw a total binary
size reduction of 0.3%. Overall this seems like the right tradeoff
for a pass that only runs in -Os/-O3+.

Some existing tests needed adjustment, as constants are now
propagated in cases that can be confusing.

@kripken
kripken requested a review from tlively September 22, 2026 20:15
@kripken
kripken requested a review from a team as a code owner September 22, 2026 20:15
Comment thread src/passes/ConstraintAnalysis.cpp Outdated
Comment on lines +392 to +401
auto oldType = (*currp)->type;
if (!Type::isSubType(rep->type, oldType)) {
// The value we know must exist here is impossible, which means it was
// cast in a way that traps at runtime. This code is unreachable.
rep = Builder(*getModule()).makeUnreachable();
refinalize = true;
} else if (rep->type != oldType) {
// We are refining.
refinalize = true;
}

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.

Should we move this logic into optimizeLocalGet so this loop can be responsible for nothing but performing the replacements? That will keep all the optimization logic together in one place.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good idea, done. This does mean passing around refinalize but it does seem nicer overall.

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.

Does ctor-eval run optimizations by default? Is that why there is a change here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, after we eval code in a function, we run default opts immediately as a followup. (To save the user needing to do the obvious wasm-opt call)

;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 84)

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.

Are we optimizing tuple-typed locals? If so, is that intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We don't optimize them directly (no parsing support for tuples), but if TupleOptimization broke such a local into normal ones, we can handle those.

Comment on lines +7089 to +7091
;; The condition here ends up comparing $x to itself. That is normally 1,
;; but not with a nan. We do not optimize floats for this reason (without
;; --fast-math, see constraint-analysis-float.wast).

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.

I guess we could still optimize in cases where we know the relevant values cannot be NaNs. Like if we had an f64.const 1 below, we would know that the LHS and RHS of the f64.eq cannot be NaN, so we could still optimize according to the normal math rules.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, there is definitely more we can do for floats here. I have some TODOs that I hope to get to.

;; A non-null value, like an internalized string, is not optimized (we could
;; emit an any.convert_extern of a strong.const, but it increases size, so
;; we leave this for passes like Precompute and GUFA).
;; TODO: should we optimize this?

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.

Probably; all the strings constants should be deduplicated during string lowering anyway.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hmm, good point. Anyhow, let's consider this later?

@tlively tlively 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 % last comment.

Comment thread src/passes/ConstraintAnalysis.cpp Outdated
Expression* optimizeLocalGet(Expression** currp,
const BasicBlockConstraintMap& constraints) {
const BasicBlockConstraintMap& constraints,
bool& refinalize) {

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.

This is a function-parallel pass, so we should be able to just track refinalize as a member of the pass class rather than passing it around.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point, done.

@kripken

kripken commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

Will only land this after landing some speedups first, to avoid a big slowdown in the middle.

…nt-locals-detection scheme - all we do is operate on all locals, except ones of the wrong types - we don't need to carefully check if we detected the right locals as relevant.
@kripken

kripken commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

Speedups landed. With them + this PR, things are faster than before!

@kripken
kripken merged commit b40790f into WebAssembly:main Sep 24, 2026
16 checks passed
@kripken
kripken deleted the c.get branch September 24, 2026 17:36
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