Skip to content

ConstraintAnalysis: Parse tees - #9109

Merged
kripken merged 29 commits into
WebAssembly:mainfrom
kripken:c.tee.parse
Sep 16, 2026
Merged

kripken merged 29 commits into
WebAssembly:mainfrom
kripken:c.tee.parse

Conversation

@kripken

@kripken kripken commented Sep 16, 2026

Copy link
Copy Markdown
Member

Parsing tees allows us to handle more code, as it is common to see
a tee at the start of a constraint, e.g.

(if
  (i32.eq ..
    (local.tee $x ..)
    (i32.const 42)
  )

This is also a bugfix, as we were not handling unparsed tees before:
we need to make sure that no tee tramples a get that we parse into
a constraint. E.g.

(i32.and
  (local.get $x)
  (local.tee $x ..)
)

We cannot parse that into $x && .. because at the AND, we have already
trampled $x. This code looks for any such conflict.

@kripken
kripken marked this pull request as ready for review September 16, 2026 19:50
@kripken
kripken requested a review from a team as a code owner September 16, 2026 19:50
@kripken
kripken requested review from tlively and removed request for a team September 16, 2026 19:50
Comment thread src/ir/constraint.cpp
push_back(get);
return LocalOperation{get->index, get->type};
}
if (auto* set = curr->dynCast<LocalSet>()) {

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.

Can we add an assertion that curr is a tee 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, added.

Comment thread src/ir/constraint.cpp Outdated
Comment on lines +814 to +815
// Insert a read, because the tee does both a write and a read.
read.insert(set->index);

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 this be guarded behind a check that the set is actually a tee? If a nested expression contains a block, we might have sets that are not tees.

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, added.

Comment thread src/ir/constraint.cpp Outdated
// common cases we want to, we parse the code in the natural order of execution,
// and maintain a list of local operations. A get before a tee indicates
// possible interference.
struct LocalOperations : public SmallVector<Expression*, 10> {

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 don't think it's worth extending SmallVector here. It would be simple enough and clearer to have the vector as a normal member.

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.

Fair enough, done.

Comment thread src/ir/constraint.cpp

// Check for any possible interference between locals, which would tell the
// caller that whatever was parsed is not valid.
bool hasLocalInterference() const {

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.

Instead of keeping a vector of local reads and writes, can we just keep the vector of reads (including tees) and determine whether there is interference online as we see more accesses? I don't see a reason to defer this analysis to a separate step.

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.

Deferring it avoids creating a std::unordered_set in the vast majority of cases, as most code has no tees at all, and it avoids writing to it in the common case of just one tee.

Comment thread src/ir/constraint.cpp Outdated
// Canonicalize EqZ to Eq of 0.
auto value = Literal::makeZero(get->type);
return LocalConstraint{get->index, Constraint{Abstract::Eq, {value}}};
auto value = Literal::makeZero(localOp->type);

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.

Can the type ever be anything other than the type of value? If not, we can simplify LocalOperation by not storing the type.

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.

Unfortunately, the type of a tee matches the local, not the value

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.

That probably won't be a problem for Literal::makeZero, though, right? The type of the tee's value must be a subtype of the tee's type, so they will have the same zero value.

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.

Fair enough, done. It feels slightly wrong to be imprecise here, but yeah, this is just for nulls...

@kripken
kripken merged commit e653452 into WebAssembly:main Sep 16, 2026
16 checks passed
@kripken
kripken deleted the c.tee.parse branch September 16, 2026 22:39
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