Skip to content

[Wasm GC] Support non-nullable locals in the "1a" form #4959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 261 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from 249 commits
Commits
Show all changes
261 commits
Select commit Hold shift + click to select a range
dcd9b03
fix
kripken Jul 20, 2022
1db5e3f
test
kripken Jul 20, 2022
168c15f
test
kripken Jul 20, 2022
92d8ed2
test
kripken Jul 20, 2022
bd3ba24
format
kripken Jul 20, 2022
7376756
test
kripken Jul 20, 2022
6a160c7
fix
kripken Jul 20, 2022
aa1e8ee
fix
kripken Jul 20, 2022
f05bf1e
fix
kripken Jul 20, 2022
164ff19
test updates
kripken Jul 20, 2022
b96f71c
fix?
kripken Jul 20, 2022
62de4b6
fix
kripken Jul 20, 2022
4951186
fix
kripken Jul 20, 2022
3693a88
comment
kripken Jul 20, 2022
050bf41
fix
kripken Jul 20, 2022
5aba92d
fix
kripken Jul 20, 2022
fd0b471
fix
kripken Jul 20, 2022
c558398
fix
kripken Jul 20, 2022
ba0c978
Merge branch '1a-sl' into 1a-fixups
kripken Jul 21, 2022
8033586
fix
kripken Jul 21, 2022
8702630
fix
kripken Jul 21, 2022
15c31de
fix
kripken Jul 21, 2022
84f6f5f
format
kripken Jul 21, 2022
dd18b76
fix
kripken Jul 21, 2022
f28dd06
hint
kripken Jul 21, 2022
af559ea
fix
kripken Jul 21, 2022
2b92b7c
fix
kripken Jul 21, 2022
cbd24a3
todo
kripken Jul 21, 2022
e5d6cd0
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Jul 21, 2022
cb44b96
fix
kripken Jul 21, 2022
73389f1
fix
kripken Jul 21, 2022
a5f35c4
fix
kripken Jul 21, 2022
0d21ef3
fix
kripken Jul 21, 2022
592f678
more
kripken Jul 21, 2022
3a67edc
more
kripken Jul 21, 2022
eec42fd
more
kripken Jul 21, 2022
ee2b2ad
fix
kripken Jul 21, 2022
c2c51dc
fix
kripken Jul 21, 2022
6bbec5e
fix
kripken Jul 21, 2022
22e410e
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Jul 22, 2022
a42e560
work
kripken Jul 22, 2022
be644ea
fix
kripken Jul 22, 2022
e882b6f
simpler
kripken Jul 22, 2022
f68ef7e
comment
kripken Jul 22, 2022
e0bd604
fix
kripken Jul 22, 2022
b978ca9
test
kripken Jul 22, 2022
9478ea4
fix
kripken Jul 22, 2022
178bb8f
format
kripken Jul 22, 2022
5257404
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Jul 29, 2022
96f0383
opts?
kripken Jul 29, 2022
7c8b38c
opt
kripken Jul 29, 2022
aaf5832
opt?
kripken Jul 29, 2022
56c9e4f
fix
kripken Jul 29, 2022
6b1671d
fast
kripken Jul 29, 2022
efe3b2a
fix
kripken Jul 30, 2022
f187a4e
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 1, 2022
91e2a2d
work
kripken Aug 1, 2022
630e826
format
kripken Aug 1, 2022
5deafa6
fix
kripken Aug 1, 2022
29b0782
chak
kripken Aug 1, 2022
0cff57b
cleanup
kripken Aug 1, 2022
2048f09
format
kripken Aug 1, 2022
4abe768
fix
kripken Aug 1, 2022
77e03b6
fix
kripken Aug 1, 2022
39a704b
fix
kripken Aug 2, 2022
271b045
fix
kripken Aug 2, 2022
1f66918
wrk
kripken Aug 2, 2022
32682dd
fix
kripken Aug 2, 2022
e852b12
fix
kripken Aug 2, 2022
7d32494
format
kripken Aug 2, 2022
902fba5
more
kripken Aug 2, 2022
21cf733
work
kripken Aug 2, 2022
9ca6fd9
comment
kripken Aug 2, 2022
b36ab47
better
kripken Aug 2, 2022
1a8afc5
more
kripken Aug 2, 2022
e5c2a1b
fix
kripken Aug 2, 2022
943f683
better
kripken Aug 2, 2022
a1a6065
faster?
kripken Aug 3, 2022
5123462
fix
kripken Aug 3, 2022
5325dc9
fix
kripken Aug 3, 2022
a4bd6f2
cleanup
kripken Aug 3, 2022
2554d94
clean
kripken Aug 3, 2022
4364b72
fix
kripken Aug 3, 2022
2b02845
fix
kripken Aug 3, 2022
8c15506
opt
kripken Aug 3, 2022
f65bb35
test
kripken Aug 3, 2022
1ee8177
faster
kripken Aug 3, 2022
40da1bb
faster?
kripken Aug 3, 2022
7f3aae3
opt?
kripken Aug 3, 2022
5bb33c3
docs
kripken Aug 3, 2022
b3ded6e
cleanup
kripken Aug 3, 2022
9c5f28d
cleanup
kripken Aug 3, 2022
49148ef
cleanup
kripken Aug 3, 2022
b4e4134
better
kripken Aug 3, 2022
993f03e
Revert "better"
kripken Aug 3, 2022
88ae913
faster
kripken Aug 4, 2022
230e3a0
fix
kripken Aug 4, 2022
d871cbc
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 5, 2022
103bed8
fix
kripken Aug 5, 2022
165c508
fix
kripken Aug 5, 2022
6ed0b9f
VALDIAT
kripken Aug 5, 2022
d3ad932
Merge remote-tracking branch 'origin/refsim' into 1a-fixups
kripken Aug 5, 2022
92bb071
comment
kripken Aug 5, 2022
5c64216
fix
kripken Aug 5, 2022
c5ab091
fix
kripken Aug 5, 2022
287b589
Merge remote-tracking branch 'origin/rseref' into 1a-fixups
kripken Aug 5, 2022
6207095
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 8, 2022
eb414ec
fix
kripken Aug 8, 2022
c931aa3
fix
kripken Aug 8, 2022
56085f0
more
kripken Aug 8, 2022
f64abc1
test
kripken Aug 8, 2022
231bcd1
test
kripken Aug 8, 2022
9532a95
testcase
kripken Aug 8, 2022
9be6041
fix
kripken Aug 8, 2022
fe14045
update
kripken Aug 8, 2022
a7bf33f
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 9, 2022
0c64c7c
fix
kripken Aug 9, 2022
985e4fe
fix
kripken Aug 11, 2022
cac0849
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 11, 2022
e3a91cf
fix
kripken Aug 11, 2022
e7a2505
fix
kripken Aug 11, 2022
51c7321
fix
kripken Aug 11, 2022
95a7215
revamp
kripken Aug 12, 2022
1432e9a
test
kripken Aug 12, 2022
60d1aad
test fail
kripken Aug 12, 2022
e1b8b86
fix
kripken Aug 12, 2022
3dfaa7d
format
kripken Aug 12, 2022
e7f3ea2
comment fix
kripken Aug 12, 2022
e0550f5
comment fix
kripken Aug 12, 2022
8429578
comment fix
kripken Aug 12, 2022
d66806f
missing vlidation
kripken Aug 13, 2022
6cb2a1f
fix
kripken Aug 15, 2022
ef65d78
fix
kripken Aug 15, 2022
497c554
fix
kripken Aug 15, 2022
662afe2
format
kripken Aug 15, 2022
511effb
fix
kripken Aug 15, 2022
c044edf
fix
kripken Aug 15, 2022
901bb84
Validator: Validate globally by default
kripken Aug 15, 2022
7251b13
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 15, 2022
cd64156
fix
kripken Aug 15, 2022
5336750
format
kripken Aug 15, 2022
c713810
Merge remote-tracking branch 'origin/validate.call' into 1a-fixups
kripken Aug 15, 2022
4b703e1
Merge remote-tracking branch 'origin/validate.globally' into 1a-fixups
kripken Aug 15, 2022
1384b13
fix
kripken Aug 15, 2022
d44e67f
form
kripken Aug 15, 2022
886e32a
Merge remote-tracking branch 'origin/validate.call' into 1a-fixups
kripken Aug 15, 2022
8d4b4e6
fix
kripken Aug 15, 2022
6900cba
more
kripken Aug 15, 2022
dd2a66f
Fuzzer: When typed-function-references is disabled, disable GC too
kripken Aug 15, 2022
47f2be0
undo
kripken Aug 15, 2022
a01f288
fix
kripken Aug 15, 2022
97e0db9
fix
kripken Aug 15, 2022
6c305be
Merge remote-tracking branch 'origin/validate.call' into 1a-fixups
kripken Aug 15, 2022
3a6af79
Merge remote-tracking branch 'origin/validate.call.2' into 1a-fixups
kripken Aug 15, 2022
b524862
Merge remote-tracking branch 'origin/fuzz.tfr' into 1a-fixups
kripken Aug 15, 2022
9b8a13e
fix
kripken Aug 15, 2022
7ff905e
fix
kripken Aug 15, 2022
909f03e
fix
kripken Aug 15, 2022
e6ca657
test
kripken Aug 15, 2022
ff56712
bad
kripken Aug 15, 2022
849d2d9
fix
kripken Aug 15, 2022
1d66418
avoid unreachable as a way to test for effects
kripken Aug 15, 2022
70e225a
test
kripken Aug 15, 2022
1531138
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 15, 2022
817c9de
Merge remote-tracking branch 'origin/validate.call.2' into 1a-fixups
kripken Aug 15, 2022
84311cb
Merge remote-tracking branch 'origin/dae-type' into 1a-fixups
kripken Aug 15, 2022
0b04f83
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 16, 2022
2740403
Merge remote-tracking branch 'origin/main' into validate.call.2
kripken Aug 16, 2022
40803cc
better
kripken Aug 16, 2022
67589ea
format
kripken Aug 16, 2022
1bbcd7a
better
kripken Aug 17, 2022
dff6053
yolo?
kripken Aug 17, 2022
e050686
(fix)
kripken Aug 17, 2022
c9fcb08
(fix)
kripken Aug 17, 2022
c17d288
comments
kripken Aug 17, 2022
acbbbdd
Merge remote-tracking branch 'origin/validate.call.2' into 1a-fixups
kripken Aug 17, 2022
098cbad
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 17, 2022
574cb5c
Generalize Tee=>TeeBrIf
kripken Aug 17, 2022
4ca37f7
Merge remote-tracking branch 'origin/typereffall' into 1a-fixups
kripken Aug 17, 2022
1710868
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 18, 2022
bb83a18
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 19, 2022
d9646fa
typos
kripken Aug 19, 2022
d8ab045
better
kripken Aug 19, 2022
2d72b83
better
kripken Aug 19, 2022
a7d9302
better
kripken Aug 19, 2022
a6b5a5c
better
kripken Aug 19, 2022
e95dcea
fix
kripken Aug 19, 2022
8c4af99
cleanup
kripken Aug 19, 2022
ee8982a
fix
kripken Aug 19, 2022
b3d8a42
fix
kripken Aug 19, 2022
128f69d
fix
kripken Aug 19, 2022
9c8342e
yolo
kripken Aug 19, 2022
2c42fa6
comment
kripken Aug 19, 2022
9b111cb
comment
kripken Aug 19, 2022
ad128f8
fix
kripken Aug 19, 2022
9feed9a
fix
kripken Aug 19, 2022
636ee9f
fix?
kripken Aug 19, 2022
0a03a48
fix?
kripken Aug 19, 2022
eaa5ff9
fix
kripken Aug 19, 2022
e3c5126
better
kripken Aug 19, 2022
5d40988
yolo
kripken Aug 19, 2022
05350f5
fix
kripken Aug 19, 2022
1171241
fix
kripken Aug 19, 2022
64738b7
comment
kripken Aug 19, 2022
9eba40c
undo
kripken Aug 19, 2022
7aeb2e1
test
kripken Aug 19, 2022
802a4a9
Merge remote-tracking branch 'origin/s-name' into 1a-fixups
kripken Aug 19, 2022
f5a1452
fix
kripken Aug 19, 2022
2ffe49d
fix
kripken Aug 19, 2022
547e853
fix
kripken Aug 19, 2022
8c51239
fix
kripken Aug 19, 2022
c05f92e
fix
kripken Aug 19, 2022
25122af
fix
kripken Aug 19, 2022
b327bb0
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 22, 2022
502251a
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 22, 2022
de68f71
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 22, 2022
38bbe46
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 22, 2022
764a859
fix
kripken Aug 22, 2022
1202a4c
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 23, 2022
51e8d5a
fix comments
kripken Aug 23, 2022
f0fbf02
update
kripken Aug 23, 2022
5136819
comment
kripken Aug 23, 2022
8c8f12f
fix fuzz bug
kripken Aug 25, 2022
a5a29c1
fix
kripken Aug 25, 2022
7d345f6
fix
kripken Aug 25, 2022
bd6c351
fix
kripken Aug 25, 2022
7919a8c
remove '1a' mentions
kripken Aug 25, 2022
963f524
comment on other text parsers
kripken Aug 25, 2022
1b9d49b
Update src/ir/LocalStructuralDominance.cpp
kripken Aug 25, 2022
4e15a4c
Merge remote-tracking branch 'origin/1a-fixups' into 1a-fixups
kripken Aug 25, 2022
e28b5de
comment
kripken Aug 25, 2022
6e160fd
NonNullableOnly
kripken Aug 25, 2022
d0637f5
format
kripken Aug 25, 2022
a82b6a0
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 25, 2022
bc863a4
todo
kripken Aug 25, 2022
80ac4f8
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 26, 2022
93e9b62
indexes=>indices
kripken Aug 26, 2022
d730464
finish merge
kripken Aug 26, 2022
e8bbf77
DO: use 2 stacks
kripken Aug 26, 2022
637b092
Revert "DO: use 2 stacks"
kripken Aug 26, 2022
b138fd8
remove
kripken Aug 26, 2022
afa15c7
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 29, 2022
1e75064
Merge remote-tracking branch 'origin/main' into 1a-fixups
kripken Aug 30, 2022
48e97c2
fix precompute
kripken Aug 30, 2022
2f9c484
remove 1a from tests
kripken Aug 30, 2022
3ac568a
rename
kripken Aug 30, 2022
f69e721
simplify
kripken Aug 30, 2022
0b0a252
restore test
kripken Aug 30, 2022
456d190
fix
kripken Aug 30, 2022
159d93e
remove spec test that overlaps with the lit test we just restored
kripken Aug 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ full changeset diff at the end of each section.

