summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asmjs/shared-constants.cpp15
-rw-r--r--src/asmjs/shared-constants.h13
-rw-r--r--src/emscripten-optimizer/parser.cpp1
-rw-r--r--src/emscripten-optimizer/parser.h1
-rw-r--r--src/wasm-builder.h5
-rw-r--r--src/wasm2asm.h243
-rw-r--r--test/empty_imported_table.2asm.js38
-rw-r--r--test/empty_table.2asm.js38
-rw-r--r--test/hello_world.2asm.js38
-rw-r--r--test/i32.2asm.js255
10 files changed, 612 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();
diff --git a/test/empty_imported_table.2asm.js b/test/empty_imported_table.2asm.js
index a709f3fcc..04d365e30 100644
--- a/test/empty_imported_table.2asm.js
+++ b/test/empty_imported_table.2asm.js
@@ -13,6 +13,44 @@ function asmFunc(global, env, buffer) {
var Math_abs = global.Math.abs;
var Math_clz32 = global.Math.clz32;
var import$table$0 = env.table;
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var wasm2asm_i32$0 = 0;
+ if ((x | 0) == (0 | 0)) wasm2asm_i32$0 = 32; else wasm2asm_i32$0 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, wasm2asm_i32$0 = 0;
+ count = 0;
+ b : {
+ l : do {
+ if ((x | 0) == (0 | 0)) {
+ wasm2asm_i32$0 = count;
+ break b;
+ }
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_rotl_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ function __wasm_rotr_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
return {
};
diff --git a/test/empty_table.2asm.js b/test/empty_table.2asm.js
index 30a200587..70e17ea9d 100644
--- a/test/empty_table.2asm.js
+++ b/test/empty_table.2asm.js
@@ -12,6 +12,44 @@ function asmFunc(global, env, buffer) {
var Math_fround = global.Math.fround;
var Math_abs = global.Math.abs;
var Math_clz32 = global.Math.clz32;
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var wasm2asm_i32$0 = 0;
+ if ((x | 0) == (0 | 0)) wasm2asm_i32$0 = 32; else wasm2asm_i32$0 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, wasm2asm_i32$0 = 0;
+ count = 0;
+ b : {
+ l : do {
+ if ((x | 0) == (0 | 0)) {
+ wasm2asm_i32$0 = count;
+ break b;
+ }
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_rotl_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ function __wasm_rotr_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
return {
};
diff --git a/test/hello_world.2asm.js b/test/hello_world.2asm.js
index c11126e24..f118213f4 100644
--- a/test/hello_world.2asm.js
+++ b/test/hello_world.2asm.js
@@ -18,6 +18,44 @@ function asmFunc(global, env, buffer) {
return x + y | 0 | 0;
}
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var wasm2asm_i32$0 = 0;
+ if ((x | 0) == (0 | 0)) wasm2asm_i32$0 = 32; else wasm2asm_i32$0 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, wasm2asm_i32$0 = 0;
+ count = 0;
+ b : {
+ l : do {
+ if ((x | 0) == (0 | 0)) {
+ wasm2asm_i32$0 = count;
+ break b;
+ }
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_rotl_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ function __wasm_rotr_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
return {
add: add
};
diff --git a/test/i32.2asm.js b/test/i32.2asm.js
new file mode 100644
index 000000000..4e8d25906
--- /dev/null
+++ b/test/i32.2asm.js
@@ -0,0 +1,255 @@
+function asmFunc(global, env, buffer) {
+ "use asm";
+ var HEAP8 = new global.Int8Array(buffer);
+ var HEAP16 = new global.Int16Array(buffer);
+ var HEAP32 = new global.Int32Array(buffer);
+ var HEAPU8 = new global.Uint8Array(buffer);
+ var HEAPU16 = new global.Uint16Array(buffer);
+ var HEAPU32 = new global.Uint32Array(buffer);
+ var HEAPF32 = new global.Float32Array(buffer);
+ var HEAPF64 = new global.Float64Array(buffer);
+ var Math_imul = global.Math.imul;
+ var Math_fround = global.Math.fround;
+ var Math_abs = global.Math.abs;
+ var Math_clz32 = global.Math.clz32;
+ function $$0(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x + y | 0 | 0;
+ }
+
+ function $$1(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x - y | 0 | 0;
+ }
+
+ function $$2(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return Math_imul(x, y) | 0;
+ }
+
+ function $$3(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) / (y | 0) | 0 | 0;
+ }
+
+ function $$4(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x >>> 0) / (y >>> 0) | 0 | 0;
+ }
+
+ function $$5(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) % (y | 0) | 0 | 0;
+ }
+
+ function $$6(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x >>> 0) % (y >>> 0) | 0 | 0;
+ }
+
+ function $$7(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x & y | 0 | 0;
+ }
+
+ function $$8(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x | y | 0 | 0;
+ }
+
+ function $$9(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x ^ y | 0 | 0;
+ }
+
+ function $$10(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x << y | 0 | 0;
+ }
+
+ function $$11(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >> y | 0 | 0;
+ }
+
+ function $$12(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >>> y | 0 | 0;
+ }
+
+ function $$13(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return __wasm_rotl_i32(x, y) | 0;
+ }
+
+ function $$14(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return __wasm_rotr_i32(x, y) | 0;
+ }
+
+ function $$15(x) {
+ x = x | 0;
+ return Math_clz32(x) | 0;
+ }
+
+ function $$16(x) {
+ x = x | 0;
+ return __wasm_ctz_i32(x) | 0;
+ }
+
+ function $$17(x) {
+ x = x | 0;
+ return __wasm_popcnt_i32(x) | 0;
+ }
+
+ function $$18(x) {
+ x = x | 0;
+ return (x | 0) == (0 | 0) | 0;
+ }
+
+ function $$19(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) == (y | 0) | 0;
+ }
+
+ function $$20(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) != (y | 0) | 0;
+ }
+
+ function $$21(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) < (y | 0) | 0;
+ }
+
+ function $$22(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >>> 0 < y >>> 0 | 0;
+ }
+
+ function $$23(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) <= (y | 0) | 0;
+ }
+
+ function $$24(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >>> 0 <= y >>> 0 | 0;
+ }
+
+ function $$25(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) > (y | 0) | 0;
+ }
+
+ function $$26(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >>> 0 > y >>> 0 | 0;
+ }
+
+ function $$27(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return (x | 0) >= (y | 0) | 0;
+ }
+
+ function $$28(x, y) {
+ x = x | 0;
+ y = y | 0;
+ return x >>> 0 >= y >>> 0 | 0;
+ }
+
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var wasm2asm_i32$0 = 0;
+ if ((x | 0) == (0 | 0)) wasm2asm_i32$0 = 32; else wasm2asm_i32$0 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, wasm2asm_i32$0 = 0;
+ count = 0;
+ b : {
+ l : do {
+ if ((x | 0) == (0 | 0)) {
+ wasm2asm_i32$0 = count;
+ break b;
+ }
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return wasm2asm_i32$0 | 0;
+ }
+
+ function __wasm_rotl_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ function __wasm_rotr_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ return {
+ add: $$0,
+ sub: $$1,
+ mul: $$2,
+ div_s: $$3,
+ div_u: $$4,
+ rem_s: $$5,
+ rem_u: $$6,
+ and: $$7,
+ or: $$8,
+ xor: $$9,
+ shl: $$10,
+ shr_s: $$11,
+ shr_u: $$12,
+ rotl: $$13,
+ rotr: $$14,
+ clz: $$15,
+ ctz: $$16,
+ popcnt: $$17,
+ eqz: $$18,
+ eq: $$19,
+ ne: $$20,
+ lt_s: $$21,
+ lt_u: $$22,
+ le_s: $$23,
+ le_u: $$24,
+ gt_s: $$25,
+ gt_u: $$26,
+ ge_s: $$27,
+ ge_u: $$28
+ };
+}
+