Skip to content

Commit 34dd4c7

Browse files
authored
wasm2js: remove unneeded break/continue labels (#2058)
1 parent 78a4f9e commit 34dd4c7

15 files changed

Lines changed: 181 additions & 126 deletions

src/emscripten-optimizer/simple_ast.cpp

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -183,65 +183,4 @@ void dump(const char *str, Ref node, bool pretty) {
183183
std::cerr << std::endl;
184184
}
185185

186-
// Traversals
187-
188-
struct TraverseInfo {
189-
TraverseInfo() = default;
190-
TraverseInfo(Ref node) : node(node) {
191-
assert(node.get());
192-
if (node->isArray()) {
193-
for (size_t i = 0; i < node->size(); i++) {
194-
maybeAdd(node[i]);
195-
}
196-
} else if (node->isAssign()) {
197-
auto assign = node->asAssign();
198-
maybeAdd(assign->target());
199-
maybeAdd(assign->value());
200-
} else if (node->isAssignName()) {
201-
auto assign = node->asAssignName();
202-
maybeAdd(assign->value());
203-
} else {
204-
// no children
205-
}
206-
}
207-
Ref node;
208-
size_t index = -1;
209-
std::vector<Ref> children;
210-
211-
private:
212-
void maybeAdd(Ref child) {
213-
if (child.get()) {
214-
children.push_back(child);
215-
}
216-
}
217-
};
218-
219-
// Traverse, calling visit after the children
220-
void traversePost(Ref node, std::function<void (Ref)> visit) {
221-
std::vector<TraverseInfo> stack;
222-
stack.push_back(TraverseInfo(node));
223-
while (!stack.empty()) {
224-
TraverseInfo& back = stack.back();
225-
if (back.index == size_t(-1)) {
226-
// This is the first time we see this. Push its children.
227-
back.index = 0;
228-
for (auto child : back.children) {
229-
stack.emplace_back(child);
230-
}
231-
continue;
232-
}
233-
if (back.index < back.children.size()) {
234-
// Visit this child.
235-
back.index++;
236-
visit(back.children[back.index - 1]);
237-
continue;
238-
}
239-
assert(back.index == back.children.size());
240-
// Time to visit the node itself
241-
auto node = back.node;
242-
stack.pop_back();
243-
visit(node);
244-
}
245-
}
246-
247186
} // namespace cashew

src/emscripten-optimizer/simple_ast.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ struct Value {
394394
return arr->size();
395395
}
396396

397+
bool empty() {
398+
return size() == 0;
399+
}
400+
397401
void setSize(size_t size) {
398402
assert(isArray());
399403
auto old = arr->size();
@@ -525,11 +529,6 @@ struct AssignName : public Value {
525529
}
526530
};
527531

528-
// AST traversals
529-
530-
// Traverse, calling visit after the children
531-
void traversePost(Ref node, std::function<void (Ref)> visit);
532-
533532
// JS printing support
534533