Current Trunk
-------------
- Add support for non-nullable locals in wasm GC.
- Add a new flag to Directize, `--pass-arg=directize-initial-contents-immutable`
which indicates the initial table contents are immutable. That is the case for
LLVM, for example, and it allows us to optimize more indirect calls to direct
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ There are a few differences between Binaryen IR and the WebAssembly language:
`(elem declare func $..)`. Binaryen will emit that data when necessary, but
it does not represent it in IR. That is, IR can be worked on without needing
to think about declaring function references.
* Binaryen IR allows non-nullable locals in the form that the wasm spec does,
(which was historically nicknamed "1a"), in which a `local.get` must be
structurally dominated by a `local.set` in order to validate (that ensures
we do not read the default value of null). Despite being aligned with the
wasm spec, there are some minor details that you may notice:
* A nameless `Block` in Binaryen IR does not interfere with validation.
Nameless blocks are never emitted into the binary format (we just emit
their contents), so we ignore them for purposes of non-nullable locals. As
a result, if you read wasm text emitted by Binaryen then you may see what
seems to be code that should not validate per the spec (and may not
validate in wasm text parsers), but that difference will not exist in the
binary format (binaries emitted by Binaryen will always work everywhere,
aside for bugs of course).
* The Binaryen pass runner will automatically fix up validation after each
pass (finding things that do not validate and fixing them up, usually by
demoting a local to be nullable). As a result you do not need to worry
much about this when writing Binaryen passes. For more details see the
`requiresNonNullableLocalFixups()` hook in `pass.h` and the
`LocalStructuralDominance` class.

