summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asmjs/shared-constants.cpp7
-rw-r--r--src/asmjs/shared-constants.h7
-rw-r--r--src/passes/CMakeLists.txt2
-rw-r--r--src/passes/RemoveCopysign.cpp106
-rw-r--r--src/passes/RemoveNonJSOps.cpp479
-rw-r--r--src/passes/pass.cpp2
-rw-r--r--src/passes/passes.h2
-rw-r--r--src/wasm2asm.h220
8 files changed, 521 insertions, 304 deletions
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp
index bf956f173..87c3574de 100644
--- a/src/asmjs/shared-constants.cpp
+++ b/src/asmjs/shared-constants.cpp
@@ -74,7 +74,6 @@ cashew::IString GLOBAL("global"),
MATH_CLZ32("Math_clz32"),
MATH_FLOOR("Math_floor"),
MATH_TRUNC("Math_trunc"),
- MATH_NEAREST("Math_NEAREST"),
MATH_SQRT("Math_sqrt"),
MATH_MIN("Math_min"),
MATH_MAX("Math_max"),
@@ -91,5 +90,9 @@ cashew::IString GLOBAL("global"),
WASM_GROW_MEMORY("__wasm_grow_memory"),
WASM_CURRENT_MEMORY("__wasm_current_memory"),
WASM_FETCH_HIGH_BITS("__wasm_fetch_high_bits"),
- INT64_TO_32_HIGH_BITS("i64toi32_i32$HIGH_BITS");
+ INT64_TO_32_HIGH_BITS("i64toi32_i32$HIGH_BITS"),
+ WASM_NEAREST_F32("__wasm_nearest_f32"),
+ WASM_NEAREST_F64("__wasm_nearest_f64"),
+ WASM_TRUNC_F32("__wasm_trunc_f32"),
+ WASM_TRUNC_F64("__wasm_trunc_f64");
}
diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h
index 973b5eb49..da2f7aad8 100644
--- a/src/asmjs/shared-constants.h
+++ b/src/asmjs/shared-constants.h
@@ -77,7 +77,6 @@ extern cashew::IString GLOBAL,
MATH_CLZ32,
MATH_FLOOR,
MATH_TRUNC,
- MATH_NEAREST,
MATH_SQRT,
MATH_MIN,
MATH_MAX,
@@ -94,7 +93,11 @@ extern cashew::IString GLOBAL,
WASM_GROW_MEMORY,
WASM_CURRENT_MEMORY,
WASM_FETCH_HIGH_BITS,
- INT64_TO_32_HIGH_BITS;
+ INT64_TO_32_HIGH_BITS,
+ WASM_NEAREST_F32,
+ WASM_NEAREST_F64,
+ WASM_TRUNC_F32,
+ WASM_TRUNC_F64;
}
#endif // wasm_asmjs_shared_constants_h
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 784a46032..2c48e3d6e 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -30,9 +30,9 @@ SET(passes_SOURCES
RedundantSetElimination.cpp
RelooperJumpThreading.cpp
ReReloop.cpp
- RemoveCopysign.cpp
RemoveImports.cpp
RemoveMemory.cpp
+ RemoveNonJSOps.cpp
RemoveUnusedBrs.cpp
RemoveUnusedNames.cpp
RemoveUnusedModuleElements.cpp
diff --git a/src/passes/RemoveCopysign.cpp b/src/passes/RemoveCopysign.cpp
deleted file mode 100644
index 2b57441ee..000000000
--- a/src/passes/RemoveCopysign.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-//
-// Removes the `f32.copysign` and `f64.copysign` instructions and replaces them
-// with equivalent bit operations. Primarily intended to be used with `wasm2asm`
-// where `Math.copysign` doesn't exist.
-//
-
-#include <wasm.h>
-#include <pass.h>
-
-#include "wasm-builder.h"
-
-namespace wasm {
-
-struct RemoveCopysignPass : public WalkerPass<PostWalker<RemoveCopysignPass>> {
- bool isFunctionParallel() override { return false; }
-
- Pass* create() override { return new RemoveCopysignPass; }
-
- void doWalkModule(Module* module) {
- if (!builder) builder = make_unique<Builder>(*module);
- PostWalker<RemoveCopysignPass>::doWalkModule(module);
- }
-
- void doWalkFunction(Function* func) {
- if (!builder) builder = make_unique<Builder>(*getModule());
- PostWalker<RemoveCopysignPass>::doWalkFunction(func);
- }
-
- void visitBinary(Binary *curr) {
- Literal signBit, otherBits;
- UnaryOp int2float, float2int;
- BinaryOp bitAnd, bitOr;
- switch (curr->op) {
- case CopySignFloat32:
- float2int = ReinterpretFloat32;
- int2float = ReinterpretInt32;
- bitAnd = AndInt32;
- bitOr = OrInt32;
- signBit = Literal(uint32_t(1 << 31));
- otherBits = Literal(uint32_t(1 << 31) - 1);
- break;
-
- case CopySignFloat64:
- float2int = ReinterpretFloat64;
- int2float = ReinterpretInt64;
- bitAnd = AndInt64;
- bitOr = OrInt64;
- signBit = Literal(uint64_t(1) << 63);
- otherBits = Literal((uint64_t(1) << 63) - 1);
- break;
-
- default: return;
- }
-
- replaceCurrent(
- builder->makeUnary(
- int2float,
- builder->makeBinary(
- bitOr,
- builder->makeBinary(
- bitAnd,
- builder->makeUnary(
- float2int,
- curr->left
- ),
- builder->makeConst(otherBits)
- ),
- builder->makeBinary(
- bitAnd,
- builder->makeUnary(
- float2int,
- curr->right
- ),
- builder->makeConst(signBit)
- )
- )
- )
- );
- }
-
-private:
- std::unique_ptr<Builder> builder;
-};
-
-Pass *createRemoveCopysignPass() {
- return new RemoveCopysignPass();
-}
-
-} // namespace wasm
-
diff --git a/src/passes/RemoveNonJSOps.cpp b/src/passes/RemoveNonJSOps.cpp
new file mode 100644
index 000000000..77809d6b3
--- /dev/null
+++ b/src/passes/RemoveNonJSOps.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//
+//
+// Removes all operations in a wasm module that aren't inherently implementable
+// in JS. This includes things like `f32.nearest` and
+// `f64.copysign`. Most operations are lowered to a call to an injected
+// intrinsic implementation. Intrinsics don't use themselves to implement
+// themselves.
+//
+
+#include <wasm.h>
+#include <pass.h>
+
+#include "asmjs/shared-constants.h"
+#include "wasm-builder.h"
+
+namespace wasm {
+
+struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> {
+ bool needNearestF32 = false;
+ bool needNearestF64 = false;
+ bool needTruncF32 = false;
+ bool needTruncF64 = false;
+ bool needCtzInt32 = false;
+ bool needPopcntInt32 = false;
+ bool needRotLInt32 = false;
+ bool needRotRInt32 = false;
+
+ bool isFunctionParallel() override { return false; }
+
+ Pass* create() override { return new RemoveNonJSOpsPass; }
+
+ void doWalkModule(Module* module) {
+ if (!builder) builder = make_unique<Builder>(*module);
+ PostWalker<RemoveNonJSOpsPass>::doWalkModule(module);
+
+ if (needNearestF32) {
+ module->addFunction(createNearest(f32));
+ }
+ if (needNearestF64) {
+ module->addFunction(createNearest(f64));
+ }
+ if (needTruncF32) {
+ module->addFunction(createTrunc(f32));
+ }
+ if (needTruncF64) {
+ module->addFunction(createTrunc(f64));
+ }
+ if (needCtzInt32) {
+ module->addFunction(createCtz());
+ }
+ if (needPopcntInt32) {
+ module->addFunction(createPopcnt());
+ }
+ if (needRotLInt32) {
+ module->addFunction(createRot(RotLInt32));
+ }
+ if (needRotRInt32) {
+ module->addFunction(createRot(RotRInt32));
+ }
+ }
+
+ Function *createNearest(Type f) {
+ // fn nearest(f: float) -> float {
+ // let ceil = ceil(f);
+ // let floor = floor(f);
+ // let fract = f - floor;
+ // if fract < 0.5 {
+ // floor
+ // } else if fract > 0.5 {
+ // ceil
+ // } else {
+ // let rem = floor / 2.0;
+ // if rem - floor(rem) == 0.0 {
+ // floor
+ // } else {
+ // ceil
+ // }
+ // }
+ // }
+
+ Index arg = 0;
+ Index ceil = 1;
+ Index floor = 2;
+ Index fract = 3;
+ Index rem = 4;
+
+ UnaryOp ceilOp = CeilFloat32;
+ UnaryOp floorOp = FloorFloat32;
+ BinaryOp subOp = SubFloat32;
+ BinaryOp ltOp = LtFloat32;
+ BinaryOp gtOp = GtFloat32;
+ BinaryOp divOp = DivFloat32;
+ BinaryOp eqOp = EqFloat32;
+ Literal litHalf((float) 0.5);
+ Literal litOne((float) 1.0);
+ Literal litZero((float) 0.0);
+ Literal litTwo((float) 2.0);
+ if (f == f64) {
+ ceilOp = CeilFloat64;
+ floorOp = FloorFloat64;
+ subOp = SubFloat64;
+ ltOp = LtFloat64;
+ gtOp = GtFloat64;
+ divOp = DivFloat64;
+ eqOp = EqFloat64;
+ litHalf = Literal((double) 0.5);
+ litOne = Literal((double) 1.0);
+ litZero = Literal((double) 0.0);
+ litTwo = Literal((double) 2.0);
+ }
+
+ Expression *body = builder->blockify(
+ builder->makeSetLocal(
+ ceil,
+ builder->makeUnary(ceilOp, builder->makeGetLocal(arg, f))
+ ),
+ builder->makeSetLocal(
+ floor,
+ builder->makeUnary(floorOp, builder->makeGetLocal(arg, f))
+ ),
+ builder->makeSetLocal(
+ fract,
+ builder->makeBinary(
+ subOp,
+ builder->makeGetLocal(arg, f),
+ builder->makeGetLocal(floor, f)
+ )
+ ),
+ builder->makeIf(
+ builder->makeBinary(
+ ltOp,
+ builder->makeGetLocal(fract, f),
+ builder->makeConst(litHalf)
+ ),
+ builder->makeGetLocal(floor, f),
+ builder->makeIf(
+ builder->makeBinary(
+ gtOp,
+ builder->makeGetLocal(fract, f),
+ builder->makeConst(litHalf)
+ ),
+ builder->makeGetLocal(ceil, f),
+ builder->blockify(
+ builder->makeSetLocal(
+ rem,
+ builder->makeBinary(
+ divOp,
+ builder->makeGetLocal(floor, f),
+ builder->makeConst(litTwo)
+ )
+ ),
+ builder->makeIf(
+ builder->makeBinary(
+ eqOp,
+ builder->makeBinary(
+ subOp,
+ builder->makeGetLocal(rem, f),
+ builder->makeUnary(
+ floorOp,
+ builder->makeGetLocal(rem, f)
+ )
+ ),
+ builder->makeConst(litZero)
+ ),
+ builder->makeGetLocal(floor, f),
+ builder->makeGetLocal(ceil, f)
+ )
+ )
+ )
+ )
+ );
+ std::vector<Type> params = {f};
+ std::vector<Type> vars = {f, f, f, f, f};
+ Name name = f == f32 ? WASM_NEAREST_F32 : WASM_NEAREST_F64;
+ return builder->makeFunction(name, std::move(params), f, std::move(vars), body);
+ }
+
+ Function *createTrunc(Type f) {
+ // fn trunc(f: float) -> float {
+ // if f < 0.0 {
+ // ceil(f)
+ // } else {
+ // floor(f)
+ // }
+ // }
+
+ Index arg = 0;
+
+ UnaryOp ceilOp = CeilFloat32;
+ UnaryOp floorOp = FloorFloat32;
+ BinaryOp ltOp = LtFloat32;
+ Literal litZero((float) 0.0);
+ if (f == f64) {
+ ceilOp = CeilFloat64;
+ floorOp = FloorFloat64;
+ ltOp = LtFloat64;
+ litZero = Literal((double) 0.0);
+ }
+
+ Expression *body = builder->makeIf(
+ builder->makeBinary(
+ ltOp,
+ builder->makeGetLocal(arg, f),
+ builder->makeConst(litZero)
+ ),
+ builder->makeUnary(ceilOp, builder->makeGetLocal(arg, f)),
+ builder->makeUnary(floorOp, builder->makeGetLocal(arg, f))
+ );
+ std::vector<Type> params = {f};
+ std::vector<Type> vars = {};
+ Name name = f == f32 ? WASM_TRUNC_F32 : WASM_TRUNC_F64;
+ return builder->makeFunction(name, std::move(params), f, std::move(vars), body);
+ }
+
+ Function* createCtz() {
+ // if eqz(x) then 32 else (32 - clz(x ^ (x - 1)))
+ Binary* xorExp = builder->makeBinary(
+ XorInt32,
+ builder->makeGetLocal(0, i32),
+ builder->makeBinary(
+ SubInt32,
+ builder->makeGetLocal(0, i32),
+ builder->makeConst(Literal(int32_t(1)))
+ )
+ );
+ Binary* subExp = builder->makeBinary(
+ SubInt32,
+ builder->makeConst(Literal(int32_t(32 - 1))),
+ builder->makeUnary(ClzInt32, xorExp)
+ );
+ If* body = builder->makeIf(
+ builder->makeUnary(
+ EqZInt32,
+ builder->makeGetLocal(0, i32)
+ ),
+ builder->makeConst(Literal(int32_t(32))),
+ subExp
+ );
+ return builder->makeFunction(
+ WASM_CTZ32,
+ std::vector<NameType>{NameType("x", i32)},
+ i32,
+ std::vector<NameType>{},
+ body
+ );
+ }
+
+ Function* createPopcnt() {
+ // popcnt implemented as:
+ // int c; for (c = 0; x != 0; c++) { x = x & (x - 1) }; return c
+ Name loopName("l");
+ Name blockName("b");
+ Break* brIf = builder->makeBreak(
+ blockName,
+ builder->makeGetLocal(1, i32),
+ builder->makeUnary(
+ EqZInt32,
+ builder->makeGetLocal(0, i32)
+ )
+ );
+ SetLocal* update = builder->makeSetLocal(
+ 0,
+ builder->makeBinary(
+ AndInt32,
+ builder->makeGetLocal(0, i32),
+ builder->makeBinary(
+ SubInt32,
+ builder->makeGetLocal(0, i32),
+ builder->makeConst(Literal(int32_t(1)))
+ )
+ )
+ );
+ SetLocal* inc = builder->makeSetLocal(
+ 1,
+ builder->makeBinary(
+ AddInt32,
+ builder->makeGetLocal(1, i32),
+ builder->makeConst(Literal(1))
+ )
+ );
+ Break* cont = builder->makeBreak(loopName);
+ Loop* loop = builder->makeLoop(
+ loopName,
+ builder->blockify(builder->makeDrop(brIf), update, inc, cont)
+ );
+ Block* loopBlock = builder->blockifyWithName(loop, blockName);
+ // TODO: not sure why this is necessary...
+ loopBlock->type = i32;
+ SetLocal* initCount = builder->makeSetLocal(1, builder->makeConst(Literal(0)));
+ return builder->makeFunction(
+ WASM_POPCNT32,
+ std::vector<NameType>{NameType("x", i32)},
+ i32,
+ std::vector<NameType>{NameType("count", i32)},
+ builder->blockify(initCount, loopBlock)
+ );
+ }
+
+ Function* createRot(BinaryOp op) {
+ // left rotate is:
+ // (((((~0) >>> k) & x) << k) | ((((~0) << (w - k)) & x) >>> (w - k)))
+ // where k is shift modulo w. reverse shifts for right rotate
+ bool isLRot = op == RotLInt32;
+ BinaryOp lshift = isLRot ? ShlInt32 : ShrUInt32;
+ BinaryOp rshift = isLRot ? ShrUInt32 : ShlInt32;
+ Literal widthMask(int32_t(32 - 1));
+ Literal width(int32_t(32));
+ auto shiftVal = [&]() {
+ return builder->makeBinary(
+ AndInt32,
+ builder->makeGetLocal(1, i32),
+ builder->makeConst(widthMask)
+ );
+ };
+ auto widthSub = [&]() {
+ return builder->makeBinary(SubInt32, builder->makeConst(width), shiftVal());
+ };
+ auto fullMask = [&]() {
+ return builder->makeConst(Literal(~int32_t(0)));
+ };
+ Binary* maskRShift = builder->makeBinary(rshift, fullMask(), shiftVal());
+ Binary* lowMask = builder->makeBinary(AndInt32, maskRShift, builder->makeGetLocal(0, i32));
+ Binary* lowShift = builder->makeBinary(lshift, lowMask, shiftVal());
+ Binary* maskLShift = builder->makeBinary(lshift, fullMask(), widthSub());
+ Binary* highMask =
+ builder->makeBinary(AndInt32, maskLShift, builder->makeGetLocal(0, i32));
+ Binary* highShift = builder->makeBinary(rshift, highMask, widthSub());
+ Binary* body = builder->makeBinary(OrInt32, lowShift, highShift);
+ return builder->makeFunction(
+ isLRot ? WASM_ROTL32 : WASM_ROTR32,
+ std::vector<NameType>{NameType("x", i32),
+ NameType("k", i32)},
+ i32,
+ std::vector<NameType>{},
+ body
+ );
+ }
+
+ void doWalkFunction(Function* func) {
+ if (!builder) builder = make_unique<Builder>(*getModule());
+ PostWalker<RemoveNonJSOpsPass>::doWalkFunction(func);
+ }
+
+ void visitBinary(Binary *curr) {
+ Name name;
+ switch (curr->op) {
+ case CopySignFloat32:
+ case CopySignFloat64:
+ rewriteCopysign(curr);
+ return;
+
+ case RotLInt32:
+ needRotLInt32 = true;
+ name = WASM_ROTL32;
+ break;
+ case RotRInt32:
+ needRotRInt32 = true;
+ name = WASM_ROTR32;
+ break;
+
+ default: return;
+ }
+ replaceCurrent(builder->makeCall(name, {curr->left, curr->right}, curr->type));
+ }
+
+ void rewriteCopysign(Binary *curr) {
+ Literal signBit, otherBits;
+ UnaryOp int2float, float2int;
+ BinaryOp bitAnd, bitOr;
+ switch (curr->op) {
+ case CopySignFloat32:
+ float2int = ReinterpretFloat32;
+ int2float = ReinterpretInt32;
+ bitAnd = AndInt32;
+ bitOr = OrInt32;
+ signBit = Literal(uint32_t(1 << 31));
+ otherBits = Literal(uint32_t(1 << 31) - 1);
+ break;
+
+ case CopySignFloat64:
+ float2int = ReinterpretFloat64;
+ int2float = ReinterpretInt64;
+ bitAnd = AndInt64;
+ bitOr = OrInt64;
+ signBit = Literal(uint64_t(1) << 63);
+ otherBits = Literal((uint64_t(1) << 63) - 1);
+ break;
+
+ default: return;
+ }
+
+ replaceCurrent(
+ builder->makeUnary(
+ int2float,
+ builder->makeBinary(
+ bitOr,
+ builder->makeBinary(
+ bitAnd,
+ builder->makeUnary(
+ float2int,
+ curr->left
+ ),
+ builder->makeConst(otherBits)
+ ),
+ builder->makeBinary(
+ bitAnd,
+ builder->makeUnary(
+ float2int,
+ curr->right
+ ),
+ builder->makeConst(signBit)
+ )
+ )
+ )
+ );
+ }
+
+ void visitUnary(Unary *curr) {
+ Name functionCall;
+ switch (curr->op) {
+ case NearestFloat32:
+ needNearestF32 = true;
+ functionCall = WASM_NEAREST_F32;
+ break;
+ case NearestFloat64:
+ needNearestF64 = true;
+ functionCall = WASM_NEAREST_F64;
+ break;
+
+ case TruncFloat32:
+ needTruncF32 = true;
+ functionCall = WASM_TRUNC_F32;
+ break;
+ case TruncFloat64:
+ needTruncF64 = true;
+ functionCall = WASM_TRUNC_F64;
+ break;
+
+ case PopcntInt32:
+ needPopcntInt32 = true;
+ functionCall = WASM_POPCNT32;
+ break;
+
+ case CtzInt32:
+ needCtzInt32 = true;
+ functionCall = WASM_CTZ32;
+ break;
+
+ default: return;
+ }
+ replaceCurrent(builder->makeCall(functionCall, {curr->value}, curr->type));
+ }
+
+private:
+ std::unique_ptr<Builder> builder;
+};
+
+Pass *createRemoveNonJSOpsPass() {
+ return new RemoveNonJSOpsPass();
+}
+
+} // namespace wasm
+
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index e820cd1fa..383752198 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -99,7 +99,7 @@ void PassRegistry::registerPasses() {
registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
registerPass("print-call-graph", "print call graph", createPrintCallGraphPass);
registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
- registerPass("remove-copysign", "removes the copysign instruction", createRemoveCopysignPass);
+ registerPass("remove-non-js-ops", "removes operations incompatible with js", createRemoveNonJSOpsPass);
registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 410b8fe11..7a96799b3 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -56,7 +56,7 @@ Pass* createPrecomputePropagatePass();
Pass* createPrinterPass();
Pass* createPrintCallGraphPass();
Pass* createRelooperJumpThreadingPass();
-Pass* createRemoveCopysignPass();
+Pass* createRemoveNonJSOpsPass();
Pass* createRemoveImportsPass();
Pass* createRemoveMemoryPass();
Pass* createRemoveUnusedBrsPass();
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 8dd569d82..465262d5b 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -32,6 +32,7 @@
#include "emscripten-optimizer/optimizer.h"
#include "mixed_arena.h"
#include "asm_v_wasm.h"
+#include "ir/names.h"
#include "ir/utils.h"
#include "passes/passes.h"
@@ -207,7 +208,6 @@ private:
void addTables(Ref ast, Module* wasm);
void addExports(Ref ast, Module* wasm);
void addGlobal(Ref ast, Global* global);
- void addWasmCompatibilityFuncs(Module* wasm);
void setNeedsAlmostASM(const char *reason);
void addMemoryGrowthFuncs(Ref ast);
bool isAssertHandled(Element& e);
@@ -225,170 +225,13 @@ private:
Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder&) = delete;
};
-static Function* makeCtzFunc(MixedArena& allocator, UnaryOp op) {
- assert(op == CtzInt32 || op == CtzInt64);
- Builder b(allocator);
- // if eqz(x) then 32 else (32 - clz(x ^ (x - 1)))
- bool is32Bit = (op == CtzInt32);
- Name funcName = is32Bit ? Name(WASM_CTZ32) : Name(WASM_CTZ64);
- BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
- BinaryOp xorOp = is32Bit ? XorInt32 : XorInt64;
- UnaryOp clzOp = is32Bit ? ClzInt32 : ClzInt64;
- UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64;
- Type argType = is32Bit ? i32 : i64;
- Binary* xorExp = b.makeBinary(
- xorOp,
- b.makeGetLocal(0, i32),
- b.makeBinary(
- subOp,
- b.makeGetLocal(0, i32),
- b.makeConst(is32Bit ? Literal(int32_t(1)) : Literal(int64_t(1)))
- )
- );
- Binary* subExp = b.makeBinary(
- subOp,
- b.makeConst(is32Bit ? Literal(int32_t(32 - 1)) : Literal(int64_t(64 - 1))),
- b.makeUnary(clzOp, xorExp)
- );
- If* body = b.makeIf(
- b.makeUnary(
- eqzOp,
- b.makeGetLocal(0, i32)
- ),
- b.makeConst(is32Bit ? Literal(int32_t(32)) : Literal(int64_t(64))),
- subExp
- );
- return b.makeFunction(
- funcName,
- std::vector<NameType>{NameType("x", argType)},
- argType,
- std::vector<NameType>{},
- body
- );
-}
-
-static Function* makePopcntFunc(MixedArena& allocator, UnaryOp op) {
- assert(op == PopcntInt32 || op == PopcntInt64);
- Builder b(allocator);
- // popcnt implemented as:
- // int c; for (c = 0; x != 0; c++) { x = x & (x - 1) }; return c
- bool is32Bit = (op == PopcntInt32);
- Name funcName = is32Bit ? Name(WASM_POPCNT32) : Name(WASM_POPCNT64);
- BinaryOp addOp = is32Bit ? AddInt32 : AddInt64;
- BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
- BinaryOp andOp = is32Bit ? AndInt32 : AndInt64;
- UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64;
- Type argType = is32Bit ? i32 : i64;
- Name loopName("l");
- Name blockName("b");
- Break* brIf = b.makeBreak(
- blockName,
- b.makeGetLocal(1, i32),
- b.makeUnary(
- eqzOp,
- b.makeGetLocal(0, argType)
- )
- );
- SetLocal* update = b.makeSetLocal(
- 0,
- b.makeBinary(
- andOp,
- b.makeGetLocal(0, argType),
- b.makeBinary(
- subOp,
- b.makeGetLocal(0, argType),
- b.makeConst(is32Bit ? Literal(int32_t(1)) : Literal(int64_t(1)))
- )
- )
- );
- SetLocal* inc = b.makeSetLocal(
- 1,
- b.makeBinary(
- addOp,
- b.makeGetLocal(1, argType),
- b.makeConst(Literal(1))
- )
- );
- Break* cont = b.makeBreak(loopName);
- Loop* loop = b.makeLoop(loopName, b.blockify(brIf, update, inc, cont));
- Block* loopBlock = b.blockifyWithName(loop, blockName);
- SetLocal* initCount = b.makeSetLocal(1, b.makeConst(Literal(0)));
- return b.makeFunction(
- funcName,
- std::vector<NameType>{NameType("x", argType)},
- argType,
- std::vector<NameType>{NameType("count", argType)},
- b.blockify(initCount, loopBlock)
- );
-}
-
-Function* makeRotFunc(MixedArena& allocator, BinaryOp op) {
- assert(op == RotLInt32 || op == RotRInt32 ||
- op == RotLInt64 || op == RotRInt64);
- Builder b(allocator);
- // left rotate is:
- // (((((~0) >>> k) & x) << k) | ((((~0) << (w - k)) & x) >>> (w - k)))
- // where k is shift modulo w. reverse shifts for right rotate
- bool is32Bit = (op == RotLInt32 || op == RotRInt32);
- bool isLRot = (op == RotLInt32 || op == RotLInt64);
- static Name names[2][2] = {{Name(WASM_ROTR64), Name(WASM_ROTR32)},
- {Name(WASM_ROTL64), Name(WASM_ROTL32)}};
- static BinaryOp shifters[2][2] = {{ShrUInt64, ShrUInt32},
- {ShlInt64, ShlInt32}};
- Name funcName = names[isLRot][is32Bit];
- BinaryOp lshift = shifters[isLRot][is32Bit];
- BinaryOp rshift = shifters[!isLRot][is32Bit];
- BinaryOp orOp = is32Bit ? OrInt32 : OrInt64;
- BinaryOp andOp = is32Bit ? AndInt32 : AndInt64;
- BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
- Type argType = is32Bit ? i32 : i64;
- Literal widthMask =
- is32Bit ? Literal(int32_t(32 - 1)) : Literal(int64_t(64 - 1));
- Literal width =
- is32Bit ? Literal(int32_t(32)) : Literal(int64_t(64));
- auto shiftVal = [&]() {
- return b.makeBinary(
- andOp,
- b.makeGetLocal(1, argType),
- b.makeConst(widthMask)
- );
- };
- auto widthSub = [&]() {
- return b.makeBinary(subOp, b.makeConst(width), shiftVal());
- };
- auto fullMask = [&]() {
- return b.makeConst(is32Bit ? Literal(~int32_t(0)) : Literal(~int64_t(0)));
- };
- Binary* maskRShift = b.makeBinary(rshift, fullMask(), shiftVal());
- Binary* lowMask = b.makeBinary(andOp, maskRShift, b.makeGetLocal(0, argType));
- Binary* lowShift = b.makeBinary(lshift, lowMask, shiftVal());
- Binary* maskLShift = b.makeBinary(lshift, fullMask(), widthSub());
- Binary* highMask =
- b.makeBinary(andOp, maskLShift, b.makeGetLocal(0, argType));
- Binary* highShift = b.makeBinary(rshift, highMask, widthSub());
- Binary* body = b.makeBinary(orOp, lowShift, highShift);
- return b.makeFunction(
- funcName,
- std::vector<NameType>{NameType("x", argType),
- NameType("k", argType)},
- argType,
- std::vector<NameType>{},
- body
- );
-}
-
-void Wasm2AsmBuilder::addWasmCompatibilityFuncs(Module* wasm) {
- wasm->addFunction(makeCtzFunc(wasm->allocator, CtzInt32));
- wasm->addFunction(makePopcntFunc(wasm->allocator, PopcntInt32));
- wasm->addFunction(makeRotFunc(wasm->allocator, RotLInt32));
- wasm->addFunction(makeRotFunc(wasm->allocator, RotRInt32));
-}
-
Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
- addWasmCompatibilityFuncs(wasm);
PassRunner runner(wasm);
runner.add<AutoDrop>();
- runner.add("remove-copysign"); // must be before i64-to-i32
+ runner.add("remove-non-js-ops"); // must be before i64-to-i32
+ // Currently the i64-to-32 lowering pass requires that `flatten` be run before
+ // it produce correct code. For some more details about this see #1480
+ runner.add("flatten");
runner.add("i64-to-i32-lowering");
runner.add("flatten");
runner.add("simplify-locals-notee-nostructure");
@@ -649,6 +492,9 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
std::cerr << "processFunction " << (fns++) << " " << func->name
<< std::endl;
}
+ // We will be symbolically referring to all variables in the function, so make
+ // sure that everything has a name and it's unique.
+ Names::ensureNames(func);
Ref ret = ValueBuilder::makeFunction(fromName(func->name));
frees.clear();
frees.resize(std::max(i32, std::max(f32, f64)) + 1);
@@ -1350,8 +1196,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
// functions, so we do a bit of a hack here to get our one `Ref` to look
// like two function arguments.
case i64: {
- unsigned lo = (unsigned) curr->value.geti64();
- unsigned hi = (unsigned) (curr->value.geti64() >> 32);
+ auto lo = (unsigned) curr->value.geti64();
+ auto hi = (unsigned) (curr->value.geti64() >> 32);
std::ostringstream out;
out << lo << "," << hi;
std::string os = out.str();
@@ -1495,20 +1341,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
visit(curr->value, EXPRESSION_RESULT)
);
break;
- case TruncFloat32:
- case TruncFloat64:
- ret = ValueBuilder::makeCall(
- MATH_TRUNC,
- visit(curr->value, EXPRESSION_RESULT)
- );
- break;
- case NearestFloat32:
- case NearestFloat64:
- ret = ValueBuilder::makeCall(
- MATH_NEAREST,
- visit(curr->value,EXPRESSION_RESULT)
- );
- break;
case SqrtFloat32:
case SqrtFloat64:
ret = ValueBuilder::makeCall(
@@ -1565,6 +1397,14 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ASM_DOUBLE
);
// TODO: more complex unary conversions
+ case NearestFloat32:
+ case NearestFloat64:
+ case TruncFloat32:
+ case TruncFloat64:
+ std::cerr << "operation should have been removed in previous passes"
+ << std::endl;
+ WASM_UNREACHABLE();
+
default:
std::cerr << "Unhandled unary float operator: " << curr
<< std::endl;
@@ -1694,12 +1534,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
case GeUInt32:
return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE,
makeSigning(right, ASM_UNSIGNED));
- case RotLInt32:
- return makeSigning(ValueBuilder::makeCall(WASM_ROTL32, left, right),
- ASM_SIGNED);
- case RotRInt32:
- return makeSigning(ValueBuilder::makeCall(WASM_ROTR32, left, right),
- ASM_SIGNED);
case EqFloat32:
case EqFloat64:
return ValueBuilder::makeBinary(left, EQ, right);
@@ -1718,6 +1552,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
case LtFloat32:
case LtFloat64:
return ValueBuilder::makeBinary(left, LT, right);
+ case RotLInt32:
+ case RotRInt32:
+ std::cerr << "should be removed already" << std::endl;
+ WASM_UNREACHABLE();
default: {
std::cerr << "Unhandled i32 binary operator: " << curr << std::endl;
abort();
@@ -1925,11 +1763,11 @@ static void makeInstantiation(Ref ret) {
var i = new Int32Array(2);
var f = new Float64Array(i.buffer);
f[0] = a;
- var ai1 = f[0];
- var ai2 = f[1];
+ var ai1 = i[0];
+ var ai2 = i[1];
f[0] = b;
- var bi1 = f[0];
- var bi2 = f[1];
+ var bi1 = i[0];
+ var bi2 = i[1];
return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
}
@@ -1956,13 +1794,13 @@ static void prefixCalls(Ref asmjs) {
assert(arr.size() >= 2);
if (arr[1]->getIString() == "f32Equal" ||
arr[1]->getIString() == "f64Equal" ||
- arr[1]->getIString() == "i64Equal") {
+ arr[1]->getIString() == "i64Equal" ||
+ arr[1]->getIString() == "isNaN") {
// ...
} else if (arr[1]->getIString() == "Math_fround") {
arr[1]->setString("Math.fround");
} else {
- Name name = arr[1]->getIString() == "isNaN" ? "Math" : ASM_MODULE;
- Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(name),
+ Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE),
arr[1]->getIString());
arr[1]->setArray(prefixed->getArray());
}