diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asmjs/shared-constants.cpp | 15 | ||||
-rw-r--r-- | src/asmjs/shared-constants.h | 13 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.cpp | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.h | 1 | ||||
-rw-r--r-- | src/wasm-builder.h | 5 | ||||
-rw-r--r-- | src/wasm2asm.h | 243 |
6 files changed, 243 insertions, 35 deletions
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index 7e153bada..b355fecb7 100644 --- a/src/asmjs/shared-constants.cpp +++ b/src/asmjs/shared-constants.cpp @@ -58,15 +58,20 @@ cashew::IString GLOBAL("global"), ENV("env"), INSTRUMENT("instrument"), MATH_IMUL("Math_imul"), - MATH_CLZ32("Math_clz32"), - MATH_POPCNT32("Math_popcnt32"), MATH_ABS("Math_abs"), MATH_CEIL("Math_ceil"), MATH_FLOOR("Math_floor"), MATH_TRUNC("Math_trunc"), MATH_NEAREST("Math_NEAREST"), MATH_SQRT("Math_sqrt"), - MATH_MIN("Math_max"), - MATH_MAX("Math_min"); - + MATH_MIN("Math_min"), + MATH_MAX("Math_max"), + CTZ32("__wasm_ctz_i32"), + CTZ64("__wasm_ctz_i64"), + POPCNT32("__wasm_popcnt_i32"), + POPCNT64("__wasm_popcnt_i64"), + ROTL32("__wasm_rotl_i32"), + ROTL64("__wasm_rotl_i64"), + ROTR32("__wasm_rotr_i32"), + ROTR64("__wasm_rotr_i64"); } diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h index dae6e7e44..92dab0b69 100644 --- a/src/asmjs/shared-constants.h +++ b/src/asmjs/shared-constants.h @@ -61,8 +61,6 @@ extern cashew::IString GLOBAL, ENV, INSTRUMENT, MATH_IMUL, - MATH_CLZ32, - MATH_POPCNT32, MATH_ABS, MATH_CEIL, MATH_FLOOR, @@ -70,8 +68,15 @@ extern cashew::IString GLOBAL, MATH_NEAREST, MATH_SQRT, MATH_MIN, - MATH_MAX; - + MATH_MAX, + CTZ32, + CTZ64, + POPCNT32, + POPCNT64, + ROTL32, + ROTL64, + ROTR32, + ROTR64; } #endif // wasm_asmjs_shared_constants_h diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index 227ce66a5..cc7d4ff37 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -50,6 +50,7 @@ IString TOPLEVEL("toplevel"), UNARY_PREFIX("unary-prefix"), UNARY_POSTFIX("unary-postfix"), MATH_FROUND("Math_fround"), + MATH_CLZ32("Math_clz32"), INT64("i64"), INT64_CONST("i64_const"), SIMD_FLOAT32X4("SIMD_Float32x4"), diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 5c9aa2c8c..cac9896cc 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -65,6 +65,7 @@ extern IString TOPLEVEL, UNARY_PREFIX, UNARY_POSTFIX, MATH_FROUND, + MATH_CLZ32, INT64, INT64_CONST, SIMD_FLOAT32X4, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 312747b93..df9f1fcaa 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -336,6 +336,11 @@ public: return block; } + template<typename ...Ts> + Block* blockify(Expression* any, Expression* append, Ts... args) { + return blockify(blockify(any, append), args...); + } + // ensure a node is a block, if it isn't already, and optionally append to the block // this variant sets a name for the block, so it will not reuse a block already named Block* blockifyWithName(Expression* any, Name name, Expression* append = nullptr) { diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 4ad81816c..e2bb2bca7 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -27,6 +27,7 @@ #include "asmjs/shared-constants.h" #include "wasm.h" +#include "wasm-builder.h" #include "emscripten-optimizer/optimizer.h" #include "mixed_arena.h" #include "asm_v_wasm.h" @@ -44,10 +45,10 @@ IString ASM_FUNC("asmFunc"), // Appends extra to block, flattening out if extra is a block as well void flattenAppend(Ref ast, Ref extra) { int index; - if (ast[0] == BLOCK) index = 1; - else if (ast[0] == DEFUN) index = 3; + if (ast->isArray() && ast[0] == BLOCK) index = 1; + else if (ast->isArray() && ast[0] == DEFUN) index = 3; else abort(); - if (extra[0] == BLOCK) { + if (extra->isArray() && extra[0] == BLOCK) { for (size_t i = 0; i < extra[1]->size(); i++) { ast[index]->push_back(extra[1][i]); } @@ -190,13 +191,174 @@ private: void addImport(Ref ast, Import *import); void addTables(Ref ast, Module *wasm); void addExports(Ref ast, Module *wasm); + void addWasmCompatibilityFuncs(Module *wasm); Wasm2AsmBuilder() = delete; Wasm2AsmBuilder(const Wasm2AsmBuilder &) = delete; - Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder &) = delete; + 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(CTZ32) : Name(CTZ64); + BinaryOp subOp = is32Bit ? SubInt32 : SubInt64; + BinaryOp xorOp = is32Bit ? XorInt32 : XorInt64; + UnaryOp clzOp = is32Bit ? ClzInt32 : ClzInt64; + UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64; + WasmType 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(POPCNT32) : Name(POPCNT64); + BinaryOp addOp = is32Bit ? AddInt32 : AddInt64; + BinaryOp subOp = is32Bit ? SubInt32 : SubInt64; + BinaryOp andOp = is32Bit ? AndInt32 : AndInt64; + UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64; + WasmType 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(ROTR64), Name(ROTR32)}, + {Name(ROTL64), Name(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; + WasmType 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); Ref ret = ValueBuilder::makeToplevel(); Ref asmFunc = ValueBuilder::makeFunction(ASM_FUNC); ret[1]->push_back(asmFunc); @@ -642,7 +804,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { Name asmLabel = curr->name; continueLabels.insert(asmLabel); Ref body = visit(curr->body, result); - Ref ret = ValueBuilder::makeDo(body, ValueBuilder::makeInt(0)); + flattenAppend(body, ValueBuilder::makeBreak(asmLabel)); + Ref ret = ValueBuilder::makeDo(body, ValueBuilder::makeInt(1)); return ValueBuilder::makeLabel(fromName(asmLabel), ret); } Ref visitBreak(Break *curr) { @@ -995,15 +1158,28 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return ret; } // normal unary - Ref value = visit(curr->value, EXPRESSION_RESULT); switch (curr->type) { case i32: { switch (curr->op) { case ClzInt32: - return ValueBuilder::makeCall(MATH_CLZ32, value); + return ValueBuilder::makeCall(MATH_CLZ32, + visit(curr->value, + EXPRESSION_RESULT)); + case CtzInt32: + return ValueBuilder::makeCall(CTZ32, visit(curr->value, + EXPRESSION_RESULT)); case PopcntInt32: - return ValueBuilder::makeCall(MATH_POPCNT32, value); - default: abort(); + return ValueBuilder::makeCall(POPCNT32, visit(curr->value, + EXPRESSION_RESULT)); + case EqZInt32: + return ValueBuilder::makeBinary( + makeAsmCoercion(visit(curr->value, + EXPRESSION_RESULT), ASM_INT), EQ, + makeAsmCoercion(ValueBuilder::makeInt(0), ASM_INT)); + default: + std::cerr << "Unhandled unary i32 operator: " << curr + << std::endl; + abort(); } } case f32: @@ -1012,41 +1188,52 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { switch (curr->op) { case NegFloat32: case NegFloat64: - ret = ValueBuilder::makeUnary(MINUS, visit(curr->value, - EXPRESSION_RESULT)); + ret = ValueBuilder::makeUnary( + MINUS, + visit(curr->value, EXPRESSION_RESULT) + ); break; case AbsFloat32: case AbsFloat64: - ret = ValueBuilder::makeCall(MATH_ABS, visit(curr->value, - EXPRESSION_RESULT)); + ret = ValueBuilder::makeCall( + MATH_ABS, + visit(curr->value, EXPRESSION_RESULT) + ); break; case CeilFloat32: case CeilFloat64: - ret = ValueBuilder::makeCall(MATH_CEIL, visit(curr->value, - EXPRESSION_RESULT)); + ret = ValueBuilder::makeCall( + MATH_CEIL, + visit(curr->value, EXPRESSION_RESULT) + ); break; case FloorFloat32: case FloorFloat64: - ret = ValueBuilder::makeCall(MATH_FLOOR, - visit(curr->value, - EXPRESSION_RESULT)); + ret = ValueBuilder::makeCall( + MATH_FLOOR, + visit(curr->value, EXPRESSION_RESULT) + ); break; case TruncFloat32: case TruncFloat64: - ret = ValueBuilder::makeCall(MATH_TRUNC, - visit(curr->value, - EXPRESSION_RESULT)); + 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)); + ret = ValueBuilder::makeCall( + MATH_NEAREST, + visit(curr->value,EXPRESSION_RESULT) + ); break; case SqrtFloat32: case SqrtFloat64: - ret = ValueBuilder::makeCall(MATH_SQRT, visit(curr->value, - EXPRESSION_RESULT)); + ret = ValueBuilder::makeCall( + MATH_SQRT, + visit(curr->value, EXPRESSION_RESULT) + ); break; // TODO: more complex unary conversions default: @@ -1180,6 +1367,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { case GeUInt32: return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE, makeSigning(right, ASM_UNSIGNED)); + case RotLInt32: + return ValueBuilder::makeCall(ROTL32, left, right); + case RotRInt32: + return ValueBuilder::makeCall(ROTR32, left, right); default: std::cerr << "Unhandled binary operator: " << curr << std::endl; abort(); |