Skip to content

Commit da716eb

Browse files
authored
wasm2js: optimize booleans (#2090)
1 parent 9384c1d commit da716eb

4 files changed

Lines changed: 78 additions & 26 deletions

File tree

src/tools/wasm2js.cpp

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,26 @@ static void optimizeJS(Ref ast) {
155155
return false;
156156
};
157157

158-
auto isConstantAnd = [](Ref node, int num) {
158+
auto isUnary = [](Ref node, IString op) {
159+
return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX &&
160+
node[1] == op;
161+
};
162+
163+
auto isConstantBitwise = [](Ref node, IString op, int num) {
159164
return node->isArray() && !node->empty() && node[0] == BINARY &&
160-
node[1] == AND && node[3]->isNumber() && node[3]->getNumber() == num;
165+
node[1] == op && node[3]->isNumber() && node[3]->getNumber() == num;
166+
};
167+
168+
auto isWhile = [](Ref node) {
169+
return node->isArray() && !node->empty() && node[0] == WHILE;
170+
};
171+
172+
auto isDo = [](Ref node) {
173+
return node->isArray() && !node->empty() && node[0] == DO;
174+
};
175+
176+
auto isIf = [](Ref node) {
177+
return node->isArray() && !node->empty() && node[0] == IF;
161178
};
162179

163180
auto removeOrZero = [&](Ref node) {
@@ -211,43 +228,48 @@ static void optimizeJS(Ref ast) {
211228
return false;
212229
};
213230

231+
auto optimizeBoolean = [&](Ref node) {
232+
// x ^ 1 => !x
233+
if (isConstantBitwise(node, XOR, 1)) {
234+
node[0]->setString(UNARY_PREFIX);
235+
node[1]->setString(L_NOT);
236+
node[3]->setNull();
237+
}
238+
return node;
239+
};
240+
214241
// Optimizations
215242

216-
// x >> 0 => x | 0
217-
traversePost(ast, [](Ref node) {
218-
if (node->isArray() && !node->empty() && node[0] == BINARY &&
219-
node[1] == RSHIFT && node[3]->isNumber()) {
220-
if (node[3]->getNumber() == 0) {
221-
node[1]->setString(OR);
222-
}
243+
// Pre-simplification
244+
traversePost(ast, [&](Ref node) {
245+
// x >> 0 => x | 0
246+
if (isConstantBitwise(node, RSHIFT, 0)) {
247+
node[1]->setString(OR);
223248
}
224249
});
225250

226251
traversePost(ast, [&](Ref node) {
227-
// x | 0 | 0 => x | 0
228-
if (isOrZero(node)) {
229-
while (isOrZero(node[2])) {
230-
node[2] = node[2][2];
231-
}
232-
if (isBitwise(node[2])) {
233-
auto child = node[2];
234-
node[1] = child[1];
235-
node[2] = child[2];
236-
node[3] = child[3];
237-
}
238-
}
239-
// x | 0 going into a bitwise op => skip the | 0
240-
else if (isBitwise(node)) {
252+
if (isBitwise(node)) {
253+
// x | 0 going into a bitwise op => skip the | 0
241254
node[2] = removeOrZero(node[2]);
242255
node[3] = removeOrZero(node[3]);
256+
// x | 0 | 0 => x | 0
257+
if (isOrZero(node)) {
258+
if (isBitwise(node[2])) {
259+
auto child = node[2];
260+
node[1] = child[1];
261+
node[2] = child[2];
262+
node[3] = child[3];
263+
}
264+
}
243265
// A load into an & may allow using a simpler heap, e.g. HEAPU8[..] & 1
244266
// (a load of a boolean) may be HEAP8[..] & 1. The signed heaps are more
245267
// commonly used, so it compresses better, and also they seem to have
246268
// better performance (perhaps since HEAPU32 is at risk of not being a
247269
// smallint).
248270
if (node[1] == AND && isHeapAccess(node[2])) {
249271
auto heap = getHeapFromAccess(node[2]);
250-
if (isConstantAnd(node, 1)) {
272+
if (isConstantBitwise(node, AND, 1)) {
251273
if (heap == HEAPU8) {
252274
setHeapOnAccess(node[2], HEAP8);
253275
} else if (heap == HEAPU16) {
@@ -263,6 +285,8 @@ static void optimizeJS(Ref ast) {
263285
// +(+x) => +x
264286
else if (isFround(node)) {
265287
node[2] = removePlusAndFround(node[2]);
288+
} else if (isUnary(node, L_NOT)) {
289+
node[2] = optimizeBoolean(node[2]);
266290
}
267291
// Assignment into a heap coerces.
268292
else if (node->isAssign()) {
@@ -273,12 +297,12 @@ static void optimizeJS(Ref ast) {
273297
if (isIntegerHeap(heap)) {
274298
if (heap == HEAP8 || heap == HEAPU8) {
275299
while (isOrZero(assign->value()) ||
276-
isConstantAnd(assign->value(), 255)) {
300+
isConstantBitwise(assign->value(), AND, 255)) {
277301
assign->value() = assign->value()[2];
278302
}
279303
} else if (heap == HEAP16 || heap == HEAPU16) {
280304
while (isOrZero(assign->value()) ||
281-
isConstantAnd(assign->value(), 65535)) {
305+
isConstantBitwise(assign->value(), AND, 65535)) {
282306
assign->value() = assign->value()[2];
283307
}
284308
} else {
@@ -294,6 +318,8 @@ static void optimizeJS(Ref ast) {
294318
}
295319
}
296320
}
321+
} else if (isWhile(node) || isDo(node) || isIf(node)) {
322+
node[1] = optimizeBoolean(node[1]);
297323
}
298324
});
299325

test/wasm2js/emscripten.2asm.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ function asmFunc(global, env, buffer) {
9393
bools((HEAP16[0 >> 1] | 0) & 1 | 0 | 0) | 0;
9494
bools((HEAP32[0 >> 2] | 0) & 1 | 0 | 0) | 0;
9595
bools((HEAPU8[0 >> 0] | 0) & 2 | 0 | 0) | 0;
96+
bools(x ^ 1 | 0 | 0) | 0;
97+
if (x ^ 1 | 0) {
98+
bools(2 | 0) | 0
99+
}
100+
if (x ^ 2 | 0) {
101+
bools(2 | 0) | 0
102+
}
103+
bools(!(x ^ 1 | 0) | 0) | 0;
96104
abort();
97105
}
98106

test/wasm2js/emscripten.2asm.js.opt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ function asmFunc(global, env, buffer) {
7474
bools(HEAP16[0 >> 1] & 1);
7575
bools(HEAP32[0 >> 2] & 1);
7676
bools(HEAPU8[0 | 0] & 2);
77+
bools($0 ^ 1);
78+
if (!$0) {
79+
bools(2)
80+
}
81+
if ($0 ^ 2) {
82+
bools(2)
83+
}
84+
bools(!!$0);
7785
abort();
7886
}
7987

test/wasm2js/emscripten.wast

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@
106106
(drop (call $bools (i32.and (i32.load16_s (i32.const 0)) (i32.const 1))))
107107
(drop (call $bools (i32.and (i32.load (i32.const 0)) (i32.const 1))))
108108
(drop (call $bools (i32.and (i32.load8_u (i32.const 0)) (i32.const 2))))
109+
(drop (call $bools (i32.xor (local.get $x) (i32.const 1))))
110+
(if
111+
(i32.xor (local.get $x) (i32.const 1))
112+
(drop (call $bools (i32.const 2)))
113+
)
114+
(if
115+
(i32.xor (local.get $x) (i32.const 2))
116+
(drop (call $bools (i32.const 2)))
117+
)
118+
(drop (call $bools (i32.eqz (i32.xor (local.get $x) (i32.const 1)))))
109119
(unreachable)
110120
)
111121
)

0 commit comments

Comments
 (0)