Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 52 additions & 26 deletions src/tools/wasm2js.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,26 @@ static void optimizeJS(Ref ast) {
return false;
};

auto isConstantAnd = [](Ref node, int num) {
auto isUnary = [](Ref node, IString op) {
return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX &&
node[1] == op;
};

auto isConstantBitwise = [](Ref node, IString op, int num) {
return node->isArray() && !node->empty() && node[0] == BINARY &&
node[1] == AND && node[3]->isNumber() && node[3]->getNumber() == num;
node[1] == op && node[3]->isNumber() && node[3]->getNumber() == num;
};

auto isWhile = [](Ref node) {
return node->isArray() && !node->empty() && node[0] == WHILE;
};

auto isDo = [](Ref node) {
return node->isArray() && !node->empty() && node[0] == DO;
};

auto isIf = [](Ref node) {
return node->isArray() && !node->empty() && node[0] == IF;
};

auto removeOrZero = [&](Ref node) {
Expand Down Expand Up @@ -211,43 +228,48 @@ static void optimizeJS(Ref ast) {
return false;
};

auto optimizeBoolean = [&](Ref node) {
// x ^ 1 => !x
if (isConstantBitwise(node, XOR, 1)) {
node[0]->setString(UNARY_PREFIX);
node[1]->setString(L_NOT);
node[3]->setNull();
}
return node;
};

// Optimizations

// x >> 0 => x | 0
traversePost(ast, [](Ref node) {
if (node->isArray() && !node->empty() && node[0] == BINARY &&
node[1] == RSHIFT && node[3]->isNumber()) {
if (node[3]->getNumber() == 0) {
node[1]->setString(OR);
}
// Pre-simplification
traversePost(ast, [&](Ref node) {
// x >> 0 => x | 0
if (isConstantBitwise(node, RSHIFT, 0)) {
node[1]->setString(OR);
}
});

traversePost(ast, [&](Ref node) {
// x | 0 | 0 => x | 0
if (isOrZero(node)) {
while (isOrZero(node[2])) {
node[2] = node[2][2];
}
if (isBitwise(node[2])) {
auto child = node[2];
node[1] = child[1];
node[2] = child[2];
node[3] = child[3];
}
}
// x | 0 going into a bitwise op => skip the | 0
else if (isBitwise(node)) {
if (isBitwise(node)) {
// x | 0 going into a bitwise op => skip the | 0
node[2] = removeOrZero(node[2]);
node[3] = removeOrZero(node[3]);
// x | 0 | 0 => x | 0
if (isOrZero(node)) {
if (isBitwise(node[2])) {
auto child = node[2];
node[1] = child[1];
node[2] = child[2];
node[3] = child[3];
}
}
// A load into an & may allow using a simpler heap, e.g. HEAPU8[..] & 1
// (a load of a boolean) may be HEAP8[..] & 1. The signed heaps are more
// commonly used, so it compresses better, and also they seem to have
// better performance (perhaps since HEAPU32 is at risk of not being a
// smallint).
if (node[1] == AND && isHeapAccess(node[2])) {
auto heap = getHeapFromAccess(node[2]);
if (isConstantAnd(node, 1)) {
if (isConstantBitwise(node, AND, 1)) {
if (heap == HEAPU8) {
setHeapOnAccess(node[2], HEAP8);
} else if (heap == HEAPU16) {
Expand All @@ -263,6 +285,8 @@ static void optimizeJS(Ref ast) {
// +(+x) => +x
else if (isFround(node)) {
node[2] = removePlusAndFround(node[2]);
} else if (isUnary(node, L_NOT)) {
node[2] = optimizeBoolean(node[2]);
}
// Assignment into a heap coerces.
else if (node->isAssign()) {
Expand All @@ -273,12 +297,12 @@ static void optimizeJS(Ref ast) {
if (isIntegerHeap(heap)) {
if (heap == HEAP8 || heap == HEAPU8) {
while (isOrZero(assign->value()) ||
isConstantAnd(assign->value(), 255)) {
isConstantBitwise(assign->value(), AND, 255)) {
assign->value() = assign->value()[2];
}
} else if (heap == HEAP16 || heap == HEAPU16) {
while (isOrZero(assign->value()) ||
isConstantAnd(assign->value(), 65535)) {
isConstantBitwise(assign->value(), AND, 65535)) {
assign->value() = assign->value()[2];
}
} else {
Expand All @@ -294,6 +318,8 @@ static void optimizeJS(Ref ast) {
}
}
}
} else if (isWhile(node) || isDo(node) || isIf(node)) {
node[1] = optimizeBoolean(node[1]);
}
});

Expand Down
8 changes: 8 additions & 0 deletions test/wasm2js/emscripten.2asm.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ function asmFunc(global, env, buffer) {
bools((HEAP16[0 >> 1] | 0) & 1 | 0 | 0) | 0;
bools((HEAP32[0 >> 2] | 0) & 1 | 0 | 0) | 0;
bools((HEAPU8[0 >> 0] | 0) & 2 | 0 | 0) | 0;
bools(x ^ 1 | 0 | 0) | 0;
if (x ^ 1 | 0) {
bools(2 | 0) | 0
}
if (x ^ 2 | 0) {
bools(2 | 0) | 0
}
bools(!(x ^ 1 | 0) | 0) | 0;
abort();
}

Expand Down
8 changes: 8 additions & 0 deletions test/wasm2js/emscripten.2asm.js.opt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ function asmFunc(global, env, buffer) {
bools(HEAP16[0 >> 1] & 1);
bools(HEAP32[0 >> 2] & 1);
bools(HEAPU8[0 | 0] & 2);
bools($0 ^ 1);
if (!$0) {
bools(2)
}
if ($0 ^ 2) {
bools(2)
}
bools(!!$0);
abort();
}

Expand Down
10 changes: 10 additions & 0 deletions test/wasm2js/emscripten.wast
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@
(drop (call $bools (i32.and (i32.load16_s (i32.const 0)) (i32.const 1))))
(drop (call $bools (i32.and (i32.load (i32.const 0)) (i32.const 1))))
(drop (call $bools (i32.and (i32.load8_u (i32.const 0)) (i32.const 2))))
(drop (call $bools (i32.xor (local.get $x) (i32.const 1))))
(if
(i32.xor (local.get $x) (i32.const 1))
(drop (call $bools (i32.const 2)))
)
(if
(i32.xor (local.get $x) (i32.const 2))
(drop (call $bools (i32.const 2)))
)
(drop (call $bools (i32.eqz (i32.xor (local.get $x) (i32.const 1)))))
(unreachable)
)
)
Expand Down