535534
struct JSPrinter {

src/tools/wasm2js.cpp

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,79 @@ static void printJS(Ref ast, T& output) {
6767
output << jser.buffer << std::endl;
6868
}
6969

70+
71+
// Traversals
72+
73+
struct TraverseInfo {
74+
TraverseInfo() = default;
75+
TraverseInfo(Ref node) : node(node) {
76+
assert(node.get());
77+
if (node->isArray()) {
78+
for (size_t i = 0; i < node->size(); i++) {
79+
maybeAdd(node[i]);
80+
}
81+
} else if (node->isAssign()) {
82+
auto assign = node->asAssign();
83+
maybeAdd(assign->target());
84+
maybeAdd(assign->value());
85+
} else if (node->isAssignName()) {
86+
auto assign = node->asAssignName();
87+
maybeAdd(assign->value());
88+
} else {
89+
// no children
90+
}
91+
}
92+
Ref node;
93+
bool scanned = false;
94+
std::vector<Ref> children;
95+
96+
private:
97+
void maybeAdd(Ref child) {
98+
if (child.get()) {
99+
children.push_back(child);
100+
}
101+
}
102+
};
103+
104+
// Traverse, calling visit after the children
105+
static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) {
106+
std::vector<TraverseInfo> stack;
107+
stack.push_back(TraverseInfo(node));
108+
while (!stack.empty()) {
109+
TraverseInfo& back = stack.back();
110+
if (!back.scanned) {
111+
back.scanned = true;
112+
// This is the first time we see this.
113+
visitPre(back.node);
114+
for (auto child : back.children) {
115+
stack.emplace_back(child);
116+
}
117+
continue;
118+
}
119+
// Time to post-visit the node itself
120+
auto node = back.node;
121+
stack.pop_back();
122+
visitPost(node);
123+
}
124+
}
125+
126+
static void traversePost(Ref node, std::function<void (Ref)> visit) {
127+
traversePrePost(node, [](Ref node) {}, visit);
128+
}
129+
70130
static void optimizeJS(Ref ast) {
71131
// helpers
72132

73133
auto isOrZero = [](Ref node) {
74-
return node->isArray() && node->size() > 0 && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
134+
return node->isArray() && !node->empty() && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
75135
};
76136

77137
auto isPlus = [](Ref node) {
78-
return node->isArray() && node->size() > 0 && node[0] == UNARY_PREFIX && node[1] == PLUS;
138+
return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX && node[1] == PLUS;
79139
};
80140

81141
auto isBitwise = [](Ref node) {
82-
if (node->isArray() && node->size() > 0 && node[0] == BINARY) {
142+
if (node->isArray() && !node->empty() && node[0] == BINARY) {
83143
auto op = node[1];
84144
return op == OR || op == AND || op == XOR || op == RSHIFT || op == TRSHIFT || op == LSHIFT;
85145
}
@@ -88,7 +148,7 @@ static void optimizeJS(Ref ast) {
88148

89149
// x >> 0 => x | 0
90150
traversePost(ast, [](Ref node) {
91-
if (node->isArray() && node->size() > 0 && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) {
151+
if (node->isArray() && !node->empty() && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) {
92152
if (node[3]->getNumber() == 0) {
93153
node[1]->setString(OR);
94154
}
@@ -124,6 +184,63 @@ static void optimizeJS(Ref ast) {
124184
}
125185
}
126186
});
187+
188+
// Remove unnecessary break/continue labels, when referring to the top level.
189+
190+
// XXX IString invalid("__wasm2js$INVALID_LABEL__");
191+
std::vector<Ref> breakCapturers;
192+
std::vector<Ref> continueCapturers;
193+
std::unordered_map<IString, Ref> labelToValue; // maps the label to the loop/etc.
194+
std::unordered_set<Value*> labelled; // all things with a label on them.
195+
Value INVALID;
196+
traversePrePost(ast, [&](Ref node) {
197+
if (node->isArray() && !node->empty()) {
198+
if (node[0] == LABEL) {
199+
auto label = node[1]->getIString();
200+
labelToValue[label] = node[2];
201+
labelled.insert(node[2].get());
202+
} else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
203+
breakCapturers.push_back(node);
204+
continueCapturers.push_back(node);
205+
} else if (node[0] == cashew::BLOCK) {
206+
if (labelled.count(node.get())) {
207+
// Cannot break to a block without the label.
208+
breakCapturers.push_back(Ref(&INVALID));
209+
}
210+
} else if (node[0] == SWITCH) {
211+
breakCapturers.push_back(node);
212+
}
213+
}
214+
}, [&](Ref node) {
215+
if (node->isArray() && !node->empty()) {
216+
if (node[0] == LABEL) {
217+
auto label = node[1]->getIString();
218+
labelToValue.erase(label);
219+
labelled.erase(node[2].get());
220+
} else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
221+
breakCapturers.pop_back();
222+
continueCapturers.pop_back();
223+
} else if (node[0] == cashew::BLOCK) {
224+
if (labelled.count(node.get())) {
225+
breakCapturers.pop_back();
226+
}
227+
} else if (node[0] == SWITCH) {
228+
breakCapturers.pop_back();
229+
} else if (node[0] == BREAK || node[0] == CONTINUE) {
230+
if (!node[1]->isNull()) {
231+
auto label = node[1]->getIString();
232+
assert(labelToValue.count(label));
233+
auto& capturers = node[0] == BREAK ? breakCapturers : continueCapturers;
234+
assert(!capturers.empty());
235+
if (capturers.back() == labelToValue[label]) {
236+
// Success, the break/continue goes exactly where we would if we
237+
// didn't have the label!
238+
node[1]->setNull();
239+
}
240+
}
241+
}
242+
}
243+
});
127244
}
128245

129246
static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, PassOptions options, Name name) {

test/wasm2js/br_table_to_loop.2asm.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function asmFunc(global, env, buffer) {
2828
case 0:
2929
break block;
3030
case 1:
31-
continue loop;
31+
continue;
3232
default:
3333
break block;
3434
};
@@ -41,11 +41,11 @@ function asmFunc(global, env, buffer) {
4141
loop : while (1) {
4242
switch (1 | 0) {
4343
case 0:
44-
continue loop;
44+
continue;
4545
case 1:
4646
break block;
4747
default:
48-
continue loop;
48+
continue;
4949
};
5050
};
5151
}

test/wasm2js/fac.2asm.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ function asmFunc(global, env, buffer) {
132132
$1$hi = i64toi32_i32$5;
133133
}
134134
}
135-
continue loop_in;
135+
continue;
136136
};
137137
}
138138
i64toi32_i32$5 = $2$hi;
@@ -179,7 +179,7 @@ function asmFunc(global, env, buffer) {
179179
i$hi = i64toi32_i32$5;
180180
}
181181
}
182-
continue loop;
182+
continue;
183183
};
184184
}
185185
i64toi32_i32$5 = res$hi;
@@ -265,9 +265,9 @@ function asmFunc(global, env, buffer) {
265265
}
266266
}
267267
if ($13) {
268-
continue loop_in
268+
continue
269269
}
270-
break loop_in;
270+
break;
271271
};
272272
}
273273
i64toi32_i32$2 = $1$hi;