As a result, you might notice that round-trip conversions (wasm => Binaryen IR
=> wasm) change code a little in some corner cases.
Expand Down
3 changes: 2 additions & 1 deletion scripts/test/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ def has_shell_timeout():
'--experimental-wasm-gc',
'--experimental-wasm-typed-funcref',
'--experimental-wasm-memory64',
'--experimental-wasm-extended-const'
'--experimental-wasm-extended-const',
'--experimental-wasm-nn-locals',
]

# external tools
Expand Down
1 change: 1 addition & 0 deletions src/ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(ir_SOURCES
possible-contents.cpp
properties.cpp
LocalGraph.cpp
LocalStructuralDominance.cpp
ReFinalize.cpp
stack-utils.cpp
table-utils.cpp
Expand Down
231 changes: 231 additions & 0 deletions src/ir/LocalStructuralDominance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* Copyright 2022 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ir/iteration.h"
#include "ir/local-structural-dominance.h"
#include "support/small_set.h"
#include "support/small_vector.h"

namespace wasm {

LocalStructuralDominance::LocalStructuralDominance(Function* func,
Module& wasm,
Mode mode) {
if (!wasm.features.hasReferenceTypes()) {
// No references, so nothing to look at.
return;
}

bool hasRefVar = false;
for (auto var : func->vars) {
if (var.isRef()) {
hasRefVar = true;
break;
}
}
if (!hasRefVar) {
return;
}

if (mode == NonNullableOnly) {
bool hasNonNullableVar = false;
for (auto var : func->vars) {
// Check if we have any non-nullable vars at all.
if (var.isNonNullable()) {
hasNonNullableVar = true;
break;
}
}
if (!hasNonNullableVar) {
return;
}
}

struct Scanner : public PostWalker<Scanner> {
std::set<Index>& nonDominatingIndices;

// The locals that have been set, and so at the current time, they
// structurally dominate.
std::vector<bool> localsSet;

Scanner(Function* func, Mode mode, std::set<Index>& nonDominatingIndices)
: nonDominatingIndices(nonDominatingIndices) {
localsSet.resize(func->getNumLocals());

// Parameters always dominate.
for (Index i = 0; i < func->getNumParams(); i++) {
localsSet[i] = true;
}

for (Index i = func->getNumParams(); i < func->getNumLocals(); i++) {
auto type = func->getLocalType(i);
// Mark locals we don't need to care about as "set". We never do any
// work for such a local.
if (!type.isRef() || (mode == NonNullableOnly && type.isNullable())) {
localsSet[i] = true;
}
}

// Note that we do not need to start a scope for the function body.
// Logically there is a scope there, but there is no code after it, so
// there is nothing to clean up when that scope exits, so we may as well
// not even create a scope. Just start walking the body now.
walk(func->body);
}

using Locals = SmallVector<Index, 5>;

// When we exit a control flow scope, we must undo the locals that it set.
std::vector<Locals> cleanupStack;

static void doBeginScope(Scanner* self, Expression** currp) {
self->cleanupStack.emplace_back();
}

static void doEndScope(Scanner* self, Expression** currp) {
for (auto index : self->cleanupStack.back()) {
assert(self->localsSet[index]);
self->localsSet[index] = false;
}
self->cleanupStack.pop_back();
}

static void doLocalSet(Scanner* self, Expression** currp) {
auto index = (*currp)->cast<LocalSet>()->index;
if (!self->localsSet[index]) {
// This local is now set until the end of this scope.
self->localsSet[index] = true;
// If we are not in the topmost scope, note this for later cleanup.
if (!self->cleanupStack.empty()) {
self->cleanupStack.back().push_back(index);
}
}
}

static void scan(Scanner* self, Expression** currp) {
// Use a loop to avoid recursing on the last child - we can just go
// straight into a loop iteration for it.
while (1) {
Expression* curr = *currp;

switch (curr->_id) {
case Expression::Id::InvalidId:
WASM_UNREACHABLE("bad id");

// local.get can just be visited immediately, as it has no children.
case Expression::Id::LocalGetId: {
auto index = curr->cast<LocalGet>()->index;
if (!self->localsSet[index]) {
self->nonDominatingIndices.insert(index);
}
return;
}
case Expression::Id::LocalSetId: {
auto* set = curr->cast<LocalSet>();
if (!self->localsSet[set->index]) {
self->pushTask(doLocalSet, currp);
}
// Immediately continue in the loop.
currp = &set->value;
continue;
}

// Control flow structures.
case Expression::Id::BlockId: {
auto* block = curr->cast<Block>();
// Blocks with no name are never emitted in the binary format, so do
// not create a scope for them.
if (block->name.is()) {
self->pushTask(Scanner::doEndScope, currp);
}
auto& list = block->list;
for (int i = int(list.size()) - 1; i >= 0; i--) {
self->pushTask(Scanner::scan, &list[i]);
}
if (block->name.is()) {
// Just call the task immediately.
doBeginScope(self, currp);
}
return;
}
case Expression::Id::IfId: {
if (curr->cast<If>()->ifFalse) {
self->pushTask(Scanner::doEndScope, currp);
self->maybePushTask(Scanner::scan, &curr->cast<If>()->ifFalse);
self->pushTask(Scanner::doBeginScope, currp);
}
self->pushTask(Scanner::doEndScope, currp);
self->pushTask(Scanner::scan, &curr->cast<If>()->ifTrue);
self->pushTask(Scanner::doBeginScope, currp);
// Immediately continue in the loop.
currp = &curr->cast<If>()->condition;
continue;
}
case Expression::Id::LoopId: {
self->pushTask(Scanner::doEndScope, currp);
// Just call the task immediately.
doBeginScope(self, currp);
// Immediately continue in the loop.
currp = &curr->cast<Loop>()->body;
continue;
}
case Expression::Id::TryId: {
auto& list = curr->cast<Try>()->catchBodies;
for (int i = int(list.size()) - 1; i >= 0; i--) {
self->pushTask(Scanner::doEndScope, currp);
self->pushTask(Scanner::scan, &list[i]);
self->pushTask(Scanner::doBeginScope, currp);
}
self->pushTask(Scanner::doEndScope, currp);
// Just call the task immediately.
doBeginScope(self, currp);
// Immediately continue in the loop.
currp = &curr->cast<Try>()->body;
continue;
}

default: {
// Control flow structures have been handled. This is an expression,
// which we scan normally.
assert(!Properties::isControlFlowStructure(curr));
PostWalker<Scanner>::scan(self, currp);
return;
Copy link
Member

Choose a reason for hiding this comment

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

If it would have performance benefits, we could special-case Unary to use the loop instead of recursing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure what you mean? Unary is not needed in this processing. Or do you mean LocalSet which has a single child? - that is already handled in the loop here.

Copy link
Member

Choose a reason for hiding this comment

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

Right, but if I understand correctly, we could trivial reimplement its scanning in terms of continue instead of the normal mechanism of pushing tasks. Since it looks like we're using a loop to improve performance, it would make sense to me to use the loop as much as possible.

Copy link
Member

Choose a reason for hiding this comment

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

Specifically, we could add this:

case Expression::Id::UnaryId: {
  // Immediately continue in the loop.
  currp = curr->cast<Unary>()->value;
  continue;
}

We could do this with any expression type that only has a single operand.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see, yes, that could be faster. I think it would add a bunch of extra code though, and need to be updated when we add new instructions, etc. I am also unsure how much it would help. I did measure something related, btw - I checked whether we pushed a single child on the stack (by measuring the size before and after) and if so, continued in the loop on that. That does what you said, but it does write and read from the stack, so it just saves the call. I saw no significant benefit there.

Copy link
Member

Choose a reason for hiding this comment

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

If the loop doesn't help much, would be simpler to do without it entirely?

Copy link
Member Author

Choose a reason for hiding this comment

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

The loop does help in itself, in my measurements. Just adding those particular additional uses of the loop didn't help.

One reason the loop helps in the current code is that not only do we avoid a call, but also we can avoid pushing a task and later popping it and doing an indirect call. (see e.g. how Loop just calls doBeginScope rather than push it)

Copy link
Member

Choose a reason for hiding this comment

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

I'm confused about why avoiding that task push, pop, and indirect call doesn't help as much when applied to other expression types. The existing code lgtm, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, it's not totally obvious to me either. It could depend on the frequency of the instructions or their size (which affects caching) for example. Overall, the current code has some added optimization complexity but not much, and each piece seemed worthwhile when I measured it.

}
}
}
}

// Only local.set needs to be visited.
void pushTask(TaskFunc func, Expression** currp) {
// Visits to anything but a set can be ignored, so only very specific
// tasks need to actually be pushed here. In particular, we don't want to
// push tasks to call doVisit* when those callbacks do nothing.
if (func == scan || func == doLocalSet || func == doBeginScope ||
func == doEndScope) {
PostWalker<Scanner>::pushTask(func, currp);
}
}
void maybePushTask(TaskFunc func, Expression** currp) {
if (*currp) {
pushTask(func, currp);
}
}
};

Scanner(func, mode, nonDominatingIndices);
}

} // namespace wasm
4 changes: 0 additions & 4 deletions src/ir/eh-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include "ir/eh-utils.h"
#include "ir/branch-utils.h"
#include "ir/find_all.h"
#include "ir/type-updating.h"

namespace wasm {

Expand Down Expand Up @@ -157,9 +156,6 @@ void handleBlockNestedPops(Function* func, Module& wasm) {
for (auto* try_ : trys.list) {
handleBlockNestedPop(try_, func, wasm);
}
// Pops we handled can be of non-defaultable types, so we may have created
// non-nullable type locals. Fix them.
TypeUpdating::handleNonDefaultableLocals(func, wasm);
}

Pop* findPop(Expression* expr) {
Expand Down
1 change: 1 addition & 0 deletions src/ir/iteration.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ template<class Specific> class AbstractChildIterator {
public:
// The vector of children in the order emitted by wasm-delegations-fields
// (which is in reverse execution order).
// TODO: rename this "reverseChildren"?
SmallVector<Expression**, 4> children;

AbstractChildIterator(Expression* parent) {
Expand Down
2 changes: 1 addition & 1 deletion src/ir/linear-execution.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> {

switch (curr->_id) {
case Expression::Id::InvalidId:
abort();
WASM_UNREACHABLE("bad id");
case Expression::Id::BlockId: {
self->pushTask(SubType::doVisitBlock, currp);
if (curr->cast<Block>()->name.is()) {
Expand Down
Loading