summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asmjs/shared-constants.cpp4
-rw-r--r--src/asmjs/shared-constants.h4
-rw-r--r--src/emscripten-optimizer/simple_ast.h15
-rw-r--r--src/passes/I64ToI32Lowering.cpp252
-rw-r--r--src/tools/wasm2asm.cpp3
-rw-r--r--src/wasm2asm.h252
-rw-r--r--test/wasm2asm.asserts.js45
-rw-r--r--test/wasm2asm.traps.js49
-rw-r--r--test/wasm2asm/conversions-modified.2asm.js260
-rw-r--r--test/wasm2asm/conversions-modified.wast419
-rw-r--r--test/wasm2asm/float-ops.2asm.js126
-rw-r--r--test/wasm2asm/float-ops.wast37
12 files changed, 1401 insertions, 65 deletions
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp
index c6efe64f9..bf956f173 100644
--- a/src/asmjs/shared-constants.cpp
+++ b/src/asmjs/shared-constants.cpp
@@ -89,5 +89,7 @@ cashew::IString GLOBAL("global"),
WASM_ROTR32("__wasm_rotr_i32"),
WASM_ROTR64("__wasm_rotr_i64"),
WASM_GROW_MEMORY("__wasm_grow_memory"),
- WASM_CURRENT_MEMORY("__wasm_current_memory");
+ WASM_CURRENT_MEMORY("__wasm_current_memory"),
+ WASM_FETCH_HIGH_BITS("__wasm_fetch_high_bits"),
+ INT64_TO_32_HIGH_BITS("i64toi32_i32$HIGH_BITS");
}
diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h
index 28142fecc..973b5eb49 100644
--- a/src/asmjs/shared-constants.h
+++ b/src/asmjs/shared-constants.h
@@ -92,7 +92,9 @@ extern cashew::IString GLOBAL,
WASM_ROTR32,
WASM_ROTR64,
WASM_GROW_MEMORY,
- WASM_CURRENT_MEMORY;
+ WASM_CURRENT_MEMORY,
+ WASM_FETCH_HIGH_BITS,
+ INT64_TO_32_HIGH_BITS;
}
#endif // wasm_asmjs_shared_constants_h
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index 62bf975f0..64aab7708 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -836,6 +836,19 @@ struct JSPrinter {
}
static char* numToString(double d, bool finalize=true) {
+ if (std::isnan(d)) {
+ if (std::signbit(d)) {
+ return (char*) "-NaN";
+ } else {
+ return (char*) "NaN";
+ }
+ } else if (!std::isfinite(d)) {
+ if (std::signbit(d)) {
+ return (char*) "-Infinity";
+ } else {
+ return (char*) "Infinity";
+ }
+ }
bool neg = d < 0;
if (neg) d = -d;
// try to emit the fewest necessary characters
@@ -1046,6 +1059,8 @@ struct JSPrinter {
ensure(1); // we temporarily append a 0
char *curr = buffer + last; // ensure might invalidate
buffer[used] = 0;
+ if (strstr(curr, "Infinity")) return;
+ if (strstr(curr, "NaN")) return;
if (strchr(curr, '.')) return; // already a decimal point, all good
char *e = strchr(curr, 'e');
if (!e) {
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index e6da9a1b8..2f6fdf122 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -40,10 +40,10 @@ static Name makeHighName(Name n) {
struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
struct TempVar {
- TempVar(Index idx, I64ToI32Lowering& pass) :
- idx(idx), pass(pass), moved(false) {}
+ TempVar(Index idx, Type ty, I64ToI32Lowering& pass) :
+ idx(idx), pass(pass), moved(false), ty(ty) {}
- TempVar(TempVar&& other) : idx(other), pass(other.pass), moved(false) {
+ TempVar(TempVar&& other) : idx(other), pass(other.pass), moved(false), ty(other.ty) {
assert(!other.moved);
other.moved = true;
}
@@ -78,18 +78,17 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
private:
void freeIdx() {
- assert(std::find(pass.freeTemps.begin(), pass.freeTemps.end(), idx) ==
- pass.freeTemps.end());
- pass.freeTemps.push_back(idx);
+ auto &freeList = pass.freeTemps[(int) ty];
+ assert(std::find(freeList.begin(), freeList.end(), idx) == freeList.end());
+ freeList.push_back(idx);
}
Index idx;
I64ToI32Lowering& pass;
bool moved; // since C++ will still destruct moved-from values
+ Type ty;
};
- static Name highBitsGlobal;
-
// false since function types need to be lowered
// TODO: allow module-level transformations in parallel passes
bool isFunctionParallel() override { return false; }
@@ -114,7 +113,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
// to return the high 32 bits.
auto* highBits = new Global();
highBits->type = i32;
- highBits->name = highBitsGlobal;
+ highBits->name = INT64_TO_32_HIGH_BITS;
highBits->init = builder->makeConst(Literal(int32_t(0)));
highBits->mutable_ = true;
module->addGlobal(highBits);
@@ -159,7 +158,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
BinaryOp secondOp = leftShift ? ShrUInt32 : ShlInt32;
Block* equalRotateBlock = builder->blockify(
builder->makeSetGlobal(
- highBitsGlobal,
+ INT64_TO_32_HIGH_BITS,
builder->makeGetLocal(lowBits, i32)
),
builder->makeGetLocal(highBits, i32)
@@ -182,7 +181,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
)
),
builder->makeSetGlobal(
- highBitsGlobal,
+ INT64_TO_32_HIGH_BITS,
builder->makeBinary(
OrInt32,
builder->makeBinary(
@@ -221,7 +220,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
)
),
builder->makeSetGlobal(
- highBitsGlobal,
+ INT64_TO_32_HIGH_BITS,
builder->makeBinary(
OrInt32,
builder->makeBinary(
@@ -328,18 +327,17 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
func->body
);
SetGlobal* setHigh = builder->makeSetGlobal(
- highBitsGlobal,
+ INT64_TO_32_HIGH_BITS,
builder->makeGetLocal(highBits, i32)
);
GetLocal* getLow = builder->makeGetLocal(lowBits, i32);
func->body = builder->blockify(setLow, setHigh, getLow);
}
}
- assert(freeTemps.size() == nextTemp - func->getNumLocals());
int idx = 0;
for (size_t i = func->getNumLocals(); i < nextTemp; i++) {
Name tmpName("i64toi32_i32$" + std::to_string(idx++));
- builder->addVar(func, tmpName, i32);
+ builder->addVar(func, tmpName, tempTypes[i]);
}
}
@@ -488,7 +486,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
);
SetLocal* setHigh = builder->makeSetLocal(
highBits,
- builder->makeGetGlobal(highBitsGlobal, i32)
+ builder->makeGetGlobal(INT64_TO_32_HIGH_BITS, i32)
);
GetLocal* getLow = builder->makeGetLocal(lowBits, i32);
Block* result = builder->blockify(doCall, setHigh, getLow);
@@ -733,15 +731,14 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
void lowerReinterpretFloat64(Unary* curr) {
// Assume that the wasm file assumes the address 0 is invalid and roundtrip
// our f64 through memory at address 0
- Expression* zero = builder->makeConst(Literal(int32_t(0)));
TempVar highBits = getTemp();
Block *result = builder->blockify(
- builder->makeStore(8, 0, 8, zero, curr->value, f64),
+ builder->makeStore(8, 0, 8, builder->makeConst(Literal(int32_t(0))), curr->value, f64),
builder->makeSetLocal(
highBits,
- builder->makeLoad(4, true, 4, 4, zero, i32)
+ builder->makeLoad(4, true, 4, 4, builder->makeConst(Literal(int32_t(0))), i32)
),
- builder->makeLoad(4, true, 0, 4, zero, i32)
+ builder->makeLoad(4, true, 0, 4, builder->makeConst(Literal(int32_t(0))), i32)
);
setOutParam(result, std::move(highBits));
replaceCurrent(result);
@@ -751,13 +748,192 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
// Assume that the wasm file assumes the address 0 is invalid and roundtrip
// our i64 through memory at address 0
TempVar highBits = fetchOutParam(curr->value);
- TempVar lowBits = getTemp();
- Expression* zero = builder->makeConst(Literal(int32_t(0)));
Block *result = builder->blockify(
- builder->makeStore(4, 0, 4, zero, curr->value, i32),
- builder->makeStore(4, 4, 4, zero, builder->makeGetLocal(highBits, i32), i32),
- builder->makeLoad(8, true, 0, 8, zero, f64)
+ builder->makeStore(4, 0, 4, builder->makeConst(Literal(int32_t(0))), curr->value, i32),
+ builder->makeStore(4, 4, 4, builder->makeConst(Literal(int32_t(0))), builder->makeGetLocal(highBits, i32), i32),
+ builder->makeLoad(8, true, 0, 8, builder->makeConst(Literal(int32_t(0))), f64)
+ );
+ replaceCurrent(result);
+ }
+
+ void lowerTruncFloatToInt(Unary *curr) {
+ // hiBits = if abs(f) >= 1.0 {
+ // if f > 0.0 {
+ // (unsigned) min(
+ // floor(f / (float) U32_MAX),
+ // (float) U32_MAX - 1,
+ // )
+ // } else {
+ // (unsigned) ceil((f - (float) (unsigned) f) / ((float) U32_MAX))
+ // }
+ // } else {
+ // 0
+ // }
+ //
+ // loBits = (unsigned) f;
+
+ Literal litZero, litOne, u32Max;
+ UnaryOp trunc, convert, abs, floor, ceil;
+ Type localType;
+ BinaryOp ge, gt, min, div, sub;
+ switch (curr->op) {
+ case TruncSFloat32ToInt64:
+ case TruncUFloat32ToInt64: {
+ litZero = Literal((float) 0);
+ litOne = Literal((float) 1);
+ u32Max = Literal(((float) UINT_MAX) + 1);
+ trunc = TruncUFloat32ToInt32;
+ convert = ConvertUInt32ToFloat32;
+ localType = f32;
+ abs = AbsFloat32;
+ ge = GeFloat32;
+ gt = GtFloat32;
+ min = MinFloat32;
+ floor = FloorFloat32;
+ ceil = CeilFloat32;
+ div = DivFloat32;
+ sub = SubFloat32;
+ break;
+ }
+ case TruncSFloat64ToInt64:
+ case TruncUFloat64ToInt64: {
+ litZero = Literal((double) 0);
+ litOne = Literal((double) 1);
+ u32Max = Literal(((double) UINT_MAX) + 1);
+ trunc = TruncUFloat64ToInt32;
+ convert = ConvertUInt32ToFloat64;
+ localType = f64;
+ abs = AbsFloat64;
+ ge = GeFloat64;
+ gt = GtFloat64;
+ min = MinFloat64;
+ floor = FloorFloat64;
+ ceil = CeilFloat64;
+ div = DivFloat64;
+ sub = SubFloat64;
+ break;
+ }
+ default: abort();
+ }
+
+ TempVar f = getTemp(localType);
+ TempVar highBits = getTemp();
+
+ Expression *gtZeroBranch = builder->makeBinary(
+ min,
+ builder->makeUnary(
+ floor,
+ builder->makeBinary(
+ div,
+ builder->makeGetLocal(f, localType),
+ builder->makeConst(u32Max)
+ )
+ ),
+ builder->makeBinary(sub, builder->makeConst(u32Max), builder->makeConst(litOne))
+ );
+ Expression *ltZeroBranch = builder->makeUnary(
+ ceil,
+ builder->makeBinary(
+ div,
+ builder->makeBinary(
+ sub,
+ builder->makeGetLocal(f, localType),
+ builder->makeUnary(convert,
+ builder->makeUnary(trunc, builder->makeGetLocal(f, localType))
+ )
+ ),
+ builder->makeConst(u32Max)
+ )
+ );
+
+ If *highBitsCalc = builder->makeIf(
+ builder->makeBinary(
+ gt,
+ builder-> makeGetLocal(f, localType),
+ builder->makeConst(litZero)
+ ),
+ builder->makeUnary(trunc, gtZeroBranch),
+ builder->makeUnary(trunc, ltZeroBranch)
+ );
+ If *highBitsVal = builder->makeIf(
+ builder->makeBinary(
+ ge,
+ builder->makeUnary(abs, builder->makeGetLocal(f, localType)),
+ builder->makeConst(litOne)
+ ),
+ highBitsCalc,
+ builder->makeConst(Literal(int32_t(0)))
+ );
+ Block *result = builder->blockify(
+ builder->makeSetLocal(f, curr->value),
+ builder->makeSetLocal(highBits, highBitsVal),
+ builder->makeUnary(trunc, builder->makeGetLocal(f, localType))
+ );
+ setOutParam(result, std::move(highBits));
+ replaceCurrent(result);
+ }
+
+ void lowerConvertIntToFloat(Unary *curr) {
+ // Here the same strategy as `emcc` is taken which takes the two halves of
+ // the 64-bit integer and creates a mathematical expression using float
+ // arithmetic to reassemble the final floating point value.
+ //
+ // For example for i64 -> f32 we generate:
+ //
+ // ((double) (unsigned) lowBits) + ((double) U32_MAX) * ((double) (int) highBits)
+ //
+ // Mostly just shuffling things around here with coercions and whatnot!
+ // Note though that all arithmetic is done with f64 to have as much
+ // precision as we can.
+ TempVar highBits = fetchOutParam(curr->value);
+ TempVar lowBits = getTemp();
+ TempVar highResult = getTemp();
+
+ UnaryOp convertHigh;
+ switch (curr->op) {
+ case ConvertSInt64ToFloat32:
+ case ConvertSInt64ToFloat64:
+ convertHigh = ConvertSInt32ToFloat64;
+ break;
+ case ConvertUInt64ToFloat32:
+ case ConvertUInt64ToFloat64:
+ convertHigh = ConvertUInt32ToFloat64;
+ break;
+ default: abort();
+ }
+
+ Expression *result = builder->blockify(
+ builder->makeSetLocal(lowBits, curr->value),
+ builder->makeSetLocal(
+ highResult,
+ builder->makeConst(Literal(int32_t(0)))
+ ),
+ builder->makeBinary(
+ AddFloat64,
+ builder->makeUnary(
+ ConvertUInt32ToFloat64,
+ builder->makeGetLocal(lowBits, i32)
+ ),
+ builder->makeBinary(
+ MulFloat64,
+ builder->makeConst(Literal((double)UINT_MAX + 1)),
+ builder->makeUnary(
+ convertHigh,
+ builder->makeGetLocal(highBits, i32)
+ )
+ )
+ )
);
+
+ switch (curr->op) {
+ case ConvertSInt64ToFloat32:
+ case ConvertUInt64ToFloat32: {
+ result = builder->makeUnary(DemoteFloat64, result);
+ break;
+ }
+ default: break;
+ }
+
replaceCurrent(result);
}
@@ -889,11 +1065,11 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
case TruncSFloat32ToInt64:
case TruncUFloat32ToInt64:
case TruncSFloat64ToInt64:
- case TruncUFloat64ToInt64:
+ case TruncUFloat64ToInt64: lowerTruncFloatToInt(curr); break;
case ConvertSInt64ToFloat32:
case ConvertSInt64ToFloat64:
case ConvertUInt64ToFloat32:
- case ConvertUInt64ToFloat64:
+ case ConvertUInt64ToFloat64: lowerConvertIntToFloat(curr); break;
default:
std::cerr << "Unhandled unary operator: " << curr->op << std::endl;
abort();
@@ -1386,7 +1562,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
),
builder->makeSetLocal(
rightHigh,
- builder->makeGetGlobal(highBitsGlobal, i32)
+ builder->makeGetGlobal(INT64_TO_32_HIGH_BITS, i32)
),
builder->makeGetLocal(lowResult, i32)
);
@@ -1680,7 +1856,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
TempVar highBits = fetchOutParam(curr->value);
SetLocal* setLow = builder->makeSetLocal(lowBits, curr->value);
SetGlobal* setHigh = builder->makeSetGlobal(
- highBitsGlobal,
+ INT64_TO_32_HIGH_BITS,
builder->makeGetLocal(highBits, i32)
);
curr->value = builder->makeGetLocal(lowBits, i32);
@@ -1693,20 +1869,24 @@ private:
std::unordered_map<Index, Index> indexMap;
std::unordered_map<Expression*, TempVar> highBitVars;
std::unordered_map<Name, TempVar> labelHighBitVars;
- std::vector<Index> freeTemps;
+ std::unordered_map<int, std::vector<Index>> freeTemps;
+ std::unordered_map<Index, Type> tempTypes;
Index nextTemp;
bool needRotl64 = false;
bool needRotr64 = false;
- TempVar getTemp() {
+ TempVar getTemp(Type ty = i32) {
Index ret;
- if (freeTemps.size() > 0) {
- ret = freeTemps.back();
- freeTemps.pop_back();
+ auto &freeList = freeTemps[(int) ty];
+ if (freeList.size() > 0) {
+ ret = freeList.back();
+ freeList.pop_back();
} else {
ret = nextTemp++;
+ tempTypes[ret] = ty;
}
- return TempVar(ret, *this);
+ assert(tempTypes[ret] == ty);
+ return TempVar(ret, ty, *this);
}
bool hasOutParam(Expression* e) {
@@ -1726,8 +1906,6 @@ private:
}
};
-Name I64ToI32Lowering::highBitsGlobal("i64toi32_i32$HIGH_BITS");
-
Pass *createI64ToI32LoweringPass() {
return new I64ToI32Lowering();
}
diff --git a/src/tools/wasm2asm.cpp b/src/tools/wasm2asm.cpp
index cc53dc834..2251a332c 100644
--- a/src/tools/wasm2asm.cpp
+++ b/src/tools/wasm2asm.cpp
@@ -39,7 +39,8 @@ int main(int argc, const char *argv[]) {
})
.add("--allow-asserts", "", "Allow compilation of .wast testing asserts",
Options::Arguments::Zero,
- [](Options* o, const std::string& argument) {
+ [&](Options* o, const std::string& argument) {
+ builderFlags.allowAsserts = true;
o->extra["asserts"] = "1";
})
.add("--pedantic", "", "Emulate WebAssembly trapping behavior",
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 9e3abaa25..8dd569d82 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -114,6 +114,7 @@ public:
struct Flags {
bool debug = false;
bool pedantic = false;
+ bool allowAsserts = false;
};
Wasm2AsmBuilder(Flags f) : flags(f) {}
@@ -213,6 +214,9 @@ private:
Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
Builder& wasmBuilder,
Element& e, Name testFuncName);
+ Ref makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName);
Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
Builder& wasmBuilder,
Element& e, Name testFuncName);
@@ -416,13 +420,35 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
}
tableSize = pow2ed;
// globals
+ bool generateFetchHighBits = false;
for (auto& global : wasm->globals) {
addGlobal(asmFunc[3], global.get());
+ if (flags.allowAsserts && global->name == INT64_TO_32_HIGH_BITS) {
+ generateFetchHighBits = true;
+ }
}
// functions
for (auto& func : wasm->functions) {
asmFunc[3]->push_back(processFunction(func.get()));
}
+ if (generateFetchHighBits) {
+ Builder builder(allocator);
+ std::vector<Type> params;
+ std::vector<Type> vars;
+ asmFunc[3]->push_back(processFunction(builder.makeFunction(
+ WASM_FETCH_HIGH_BITS,
+ std::move(params),
+ i32,
+ std::move(vars),
+ builder.makeGetGlobal(INT64_TO_32_HIGH_BITS, i32)
+ )));
+ auto e = new Export();
+ e->name = WASM_FETCH_HIGH_BITS;
+ e->value = WASM_FETCH_HIGH_BITS;
+ e->kind = ExternalKind::Function;
+ wasm->addExport(e);
+ }
+
addTables(asmFunc[3], wasm);
// memory XXX
addExports(asmFunc[3], wasm);
@@ -1320,6 +1346,18 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref visitConst(Const* curr) {
switch (curr->type) {
case i32: return ValueBuilder::makeInt(curr->value.geti32());
+ // An i64 argument translates to two actual arguments to asm.js
+ // 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);
+ std::ostringstream out;
+ out << lo << "," << hi;
+ std::string os = out.str();
+ IString name(os.c_str(), false);
+ return ValueBuilder::makeName(name);
+ }
case f32: {
Ref ret = ValueBuilder::makeCall(MATH_FROUND);
Const fake(allocator);
@@ -1393,6 +1431,31 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), zero)
);
}
+ // generate (~~expr), what Emscripten does
+ case TruncSFloat32ToInt32:
+ case TruncSFloat64ToInt32:
+ return ValueBuilder::makeUnary(
+ B_NOT,
+ ValueBuilder::makeUnary(
+ B_NOT,
+ visit(curr->value, EXPRESSION_RESULT)
+ ));
+
+ // generate (~~expr >>> 0), what Emscripten does
+ case TruncUFloat32ToInt32:
+ case TruncUFloat64ToInt32:
+ return ValueBuilder::makeBinary(
+ ValueBuilder::makeUnary(
+ B_NOT,
+ ValueBuilder::makeUnary(
+ B_NOT,
+ visit(curr->value, EXPRESSION_RESULT)
+ )
+ ),
+ TRSHIFT,
+ ValueBuilder::makeNum(0)
+ );
+
default: {
std::cerr << "Unhandled unary i32 operator: " << curr
<< std::endl;
@@ -1470,6 +1533,37 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), zero)
);
}
+ // Coerce the integer to a float as emscripten does
+ case ConvertSInt32ToFloat32:
+ return makeAsmCoercion(
+ makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_INT),
+ ASM_FLOAT
+ );
+ case ConvertSInt32ToFloat64:
+ return makeAsmCoercion(
+ makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_INT),
+ ASM_DOUBLE
+ );
+
+ // Generate (expr >>> 0), followed by a coercion
+ case ConvertUInt32ToFloat32:
+ return makeAsmCoercion(
+ ValueBuilder::makeBinary(
+ visit(curr->value, EXPRESSION_RESULT),
+ TRSHIFT,
+ ValueBuilder::makeInt(0)
+ ),
+ ASM_FLOAT
+ );
+ case ConvertUInt32ToFloat64:
+ return makeAsmCoercion(
+ ValueBuilder::makeBinary(
+ visit(curr->value, EXPRESSION_RESULT),
+ TRSHIFT,
+ ValueBuilder::makeInt(0)
+ ),
+ ASM_DOUBLE
+ );
// TODO: more complex unary conversions
default:
std::cerr << "Unhandled unary float operator: " << curr
@@ -1756,7 +1850,36 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
return ExpressionProcessor(this, func).visit(func->body, result);
}
-static Ref makeInstantiation() {
+static void makeInstantiation(Ref ret) {
+ // var __array_buffer = new ArrayBuffer(..)
+ Ref mem = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000)));
+ Ref arrayBuffer = ValueBuilder::makeVar();
+ Name buffer("__array_buffer");
+ ValueBuilder::appendToVar(arrayBuffer, buffer, mem);
+ flattenAppend(ret, arrayBuffer);
+
+ // var HEAP32 = new Int32Array(__array_buffer);
+ Ref heap32Array = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(INT32ARRAY, ValueBuilder::makeName(buffer)));
+ Ref heap32 = ValueBuilder::makeVar();
+ ValueBuilder::appendToVar(heap32, HEAP32, heap32Array);
+ flattenAppend(ret, heap32);
+
+ // var HEAPF32 = new Float32Array(__array_buffer);
+ Ref heapf32Array = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(FLOAT32ARRAY, ValueBuilder::makeName(buffer)));
+ Ref heapf32 = ValueBuilder::makeVar();
+ ValueBuilder::appendToVar(heapf32, HEAPF32, heapf32Array);
+ flattenAppend(ret, heapf32);
+
+ // var HEAPF64 = new Float64Array(__array_buffer);
+ Ref heapf64Array = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(FLOAT64ARRAY, ValueBuilder::makeName(buffer)));
+ Ref heapf64 = ValueBuilder::makeVar();
+ ValueBuilder::appendToVar(heapf64, HEAPF64, heapf64Array);
+ flattenAppend(ret, heapf64);
+
Ref lib = ValueBuilder::makeObject();
auto insertItem = [&](IString item) {
ValueBuilder::appendToObject(lib, item, ValueBuilder::makeName(item));
@@ -1771,12 +1894,56 @@ static Ref makeInstantiation() {
insertItem(FLOAT32ARRAY);
insertItem(FLOAT64ARRAY);
Ref env = ValueBuilder::makeObject();
- Ref mem = ValueBuilder::makeNew(
- ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000)));
- Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env, mem);
- Ref ret = ValueBuilder::makeVar();
- ValueBuilder::appendToVar(ret, ASM_MODULE, call);
- return ret;
+ Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env,
+ ValueBuilder::makeName(buffer));
+ Ref module = ValueBuilder::makeVar();
+ ValueBuilder::appendToVar(module, ASM_MODULE, call);
+ flattenAppend(ret, module);
+
+ // When equating floating point values in spec tests we want to use bitwise
+ // equality like wasm does. Unfortunately though NaN makes this tricky. JS
+ // implementations like Spidermonkey and JSC will canonicalize NaN loads from
+ // `Float32Array`, but V8 will not. This means that NaN representations are
+ // kind of all over the place and difficult to bitwise equate.
+ //
+ // To work around this problem we just use a small shim which considers all
+ // NaN representations equivalent and otherwise tests for bitwise equality.
+ flattenAppend(ret, ValueBuilder::makeName(R"(
+ function f32Equal(a, b) {
+ var i = new Int32Array(1);
+ var f = new Float32Array(i.buffer);
+ f[0] = a;
+ var ai = f[0];
+ f[0] = b;
+ var bi = f[0];
+
+ return (isNaN(a) && isNaN(b)) || a == b;
+ }
+ )"));
+ flattenAppend(ret, ValueBuilder::makeName(R"(
+ function f64Equal(a, b) {
+ var i = new Int32Array(2);
+ var f = new Float64Array(i.buffer);
+ f[0] = a;
+ var ai1 = f[0];
+ var ai2 = f[1];
+ f[0] = b;
+ var bi1 = f[0];
+ var bi2 = f[1];
+
+ return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
+ }
+ )"));
+
+ // 64-bit numbers get a different ABI w/ wasm2asm, and in general you can't
+ // actually export them from wasm at the boundary. We hack around this though
+ // to get the spec tests working.
+ flattenAppend(ret, ValueBuilder::makeName(R"(
+ function i64Equal(actual_lo, expected_lo, expected_hi) {
+ return actual_lo == (expected_lo | 0) &&
+ asmModule.__wasm_fetch_high_bits() == (expected_hi | 0);
+ }
+ )"));
}
static void prefixCalls(Ref asmjs) {
@@ -1787,11 +1954,25 @@ static void prefixCalls(Ref asmjs) {
}
if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) {
assert(arr.size() >= 2);
- Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE),
- arr[1]->getIString());
- arr[1]->setArray(prefixed->getArray());
+ if (arr[1]->getIString() == "f32Equal" ||
+ arr[1]->getIString() == "f64Equal" ||
+ arr[1]->getIString() == "i64Equal") {
+ // ...
+ } 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),
+ arr[1]->getIString());
+ arr[1]->setArray(prefixed->getArray());
+ }
}
}
+
+ if (asmjs->isAssign()) {
+ prefixCalls(asmjs->asAssign()->target());
+ prefixCalls(asmjs->asAssign()->value());
+ }
}
Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
@@ -1812,18 +1993,29 @@ Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
Expression* expected = sexpBuilder.parseExpression(e[2]);
Type resType = expected->type;
actual->type = resType;
- BinaryOp eqOp;
switch (resType) {
- case i32: eqOp = EqInt32; break;
- case i64: eqOp = EqInt64; break;
- case f32: eqOp = EqFloat32; break;
- case f64: eqOp = EqFloat64; break;
+ case i32:
+ body = wasmBuilder.makeBinary(EqInt32, actual, expected);
+ break;
+
+ case i64:
+ body = wasmBuilder.makeCall("i64Equal", {actual, expected}, i32);
+ break;
+
+ case f32: {
+ body = wasmBuilder.makeCall("f32Equal", {actual, expected}, i32);
+ break;
+ }
+ case f64: {
+ body = wasmBuilder.makeCall("f64Equal", {actual, expected}, i32);
+ break;
+ }
+
default: {
std::cerr << "Unhandled type in assert: " << resType << std::endl;
abort();
}
}
- body = wasmBuilder.makeBinary(eqOp, actual, expected);
} else {
assert(false && "Unexpected number of parameters in assert_return");
}
@@ -1841,6 +2033,25 @@ Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
return jsFunc;
}
+Ref Wasm2AsmBuilder::makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName) {
+ Expression* actual = sexpBuilder.parseExpression(e[1]);
+ Expression* body = wasmBuilder.makeCallImport("isNaN", {actual}, i32);
+ std::unique_ptr<Function> testFunc(
+ wasmBuilder.makeFunction(
+ testFuncName,
+ std::vector<NameType>{},
+ body->type,
+ std::vector<NameType>{},
+ body
+ )
+ );
+ Ref jsFunc = processFunction(testFunc.get());
+ prefixCalls(jsFunc);
+ return jsFunc;
+}
+
Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
Builder& wasmBuilder,
Element& e, Name testFuncName) {
@@ -1855,6 +2066,7 @@ Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
);
IString expectedErr = e[2]->str();
Ref innerFunc = processFunction(exprFunc.get());
+ prefixCalls(innerFunc);
Ref outerFunc = ValueBuilder::makeFunction(testFuncName);
outerFunc[3]->push_back(innerFunc);
Ref tryBlock = ValueBuilder::makeBlock();
@@ -2042,6 +2254,7 @@ void Wasm2AsmBuilder::addMemoryGrowthFuncs(Ref ast) {
bool Wasm2AsmBuilder::isAssertHandled(Element& e) {
return e.isList() && e.size() >= 2 && e[0]->isStr()
&& (e[0]->str() == Name("assert_return") ||
+ e[0]->str() == Name("assert_return_nan") ||
(flags.pedantic && e[0]->str() == Name("assert_trap")))
&& e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr()
&& (*e[1])[0]->str() == Name("invoke");
@@ -2051,7 +2264,7 @@ Ref Wasm2AsmBuilder::processAsserts(Element& root,
SExpressionWasmBuilder& sexpBuilder) {
Builder wasmBuilder(sexpBuilder.getAllocator());
Ref ret = ValueBuilder::makeBlock();
- flattenAppend(ret, makeInstantiation());
+ makeInstantiation(ret);
for (size_t i = 1; i < root.size(); ++i) {
Element& e = *root[i];
if (!isAssertHandled(e)) {
@@ -2060,6 +2273,7 @@ Ref Wasm2AsmBuilder::processAsserts(Element& root,
}
Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false));
bool isReturn = (e[0]->str() == Name("assert_return"));
+ bool isReturnNan = (e[0]->str() == Name("assert_return_nan"));
Element& testOp = *e[1];
// Replace "invoke" with "call"
testOp[0]->setString(IString("call"), false, false);
@@ -2068,7 +2282,9 @@ Ref Wasm2AsmBuilder::processAsserts(Element& root,
Ref testFunc = isReturn ?
makeAssertReturnFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
- makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName);
+ (isReturnNan ?
+ makeAssertReturnNanFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
+ makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName));
flattenAppend(ret, testFunc);
std::stringstream failFuncName;
diff --git a/test/wasm2asm.asserts.js b/test/wasm2asm.asserts.js
index 0b347b02c..a79b4dc7a 100644
--- a/test/wasm2asm.asserts.js
+++ b/test/wasm2asm.asserts.js
@@ -70,13 +70,22 @@ function asmFunc(global, env, buffer) {
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_fetch_high_bits() {
+ return i64toi32_i32$HIGH_BITS | 0;
+ }
+
return {
empty: $0,
add: $1,
- div_s: $2
+ div_s: $2,
+ __wasm_fetch_high_bits: __wasm_fetch_high_bits
};
}
+var __array_buffer = new ArrayBuffer(65536);
+var HEAP32 = new Int32Array(__array_buffer);
+var HEAPF32 = new Float32Array(__array_buffer);
+var HEAPF64 = new Float64Array(__array_buffer);
var asmModule = asmFunc({
Math: Math,
Int8Array: Int8Array,
@@ -89,7 +98,39 @@ var asmModule = asmFunc({
Float64Array: Float64Array
}, {
-}, new ArrayBuffer(65536));
+}, __array_buffer);
+
+ function f32Equal(a, b) {
+ var i = new Int32Array(1);
+ var f = new Float32Array(i.buffer);
+ f[0] = a;
+ var ai = f[0];
+ f[0] = b;
+ var bi = f[0];
+
+ return (isNaN(a) && isNaN(b)) || a == b;
+ }
+ ;
+
+ function f64Equal(a, b) {
+ var i = new Int32Array(2);
+ var f = new Float64Array(i.buffer);
+ f[0] = a;
+ var ai1 = f[0];
+ var ai2 = f[1];
+ f[0] = b;
+ var bi1 = f[0];
+ var bi2 = f[1];
+
+ return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
+ }
+ ;
+
+ function i64Equal(actual_lo, expected_lo, expected_hi) {
+ return actual_lo == (expected_lo | 0) &&
+ asmModule.__wasm_fetch_high_bits() == (expected_hi | 0);
+ }
+ ;
function check1() {
var wasm2asm_i32$0 = 0;
asmModule.empty();
diff --git a/test/wasm2asm.traps.js b/test/wasm2asm.traps.js
index 45a04f4f1..cc16410f1 100644
--- a/test/wasm2asm.traps.js
+++ b/test/wasm2asm.traps.js
@@ -70,13 +70,22 @@ function asmFunc(global, env, buffer) {
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_fetch_high_bits() {
+ return i64toi32_i32$HIGH_BITS | 0;
+ }
+
return {
empty: $0,
add: $1,
- div_s: $2
+ div_s: $2,
+ __wasm_fetch_high_bits: __wasm_fetch_high_bits
};
}
+var __array_buffer = new ArrayBuffer(65536);
+var HEAP32 = new Int32Array(__array_buffer);
+var HEAPF32 = new Float32Array(__array_buffer);
+var HEAPF64 = new Float64Array(__array_buffer);
var asmModule = asmFunc({
Math: Math,
Int8Array: Int8Array,
@@ -89,7 +98,39 @@ var asmModule = asmFunc({
Float64Array: Float64Array
}, {
-}, new ArrayBuffer(65536));
+}, __array_buffer);
+
+ function f32Equal(a, b) {
+ var i = new Int32Array(1);
+ var f = new Float32Array(i.buffer);
+ f[0] = a;
+ var ai = f[0];
+ f[0] = b;
+ var bi = f[0];
+
+ return (isNaN(a) && isNaN(b)) || a == b;
+ }
+ ;
+
+ function f64Equal(a, b) {
+ var i = new Int32Array(2);
+ var f = new Float64Array(i.buffer);
+ f[0] = a;
+ var ai1 = f[0];
+ var ai2 = f[1];
+ f[0] = b;
+ var bi1 = f[0];
+ var bi2 = f[1];
+
+ return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
+ }
+ ;
+
+ function i64Equal(actual_lo, expected_lo, expected_hi) {
+ return actual_lo == (expected_lo | 0) &&
+ asmModule.__wasm_fetch_high_bits() == (expected_hi | 0);
+ }
+ ;
function check1() {
var wasm2asm_i32$0 = 0;
asmModule.empty();
@@ -105,7 +146,7 @@ function check2() {
if (!check2()) fail2();
function check3() {
function f() {
- div_s(0 | 0, 0 | 0);
+ asmModule.div_s(0 | 0, 0 | 0);
}
try {
@@ -119,7 +160,7 @@ function check3() {
if (!check3()) fail3();
function check4() {
function f() {
- div_s(2147483648 | 0, 4294967295 | 0);
+ asmModule.div_s(2147483648 | 0, 4294967295 | 0);
}
try {
diff --git a/test/wasm2asm/conversions-modified.2asm.js b/test/wasm2asm/conversions-modified.2asm.js
new file mode 100644
index 000000000..89c52e3a5
--- /dev/null
+++ b/test/wasm2asm/conversions-modified.2asm.js
@@ -0,0 +1,260 @@
+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;
+ var Math_min = global.Math.min;
+ var Math_max = global.Math.max;
+ var Math_floor = global.Math.floor;
+ var Math_ceil = global.Math.ceil;
+ var Math_sqrt = global.Math.sqrt;
+ var i64toi32_i32$HIGH_BITS = 0;
+ function $0(x) {
+ x = x | 0;
+ var i64toi32_i32$1 = 0, i64toi32_i32$0 = 0;
+ i64toi32_i32$1 = x;
+ i64toi32_i32$0 = i64toi32_i32$1 >> 31 | 0;
+ i64toi32_i32$1 = i64toi32_i32$1;
+ i64toi32_i32$HIGH_BITS = i64toi32_i32$0;
+ return i64toi32_i32$1 | 0;
+ }
+
+ function $1(x) {
+ x = x | 0;
+ i64toi32_i32$HIGH_BITS = 0;
+ return x | 0;
+ }
+
+ function $2(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ return x | 0;
+ }
+
+ function $3(x) {
+ x = Math_fround(x);
+ return ~~x | 0;
+ }
+
+ function $4(x) {
+ x = Math_fround(x);
+ return ~~x >>> 0 | 0;
+ }
+
+ function $5(x) {
+ x = +x;
+ return ~~x | 0;
+ }
+
+ function $6(x) {
+ x = +x;
+ return ~~x >>> 0 | 0;
+ }
+
+ function $7(x) {
+ x = Math_fround(x);
+ var i64toi32_i32$0 = Math_fround(0), $2 = 0, $3 = 0, i64toi32_i32$2 = 0;
+ i64toi32_i32$0 = x;
+ if (Math_fround(Math_abs(i64toi32_i32$0)) >= Math_fround(1.0)) {
+ if (i64toi32_i32$0 > Math_fround(0.0)) $2 = ~~Math_fround(Math_min(Math_fround(Math_floor(Math_fround(i64toi32_i32$0 / Math_fround(4294967296.0)))), Math_fround(Math_fround(4294967296.0) - Math_fround(1.0)))) >>> 0; else $2 = ~~Math_fround(Math_ceil(Math_fround(Math_fround(i64toi32_i32$0 - Math_fround(~~i64toi32_i32$0 >>> 0 >>> 0)) / Math_fround(4294967296.0)))) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ i64toi32_i32$2 = ~~i64toi32_i32$0 >>> 0;
+ i64toi32_i32$HIGH_BITS = $3;
+ return i64toi32_i32$2 | 0;
+ }
+
+ function $8(x) {
+ x = Math_fround(x);
+ var i64toi32_i32$0 = Math_fround(0), $2 = 0, $3 = 0, i64toi32_i32$2 = 0;
+ i64toi32_i32$0 = x;
+ if (Math_fround(Math_abs(i64toi32_i32$0)) >= Math_fround(1.0)) {
+ if (i64toi32_i32$0 > Math_fround(0.0)) $2 = ~~Math_fround(Math_min(Math_fround(Math_floor(Math_fround(i64toi32_i32$0 / Math_fround(4294967296.0)))), Math_fround(Math_fround(4294967296.0) - Math_fround(1.0)))) >>> 0; else $2 = ~~Math_fround(Math_ceil(Math_fround(Math_fround(i64toi32_i32$0 - Math_fround(~~i64toi32_i32$0 >>> 0 >>> 0)) / Math_fround(4294967296.0)))) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ i64toi32_i32$2 = ~~i64toi32_i32$0 >>> 0;
+ i64toi32_i32$HIGH_BITS = $3;
+ return i64toi32_i32$2 | 0;
+ }
+
+ function $9(x) {
+ x = +x;
+ var i64toi32_i32$0 = 0.0, $2 = 0, $3 = 0, i64toi32_i32$2 = 0;
+ i64toi32_i32$0 = x;
+ if (Math_abs(i64toi32_i32$0) >= 1.0) {
+ if (i64toi32_i32$0 > 0.0) $2 = ~~Math_min(Math_floor(i64toi32_i32$0 / 4294967296.0), 4294967296.0 - 1.0) >>> 0; else $2 = ~~Math_ceil((i64toi32_i32$0 - +(~~i64toi32_i32$0 >>> 0 >>> 0)) / 4294967296.0) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ i64toi32_i32$2 = ~~i64toi32_i32$0 >>> 0;
+ i64toi32_i32$HIGH_BITS = $3;
+ return i64toi32_i32$2 | 0;
+ }
+
+ function $10(x) {
+ x = +x;
+ var i64toi32_i32$0 = 0.0, $2 = 0, $3 = 0, i64toi32_i32$2 = 0;
+ i64toi32_i32$0 = x;
+ if (Math_abs(i64toi32_i32$0) >= 1.0) {
+ if (i64toi32_i32$0 > 0.0) $2 = ~~Math_min(Math_floor(i64toi32_i32$0 / 4294967296.0), 4294967296.0 - 1.0) >>> 0; else $2 = ~~Math_ceil((i64toi32_i32$0 - +(~~i64toi32_i32$0 >>> 0 >>> 0)) / 4294967296.0) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ i64toi32_i32$2 = ~~i64toi32_i32$0 >>> 0;
+ i64toi32_i32$HIGH_BITS = $3;
+ return i64toi32_i32$2 | 0;
+ }
+
+ function $11(x) {
+ x = x | 0;
+ return Math_fround(Math_fround(x | 0));
+ }
+
+ function $12(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ return Math_fround(Math_fround(+(x >>> 0) + 4294967296.0 * +(x$hi | 0)));
+ }
+
+ function $13(x) {
+ x = x | 0;
+ return +(+(x | 0));
+ }
+
+ function $14(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ return +(+(x >>> 0) + 4294967296.0 * +(x$hi | 0));
+ }
+
+ function $15(x) {
+ x = x | 0;
+ return Math_fround(Math_fround(x >>> 0));
+ }
+
+ function $16(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ return Math_fround(Math_fround(+(x >>> 0) + 4294967296.0 * +(x$hi >>> 0)));
+ }
+
+ function $17(x) {
+ x = x | 0;
+ return +(+(x >>> 0));
+ }
+
+ function $18(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ return +(+(x >>> 0) + 4294967296.0 * +(x$hi >>> 0));
+ }
+
+ function $19(x) {
+ x = Math_fround(x);
+ return +(+x);
+ }
+
+ function $20(x) {
+ x = +x;
+ return Math_fround(Math_fround(x));
+ }
+
+ function $21(x) {
+ x = x | 0;
+ return Math_fround((HEAP32[0] = x, HEAPF32[0]));
+ }
+
+ function $22(x, x$hi) {
+ x = x | 0;
+ x$hi = x$hi | 0;
+ HEAP32[0 >> 2] = x;
+ HEAP32[(0 + 4 | 0) >> 2] = x$hi;
+ return +(+HEAPF64[0 >> 3]);
+ }
+
+ function $23(x) {
+ x = Math_fround(x);
+ return (HEAPF32[0] = x, HEAP32[0]) | 0;
+ }
+
+ function $24(x) {
+ x = +x;
+ var i64toi32_i32$1 = 0;
+ HEAPF64[0 >> 3] = x;
+ i64toi32_i32$1 = HEAP32[0 >> 2] | 0;
+ i64toi32_i32$HIGH_BITS = HEAP32[(0 + 4 | 0) >> 2] | 0;
+ return i64toi32_i32$1 | 0;
+ }
+
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var $1 = 0;
+ if ((x | 0) == (0 | 0)) $1 = 32; else $1 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return $1 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, $2 = 0;
+ count = 0;
+ b : {
+ l : do {
+ $2 = count;
+ if ((x | 0) == (0 | 0)) break b;
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return $2 | 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 {
+ i64_extend_s_i32: $0,
+ i64_extend_u_i32: $1,
+ i32_wrap_i64: $2,
+ i32_trunc_s_f32: $3,
+ i32_trunc_u_f32: $4,
+ i32_trunc_s_f64: $5,
+ i32_trunc_u_f64: $6,
+ i64_trunc_s_f32: $7,
+ i64_trunc_u_f32: $8,
+ i64_trunc_s_f64: $9,
+ i64_trunc_u_f64: $10,
+ f32_convert_s_i32: $11,
+ f32_convert_s_i64: $12,
+ f64_convert_s_i32: $13,
+ f64_convert_s_i64: $14,
+ f32_convert_u_i32: $15,
+ f32_convert_u_i64: $16,
+ f64_convert_u_i32: $17,
+ f64_convert_u_i64: $18,
+ f64_promote_f32: $19,
+ f32_demote_f64: $20,
+ f32_reinterpret_i32: $21,
+ f64_reinterpret_i64: $22,
+ i32_reinterpret_f32: $23,
+ i64_reinterpret_f64: $24
+ };
+}
+
diff --git a/test/wasm2asm/conversions-modified.wast b/test/wasm2asm/conversions-modified.wast
new file mode 100644
index 000000000..4e410032b
--- /dev/null
+++ b/test/wasm2asm/conversions-modified.wast
@@ -0,0 +1,419 @@
+(module
+ (func (export "i64.extend_s_i32") (param $x i32) (result i64) (i64.extend_s/i32 (get_local $x)))
+ (func (export "i64.extend_u_i32") (param $x i32) (result i64) (i64.extend_u/i32 (get_local $x)))
+ (func (export "i32.wrap_i64") (param $x i64) (result i32) (i32.wrap/i64 (get_local $x)))
+ (func (export "i32.trunc_s_f32") (param $x f32) (result i32) (i32.trunc_s/f32 (get_local $x)))
+ (func (export "i32.trunc_u_f32") (param $x f32) (result i32) (i32.trunc_u/f32 (get_local $x)))
+ (func (export "i32.trunc_s_f64") (param $x f64) (result i32) (i32.trunc_s/f64 (get_local $x)))
+ (func (export "i32.trunc_u_f64") (param $x f64) (result i32) (i32.trunc_u/f64 (get_local $x)))
+ (func (export "i64.trunc_s_f32") (param $x f32) (result i64) (i64.trunc_s/f32 (get_local $x)))
+ (func (export "i64.trunc_u_f32") (param $x f32) (result i64) (i64.trunc_u/f32 (get_local $x)))
+ (func (export "i64.trunc_s_f64") (param $x f64) (result i64) (i64.trunc_s/f64 (get_local $x)))
+ (func (export "i64.trunc_u_f64") (param $x f64) (result i64) (i64.trunc_u/f64 (get_local $x)))
+ (func (export "f32.convert_s_i32") (param $x i32) (result f32) (f32.convert_s/i32 (get_local $x)))
+ (func (export "f32.convert_s_i64") (param $x i64) (result f32) (f32.convert_s/i64 (get_local $x)))
+ (func (export "f64.convert_s_i32") (param $x i32) (result f64) (f64.convert_s/i32 (get_local $x)))
+ (func (export "f64.convert_s_i64") (param $x i64) (result f64) (f64.convert_s/i64 (get_local $x)))
+ (func (export "f32.convert_u_i32") (param $x i32) (result f32) (f32.convert_u/i32 (get_local $x)))
+ (func (export "f32.convert_u_i64") (param $x i64) (result f32) (f32.convert_u/i64 (get_local $x)))
+ (func (export "f64.convert_u_i32") (param $x i32) (result f64) (f64.convert_u/i32 (get_local $x)))
+ (func (export "f64.convert_u_i64") (param $x i64) (result f64) (f64.convert_u/i64 (get_local $x)))
+ (func (export "f64.promote_f32") (param $x f32) (result f64) (f64.promote/f32 (get_local $x)))
+ (func (export "f32.demote_f64") (param $x f64) (result f32) (f32.demote/f64 (get_local $x)))
+ (func (export "f32.reinterpret_i32") (param $x i32) (result f32) (f32.reinterpret/i32 (get_local $x)))
+ (func (export "f64.reinterpret_i64") (param $x i64) (result f64) (f64.reinterpret/i64 (get_local $x)))
+ (func (export "i32.reinterpret_f32") (param $x f32) (result i32) (i32.reinterpret/f32 (get_local $x)))
+ (func (export "i64.reinterpret_f64") (param $x f64) (result i64) (i64.reinterpret/f64 (get_local $x)))
+)
+
+(assert_return (invoke "i64.extend_s_i32" (i32.const 0)) (i64.const 0))
+(assert_return (invoke "i64.extend_s_i32" (i32.const 10000)) (i64.const 10000))
+(assert_return (invoke "i64.extend_s_i32" (i32.const -10000)) (i64.const -10000))
+(assert_return (invoke "i64.extend_s_i32" (i32.const -1)) (i64.const -1))
+(assert_return (invoke "i64.extend_s_i32" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff))
+(assert_return (invoke "i64.extend_s_i32" (i32.const 0x80000000)) (i64.const 0xffffffff80000000))
+
+(assert_return (invoke "i64.extend_u_i32" (i32.const 0)) (i64.const 0))
+(assert_return (invoke "i64.extend_u_i32" (i32.const 10000)) (i64.const 10000))
+(assert_return (invoke "i64.extend_u_i32" (i32.const -10000)) (i64.const 0x00000000ffffd8f0))
+(assert_return (invoke "i64.extend_u_i32" (i32.const -1)) (i64.const 0xffffffff))
+(assert_return (invoke "i64.extend_u_i32" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff))
+(assert_return (invoke "i64.extend_u_i32" (i32.const 0x80000000)) (i64.const 0x0000000080000000))
+
+(assert_return (invoke "i32.wrap_i64" (i64.const -1)) (i32.const -1))
+(assert_return (invoke "i32.wrap_i64" (i64.const -100000)) (i32.const -100000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x80000000)) (i32.const 0x80000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff7fffffff)) (i32.const 0x7fffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000000)) (i32.const 0x00000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xfffffffeffffffff)) (i32.const 0xffffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000001)) (i32.const 0x00000001))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0)) (i32.const 0))
+(assert_return (invoke "i32.wrap_i64" (i64.const 1311768467463790320)) (i32.const 0x9abcdef0))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x00000000ffffffff)) (i32.const 0xffffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000000)) (i32.const 0x00000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000001)) (i32.const 0x00000001))
+
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 0x1p-149)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -0x1p-149)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 1.0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 0x1.19999ap+0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 1.5)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -1.0)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -0x1.19999ap+0)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -1.5)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -1.9)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -2.0)) (i32.const -2))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const 2147483520.0)) (i32.const 2147483520))
+(assert_return (invoke "i32.trunc_s_f32" (f32.const -2147483648.0)) (i32.const -2147483648))
+(assert_trap (invoke "i32.trunc_s_f32" (f32.const 2147483648.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f32" (f32.const -2147483904.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f32" (f32.const infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f32" (f32.const -infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f32" (f32.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const -0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 0x1p-149)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const -0x1p-149)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 1.0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 0x1.19999ap+0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 1.5)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 1.9)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 2.0)) (i32.const 2))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000
+(assert_return (invoke "i32.trunc_u_f32" (f32.const 4294967040.0)) (i32.const -256))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const -0x1.ccccccp-1)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f32" (f32.const -0x1.fffffep-1)) (i32.const 0))
+(assert_trap (invoke "i32.trunc_u_f32" (f32.const 4294967296.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f32" (f32.const -1.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f32" (f32.const infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f32" (f32.const -infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f32" (f32.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 0x0.0000000000001p-1022)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -0x0.0000000000001p-1022)) (i32.const 0))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 1.0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 0x1.199999999999ap+0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 1.5)) (i32.const 1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -1.0)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -0x1.199999999999ap+0)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -1.5)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -1.9)) (i32.const -1))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -2.0)) (i32.const -2))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const 2147483647.0)) (i32.const 2147483647))
+(assert_return (invoke "i32.trunc_s_f64" (f64.const -2147483648.0)) (i32.const -2147483648))
+(assert_trap (invoke "i32.trunc_s_f64" (f64.const 2147483648.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f64" (f64.const -2147483649.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f64" (f64.const infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f64" (f64.const -infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_s_f64" (f64.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const -0.0)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 0x0.0000000000001p-1022)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const -0x0.0000000000001p-1022)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 1.0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 0x1.199999999999ap+0)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 1.5)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 1.9)) (i32.const 1))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 2.0)) (i32.const 2))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 4294967295.0)) (i32.const -1))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0))
+(assert_return (invoke "i32.trunc_u_f64" (f64.const 1e8)) (i32.const 100000000))
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const 4294967296.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const -1.0)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const 1e16)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const 1e30)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const 9223372036854775808)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const -infinity)) "integer overflow")
+(assert_trap (invoke "i32.trunc_u_f64" (f64.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 0x1p-149)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -0x1p-149)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 1.0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 0x1.19999ap+0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 1.5)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -1.0)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -0x1.19999ap+0)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -1.5)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -1.9)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -2.0)) (i64.const -2))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000
+(assert_return (invoke "i64.trunc_s_f32" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920))
+(assert_return (invoke "i64.trunc_s_f32" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808))
+(assert_trap (invoke "i64.trunc_s_f32" (f32.const 9223372036854775808.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f32" (f32.const -9223373136366403584.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f32" (f32.const infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f32" (f32.const -infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f32" (f32.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const -0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 0x1p-149)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const -0x1p-149)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 1.0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 0x1.19999ap+0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 1.5)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 4294967296)) (i64.const 4294967296))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const 18446742974197923840.0)) (i64.const -1099511627776))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const -0x1.ccccccp-1)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f32" (f32.const -0x1.fffffep-1)) (i64.const 0))
+(assert_trap (invoke "i64.trunc_u_f32" (f32.const 18446744073709551616.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f32" (f32.const -1.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f32" (f32.const infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f32" (f32.const -infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f32" (f32.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 1.0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 0x1.199999999999ap+0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 1.5)) (i64.const 1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -1.0)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -0x1.199999999999ap+0)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -1.5)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -1.9)) (i64.const -1))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -2.0)) (i64.const -2))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000
+(assert_return (invoke "i64.trunc_s_f64" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784))
+(assert_return (invoke "i64.trunc_s_f64" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808))
+(assert_trap (invoke "i64.trunc_s_f64" (f64.const 9223372036854775808.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f64" (f64.const -9223372036854777856.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f64" (f64.const infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f64" (f64.const -infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_s_f64" (f64.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const -0.0)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 1.0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 0x1.199999999999ap+0)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 1.5)) (i64.const 1))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 4294967295)) (i64.const 0xffffffff))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 4294967296)) (i64.const 0x100000000))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 18446744073709549568.0)) (i64.const -2048))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 1e8)) (i64.const 100000000))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 1e16)) (i64.const 10000000000000000))
+(assert_return (invoke "i64.trunc_u_f64" (f64.const 9223372036854775808)) (i64.const -9223372036854775808))
+(assert_trap (invoke "i64.trunc_u_f64" (f64.const 18446744073709551616.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f64" (f64.const -1.0)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f64" (f64.const infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f64" (f64.const -infinity)) "integer overflow")
+(assert_trap (invoke "i64.trunc_u_f64" (f64.const nan)) "invalid conversion to integer")
+
+(assert_return (invoke "f32.convert_s_i32" (i32.const 1)) (f32.const 1.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const -1)) (f32.const -1.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const 0)) (f32.const 0.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const 2147483647)) (f32.const 2147483648))
+(assert_return (invoke "f32.convert_s_i32" (i32.const -2147483648)) (f32.const -2147483648))
+(assert_return (invoke "f32.convert_s_i32" (i32.const 1234567890)) (f32.const 0x1.26580cp+30))
+;; Test rounding directions.
+(assert_return (invoke "f32.convert_s_i32" (i32.const 16777217)) (f32.const 16777216.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const -16777217)) (f32.const -16777216.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const 16777219)) (f32.const 16777220.0))
+(assert_return (invoke "f32.convert_s_i32" (i32.const -16777219)) (f32.const -16777220.0))
+
+(assert_return (invoke "f32.convert_s_i64" (i64.const 1)) (f32.const 1.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const -1)) (f32.const -1.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const 0)) (f32.const 0.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const 9223372036854775807)) (f32.const 9223372036854775807))
+(assert_return (invoke "f32.convert_s_i64" (i64.const -9223372036854775808)) (f32.const -9223372036854775808))
+(assert_return (invoke "f32.convert_s_i64" (i64.const 314159265358979)) (f32.const 0x1.1db9e8p+48)) ;; PI
+;; Test rounding directions.
+(assert_return (invoke "f32.convert_s_i64" (i64.const 16777217)) (f32.const 16777216.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const -16777217)) (f32.const -16777216.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const 16777219)) (f32.const 16777220.0))
+(assert_return (invoke "f32.convert_s_i64" (i64.const -16777219)) (f32.const -16777220.0))
+
+(assert_return (invoke "f64.convert_s_i32" (i32.const 1)) (f64.const 1.0))
+(assert_return (invoke "f64.convert_s_i32" (i32.const -1)) (f64.const -1.0))
+(assert_return (invoke "f64.convert_s_i32" (i32.const 0)) (f64.const 0.0))
+(assert_return (invoke "f64.convert_s_i32" (i32.const 2147483647)) (f64.const 2147483647))
+(assert_return (invoke "f64.convert_s_i32" (i32.const -2147483648)) (f64.const -2147483648))
+(assert_return (invoke "f64.convert_s_i32" (i32.const 987654321)) (f64.const 987654321))
+
+(assert_return (invoke "f64.convert_s_i64" (i64.const 1)) (f64.const 1.0))
+(assert_return (invoke "f64.convert_s_i64" (i64.const -1)) (f64.const -1.0))
+(assert_return (invoke "f64.convert_s_i64" (i64.const 0)) (f64.const 0.0))
+(assert_return (invoke "f64.convert_s_i64" (i64.const 9223372036854775807)) (f64.const 9223372036854775807))
+(assert_return (invoke "f64.convert_s_i64" (i64.const -9223372036854775808)) (f64.const -9223372036854775808))
+(assert_return (invoke "f64.convert_s_i64" (i64.const 4669201609102990)) (f64.const 4669201609102990)) ;; Feigenbaum
+;; Test rounding directions.
+(assert_return (invoke "f64.convert_s_i64" (i64.const 9007199254740993)) (f64.const 9007199254740992))
+(assert_return (invoke "f64.convert_s_i64" (i64.const -9007199254740993)) (f64.const -9007199254740992))
+(assert_return (invoke "f64.convert_s_i64" (i64.const 9007199254740995)) (f64.const 9007199254740996))
+(assert_return (invoke "f64.convert_s_i64" (i64.const -9007199254740995)) (f64.const -9007199254740996))
+
+(assert_return (invoke "f32.convert_u_i32" (i32.const 1)) (f32.const 1.0))
+(assert_return (invoke "f32.convert_u_i32" (i32.const 0)) (f32.const 0.0))
+(assert_return (invoke "f32.convert_u_i32" (i32.const 2147483647)) (f32.const 2147483648))
+(assert_return (invoke "f32.convert_u_i32" (i32.const -2147483648)) (f32.const 2147483648))
+(assert_return (invoke "f32.convert_u_i32" (i32.const 0x12345678)) (f32.const 0x1.234568p+28))
+(assert_return (invoke "f32.convert_u_i32" (i32.const 0xffffffff)) (f32.const 4294967296.0))
+;; Test rounding directions.
+(assert_return (invoke "f32.convert_u_i32" (i32.const 16777217)) (f32.const 16777216.0))
+(assert_return (invoke "f32.convert_u_i32" (i32.const 16777219)) (f32.const 16777220.0))
+
+(assert_return (invoke "f32.convert_u_i64" (i64.const 1)) (f32.const 1.0))
+(assert_return (invoke "f32.convert_u_i64" (i64.const 0)) (f32.const 0.0))
+(assert_return (invoke "f32.convert_u_i64" (i64.const 9223372036854775807)) (f32.const 9223372036854775807))
+(assert_return (invoke "f32.convert_u_i64" (i64.const -9223372036854775808)) (f32.const 9223372036854775808))
+(assert_return (invoke "f32.convert_u_i64" (i64.const 0xffffffffffffffff)) (f32.const 18446744073709551616.0))
+;; Test rounding directions.
+(assert_return (invoke "f32.convert_u_i64" (i64.const 16777217)) (f32.const 16777216.0))
+(assert_return (invoke "f32.convert_u_i64" (i64.const 16777219)) (f32.const 16777220.0))
+
+(assert_return (invoke "f64.convert_u_i32" (i32.const 1)) (f64.const 1.0))
+(assert_return (invoke "f64.convert_u_i32" (i32.const 0)) (f64.const 0.0))
+(assert_return (invoke "f64.convert_u_i32" (i32.const 2147483647)) (f64.const 2147483647))
+(assert_return (invoke "f64.convert_u_i32" (i32.const -2147483648)) (f64.const 2147483648))
+(assert_return (invoke "f64.convert_u_i32" (i32.const 0xffffffff)) (f64.const 4294967295.0))
+
+(assert_return (invoke "f64.convert_u_i64" (i64.const 1)) (f64.const 1.0))
+(assert_return (invoke "f64.convert_u_i64" (i64.const 0)) (f64.const 0.0))
+(assert_return (invoke "f64.convert_u_i64" (i64.const 9223372036854775807)) (f64.const 9223372036854775807))
+(assert_return (invoke "f64.convert_u_i64" (i64.const -9223372036854775808)) (f64.const 9223372036854775808))
+(assert_return (invoke "f64.convert_u_i64" (i64.const 0xffffffffffffffff)) (f64.const 18446744073709551616.0))
+;; Test rounding directions.
+(assert_return (invoke "f64.convert_u_i64" (i64.const 9007199254740993)) (f64.const 9007199254740992))
+(assert_return (invoke "f64.convert_u_i64" (i64.const 9007199254740995)) (f64.const 9007199254740996))
+
+(assert_return (invoke "f64.promote_f32" (f32.const 0.0)) (f64.const 0.0))
+(assert_return (invoke "f64.promote_f32" (f32.const -0.0)) (f64.const -0.0))
+(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-149)) (f64.const 0x1p-149))
+(assert_return (invoke "f64.promote_f32" (f32.const -0x1p-149)) (f64.const -0x1p-149))
+(assert_return (invoke "f64.promote_f32" (f32.const 1.0)) (f64.const 1.0))
+(assert_return (invoke "f64.promote_f32" (f32.const -1.0)) (f64.const -1.0))
+(assert_return (invoke "f64.promote_f32" (f32.const -0x1.fffffep+127)) (f64.const -0x1.fffffep+127))
+(assert_return (invoke "f64.promote_f32" (f32.const 0x1.fffffep+127)) (f64.const 0x1.fffffep+127))
+;; Generated randomly by picking a random int and reinterpret it to float.
+(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-119)) (f64.const 0x1p-119))
+;; Generated randomly by picking a random float.
+(assert_return (invoke "f64.promote_f32" (f32.const 0x1.8f867ep+125)) (f64.const 6.6382536710104395e+37))
+(assert_return (invoke "f64.promote_f32" (f32.const infinity)) (f64.const infinity))
+(assert_return (invoke "f64.promote_f32" (f32.const -infinity)) (f64.const -infinity))
+(assert_return (invoke "f64.promote_f32" (f32.const nan)) (f64.const nan))
+
+(assert_return (invoke "f32.demote_f64" (f64.const 0.0)) (f32.const 0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const -0.0)) (f32.const -0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x0.0000000000001p-1022)) (f32.const 0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x0.0000000000001p-1022)) (f32.const -0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 1.0)) (f32.const 1.0))
+(assert_return (invoke "f32.demote_f64" (f64.const -1.0)) (f32.const -1.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffe0000000p-127)) (f32.const 0x1p-126))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffe0000000p-127)) (f32.const -0x1p-126))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffdfffffffp-127)) (f32.const 0x1.fffffcp-127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffdfffffffp-127)) (f32.const -0x1.fffffcp-127))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-149)) (f32.const 0x1p-149))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-149)) (f32.const -0x1p-149))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000000p+127)) (f32.const 0x1.fffffcp+127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000000p+127)) (f32.const -0x1.fffffcp+127))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000001p+127)) (f32.const 0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000001p+127)) (f32.const -0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffefffffffp+127)) (f32.const 0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffefffffffp+127)) (f32.const -0x1.fffffep+127))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.ffffffp+127)) (f32.const infinity))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.ffffffp+127)) (f32.const -infinity))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-119)) (f32.const 0x1p-119))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.8f867ep+125)) (f32.const 0x1.8f867ep+125))
+(assert_return (invoke "f32.demote_f64" (f64.const infinity)) (f32.const infinity))
+(assert_return (invoke "f32.demote_f64" (f64.const -infinity)) (f32.const -infinity))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p+0)) (f32.const 1.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffffffffffp-1)) (f32.const 1.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+0)) (f32.const 0x1.000000p+0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+0)) (f32.const 0x1.000002p+0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+0)) (f32.const 0x1.000002p+0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+0)) (f32.const 0x1.000004p+0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000050000000p+0)) (f32.const 0x1.000004p+0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+24)) (f32.const 0x1.0p+24))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+24)) (f32.const 0x1.000002p+24))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+24)) (f32.const 0x1.000002p+24))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+24)) (f32.const 0x1.000004p+24))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.4eae4f7024c7p+108)) (f32.const 0x1.4eae5p+108))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.a12e71e358685p-113)) (f32.const 0x1.a12e72p-113))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.cb98354d521ffp-127)) (f32.const 0x1.cb9834p-127))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.6972b30cfb562p+1)) (f32.const -0x1.6972b4p+1))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.bedbe4819d4c4p+112)) (f32.const -0x1.bedbe4p+112))
+(assert_return (invoke "f32.demote_f64" (f64.const nan)) (f32.const nan))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-1022)) (f32.const 0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-1022)) (f32.const -0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0p-150)) (f32.const 0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0p-150)) (f32.const -0.0))
+(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p-150)) (f32.const 0x1p-149))
+(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0000000000001p-150)) (f32.const -0x1p-149))
+
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0)) (f32.const 0.0))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x80000000)) (f32.const -0.0))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 1)) (f32.const 0x1p-149))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const -1)) (f32.const -nan:0x7fffff))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 123456789)) (f32.const 0x1.b79a2ap-113))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const -2147483647)) (f32.const -0x1p-149))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7f800000)) (f32.const infinity))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xff800000)) (f32.const -infinity))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fc00000)) (f32.const nan))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffc00000)) (f32.const -nan))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fa00000)) (f32.const nan:0x200000))
+(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffa00000)) (f32.const -nan:0x200000))
+
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0)) (f64.const 0.0))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 1)) (f64.const 0x0.0000000000001p-1022))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const -1)) (f64.const -nan:0xfffffffffffff))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x8000000000000000)) (f64.const -0.0))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 1234567890)) (f64.const 0x0.00000499602d2p-1022))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const -9223372036854775807)) (f64.const -0x0.0000000000001p-1022))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff0000000000000)) (f64.const infinity))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff0000000000000)) (f64.const -infinity))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff8000000000000)) (f64.const nan))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff8000000000000)) (f64.const -nan))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff4000000000000)) (f64.const nan:0x4000000000000))
+(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff4000000000000)) (f64.const -nan:0x4000000000000))
+
+(assert_return (invoke "i32.reinterpret_f32" (f32.const 0.0)) (i32.const 0))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const -0.0)) (i32.const 0x80000000))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1p-149)) (i32.const 1))
+;;(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x7fffff)) (i32.const -1))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1p-149)) (i32.const 0x80000001))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const 1.0)) (i32.const 1065353216))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const 3.1415926)) (i32.const 1078530010))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1.fffffep+127)) (i32.const 2139095039))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1.fffffep+127)) (i32.const -8388609))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const infinity)) (i32.const 0x7f800000))
+(assert_return (invoke "i32.reinterpret_f32" (f32.const -infinity)) (i32.const 0xff800000))
+;;(assert_return (invoke "i32.reinterpret_f32" (f32.const nan)) (i32.const 0x7fc00000))
+;;(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan)) (i32.const 0xffc00000))
+;;(assert_return (invoke "i32.reinterpret_f32" (f32.const nan:0x200000)) (i32.const 0x7fa00000))
+;;(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x200000)) (i32.const 0xffa00000))
+
+(assert_return (invoke "i64.reinterpret_f64" (f64.const 0.0)) (i64.const 0))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const -0.0)) (i64.const 0x8000000000000000))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 1))
+;;(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0xfffffffffffff)) (i64.const -1))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0x8000000000000001))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const 1.0)) (i64.const 4607182418800017408))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const 3.14159265358979)) (i64.const 4614256656552045841))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x1.fffffffffffffp+1023)) (i64.const 9218868437227405311))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x1.fffffffffffffp+1023)) (i64.const -4503599627370497))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const infinity)) (i64.const 0x7ff0000000000000))
+(assert_return (invoke "i64.reinterpret_f64" (f64.const -infinity)) (i64.const 0xfff0000000000000))
+;;(assert_return (invoke "i64.reinterpret_f64" (f64.const nan)) (i64.const 0x7ff8000000000000))
+;;(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan)) (i64.const 0xfff8000000000000))
+;;(assert_return (invoke "i64.reinterpret_f64" (f64.const nan:0x4000000000000)) (i64.const 0x7ff4000000000000))
+;;(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0x4000000000000)) (i64.const 0xfff4000000000000))
diff --git a/test/wasm2asm/float-ops.2asm.js b/test/wasm2asm/float-ops.2asm.js
index bad4bae9d..4d34766db 100644
--- a/test/wasm2asm/float-ops.2asm.js
+++ b/test/wasm2asm/float-ops.2asm.js
@@ -235,6 +235,114 @@ function asmFunc(global, env, buffer) {
return Math_fround((HEAP32[0] = (HEAPF32[0] = $0, HEAP32[0]) & 2147483647 | 0 | ((HEAPF32[0] = $1, HEAP32[0]) & 2147483648 | 0) | 0, HEAPF32[0]));
}
+ function $35($0) {
+ $0 = $0 | 0;
+ return Math_fround(Math_fround($0 | 0));
+ }
+
+ function $36($0) {
+ $0 = $0 | 0;
+ return +(+($0 | 0));
+ }
+
+ function $37($0) {
+ $0 = $0 | 0;
+ return Math_fround(Math_fround($0 >>> 0));
+ }
+
+ function $38($0) {
+ $0 = $0 | 0;
+ return +(+($0 >>> 0));
+ }
+
+ function $39($0) {
+ $0 = Math_fround($0);
+ return ~~$0 | 0;
+ }
+
+ function $40($0) {
+ $0 = +$0;
+ return ~~$0 | 0;
+ }
+
+ function $41($0) {
+ $0 = Math_fround($0);
+ return ~~$0 >>> 0 | 0;
+ }
+
+ function $42($0) {
+ $0 = +$0;
+ return ~~$0 >>> 0 | 0;
+ }
+
+ function $43($0, $0$hi) {
+ $0 = $0 | 0;
+ $0$hi = $0$hi | 0;
+ return Math_fround(Math_fround(+($0 >>> 0) + 4294967296.0 * +($0$hi | 0)));
+ }
+
+ function $44($0, $0$hi) {
+ $0 = $0 | 0;
+ $0$hi = $0$hi | 0;
+ return +(+($0 >>> 0) + 4294967296.0 * +($0$hi | 0));
+ }
+
+ function $45($0, $0$hi) {
+ $0 = $0 | 0;
+ $0$hi = $0$hi | 0;
+ return Math_fround(Math_fround(+($0 >>> 0) + 4294967296.0 * +($0$hi >>> 0)));
+ }
+
+ function $46($0, $0$hi) {
+ $0 = $0 | 0;
+ $0$hi = $0$hi | 0;
+ return +(+($0 >>> 0) + 4294967296.0 * +($0$hi >>> 0));
+ }
+
+ function $47($0) {
+ $0 = Math_fround($0);
+ var i64toi32_i32$0 = Math_fround(0), $2 = 0, $3 = 0;
+ i64toi32_i32$0 = $0;
+ if (Math_fround(Math_abs(i64toi32_i32$0)) >= Math_fround(1.0)) {
+ if (i64toi32_i32$0 > Math_fround(0.0)) $2 = ~~Math_fround(Math_min(Math_fround(Math_floor(Math_fround(i64toi32_i32$0 / Math_fround(4294967296.0)))), Math_fround(Math_fround(4294967296.0) - Math_fround(1.0)))) >>> 0; else $2 = ~~Math_fround(Math_ceil(Math_fround(Math_fround(i64toi32_i32$0 - Math_fround(~~i64toi32_i32$0 >>> 0 >>> 0)) / Math_fround(4294967296.0)))) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ return (~~i64toi32_i32$0 >>> 0 | 0) == (0 | 0) & ($3 | 0) == (0 | 0) | 0 | 0;
+ }
+
+ function $48($0) {
+ $0 = +$0;
+ var i64toi32_i32$0 = 0.0, $2 = 0, $3 = 0;
+ i64toi32_i32$0 = $0;
+ if (Math_abs(i64toi32_i32$0) >= 1.0) {
+ if (i64toi32_i32$0 > 0.0) $2 = ~~Math_min(Math_floor(i64toi32_i32$0 / 4294967296.0), 4294967296.0 - 1.0) >>> 0; else $2 = ~~Math_ceil((i64toi32_i32$0 - +(~~i64toi32_i32$0 >>> 0 >>> 0)) / 4294967296.0) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ return (~~i64toi32_i32$0 >>> 0 | 0) == (0 | 0) & ($3 | 0) == (0 | 0) | 0 | 0;
+ }
+
+ function $49($0) {
+ $0 = Math_fround($0);
+ var i64toi32_i32$0 = Math_fround(0), $2 = 0, $3 = 0;
+ i64toi32_i32$0 = $0;
+ if (Math_fround(Math_abs(i64toi32_i32$0)) >= Math_fround(1.0)) {
+ if (i64toi32_i32$0 > Math_fround(0.0)) $2 = ~~Math_fround(Math_min(Math_fround(Math_floor(Math_fround(i64toi32_i32$0 / Math_fround(4294967296.0)))), Math_fround(Math_fround(4294967296.0) - Math_fround(1.0)))) >>> 0; else $2 = ~~Math_fround(Math_ceil(Math_fround(Math_fround(i64toi32_i32$0 - Math_fround(~~i64toi32_i32$0 >>> 0 >>> 0)) / Math_fround(4294967296.0)))) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ return (~~i64toi32_i32$0 >>> 0 | 0) == (0 | 0) & ($3 | 0) == (0 | 0) | 0 | 0;
+ }
+
+ function $50($0) {
+ $0 = +$0;
+ var i64toi32_i32$0 = 0.0, $2 = 0, $3 = 0;
+ i64toi32_i32$0 = $0;
+ if (Math_abs(i64toi32_i32$0) >= 1.0) {
+ if (i64toi32_i32$0 > 0.0) $2 = ~~Math_min(Math_floor(i64toi32_i32$0 / 4294967296.0), 4294967296.0 - 1.0) >>> 0; else $2 = ~~Math_ceil((i64toi32_i32$0 - +(~~i64toi32_i32$0 >>> 0 >>> 0)) / 4294967296.0) >>> 0;
+ $3 = $2;
+ } else $3 = 0;
+ return (~~i64toi32_i32$0 >>> 0 | 0) == (0 | 0) & ($3 | 0) == (0 | 0) | 0 | 0;
+ }
+
function __wasm_ctz_i32(x) {
x = x | 0;
var $1 = 0;
@@ -303,7 +411,23 @@ function asmFunc(global, env, buffer) {
f64_floor: $29,
f64_ceil: $30,
f32_sqrt: $31,
- f64_sqrt: $32
+ f64_sqrt: $32,
+ i32_to_f32: $35,
+ i32_to_f64: $36,
+ u32_to_f32: $37,
+ u32_to_f64: $38,
+ f32_to_i32: $39,
+ f64_to_i32: $40,
+ f32_to_u32: $41,
+ f64_to_u32: $42,
+ i64_to_f32: $43,
+ i64_to_f64: $44,
+ u64_to_f32: $45,
+ u64_to_f64: $46,
+ f32_to_i64: $47,
+ f64_to_i64: $48,
+ f32_to_u64: $49,
+ f64_to_u64: $50
};
}
diff --git a/test/wasm2asm/float-ops.wast b/test/wasm2asm/float-ops.wast
index de26c9931..f724149cb 100644
--- a/test/wasm2asm/float-ops.wast
+++ b/test/wasm2asm/float-ops.wast
@@ -107,4 +107,41 @@
(f64.copysign (get_local $0) (get_local $1)))
(func $copysign32 (param $0 f32) (param $1 f32) (result f32)
(f32.copysign (get_local $0) (get_local $1)))
+
+ ;; float<->int
+ (func (export "i32_to_f32") (param $0 i32) (result f32)
+ (f32.convert_s/i32 (get_local $0)))
+ (func (export "i32_to_f64") (param $0 i32) (result f64)
+ (f64.convert_s/i32 (get_local $0)))
+ (func (export "u32_to_f32") (param $0 i32) (result f32)
+ (f32.convert_u/i32 (get_local $0)))
+ (func (export "u32_to_f64") (param $0 i32) (result f64)
+ (f64.convert_u/i32 (get_local $0)))
+
+ (func (export "f32_to_i32") (param $0 f32) (result i32)
+ (i32.trunc_s/f32 (get_local $0)))
+ (func (export "f64_to_i32") (param $0 f64) (result i32)
+ (i32.trunc_s/f64 (get_local $0)))
+ (func (export "f32_to_u32") (param $0 f32) (result i32)
+ (i32.trunc_u/f32 (get_local $0)))
+ (func (export "f64_to_u32") (param $0 f64) (result i32)
+ (i32.trunc_u/f64 (get_local $0)))
+
+ (func (export "i64_to_f32") (param $0 i64) (result f32)
+ (f32.convert_s/i64 (get_local $0)))
+ (func (export "i64_to_f64") (param $0 i64) (result f64)
+ (f64.convert_s/i64 (get_local $0)))
+ (func (export "u64_to_f32") (param $0 i64) (result f32)
+ (f32.convert_u/i64 (get_local $0)))
+ (func (export "u64_to_f64") (param $0 i64) (result f64)
+ (f64.convert_u/i64 (get_local $0)))
+
+ (func (export "f32_to_i64") (param $0 f32) (result i32)
+ (i64.eq (i64.trunc_s/f32 (get_local $0)) (i64.const 0)))
+ (func (export "f64_to_i64") (param $0 f64) (result i32)
+ (i64.eq (i64.trunc_s/f64 (get_local $0)) (i64.const 0)))
+ (func (export "f32_to_u64") (param $0 f32) (result i32)
+ (i64.eq (i64.trunc_u/f32 (get_local $0)) (i64.const 0)))
+ (func (export "f64_to_u64") (param $0 f64) (result i32)
+ (i64.eq (i64.trunc_u/f64 (get_local $0)) (i64.const 0)))
)