test/wasm2js/i32.2asm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function asmFunc(global, env, buffer) {
208208
}
209209
var$0 = var$0 & var$0 - 1;
210210
var$1 = var$1 + 1 | 0;
211-
continue label$2;
211+
continue;
212212
};
213213
}
214214
return $5_1;

test/wasm2js/i64-ctz.2asm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ function asmFunc(global, env, buffer) {
236236
}
237237
var$1 = i64toi32_i32$1;
238238
var$1$hi = i64toi32_i32$4;
239-
continue label$2;
239+
continue;
240240
};
241241
}
242242
i64toi32_i32$4 = $5$hi;

test/wasm2js/i64.2asm.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,9 +3357,9 @@ function asmFunc(global, env, buffer) {
33573357
var$7$hi = i64toi32_i32$3;
33583358
var$2 = var$2 + -1 | 0;
33593359
if (var$2) {
3360-
continue label$15
3360+
continue
33613361
}
3362-
break label$15;
3362+
break;
33633363
};
33643364
break label$13;
33653365
}
@@ -3556,7 +3556,7 @@ function asmFunc(global, env, buffer) {
35563556
}
35573557
var$1 = i64toi32_i32$1;
35583558
var$1$hi = i64toi32_i32$4;
3559-
continue label$2;
3559+
continue;
35603560
};
35613561
}
35623562
i64toi32_i32$4 = $5$hi;

0 commit comments

Comments
 (0)