diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asmjs/shared-constants.cpp | 7 | ||||
-rw-r--r-- | src/asmjs/shared-constants.h | 7 | ||||
-rw-r--r-- | src/passes/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/passes/RemoveCopysign.cpp | 106 | ||||
-rw-r--r-- | src/passes/RemoveNonJSOps.cpp | 479 | ||||
-rw-r--r-- | src/passes/pass.cpp | 2 | ||||
-rw-r--r-- | src/passes/passes.h | 2 | ||||
-rw-r--r-- | src/wasm2asm.h | 220 |
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()); } |