diff options
author | Alon Zakai <alonzakai@gmail.com> | 2016-05-18 23:25:55 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2016-05-18 23:25:55 -0700 |
commit | ce03a34d97b1afa3b7abd88d04bbbaffe346f1d9 (patch) | |
tree | 3b355032ee402a595954f703558a7dc84b7a55cf /src | |
parent | 4a7080b8dc98422b63dc5db3245029d3f689de15 (diff) | |
parent | 50c94e3e9fb6cdf043c7841d73299ee8be5d2cbd (diff) | |
download | binaryen-ce03a34d97b1afa3b7abd88d04bbbaffe346f1d9.tar.gz binaryen-ce03a34d97b1afa3b7abd88d04bbbaffe346f1d9.tar.bz2 binaryen-ce03a34d97b1afa3b7abd88d04bbbaffe346f1d9.zip |
Merge pull request #526 from WebAssembly/spec-test-update
Spec test updates, and many validation fixes
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 99 | ||||
-rw-r--r-- | src/binaryen-c.cpp | 142 | ||||
-rw-r--r-- | src/binaryen-c.h | 142 | ||||
-rw-r--r-- | src/cfg/Relooper.cpp | 4 | ||||
-rw-r--r-- | src/cfg/Relooper.h | 2 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 46 | ||||
-rw-r--r-- | src/passes/PostEmscripten.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 200 | ||||
-rw-r--r-- | src/passes/Vacuum.cpp | 2 | ||||
-rw-r--r-- | src/s2wasm.h | 96 | ||||
-rw-r--r-- | src/wasm-binary.h | 247 | ||||
-rw-r--r-- | src/wasm-builder.h | 39 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 191 | ||||
-rw-r--r-- | src/wasm-js.cpp | 10 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 164 | ||||
-rw-r--r-- | src/wasm-validator.h | 111 | ||||
-rw-r--r-- | src/wasm.cpp | 67 | ||||
-rw-r--r-- | src/wasm.h | 126 |
18 files changed, 1053 insertions, 637 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 0f4852250..d1959b918 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -307,63 +307,58 @@ private: } BinaryOp parseAsmBinaryOp(IString op, Ref left, Ref right, AsmData *asmData) { - if (op == PLUS) return BinaryOp::Add; - if (op == MINUS) return BinaryOp::Sub; - if (op == MUL) return BinaryOp::Mul; - if (op == AND) return BinaryOp::And; - if (op == OR) return BinaryOp::Or; - if (op == XOR) return BinaryOp::Xor; - if (op == LSHIFT) return BinaryOp::Shl; - if (op == RSHIFT) return BinaryOp::ShrS; - if (op == TRSHIFT) return BinaryOp::ShrU; - if (op == EQ) return BinaryOp::Eq; - if (op == NE) return BinaryOp::Ne; WasmType leftType = detectWasmType(left, asmData); -#if 0 - std::cout << "CHECK\n"; - left->stringify(std::cout); - std::cout << " => " << printWasmType(leftType); - std::cout << '\n'; - right->stringify(std::cout); - std::cout << " => " << printWasmType(detectWasmType(right, asmData)) << "\n"; -#endif bool isInteger = leftType == WasmType::i32; + + if (op == PLUS) return isInteger ? BinaryOp::AddInt32 : (leftType == f32 ? BinaryOp::AddFloat32 : BinaryOp::AddFloat64); + if (op == MINUS) return isInteger ? BinaryOp::SubInt32 : (leftType == f32 ? BinaryOp::SubFloat32 : BinaryOp::SubFloat64); + if (op == MUL) return isInteger ? BinaryOp::MulInt32 : (leftType == f32 ? BinaryOp::MulFloat32 : BinaryOp::MulFloat64); + if (op == AND) return BinaryOp::AndInt32; + if (op == OR) return BinaryOp::OrInt32; + if (op == XOR) return BinaryOp::XorInt32; + if (op == LSHIFT) return BinaryOp::ShlInt32; + if (op == RSHIFT) return BinaryOp::ShrSInt32; + if (op == TRSHIFT) return BinaryOp::ShrUInt32; + if (op == EQ) return isInteger ? BinaryOp::EqInt32 : (leftType == f32 ? BinaryOp::EqFloat32 : BinaryOp::EqFloat64); + if (op == NE) return isInteger ? BinaryOp::NeInt32 : (leftType == f32 ? BinaryOp::NeFloat32 : BinaryOp::NeFloat64); + bool isUnsigned = isUnsignedCoercion(left) || isUnsignedCoercion(right); + if (op == DIV) { if (isInteger) { - return isUnsigned ? BinaryOp::DivU : BinaryOp::DivS; + return isUnsigned ? BinaryOp::DivUInt32 : BinaryOp::DivSInt32; } - return BinaryOp::Div; + return leftType == f32 ? BinaryOp::DivFloat32 : BinaryOp::DivFloat64; } if (op == MOD) { if (isInteger) { - return isUnsigned ? BinaryOp::RemU : BinaryOp::RemS; + return isUnsigned ? BinaryOp::RemUInt32 : BinaryOp::RemSInt32; } - return BinaryOp::RemS; // XXX no floating-point remainder op, this must be handled by the caller + return BinaryOp::RemSInt32; // XXX no floating-point remainder op, this must be handled by the caller } if (op == GE) { if (isInteger) { - return isUnsigned ? BinaryOp::GeU : BinaryOp::GeS; + return isUnsigned ? BinaryOp::GeUInt32 : BinaryOp::GeSInt32; } - return BinaryOp::Ge; + return leftType == f32 ? BinaryOp::GeFloat32 : BinaryOp::GeFloat64; } if (op == GT) { if (isInteger) { - return isUnsigned ? BinaryOp::GtU : BinaryOp::GtS; + return isUnsigned ? BinaryOp::GtUInt32 : BinaryOp::GtSInt32; } - return BinaryOp::Gt; + return leftType == f32 ? BinaryOp::GtFloat32 : BinaryOp::GtFloat64; } if (op == LE) { if (isInteger) { - return isUnsigned ? BinaryOp::LeU : BinaryOp::LeS; + return isUnsigned ? BinaryOp::LeUInt32 : BinaryOp::LeSInt32; } - return BinaryOp::Le; + return leftType == f32 ? BinaryOp::LeFloat32 : BinaryOp::LeFloat64; } if (op == LT) { if (isInteger) { - return isUnsigned ? BinaryOp::LtU : BinaryOp::LtS; + return isUnsigned ? BinaryOp::LtUInt32 : BinaryOp::LtSInt32; } - return BinaryOp::Lt; + return leftType == f32 ? BinaryOp::LtFloat32 : BinaryOp::LtFloat64; } abort_on("bad wasm binary op", op); abort(); // avoid warning @@ -733,7 +728,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { assert(functionTableStarts.find(tableName) != functionTableStarts.end()); auto sub = allocator.alloc<Binary>(); // note that the target is already masked, so we just offset it, we don't need to guard against overflow (which would be an error anyhow) - sub->op = Add; + sub->op = AddInt32; sub->left = call->target; sub->right = allocator.alloc<Const>()->set(Literal((int32_t)functionTableStarts[tableName])); sub->type = WasmType::i32; @@ -1008,7 +1003,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->left = process(ast[2]); ret->right = process(ast[3]); ret->finalize(); - if (binary == BinaryOp::RemS && isWasmTypeFloat(ret->type)) { + if (binary == BinaryOp::RemSInt32 && isWasmTypeFloat(ret->type)) { // WebAssembly does not have floating-point remainder, we have to emit a call to a special import of ours CallImport *call = allocator.alloc<CallImport>(); call->target = F64_REM; @@ -1126,18 +1121,19 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (asmType == ASM_INT) { // wasm has no unary negation for int, so do 0- auto ret = allocator.alloc<Binary>(); - ret->op = Sub; + ret->op = SubInt32; ret->left = allocator.alloc<Const>()->set(Literal((int32_t)0)); ret->right = process(ast[2]); ret->type = WasmType::i32; return ret; } auto ret = allocator.alloc<Unary>(); - ret->op = Neg; ret->value = process(ast[2]); if (asmType == ASM_DOUBLE) { + ret->op = NegFloat64; ret->type = WasmType::f64; } else if (asmType == ASM_FLOAT) { + ret->op = NegFloat32; ret->type = WasmType::f32; } else { abort(); @@ -1181,14 +1177,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } // no bitwise unary not, so do xor with -1 auto ret = allocator.alloc<Binary>(); - ret->op = Xor; + ret->op = XorInt32; ret->left = process(ast[2]); ret->right = allocator.alloc<Const>()->set(Literal(int32_t(-1))); ret->type = WasmType::i32; return ret; } else if (ast[1] == L_NOT) { auto ret = allocator.alloc<Unary>(); - ret->op = EqZ; + ret->op = EqZInt32; ret->value = process(ast[2]); ret->type = i32; return ret; @@ -1206,7 +1202,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (name == Math_imul) { assert(ast[2]->size() == 2); auto ret = allocator.alloc<Binary>(); - ret->op = Mul; + ret->op = MulInt32; ret->left = process(ast[2][0]); ret->right = process(ast[2][1]); ret->type = WasmType::i32; @@ -1215,7 +1211,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (name == Math_clz32 || name == llvm_cttz_i32) { assert(ast[2]->size() == 1); auto ret = allocator.alloc<Unary>(); - ret->op = name == Math_clz32 ? Clz : Ctz; + ret->op = name == Math_clz32 ? ClzInt32 : CtzInt32; ret->value = process(ast[2][0]); ret->type = WasmType::i32; return ret; @@ -1262,14 +1258,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return ret; }; auto isNegative = allocator.alloc<Binary>(); - isNegative->op = LtS; + isNegative->op = LtSInt32; isNegative->left = get(); isNegative->right = allocator.alloc<Const>()->set(Literal(0)); isNegative->finalize(); auto block = allocator.alloc<Block>(); block->list.push_back(set); auto flip = allocator.alloc<Binary>(); - flip->op = Sub; + flip->op = SubInt32; flip->left = allocator.alloc<Const>()->set(Literal(0)); flip->right = get(); flip->type = i32; @@ -1283,7 +1279,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return block; } else if (value->type == f32 || value->type == f64) { auto ret = allocator.alloc<Unary>(); - ret->op = Abs; + ret->op = value->type == f32 ? AbsFloat32 : AbsFloat64; ret->value = value; ret->type = value->type; return ret; @@ -1294,15 +1290,18 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (name == Math_floor || name == Math_sqrt || name == Math_ceil) { // overloaded on type: f32 or f64 Expression* value = process(ast[2][0]); - if (value->type == f32 || value->type == f64) { - auto ret = allocator.alloc<Unary>(); - ret->op = name == Math_floor ? Floor : name == Math_ceil ? Ceil : Sqrt; - ret->value = value; + auto ret = allocator.alloc<Unary>(); + ret->value = value; + if (value->type == f32) { + ret->op = name == Math_floor ? FloorFloat32 : name == Math_ceil ? CeilFloat32 : SqrtFloat32; + ret->type = value->type; + } else if (value->type == f64) { + ret->op = name == Math_floor ? FloorFloat64 : name == Math_ceil ? CeilFloat64 : SqrtFloat64; ret->type = value->type; - return ret; } else { abort(); } + return ret; } Expression* ret; ExpressionList* operands; @@ -1404,7 +1403,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Break *breakOut = allocator.alloc<Break>(); breakOut->name = out; If *condition = allocator.alloc<If>(); - condition->condition = builder.makeUnary(EqZ, process(ast[1])); + condition->condition = builder.makeUnary(EqZInt32, process(ast[1])); condition->ifTrue = breakOut; auto body = allocator.alloc<Block>(); body->list.push_back(condition); @@ -1501,7 +1500,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Break *breakOut = allocator.alloc<Break>(); breakOut->name = out; If *condition = allocator.alloc<If>(); - condition->condition = builder.makeUnary(EqZ, process(fcond)); + condition->condition = builder.makeUnary(EqZInt32, process(fcond)); condition->ifTrue = breakOut; auto body = allocator.alloc<Block>(); body->list.push_back(condition); @@ -1627,7 +1626,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } } Binary* offsetor = allocator.alloc<Binary>(); - offsetor->op = BinaryOp::Sub; + offsetor->op = BinaryOp::SubInt32; offsetor->left = br->condition; offsetor->right = allocator.alloc<Const>()->set(Literal(min)); offsetor->type = i32; diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 62e35f025..7fb39ffe4 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -98,17 +98,28 @@ BinaryenLiteral BinaryenLiteralFloat64Bits(int64_t x) { return toBinaryenLiteral // Expressions -BinaryenOp BinaryenClz(void) { return Clz; } -BinaryenOp BinaryenCtz(void) { return Ctz; } -BinaryenOp BinaryenPopcnt(void) { return Popcnt; } -BinaryenOp BinaryenNeg(void) { return Neg; } -BinaryenOp BinaryenAbs(void) { return Abs; } -BinaryenOp BinaryenCeil(void) { return Ceil; } -BinaryenOp BinaryenFloor(void) { return Floor; } -BinaryenOp BinaryenTrunc(void) { return Trunc; } -BinaryenOp BinaryenNearest(void) { return Nearest; } -BinaryenOp BinaryenSqrt(void) { return Sqrt; } -BinaryenOp BinaryenEqZ(void) { return EqZ; } +BinaryenOp BinaryenClzInt32(void) { return ClzInt32; } +BinaryenOp BinaryenCtzInt32(void) { return CtzInt32; } +BinaryenOp BinaryenPopcntInt32(void) { return PopcntInt32; } +BinaryenOp BinaryenNegFloat32(void) { return NegFloat32; } +BinaryenOp BinaryenAbsFloat32(void) { return AbsFloat32; } +BinaryenOp BinaryenCeilFloat32(void) { return CeilFloat32; } +BinaryenOp BinaryenFloorFloat32(void) { return FloorFloat32; } +BinaryenOp BinaryenTruncFloat32(void) { return TruncFloat32; } +BinaryenOp BinaryenNearestFloat32(void) { return NearestFloat32; } +BinaryenOp BinaryenSqrtFloat32(void) { return SqrtFloat32; } +BinaryenOp BinaryenEqZInt32(void) { return EqZInt32; } +BinaryenOp BinaryenClzInt64(void) { return ClzInt64; } +BinaryenOp BinaryenCtzInt64(void) { return CtzInt64; } +BinaryenOp BinaryenPopcntInt64(void) { return PopcntInt64; } +BinaryenOp BinaryenNegFloat64(void) { return NegFloat64; } +BinaryenOp BinaryenAbsFloat64(void) { return AbsFloat64; } +BinaryenOp BinaryenCeilFloat64(void) { return CeilFloat64; } +BinaryenOp BinaryenFloorFloat64(void) { return FloorFloat64; } +BinaryenOp BinaryenTruncFloat64(void) { return TruncFloat64; } +BinaryenOp BinaryenNearestFloat64(void) { return NearestFloat64; } +BinaryenOp BinaryenSqrtFloat64(void) { return SqrtFloat64; } +BinaryenOp BinaryenEqZInt64(void) { return EqZInt64; } BinaryenOp BinaryenExtendSInt32(void) { return ExtendSInt32; } BinaryenOp BinaryenExtentUInt32(void) { return ExtendUInt32; } BinaryenOp BinaryenWrapInt64(void) { return WrapInt64; } @@ -134,39 +145,82 @@ BinaryenOp BinaryenPromoteFloat32(void) { return PromoteFloat32; } BinaryenOp BinaryenDemoteFloat64(void) { return DemoteFloat64; } BinaryenOp BinaryenReinterpretInt32(void) { return ReinterpretInt32; } BinaryenOp BinaryenReinterpretInt64(void) { return ReinterpretInt64; } -BinaryenOp BinaryenAdd(void) { return Add; } -BinaryenOp BinaryenSub(void) { return Sub; } -BinaryenOp BinaryenMul(void) { return Mul; } -BinaryenOp BinaryenDivS(void) { return DivS; } -BinaryenOp BinaryenDivU(void) { return DivU; } -BinaryenOp BinaryenRemS(void) { return RemS; } -BinaryenOp BinaryenRemU(void) { return RemU; } -BinaryenOp BinaryenAnd(void) { return And; } -BinaryenOp BinaryenOr(void) { return Or; } -BinaryenOp BinaryenXor(void) { return Xor; } -BinaryenOp BinaryenShl(void) { return Shl; } -BinaryenOp BinaryenShrU(void) { return ShrU; } -BinaryenOp BinaryenShrS(void) { return ShrS; } -BinaryenOp BinaryenRotL(void) { return RotL; } -BinaryenOp BinaryenRotR(void) { return RotR; } -BinaryenOp BinaryenDiv(void) { return Div; } -BinaryenOp BinaryenCopySign(void) { return CopySign; } -BinaryenOp BinaryenMin(void) { return Min; } -BinaryenOp BinaryenMax(void) { return Max; } -BinaryenOp BinaryenEq(void) { return Eq; } -BinaryenOp BinaryenNe(void) { return Ne; } -BinaryenOp BinaryenLtS(void) { return LtS; } -BinaryenOp BinaryenLtU(void) { return LtU; } -BinaryenOp BinaryenLeS(void) { return LeS; } -BinaryenOp BinaryenLeU(void) { return LeU; } -BinaryenOp BinaryenGtS(void) { return GtS; } -BinaryenOp BinaryenGtU(void) { return GtU; } -BinaryenOp BinaryenGeS(void) { return GeS; } -BinaryenOp BinaryenGeU(void) { return GeU; } -BinaryenOp BinaryenLt(void) { return Lt; } -BinaryenOp BinaryenLe(void) { return Le; } -BinaryenOp BinaryenGt(void) { return Gt; } -BinaryenOp BinaryenGe(void) { return Ge; } +BinaryenOp BinaryenAddInt32(void) { return AddInt32; } +BinaryenOp BinaryenSubInt32(void) { return SubInt32; } +BinaryenOp BinaryenMulInt32(void) { return MulInt32; } +BinaryenOp BinaryenDivSInt32(void) { return DivSInt32; } +BinaryenOp BinaryenDivUInt32(void) { return DivUInt32; } +BinaryenOp BinaryenRemSInt32(void) { return RemSInt32; } +BinaryenOp BinaryenRemUInt32(void) { return RemUInt32; } +BinaryenOp BinaryenAndInt32(void) { return AndInt32; } +BinaryenOp BinaryenOrInt32(void) { return OrInt32; } +BinaryenOp BinaryenXorInt32(void) { return XorInt32; } +BinaryenOp BinaryenShlInt32(void) { return ShlInt32; } +BinaryenOp BinaryenShrUInt32(void) { return ShrUInt32; } +BinaryenOp BinaryenShrSInt32(void) { return ShrSInt32; } +BinaryenOp BinaryenRotLInt32(void) { return RotLInt32; } +BinaryenOp BinaryenRotRInt32(void) { return RotRInt32; } +BinaryenOp BinaryenEqInt32(void) { return EqInt32; } +BinaryenOp BinaryenNeInt32(void) { return NeInt32; } +BinaryenOp BinaryenLtSInt32(void) { return LtSInt32; } +BinaryenOp BinaryenLtUInt32(void) { return LtUInt32; } +BinaryenOp BinaryenLeSInt32(void) { return LeSInt32; } +BinaryenOp BinaryenLeUInt32(void) { return LeUInt32; } +BinaryenOp BinaryenGtSInt32(void) { return GtSInt32; } +BinaryenOp BinaryenGtUInt32(void) { return GtUInt32; } +BinaryenOp BinaryenGeSInt32(void) { return GeSInt32; } +BinaryenOp BinaryenGeUInt32(void) { return GeUInt32; } +BinaryenOp BinaryenAddInt64(void) { return AddInt64; } +BinaryenOp BinaryenSubInt64(void) { return SubInt64; } +BinaryenOp BinaryenMulInt64(void) { return MulInt64; } +BinaryenOp BinaryenDivSInt64(void) { return DivSInt64; } +BinaryenOp BinaryenDivUInt64(void) { return DivUInt64; } +BinaryenOp BinaryenRemSInt64(void) { return RemSInt64; } +BinaryenOp BinaryenRemUInt64(void) { return RemUInt64; } +BinaryenOp BinaryenAndInt64(void) { return AndInt64; } +BinaryenOp BinaryenOrInt64(void) { return OrInt64; } +BinaryenOp BinaryenXorInt64(void) { return XorInt64; } +BinaryenOp BinaryenShlInt64(void) { return ShlInt64; } +BinaryenOp BinaryenShrUInt64(void) { return ShrUInt64; } +BinaryenOp BinaryenShrSInt64(void) { return ShrSInt64; } +BinaryenOp BinaryenRotLInt64(void) { return RotLInt64; } +BinaryenOp BinaryenRotRInt64(void) { return RotRInt64; } +BinaryenOp BinaryenEqInt64(void) { return EqInt64; } +BinaryenOp BinaryenNeInt64(void) { return NeInt64; } +BinaryenOp BinaryenLtSInt64(void) { return LtSInt64; } +BinaryenOp BinaryenLtUInt64(void) { return LtUInt64; } +BinaryenOp BinaryenLeSInt64(void) { return LeSInt64; } +BinaryenOp BinaryenLeUInt64(void) { return LeUInt64; } +BinaryenOp BinaryenGtSInt64(void) { return GtSInt64; } +BinaryenOp BinaryenGtUInt64(void) { return GtUInt64; } +BinaryenOp BinaryenGeSInt64(void) { return GeSInt64; } +BinaryenOp BinaryenGeUInt64(void) { return GeUInt64; } +BinaryenOp BinaryenAddFloat32(void) { return AddFloat32; } +BinaryenOp BinaryenSubFloat32(void) { return SubFloat32; } +BinaryenOp BinaryenMulFloat32(void) { return MulFloat32; } +BinaryenOp BinaryenDivFloat32(void) { return DivFloat32; } +BinaryenOp BinaryenCopySignFloat32(void) { return CopySignFloat32; } +BinaryenOp BinaryenMinFloat32(void) { return MinFloat32; } +BinaryenOp BinaryenMaxFloat32(void) { return MaxFloat32; } +BinaryenOp BinaryenEqFloat32(void) { return EqFloat32; } +BinaryenOp BinaryenNeFloat32(void) { return NeFloat32; } +BinaryenOp BinaryenLtFloat32(void) { return LtFloat32; } +BinaryenOp BinaryenLeFloat32(void) { return LeFloat32; } +BinaryenOp BinaryenGtFloat32(void) { return GtFloat32; } +BinaryenOp BinaryenGeFloat32(void) { return GeFloat32; } +BinaryenOp BinaryenAddFloat64(void) { return AddFloat64; } +BinaryenOp BinaryenSubFloat64(void) { return SubFloat64; } +BinaryenOp BinaryenMulFloat64(void) { return MulFloat64; } +BinaryenOp BinaryenDivFloat64(void) { return DivFloat64; } +BinaryenOp BinaryenCopySignFloat64(void) { return CopySignFloat64; } +BinaryenOp BinaryenMinFloat64(void) { return MinFloat64; } +BinaryenOp BinaryenMaxFloat64(void) { return MaxFloat64; } +BinaryenOp BinaryenEqFloat64(void) { return EqFloat64; } +BinaryenOp BinaryenNeFloat64(void) { return NeFloat64; } +BinaryenOp BinaryenLtFloat64(void) { return LtFloat64; } +BinaryenOp BinaryenLeFloat64(void) { return LeFloat64; } +BinaryenOp BinaryenGtFloat64(void) { return GtFloat64; } +BinaryenOp BinaryenGeFloat64(void) { return GeFloat64; } BinaryenOp BinaryenPageSize(void) { return PageSize; } BinaryenOp BinaryenCurrentMemory(void) { return CurrentMemory; } BinaryenOp BinaryenGrowMemory(void) { return GrowMemory; } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index ae3e0c019..d3858489c 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -114,17 +114,28 @@ struct BinaryenLiteral BinaryenLiteralFloat64Bits(int64_t x); typedef int32_t BinaryenOp; -BinaryenOp BinaryenClz(void); -BinaryenOp BinaryenCtz(void); -BinaryenOp BinaryenPopcnt(void); -BinaryenOp BinaryenNeg(void); -BinaryenOp BinaryenAbs(void); -BinaryenOp BinaryenCeil(void); -BinaryenOp BinaryenFloor(void); -BinaryenOp BinaryenTrunc(void); -BinaryenOp BinaryenNearest(void); -BinaryenOp BinaryenSqrt(void); -BinaryenOp BinaryenEqZ(void); +BinaryenOp BinaryenClzInt32(void); +BinaryenOp BinaryenCtzInt32(void); +BinaryenOp BinaryenPopcntInt32(void); +BinaryenOp BinaryenNegFloat32(void); +BinaryenOp BinaryenAbsFloat32(void); +BinaryenOp BinaryenCeilFloat32(void); +BinaryenOp BinaryenFloorFloat32(void); +BinaryenOp BinaryenTruncFloat32(void); +BinaryenOp BinaryenNearestFloat32(void); +BinaryenOp BinaryenSqrtFloat32(void); +BinaryenOp BinaryenEqZInt32(void); +BinaryenOp BinaryenClzInt64(void); +BinaryenOp BinaryenCtzInt64(void); +BinaryenOp BinaryenPopcntInt64(void); +BinaryenOp BinaryenNegFloat64(void); +BinaryenOp BinaryenAbsFloat64(void); +BinaryenOp BinaryenCeilFloat64(void); +BinaryenOp BinaryenFloorFloat64(void); +BinaryenOp BinaryenTruncFloat64(void); +BinaryenOp BinaryenNearestFloat64(void); +BinaryenOp BinaryenSqrtFloat64(void); +BinaryenOp BinaryenEqZInt64(void); BinaryenOp BinaryenExtendSInt32(void); BinaryenOp BinaryenExtentUInt32(void); BinaryenOp BinaryenWrapInt64(void); @@ -150,39 +161,82 @@ BinaryenOp BinaryenPromoteFloat32(void); BinaryenOp BinaryenDemoteFloat64(void); BinaryenOp BinaryenReinterpretInt32(void); BinaryenOp BinaryenReinterpretInt64(void); -BinaryenOp BinaryenAdd(void); -BinaryenOp BinaryenSub(void); -BinaryenOp BinaryenMul(void); -BinaryenOp BinaryenDivS(void); -BinaryenOp BinaryenDivU(void); -BinaryenOp BinaryenRemS(void); -BinaryenOp BinaryenRemU(void); -BinaryenOp BinaryenAnd(void); -BinaryenOp BinaryenOr(void); -BinaryenOp BinaryenXor(void); -BinaryenOp BinaryenShl(void); -BinaryenOp BinaryenShrU(void); -BinaryenOp BinaryenShrS(void); -BinaryenOp BinaryenRotL(void); -BinaryenOp BinaryenRotR(void); -BinaryenOp BinaryenDiv(void); -BinaryenOp BinaryenCopySign(void); -BinaryenOp BinaryenMin(void); -BinaryenOp BinaryenMax(void); -BinaryenOp BinaryenEq(void); -BinaryenOp BinaryenNe(void); -BinaryenOp BinaryenLtS(void); -BinaryenOp BinaryenLtU(void); -BinaryenOp BinaryenLeS(void); -BinaryenOp BinaryenLeU(void); -BinaryenOp BinaryenGtS(void); -BinaryenOp BinaryenGtU(void); -BinaryenOp BinaryenGeS(void); -BinaryenOp BinaryenGeU(void); -BinaryenOp BinaryenLt(void); -BinaryenOp BinaryenLe(void); -BinaryenOp BinaryenGt(void); -BinaryenOp BinaryenGe(void); +BinaryenOp BinaryenAddInt32(void); +BinaryenOp BinaryenSubInt32(void); +BinaryenOp BinaryenMulInt32(void); +BinaryenOp BinaryenDivSInt32(void); +BinaryenOp BinaryenDivUInt32(void); +BinaryenOp BinaryenRemSInt32(void); +BinaryenOp BinaryenRemUInt32(void); +BinaryenOp BinaryenAndInt32(void); +BinaryenOp BinaryenOrInt32(void); +BinaryenOp BinaryenXorInt32(void); +BinaryenOp BinaryenShlInt32(void); +BinaryenOp BinaryenShrUInt32(void); +BinaryenOp BinaryenShrSInt32(void); +BinaryenOp BinaryenRotLInt32(void); +BinaryenOp BinaryenRotRInt32(void); +BinaryenOp BinaryenEqInt32(void); +BinaryenOp BinaryenNeInt32(void); +BinaryenOp BinaryenLtSInt32(void); +BinaryenOp BinaryenLtUInt32(void); +BinaryenOp BinaryenLeSInt32(void); +BinaryenOp BinaryenLeUInt32(void); +BinaryenOp BinaryenGtSInt32(void); +BinaryenOp BinaryenGtUInt32(void); +BinaryenOp BinaryenGeSInt32(void); +BinaryenOp BinaryenGeUInt32(void); +BinaryenOp BinaryenAddInt64(void); +BinaryenOp BinaryenSubInt64(void); +BinaryenOp BinaryenMulInt64(void); +BinaryenOp BinaryenDivSInt64(void); +BinaryenOp BinaryenDivUInt64(void); +BinaryenOp BinaryenRemSInt64(void); +BinaryenOp BinaryenRemUInt64(void); +BinaryenOp BinaryenAndInt64(void); +BinaryenOp BinaryenOrInt64(void); +BinaryenOp BinaryenXorInt64(void); +BinaryenOp BinaryenShlInt64(void); +BinaryenOp BinaryenShrUInt64(void); +BinaryenOp BinaryenShrSInt64(void); +BinaryenOp BinaryenRotLInt64(void); +BinaryenOp BinaryenRotRInt64(void); +BinaryenOp BinaryenEqInt64(void); +BinaryenOp BinaryenNeInt64(void); +BinaryenOp BinaryenLtSInt64(void); +BinaryenOp BinaryenLtUInt64(void); +BinaryenOp BinaryenLeSInt64(void); +BinaryenOp BinaryenLeUInt64(void); +BinaryenOp BinaryenGtSInt64(void); +BinaryenOp BinaryenGtUInt64(void); +BinaryenOp BinaryenGeSInt64(void); +BinaryenOp BinaryenGeUInt64(void); +BinaryenOp BinaryenAddFloat32(void); +BinaryenOp BinaryenSubFloat32(void); +BinaryenOp BinaryenMulFloat32(void); +BinaryenOp BinaryenDivFloat32(void); +BinaryenOp BinaryenCopySignFloat32(void); +BinaryenOp BinaryenMinFloat32(void); +BinaryenOp BinaryenMaxFloat32(void); +BinaryenOp BinaryenEqFloat32(void); +BinaryenOp BinaryenNeFloat32(void); +BinaryenOp BinaryenLtFloat32(void); +BinaryenOp BinaryenLeFloat32(void); +BinaryenOp BinaryenGtFloat32(void); +BinaryenOp BinaryenGeFloat32(void); +BinaryenOp BinaryenAddFloat64(void); +BinaryenOp BinaryenSubFloat64(void); +BinaryenOp BinaryenMulFloat64(void); +BinaryenOp BinaryenDivFloat64(void); +BinaryenOp BinaryenCopySignFloat64(void); +BinaryenOp BinaryenMinFloat64(void); +BinaryenOp BinaryenMaxFloat64(void); +BinaryenOp BinaryenEqFloat64(void); +BinaryenOp BinaryenNeFloat64(void); +BinaryenOp BinaryenLtFloat64(void); +BinaryenOp BinaryenLeFloat64(void); +BinaryenOp BinaryenGtFloat64(void); +BinaryenOp BinaryenGeFloat64(void); BinaryenOp BinaryenPageSize(void); BinaryenOp BinaryenCurrentMemory(void); BinaryenOp BinaryenGrowMemory(void); diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp index 14941833b..06867c748 100644 --- a/src/cfg/Relooper.cpp +++ b/src/cfg/Relooper.cpp @@ -199,9 +199,9 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { } } } else { - auto* Now = Builder.makeUnary(wasm::EqZ, Details->Condition); + auto* Now = Builder.makeUnary(wasm::EqZInt32, Details->Condition); if (RemainingConditions) { - RemainingConditions = Builder.makeBinary(wasm::And, RemainingConditions, Now); + RemainingConditions = Builder.makeBinary(wasm::AndInt32, RemainingConditions, Now); } else { RemainingConditions = Now; } diff --git a/src/cfg/Relooper.h b/src/cfg/Relooper.h index 706b3d065..3040b0fc2 100644 --- a/src/cfg/Relooper.h +++ b/src/cfg/Relooper.h @@ -50,7 +50,7 @@ public: return makeSetLocal(labelHelper, makeConst(wasm::Literal(int32_t(value)))); } wasm::Binary* makeCheckLabel(wasm::Index value) { - return makeBinary(wasm::Eq, makeGetLabel(), makeConst(wasm::Literal(int32_t(value)))); + return makeBinary(wasm::EqInt32, makeGetLabel(), makeConst(wasm::Literal(int32_t(value)))); } wasm::Break* makeBreak(int id) { return wasm::Builder::makeBreak(getBreakName(id)); diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 368b483ad..3d98d0a67 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -32,28 +32,50 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, // flip branches to get rid of an i32.eqz if (curr->ifFalse) { auto condition = curr->condition->dynCast<Unary>(); - if (condition && condition->op == EqZ && condition->value->type == i32) { + if (condition && condition->op == EqZInt32 && condition->value->type == i32) { curr->condition = condition->value; std::swap(curr->ifTrue, curr->ifFalse); } } } void visitUnary(Unary* curr) { - if (curr->op == EqZ) { + if (curr->op == EqZInt32) { // fold comparisons that flow into an EqZ auto* child = curr->value->dynCast<Binary>(); if (child && (child->type == i32 || child->type == i64)) { switch (child->op) { - case Eq: child->op = Ne; break; - case Ne: child->op = Eq; break; - case LtS: child->op = GeS; break; - case LtU: child->op = GeU; break; - case LeS: child->op = GtS; break; - case LeU: child->op = GtU; break; - case GtS: child->op = LeS; break; - case GtU: child->op = LeU; break; - case GeS: child->op = LtS; break; - case GeU: child->op = LtU; break; + case EqInt32: child->op = NeInt32; break; + case NeInt32: child->op = EqInt32; break; + case LtSInt32: child->op = GeSInt32; break; + case LtUInt32: child->op = GeUInt32; break; + case LeSInt32: child->op = GtSInt32; break; + case LeUInt32: child->op = GtUInt32; break; + case GtSInt32: child->op = LeSInt32; break; + case GtUInt32: child->op = LeUInt32; break; + case GeSInt32: child->op = LtSInt32; break; + case GeUInt32: child->op = LtUInt32; break; + case EqInt64: child->op = NeInt64; break; + case NeInt64: child->op = EqInt64; break; + case LtSInt64: child->op = GeSInt64; break; + case LtUInt64: child->op = GeUInt64; break; + case LeSInt64: child->op = GtSInt64; break; + case LeUInt64: child->op = GtUInt64; break; + case GtSInt64: child->op = LeSInt64; break; + case GtUInt64: child->op = LeUInt64; break; + case GeSInt64: child->op = LtSInt64; break; + case GeUInt64: child->op = LtUInt64; break; + case EqFloat32: child->op = NeFloat32; break; + case NeFloat32: child->op = EqFloat32; break; + case LtFloat32: child->op = GeFloat32; break; + case LeFloat32: child->op = GtFloat32; break; + case GtFloat32: child->op = LeFloat32; break; + case GeFloat32: child->op = LtFloat32; break; + case EqFloat64: child->op = NeFloat64; break; + case NeFloat64: child->op = EqFloat64; break; + case LtFloat64: child->op = GeFloat64; break; + case LeFloat64: child->op = GtFloat64; break; + case GtFloat64: child->op = LeFloat64; break; + case GeFloat64: child->op = LtFloat64; break; default: return; } replaceCurrent(child); diff --git a/src/passes/PostEmscripten.cpp b/src/passes/PostEmscripten.cpp index 43fd1b2bc..4bd828a00 100644 --- a/src/passes/PostEmscripten.cpp +++ b/src/passes/PostEmscripten.cpp @@ -47,7 +47,7 @@ struct PostEmscripten : public WalkerPass<PostWalker<PostEmscripten, Visitor<Pos if (curr->offset) return; Expression* ptr = curr->ptr; auto add = ptr->dynCast<Binary>(); - if (!add || add->op != Add) return; + if (!add || add->op != AddInt32) return; assert(add->type == i32); auto c = add->right->dynCast<Const>(); if (!c) { diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 121287846..04b1d0edb 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -283,44 +283,55 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } void visitUnary(Unary *curr) { o << '('; - prepareColor(o) << printWasmType(curr->isRelational() ? curr->value->type : curr->type) << '.'; + prepareColor(o); switch (curr->op) { - case Clz: o << "clz"; break; - case Ctz: o << "ctz"; break; - case Popcnt: o << "popcnt"; break; - case EqZ: o << "eqz"; break; - case Neg: o << "neg"; break; - case Abs: o << "abs"; break; - case Ceil: o << "ceil"; break; - case Floor: o << "floor"; break; - case Trunc: o << "trunc"; break; - case Nearest: o << "nearest"; break; - case Sqrt: o << "sqrt"; break; - case ExtendSInt32: o << "extend_s/i32"; break; - case ExtendUInt32: o << "extend_u/i32"; break; - case WrapInt64: o << "wrap/i64"; break; - case TruncSFloat32ToInt32: - case TruncSFloat32ToInt64: o << "trunc_s/f32"; break; - case TruncUFloat32ToInt32: - case TruncUFloat32ToInt64: o << "trunc_u/f32"; break; - case TruncSFloat64ToInt32: - case TruncSFloat64ToInt64: o << "trunc_s/f64"; break; - case TruncUFloat64ToInt32: - case TruncUFloat64ToInt64: o << "trunc_u/f64"; break; - case ReinterpretFloat32: o << "reinterpret/f32"; break; - case ReinterpretFloat64: o << "reinterpret/f64"; break; - case ConvertUInt32ToFloat32: - case ConvertUInt32ToFloat64: o << "convert_u/i32"; break; - case ConvertSInt32ToFloat32: - case ConvertSInt32ToFloat64: o << "convert_s/i32"; break; - case ConvertUInt64ToFloat32: - case ConvertUInt64ToFloat64: o << "convert_u/i64"; break; - case ConvertSInt64ToFloat32: - case ConvertSInt64ToFloat64: o << "convert_s/i64"; break; - case PromoteFloat32: o << "promote/f32"; break; - case DemoteFloat64: o << "demote/f64"; break; - case ReinterpretInt32: o << "reinterpret/i32"; break; - case ReinterpretInt64: o << "reinterpret/i64"; break; + case ClzInt32: o << "i32.clz"; break; + case CtzInt32: o << "i32.ctz"; break; + case PopcntInt32: o << "i32.popcnt"; break; + case EqZInt32: o << "i32.eqz"; break; + case ClzInt64: o << "i64.clz"; break; + case CtzInt64: o << "i64.ctz"; break; + case PopcntInt64: o << "i64.popcnt"; break; + case EqZInt64: o << "i64.eqz"; break; + case NegFloat32: o << "f32.neg"; break; + case AbsFloat32: o << "f32.abs"; break; + case CeilFloat32: o << "f32.ceil"; break; + case FloorFloat32: o << "f32.floor"; break; + case TruncFloat32: o << "f32.trunc"; break; + case NearestFloat32: o << "f32.nearest"; break; + case SqrtFloat32: o << "f32.sqrt"; break; + case NegFloat64: o << "f64.neg"; break; + case AbsFloat64: o << "f64.abs"; break; + case CeilFloat64: o << "f64.ceil"; break; + case FloorFloat64: o << "f64.floor"; break; + case TruncFloat64: o << "f64.trunc"; break; + case NearestFloat64: o << "f64.nearest"; break; + case SqrtFloat64: o << "f64.sqrt"; break; + case ExtendSInt32: o << "i64.extend_s/i32"; break; + case ExtendUInt32: o << "i64.extend_u/i32"; break; + case WrapInt64: o << "i32.wrap/i64"; break; + case TruncSFloat32ToInt32: o << "i32.trunc_s/f32"; break; + case TruncSFloat32ToInt64: o << "i64.trunc_s/f32"; break; + case TruncUFloat32ToInt32: o << "i32.trunc_u/f32"; break; + case TruncUFloat32ToInt64: o << "i64.trunc_u/f32"; break; + case TruncSFloat64ToInt32: o << "i32.trunc_s/f64"; break; + case TruncSFloat64ToInt64: o << "i64.trunc_s/f64"; break; + case TruncUFloat64ToInt32: o << "i32.trunc_u/f64"; break; + case TruncUFloat64ToInt64: o << "i64.trunc_u/f64"; break; + case ReinterpretFloat32: o << "i32.reinterpret/f32"; break; + case ReinterpretFloat64: o << "i64.reinterpret/f64"; break; + case ConvertUInt32ToFloat32: o << "f32.convert_u/i32"; break; + case ConvertUInt32ToFloat64: o << "f64.convert_u/i32"; break; + case ConvertSInt32ToFloat32: o << "f32.convert_s/i32"; break; + case ConvertSInt32ToFloat64: o << "f64.convert_s/i32"; break; + case ConvertUInt64ToFloat32: o << "f32.convert_u/i64"; break; + case ConvertUInt64ToFloat64: o << "f64.convert_u/i64"; break; + case ConvertSInt64ToFloat32: o << "f32.convert_s/i64"; break; + case ConvertSInt64ToFloat64: o << "f64.convert_s/i64"; break; + case PromoteFloat32: o << "f64.promote/f32"; break; + case DemoteFloat64: o << "f32.demote/f64"; break; + case ReinterpretInt32: o << "f32.reinterpret/i32"; break; + case ReinterpretInt64: o << "f64.reinterpret/i64"; break; default: abort(); } incIndent(); @@ -329,41 +340,88 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } void visitBinary(Binary *curr) { o << '('; - prepareColor(o) << printWasmType(curr->isRelational() ? curr->left->type : curr->type) << '.'; + prepareColor(o); switch (curr->op) { - case Add: o << "add"; break; - case Sub: o << "sub"; break; - case Mul: o << "mul"; break; - case DivS: o << "div_s"; break; - case DivU: o << "div_u"; break; - case RemS: o << "rem_s"; break; - case RemU: o << "rem_u"; break; - case And: o << "and"; break; - case Or: o << "or"; break; - case Xor: o << "xor"; break; - case Shl: o << "shl"; break; - case ShrU: o << "shr_u"; break; - case ShrS: o << "shr_s"; break; - case RotL: o << "rotl"; break; - case RotR: o << "rotr"; break; - case Div: o << "div"; break; - case CopySign: o << "copysign"; break; - case Min: o << "min"; break; - case Max: o << "max"; break; - case Eq: o << "eq"; break; - case Ne: o << "ne"; break; - case LtS: o << "lt_s"; break; - case LtU: o << "lt_u"; break; - case LeS: o << "le_s"; break; - case LeU: o << "le_u"; break; - case GtS: o << "gt_s"; break; - case GtU: o << "gt_u"; break; - case GeS: o << "ge_s"; break; - case GeU: o << "ge_u"; break; - case Lt: o << "lt"; break; - case Le: o << "le"; break; - case Gt: o << "gt"; break; - case Ge: o << "ge"; break; + case AddInt32: o << "i32.add"; break; + case SubInt32: o << "i32.sub"; break; + case MulInt32: o << "i32.mul"; break; + case DivSInt32: o << "i32.div_s"; break; + case DivUInt32: o << "i32.div_u"; break; + case RemSInt32: o << "i32.rem_s"; break; + case RemUInt32: o << "i32.rem_u"; break; + case AndInt32: o << "i32.and"; break; + case OrInt32: o << "i32.or"; break; + case XorInt32: o << "i32.xor"; break; + case ShlInt32: o << "i32.shl"; break; + case ShrUInt32: o << "i32.shr_u"; break; + case ShrSInt32: o << "i32.shr_s"; break; + case RotLInt32: o << "i32.rotl"; break; + case RotRInt32: o << "i32.rotr"; break; + case EqInt32: o << "i32.eq"; break; + case NeInt32: o << "i32.ne"; break; + case LtSInt32: o << "i32.lt_s"; break; + case LtUInt32: o << "i32.lt_u"; break; + case LeSInt32: o << "i32.le_s"; break; + case LeUInt32: o << "i32.le_u"; break; + case GtSInt32: o << "i32.gt_s"; break; + case GtUInt32: o << "i32.gt_u"; break; + case GeSInt32: o << "i32.ge_s"; break; + case GeUInt32: o << "i32.ge_u"; break; + + case AddInt64: o << "i64.add"; break; + case SubInt64: o << "i64.sub"; break; + case MulInt64: o << "i64.mul"; break; + case DivSInt64: o << "i64.div_s"; break; + case DivUInt64: o << "i64.div_u"; break; + case RemSInt64: o << "i64.rem_s"; break; + case RemUInt64: o << "i64.rem_u"; break; + case AndInt64: o << "i64.and"; break; + case OrInt64: o << "i64.or"; break; + case XorInt64: o << "i64.xor"; break; + case ShlInt64: o << "i64.shl"; break; + case ShrUInt64: o << "i64.shr_u"; break; + case ShrSInt64: o << "i64.shr_s"; break; + case RotLInt64: o << "i64.rotl"; break; + case RotRInt64: o << "i64.rotr"; break; + case EqInt64: o << "i64.eq"; break; + case NeInt64: o << "i64.ne"; break; + case LtSInt64: o << "i64.lt_s"; break; + case LtUInt64: o << "i64.lt_u"; break; + case LeSInt64: o << "i64.le_s"; break; + case LeUInt64: o << "i64.le_u"; break; + case GtSInt64: o << "i64.gt_s"; break; + case GtUInt64: o << "i64.gt_u"; break; + case GeSInt64: o << "i64.ge_s"; break; + case GeUInt64: o << "i64.ge_u"; break; + + case AddFloat32: o << "f32.add"; break; + case SubFloat32: o << "f32.sub"; break; + case MulFloat32: o << "f32.mul"; break; + case DivFloat32: o << "f32.div"; break; + case CopySignFloat32: o << "f32.copysign"; break; + case MinFloat32: o << "f32.min"; break; + case MaxFloat32: o << "f32.max"; break; + case EqFloat32: o << "f32.eq"; break; + case NeFloat32: o << "f32.ne"; break; + case LtFloat32: o << "f32.lt"; break; + case LeFloat32: o << "f32.le"; break; + case GtFloat32: o << "f32.gt"; break; + case GeFloat32: o << "f32.ge"; break; + + case AddFloat64: o << "f64.add"; break; + case SubFloat64: o << "f64.sub"; break; + case MulFloat64: o << "f64.mul"; break; + case DivFloat64: o << "f64.div"; break; + case CopySignFloat64: o << "f64.copysign"; break; + case MinFloat64: o << "f64.min"; break; + case MaxFloat64: o << "f64.max"; break; + case EqFloat64: o << "f64.eq"; break; + case NeFloat64: o << "f64.ne"; break; + case LtFloat64: o << "f64.lt"; break; + case LeFloat64: o << "f64.le"; break; + case GtFloat64: o << "f64.gt"; break; + case GeFloat64: o << "f64.ge"; break; + default: abort(); } restoreNormalColor(o); diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 10d04f160..458ce63ca 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -81,7 +81,7 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum, Visitor<Vacuum>>> { } else if (curr->ifTrue->is<Nop>()) { curr->ifTrue = curr->ifFalse; curr->ifFalse = nullptr; - curr->condition = Builder(*getModule()).makeUnary(EqZ, curr->condition); + curr->condition = Builder(*getModule()).makeUnary(EqZInt32, curr->condition); } } if (!curr->ifFalse) { diff --git a/src/s2wasm.h b/src/s2wasm.h index 54d36011f..082cbed1a 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -529,7 +529,6 @@ class S2WasmBuilder { last = last->cast<Loop>()->body; } last->cast<Block>()->list.push_back(curr); - last->cast<Block>()->finalize(); }; bstack.push_back(func->body); std::vector<Expression*> estack; @@ -635,7 +634,7 @@ class S2WasmBuilder { auto curr = allocator->alloc<Unary>(); curr->op = op; curr->value = getInput(); - curr->type = type; + curr->finalize(); setOutput(curr, assign); }; auto makeHost = [&](HostOp op) { @@ -740,12 +739,15 @@ class S2WasmBuilder { setOutput(curr, assign); } }; + #define BINARY_INT_OR_FLOAT(op) (type == i32 ? BinaryOp::op##Int32 : (type == i64 ? BinaryOp::op##Int64 : (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64))) + #define BINARY_INT(op) (type == i32 ? BinaryOp::op##Int32 : BinaryOp::op##Int64) + #define BINARY_FLOAT(op) (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64) auto handleTyped = [&](WasmType type) { switch (*s) { case 'a': { - if (match("add")) makeBinary(BinaryOp::Add, type); - else if (match("and")) makeBinary(BinaryOp::And, type); - else if (match("abs")) makeUnary(UnaryOp::Abs, type); + if (match("add")) makeBinary(BINARY_INT_OR_FLOAT(Add), type); + else if (match("and")) makeBinary(BINARY_INT(And), type); + else if (match("abs")) makeUnary(type == f32 ? UnaryOp::AbsFloat32 : UnaryOp::AbsFloat64, type); else abort_on("type.a"); break; } @@ -768,100 +770,100 @@ class S2WasmBuilder { else if (match("convert_u/i32")) makeUnary(type == f32 ? UnaryOp::ConvertUInt32ToFloat32 : UnaryOp::ConvertUInt32ToFloat64, type); else if (match("convert_s/i64")) makeUnary(type == f32 ? UnaryOp::ConvertSInt64ToFloat32 : UnaryOp::ConvertSInt64ToFloat64, type); else if (match("convert_u/i64")) makeUnary(type == f32 ? UnaryOp::ConvertUInt64ToFloat32 : UnaryOp::ConvertUInt64ToFloat64, type); - else if (match("clz")) makeUnary(UnaryOp::Clz, type); - else if (match("ctz")) makeUnary(UnaryOp::Ctz, type); - else if (match("copysign")) makeBinary(BinaryOp::CopySign, type); - else if (match("ceil")) makeUnary(UnaryOp::Ceil, type); + else if (match("clz")) makeUnary(type == i32 ? UnaryOp::ClzInt32 : UnaryOp::ClzInt64, type); + else if (match("ctz")) makeUnary(type == i32 ? UnaryOp::CtzInt32 : UnaryOp::CtzInt64, type); + else if (match("copysign")) makeBinary(BINARY_FLOAT(CopySign), type); + else if (match("ceil")) makeUnary(type == f32 ? UnaryOp::CeilFloat32 : UnaryOp::CeilFloat64, type); else abort_on("type.c"); break; } case 'd': { if (match("demote/f64")) makeUnary(UnaryOp::DemoteFloat64, type); - else if (match("div_s")) makeBinary(BinaryOp::DivS, type); - else if (match("div_u")) makeBinary(BinaryOp::DivU, type); - else if (match("div")) makeBinary(BinaryOp::Div, type); + else if (match("div_s")) makeBinary(BINARY_INT(DivS), type); + else if (match("div_u")) makeBinary(BINARY_INT(DivU), type); + else if (match("div")) makeBinary(BINARY_FLOAT(Div), type); else abort_on("type.g"); break; } case 'e': { - if (match("eqz")) makeUnary(UnaryOp::EqZ, i32); - else if (match("eq")) makeBinary(BinaryOp::Eq, i32); + if (match("eqz")) makeUnary(type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type); + else if (match("eq")) makeBinary(BINARY_INT_OR_FLOAT(Eq), i32); else if (match("extend_s/i32")) makeUnary(UnaryOp::ExtendSInt32, type); else if (match("extend_u/i32")) makeUnary(UnaryOp::ExtendUInt32, type); else abort_on("type.e"); break; } case 'f': { - if (match("floor")) makeUnary(UnaryOp::Floor, type); + if (match("floor")) makeUnary(type == f32 ? UnaryOp::FloorFloat32 : UnaryOp::FloorFloat64, type); else abort_on("type.e"); break; } case 'g': { - if (match("gt_s")) makeBinary(BinaryOp::GtS, i32); - else if (match("gt_u")) makeBinary(BinaryOp::GtU, i32); - else if (match("ge_s")) makeBinary(BinaryOp::GeS, i32); - else if (match("ge_u")) makeBinary(BinaryOp::GeU, i32); - else if (match("gt")) makeBinary(BinaryOp::Gt, i32); - else if (match("ge")) makeBinary(BinaryOp::Ge, i32); + if (match("gt_s")) makeBinary(BINARY_INT(GtS), i32); + else if (match("gt_u")) makeBinary(BINARY_INT(GtU), i32); + else if (match("ge_s")) makeBinary(BINARY_INT(GeS), i32); + else if (match("ge_u")) makeBinary(BINARY_INT(GeU), i32); + else if (match("gt")) makeBinary(BINARY_FLOAT(Gt), i32); + else if (match("ge")) makeBinary(BINARY_FLOAT(Ge), i32); else abort_on("type.g"); break; } case 'l': { - if (match("lt_s")) makeBinary(BinaryOp::LtS, i32); - else if (match("lt_u")) makeBinary(BinaryOp::LtU, i32); - else if (match("le_s")) makeBinary(BinaryOp::LeS, i32); - else if (match("le_u")) makeBinary(BinaryOp::LeU, i32); + if (match("lt_s")) makeBinary(BINARY_INT(LtS), i32); + else if (match("lt_u")) makeBinary(BINARY_INT(LtU), i32); + else if (match("le_s")) makeBinary(BINARY_INT(LeS), i32); + else if (match("le_u")) makeBinary(BINARY_INT(LeU), i32); else if (match("load")) makeLoad(type); - else if (match("lt")) makeBinary(BinaryOp::Lt, i32); - else if (match("le")) makeBinary(BinaryOp::Le, i32); + else if (match("lt")) makeBinary(BINARY_FLOAT(Lt), i32); + else if (match("le")) makeBinary(BINARY_FLOAT(Le), i32); else abort_on("type.g"); break; } case 'm': { - if (match("mul")) makeBinary(BinaryOp::Mul, type); - else if (match("min")) makeBinary(BinaryOp::Min, type); - else if (match("max")) makeBinary(BinaryOp::Max, type); + if (match("mul")) makeBinary(BINARY_INT_OR_FLOAT(Mul), type); + else if (match("min")) makeBinary(BINARY_FLOAT(Min), type); + else if (match("max")) makeBinary(BINARY_FLOAT(Max), type); else abort_on("type.m"); break; } case 'n': { - if (match("neg")) makeUnary(UnaryOp::Neg, type); - else if (match("nearest")) makeUnary(UnaryOp::Nearest, type); - else if (match("ne")) makeBinary(BinaryOp::Ne, i32); + if (match("neg")) makeUnary(type == f32 ? UnaryOp::NegFloat32 : UnaryOp::NegFloat64, type); + else if (match("nearest")) makeUnary(type == f32 ? UnaryOp::NearestFloat32 : UnaryOp::NearestFloat64, type); + else if (match("ne")) makeBinary(BINARY_INT_OR_FLOAT(Ne), i32); else abort_on("type.n"); break; } case 'o': { - if (match("or")) makeBinary(BinaryOp::Or, type); + if (match("or")) makeBinary(BINARY_INT(Or), type); else abort_on("type.o"); break; } case 'p': { if (match("promote/f32")) makeUnary(UnaryOp::PromoteFloat32, type); - else if (match("popcnt")) makeUnary(UnaryOp::Popcnt, type); + else if (match("popcnt")) makeUnary(type == i32 ? UnaryOp::PopcntInt32 : UnaryOp::PopcntInt64, type); else abort_on("type.p"); break; } case 'r': { - if (match("rem_s")) makeBinary(BinaryOp::RemS, type); - else if (match("rem_u")) makeBinary(BinaryOp::RemU, type); + if (match("rem_s")) makeBinary(BINARY_INT(RemS), type); + else if (match("rem_u")) makeBinary(BINARY_INT(RemU), type); else if (match("reinterpret/i32")) makeUnary(UnaryOp::ReinterpretInt32, type); else if (match("reinterpret/i64")) makeUnary(UnaryOp::ReinterpretInt64, type); else if (match("reinterpret/f32")) makeUnary(UnaryOp::ReinterpretFloat32, type); else if (match("reinterpret/f64")) makeUnary(UnaryOp::ReinterpretFloat64, type); - else if (match("rotl")) makeBinary(BinaryOp::RotL, type); - else if (match("rotr")) makeBinary(BinaryOp::RotR, type); + else if (match("rotl")) makeBinary(BINARY_INT(RotL), type); + else if (match("rotr")) makeBinary(BINARY_INT(RotR), type); else abort_on("type.r"); break; } case 's': { - if (match("shr_s")) makeBinary(BinaryOp::ShrS, type); - else if (match("shr_u")) makeBinary(BinaryOp::ShrU, type); - else if (match("shl")) makeBinary(BinaryOp::Shl, type); - else if (match("sub")) makeBinary(BinaryOp::Sub, type); + if (match("shr_s")) makeBinary(BINARY_INT(ShrS), type); + else if (match("shr_u")) makeBinary(BINARY_INT(ShrU), type); + else if (match("shl")) makeBinary(BINARY_INT(Shl), type); + else if (match("sub")) makeBinary(BINARY_INT_OR_FLOAT(Sub), type); else if (match("store")) makeStore(type); else if (match("select")) makeSelect(type); - else if (match("sqrt")) makeUnary(UnaryOp::Sqrt, type); + else if (match("sqrt")) makeUnary(type == f32 ? UnaryOp::SqrtFloat32 : UnaryOp::SqrtFloat64, type); else abort_on("type.s"); break; } @@ -870,7 +872,7 @@ class S2WasmBuilder { else if (match("trunc_u/f32")) makeUnary(type == i32 ? UnaryOp::TruncUFloat32ToInt32 : UnaryOp::TruncUFloat32ToInt64, type); else if (match("trunc_s/f64")) makeUnary(type == i32 ? UnaryOp::TruncSFloat64ToInt32 : UnaryOp::TruncSFloat64ToInt64, type); else if (match("trunc_u/f64")) makeUnary(type == i32 ? UnaryOp::TruncUFloat64ToInt32 : UnaryOp::TruncUFloat64ToInt64, type); - else if (match("trunc")) makeUnary(UnaryOp::Trunc, type); + else if (match("trunc")) makeUnary(type == f32 ? UnaryOp::TruncFloat32 : UnaryOp::TruncFloat64, type); else abort_on("type.t"); break; } @@ -880,7 +882,7 @@ class S2WasmBuilder { break; } case 'x': { - if (match("xor")) makeBinary(BinaryOp::Xor, type); + if (match("xor")) makeBinary(BINARY_INT(Xor), type); else abort_on("type.x"); break; } @@ -921,6 +923,7 @@ class S2WasmBuilder { addToBlock(curr); bstack.push_back(curr); } else if (match("end_block")) { + bstack.back()->cast<Block>()->finalize(); bstack.pop_back(); } else if (match(".LBB")) { s = strchr(s, '\n'); @@ -996,6 +999,7 @@ class S2WasmBuilder { } } // finishing touches + bstack.back()->cast<Block>()->finalize(); bstack.pop_back(); // remove the base block for the function body assert(bstack.empty()); assert(estack.empty()); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 795e89d98..9f061db47 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1011,17 +1011,28 @@ public: if (debug) std::cerr << "zz node: Unary" << std::endl; recurse(curr->value); switch (curr->op) { - case Clz: o << int8_t(curr->type == i32 ? BinaryConsts::I32Clz : BinaryConsts::I64Clz); break; - case Ctz: o << int8_t(curr->type == i32 ? BinaryConsts::I32Ctz : BinaryConsts::I64Ctz); break; - case Popcnt: o << int8_t(curr->type == i32 ? BinaryConsts::I32Popcnt : BinaryConsts::I64Popcnt); break; - case EqZ: o << int8_t(curr->type == i32 ? BinaryConsts::I32EqZ : BinaryConsts::I64EqZ); break; - case Neg: o << int8_t(curr->type == f32 ? BinaryConsts::F32Neg : BinaryConsts::F64Neg); break; - case Abs: o << int8_t(curr->type == f32 ? BinaryConsts::F32Abs : BinaryConsts::F64Abs); break; - case Ceil: o << int8_t(curr->type == f32 ? BinaryConsts::F32Ceil : BinaryConsts::F64Ceil); break; - case Floor: o << int8_t(curr->type == f32 ? BinaryConsts::F32Floor : BinaryConsts::F64Floor); break; - case Trunc: o << int8_t(curr->type == f32 ? BinaryConsts::F32Trunc : BinaryConsts::F64Trunc); break; - case Nearest: o << int8_t(curr->type == f32 ? BinaryConsts::F32NearestInt : BinaryConsts::F64NearestInt); break; - case Sqrt: o << int8_t(curr->type == f32 ? BinaryConsts::F32Sqrt : BinaryConsts::F64Sqrt); break; + case ClzInt32: o << int8_t(BinaryConsts::I32Clz); break; + case CtzInt32: o << int8_t(BinaryConsts::I32Ctz); break; + case PopcntInt32: o << int8_t(BinaryConsts::I32Popcnt); break; + case EqZInt32: o << int8_t(BinaryConsts::I32EqZ); break; + case ClzInt64: o << int8_t(BinaryConsts::I64Clz); break; + case CtzInt64: o << int8_t(BinaryConsts::I64Ctz); break; + case PopcntInt64: o << int8_t(BinaryConsts::I64Popcnt); break; + case EqZInt64: o << int8_t(BinaryConsts::I64EqZ); break; + case NegFloat32: o << int8_t(BinaryConsts::F32Neg); break; + case AbsFloat32: o << int8_t(BinaryConsts::F32Abs); break; + case CeilFloat32: o << int8_t(BinaryConsts::F32Ceil); break; + case FloorFloat32: o << int8_t(BinaryConsts::F32Floor); break; + case TruncFloat32: o << int8_t(BinaryConsts::F32Trunc); break; + case NearestFloat32: o << int8_t(BinaryConsts::F32NearestInt); break; + case SqrtFloat32: o << int8_t(BinaryConsts::F32Sqrt); break; + case NegFloat64: o << int8_t(BinaryConsts::F64Neg); break; + case AbsFloat64: o << int8_t(BinaryConsts::F64Abs); break; + case CeilFloat64: o << int8_t(BinaryConsts::F64Ceil); break; + case FloorFloat64: o << int8_t(BinaryConsts::F64Floor); break; + case TruncFloat64: o << int8_t(BinaryConsts::F64Trunc); break; + case NearestFloat64: o << int8_t(BinaryConsts::F64NearestInt); break; + case SqrtFloat64: o << int8_t(BinaryConsts::F64Sqrt); break; case ExtendSInt32: o << int8_t(BinaryConsts::I64STruncI32); break; case ExtendUInt32: o << int8_t(BinaryConsts::I64UTruncI32); break; case WrapInt64: o << int8_t(BinaryConsts::I32ConvertI64); break; @@ -1054,72 +1065,89 @@ public: if (debug) std::cerr << "zz node: Binary" << std::endl; recurse(curr->left); recurse(curr->right); - #define TYPED_CODE(code) { \ - switch (getReachableWasmType(curr->left->type, curr->right->type)) { \ - case i32: o << int8_t(BinaryConsts::I32##code); break; \ - case i64: o << int8_t(BinaryConsts::I64##code); break; \ - case f32: o << int8_t(BinaryConsts::F32##code); break; \ - case f64: o << int8_t(BinaryConsts::F64##code); break; \ - default: abort(); \ - } \ - break; \ - } - #define INT_TYPED_CODE(code) { \ - switch (getReachableWasmType(curr->left->type, curr->right->type)) { \ - case i32: o << int8_t(BinaryConsts::I32##code); break; \ - case i64: o << int8_t(BinaryConsts::I64##code); break; \ - default: abort(); \ - } \ - break; \ - } - #define FLOAT_TYPED_CODE(code) { \ - switch (getReachableWasmType(curr->left->type, curr->right->type)) { \ - case f32: o << int8_t(BinaryConsts::F32##code); break; \ - case f64: o << int8_t(BinaryConsts::F64##code); break; \ - default: abort(); \ - } \ - break; \ - } switch (curr->op) { - case Add: TYPED_CODE(Add); - case Sub: TYPED_CODE(Sub); - case Mul: TYPED_CODE(Mul); - case DivS: INT_TYPED_CODE(DivS); - case DivU: INT_TYPED_CODE(DivU); - case RemS: INT_TYPED_CODE(RemS); - case RemU: INT_TYPED_CODE(RemU); - case And: INT_TYPED_CODE(And); - case Or: INT_TYPED_CODE(Or); - case Xor: INT_TYPED_CODE(Xor); - case Shl: INT_TYPED_CODE(Shl);; - case ShrU: INT_TYPED_CODE(ShrU); - case ShrS: INT_TYPED_CODE(ShrS); - case RotL: INT_TYPED_CODE(RotL); - case RotR: INT_TYPED_CODE(RotR); - case Div: FLOAT_TYPED_CODE(Div); - case CopySign: FLOAT_TYPED_CODE(CopySign); - case Min: FLOAT_TYPED_CODE(Min); - case Max: FLOAT_TYPED_CODE(Max); - case Eq: TYPED_CODE(Eq); - case Ne: TYPED_CODE(Ne); - case LtS: INT_TYPED_CODE(LtS); - case LtU: INT_TYPED_CODE(LtU); - case LeS: INT_TYPED_CODE(LeS); - case LeU: INT_TYPED_CODE(LeU); - case GtS: INT_TYPED_CODE(GtS); - case GtU: INT_TYPED_CODE(GtU); - case GeS: INT_TYPED_CODE(GeS); - case GeU: INT_TYPED_CODE(GeU); - case Lt: FLOAT_TYPED_CODE(Lt); - case Le: FLOAT_TYPED_CODE(Le); - case Gt: FLOAT_TYPED_CODE(Gt); - case Ge: FLOAT_TYPED_CODE(Ge); - default: abort(); + case AddInt32: o << int8_t(BinaryConsts::I32Add); break; + case SubInt32: o << int8_t(BinaryConsts::I32Sub); break; + case MulInt32: o << int8_t(BinaryConsts::I32Mul); break; + case DivSInt32: o << int8_t(BinaryConsts::I32DivS); break; + case DivUInt32: o << int8_t(BinaryConsts::I32DivU); break; + case RemSInt32: o << int8_t(BinaryConsts::I32RemS); break; + case RemUInt32: o << int8_t(BinaryConsts::I32RemU); break; + case AndInt32: o << int8_t(BinaryConsts::I32And); break; + case OrInt32: o << int8_t(BinaryConsts::I32Or); break; + case XorInt32: o << int8_t(BinaryConsts::I32Xor); break; + case ShlInt32: o << int8_t(BinaryConsts::I32Shl); break; + case ShrUInt32: o << int8_t(BinaryConsts::I32ShrU); break; + case ShrSInt32: o << int8_t(BinaryConsts::I32ShrS); break; + case RotLInt32: o << int8_t(BinaryConsts::I32RotL); break; + case RotRInt32: o << int8_t(BinaryConsts::I32RotR); break; + case EqInt32: o << int8_t(BinaryConsts::I32Eq); break; + case NeInt32: o << int8_t(BinaryConsts::I32Ne); break; + case LtSInt32: o << int8_t(BinaryConsts::I32LtS); break; + case LtUInt32: o << int8_t(BinaryConsts::I32LtU); break; + case LeSInt32: o << int8_t(BinaryConsts::I32LeS); break; + case LeUInt32: o << int8_t(BinaryConsts::I32LeU); break; + case GtSInt32: o << int8_t(BinaryConsts::I32GtS); break; + case GtUInt32: o << int8_t(BinaryConsts::I32GtU); break; + case GeSInt32: o << int8_t(BinaryConsts::I32GeS); break; + case GeUInt32: o << int8_t(BinaryConsts::I32GeU); break; + + case AddInt64: o << int8_t(BinaryConsts::I64Add); break; + case SubInt64: o << int8_t(BinaryConsts::I64Sub); break; + case MulInt64: o << int8_t(BinaryConsts::I64Mul); break; + case DivSInt64: o << int8_t(BinaryConsts::I64DivS); break; + case DivUInt64: o << int8_t(BinaryConsts::I64DivU); break; + case RemSInt64: o << int8_t(BinaryConsts::I64RemS); break; + case RemUInt64: o << int8_t(BinaryConsts::I64RemU); break; + case AndInt64: o << int8_t(BinaryConsts::I64And); break; + case OrInt64: o << int8_t(BinaryConsts::I64Or); break; + case XorInt64: o << int8_t(BinaryConsts::I64Xor); break; + case ShlInt64: o << int8_t(BinaryConsts::I64Shl); break; + case ShrUInt64: o << int8_t(BinaryConsts::I64ShrU); break; + case ShrSInt64: o << int8_t(BinaryConsts::I64ShrS); break; + case RotLInt64: o << int8_t(BinaryConsts::I64RotL); break; + case RotRInt64: o << int8_t(BinaryConsts::I64RotR); break; + case EqInt64: o << int8_t(BinaryConsts::I64Eq); break; + case NeInt64: o << int8_t(BinaryConsts::I64Ne); break; + case LtSInt64: o << int8_t(BinaryConsts::I64LtS); break; + case LtUInt64: o << int8_t(BinaryConsts::I64LtU); break; + case LeSInt64: o << int8_t(BinaryConsts::I64LeS); break; + case LeUInt64: o << int8_t(BinaryConsts::I64LeU); break; + case GtSInt64: o << int8_t(BinaryConsts::I64GtS); break; + case GtUInt64: o << int8_t(BinaryConsts::I64GtU); break; + case GeSInt64: o << int8_t(BinaryConsts::I64GeS); break; + case GeUInt64: o << int8_t(BinaryConsts::I64GeU); break; + + case AddFloat32: o << int8_t(BinaryConsts::F32Add); break; + case SubFloat32: o << int8_t(BinaryConsts::F32Sub); break; + case MulFloat32: o << int8_t(BinaryConsts::F32Mul); break; + case DivFloat32: o << int8_t(BinaryConsts::F32Div); break; + case CopySignFloat32: o << int8_t(BinaryConsts::F32CopySign);break; + case MinFloat32: o << int8_t(BinaryConsts::F32Min); break; + case MaxFloat32: o << int8_t(BinaryConsts::F32Max); break; + case EqFloat32: o << int8_t(BinaryConsts::F32Eq); break; + case NeFloat32: o << int8_t(BinaryConsts::F32Ne); break; + case LtFloat32: o << int8_t(BinaryConsts::F32Lt); break; + case LeFloat32: o << int8_t(BinaryConsts::F32Le); break; + case GtFloat32: o << int8_t(BinaryConsts::F32Gt); break; + case GeFloat32: o << int8_t(BinaryConsts::F32Ge); break; + + case AddFloat64: o << int8_t(BinaryConsts::F64Add); break; + case SubFloat64: o << int8_t(BinaryConsts::F64Sub); break; + case MulFloat64: o << int8_t(BinaryConsts::F64Mul); break; + case DivFloat64: o << int8_t(BinaryConsts::F64Div); break; + case CopySignFloat64: o << int8_t(BinaryConsts::F64CopySign);break; + case MinFloat64: o << int8_t(BinaryConsts::F64Min); break; + case MaxFloat64: o << int8_t(BinaryConsts::F64Max); break; + case EqFloat64: o << int8_t(BinaryConsts::F64Eq); break; + case NeFloat64: o << int8_t(BinaryConsts::F64Ne); break; + case LtFloat64: o << int8_t(BinaryConsts::F64Lt); break; + case LeFloat64: o << int8_t(BinaryConsts::F64Le); break; + case GtFloat64: o << int8_t(BinaryConsts::F64Gt); break; + case GeFloat64: o << int8_t(BinaryConsts::F64Ge); break; + default: abort(); } - #undef TYPED_CODE - #undef INT_TYPED_CODE - #undef FLOAT_TYPED_CODE } void visitSelect(Select *curr) { if (debug) std::cerr << "zz node: Select" << std::endl; @@ -1896,26 +1924,26 @@ public: bool maybeVisitUnary(Expression*& out, uint8_t code) { Unary* curr; switch (code) { - case BinaryConsts::I32Clz: curr = allocator.alloc<Unary>(); curr->op = Clz; curr->type = i32; break; - case BinaryConsts::I64Clz: curr = allocator.alloc<Unary>(); curr->op = Clz; curr->type = i64; break; - case BinaryConsts::I32Ctz: curr = allocator.alloc<Unary>(); curr->op = Ctz; curr->type = i32; break; - case BinaryConsts::I64Ctz: curr = allocator.alloc<Unary>(); curr->op = Ctz; curr->type = i64; break; - case BinaryConsts::I32Popcnt: curr = allocator.alloc<Unary>(); curr->op = Popcnt; curr->type = i32; break; - case BinaryConsts::I64Popcnt: curr = allocator.alloc<Unary>(); curr->op = Popcnt; curr->type = i64; break; - case BinaryConsts::I32EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZ; curr->type = i32; break; - case BinaryConsts::I64EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZ; curr->type = i64; break; - case BinaryConsts::F32Neg: curr = allocator.alloc<Unary>(); curr->op = Neg; curr->type = f32; break; - case BinaryConsts::F64Neg: curr = allocator.alloc<Unary>(); curr->op = Neg; curr->type = f64; break; - case BinaryConsts::F32Abs: curr = allocator.alloc<Unary>(); curr->op = Abs; curr->type = f32; break; - case BinaryConsts::F64Abs: curr = allocator.alloc<Unary>(); curr->op = Abs; curr->type = f64; break; - case BinaryConsts::F32Ceil: curr = allocator.alloc<Unary>(); curr->op = Ceil; curr->type = f32; break; - case BinaryConsts::F64Ceil: curr = allocator.alloc<Unary>(); curr->op = Ceil; curr->type = f64; break; - case BinaryConsts::F32Floor: curr = allocator.alloc<Unary>(); curr->op = Floor; curr->type = f32; break; - case BinaryConsts::F64Floor: curr = allocator.alloc<Unary>(); curr->op = Floor; curr->type = f64; break; - case BinaryConsts::F32NearestInt: curr = allocator.alloc<Unary>(); curr->op = Nearest; curr->type = f32; break; - case BinaryConsts::F64NearestInt: curr = allocator.alloc<Unary>(); curr->op = Nearest; curr->type = f64; break; - case BinaryConsts::F32Sqrt: curr = allocator.alloc<Unary>(); curr->op = Sqrt; curr->type = f32; break; - case BinaryConsts::F64Sqrt: curr = allocator.alloc<Unary>(); curr->op = Sqrt; curr->type = f64; break; + case BinaryConsts::I32Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt32; curr->type = i32; break; + case BinaryConsts::I64Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt64; curr->type = i64; break; + case BinaryConsts::I32Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt32; curr->type = i32; break; + case BinaryConsts::I64Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt64; curr->type = i64; break; + case BinaryConsts::I32Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt32; curr->type = i32; break; + case BinaryConsts::I64Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt64; curr->type = i64; break; + case BinaryConsts::I32EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt32; curr->type = i32; break; + case BinaryConsts::I64EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt64; curr->type = i32; break; + case BinaryConsts::F32Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat32; curr->type = f32; break; + case BinaryConsts::F64Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat64; curr->type = f64; break; + case BinaryConsts::F32Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat32; curr->type = f32; break; + case BinaryConsts::F64Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat64; curr->type = f64; break; + case BinaryConsts::F32Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat32; curr->type = f32; break; + case BinaryConsts::F64Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat64; curr->type = f64; break; + case BinaryConsts::F32Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat32; curr->type = f32; break; + case BinaryConsts::F64Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat64; curr->type = f64; break; + case BinaryConsts::F32NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat32; curr->type = f32; break; + case BinaryConsts::F64NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat64; curr->type = f64; break; + case BinaryConsts::F32Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat32; curr->type = f32; break; + case BinaryConsts::F64Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat64; curr->type = f64; break; case BinaryConsts::F32UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat32; curr->type = f32; break; case BinaryConsts::F64UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat64; curr->type = f64; break; case BinaryConsts::F32SConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt32ToFloat32; curr->type = f32; break; @@ -1930,16 +1958,16 @@ public: case BinaryConsts::I32ConvertI64: curr = allocator.alloc<Unary>(); curr->op = WrapInt64; curr->type = i32; break; case BinaryConsts::I32UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt32; curr->type = i32; break; - case BinaryConsts::I32UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt64; curr->type = i32; break; + case BinaryConsts::I32UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt32; curr->type = i32; break; case BinaryConsts::I32STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt32; curr->type = i32; break; - case BinaryConsts::I32STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt64; curr->type = i32; break; - case BinaryConsts::I64UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt32; curr->type = i64; break; + case BinaryConsts::I32STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt32; curr->type = i32; break; + case BinaryConsts::I64UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt64; curr->type = i64; break; case BinaryConsts::I64UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt64; curr->type = i64; break; - case BinaryConsts::I64STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt32; curr->type = i64; break; + case BinaryConsts::I64STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt64; curr->type = i64; break; case BinaryConsts::I64STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt64; curr->type = i64; break; - case BinaryConsts::F32Trunc: curr = allocator.alloc<Unary>(); curr->op = Trunc; curr->type = f32; break; - case BinaryConsts::F64Trunc: curr = allocator.alloc<Unary>(); curr->op = Trunc; curr->type = f64; break; + case BinaryConsts::F32Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat32; curr->type = f32; break; + case BinaryConsts::F64Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat64; curr->type = f64; break; case BinaryConsts::F32ConvertF64: curr = allocator.alloc<Unary>(); curr->op = DemoteFloat64; curr->type = f32; break; case BinaryConsts::F64ConvertF32: curr = allocator.alloc<Unary>(); curr->op = PromoteFloat32; curr->type = f64; break; @@ -1957,20 +1985,19 @@ public: } bool maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; - #define TYPED_CODE(code) { \ - case BinaryConsts::I32##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = i32; break; \ - case BinaryConsts::I64##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = i64; break; \ - case BinaryConsts::F32##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = f32; break; \ - case BinaryConsts::F64##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = f64; break; \ - } #define INT_TYPED_CODE(code) { \ - case BinaryConsts::I32##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = i32; break; \ - case BinaryConsts::I64##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = i64; break; \ + case BinaryConsts::I32##code: curr = allocator.alloc<Binary>(); curr->op = code##Int32; curr->type = i32; break; \ + case BinaryConsts::I64##code: curr = allocator.alloc<Binary>(); curr->op = code##Int64; curr->type = i64; break; \ } #define FLOAT_TYPED_CODE(code) { \ - case BinaryConsts::F32##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = f32; break; \ - case BinaryConsts::F64##code: curr = allocator.alloc<Binary>(); curr->op = code; curr->type = f64; break; \ + case BinaryConsts::F32##code: curr = allocator.alloc<Binary>(); curr->op = code##Float32; curr->type = f32; break; \ + case BinaryConsts::F64##code: curr = allocator.alloc<Binary>(); curr->op = code##Float64; curr->type = f64; break; \ } + #define TYPED_CODE(code) { \ + INT_TYPED_CODE(code) \ + FLOAT_TYPED_CODE(code) \ + } + switch (code) { TYPED_CODE(Add); TYPED_CODE(Sub); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index c20c25f06..4dc195d5c 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -136,44 +136,7 @@ public: Unary* makeUnary(UnaryOp op, Expression *value) { auto* ret = allocator.alloc<Unary>(); ret->op = op; ret->value = value; - switch (op) { - case Clz: - case Ctz: - case Popcnt: - case Neg: - case Abs: - case Ceil: - case Floor: - case Trunc: - case Nearest: - case Sqrt: ret->type = value->type; break; - case EqZ: ret->type = i32; break; - case ExtendSInt32: case ExtendUInt32: ret->type = i64; break; - case WrapInt64: ret->type = i32; break; - case PromoteFloat32: ret->type = f64; break; - case DemoteFloat64: ret->type = f32; break; - case TruncSFloat32ToInt32: - case TruncUFloat32ToInt32: - case TruncSFloat64ToInt32: - case TruncUFloat64ToInt32: - case ReinterpretFloat32: ret->type = i32; break; - case TruncSFloat32ToInt64: - case TruncUFloat32ToInt64: - case TruncSFloat64ToInt64: - case TruncUFloat64ToInt64: - case ReinterpretFloat64: ret->type = i64; break; - case ReinterpretInt32: - case ConvertSInt32ToFloat32: - case ConvertUInt32ToFloat32: - case ConvertSInt64ToFloat32: - case ConvertUInt64ToFloat32: ret->type = f32; break; - case ReinterpretInt64: - case ConvertSInt32ToFloat64: - case ConvertUInt32ToFloat64: - case ConvertSInt64ToFloat64: - case ConvertUInt64ToFloat64: ret->type = f64; break; - default: abort(); - } + ret->finalize(); return ret; } Binary* makeBinary(BinaryOp op, Expression *left, Expression *right) { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index c40132b18..b8f6554ec 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -145,7 +145,7 @@ private: class FunctionScope { public: - std::map<IString, Literal> locals; + std::vector<Literal> locals; Function* function; FunctionScope(Function* function, LiteralList& arguments) @@ -156,6 +156,7 @@ private: << arguments.size() << " arguments." << std::endl; abort(); } + locals.resize(function->getNumLocals()); for (size_t i = 0; i < function->getNumLocals(); i++) { if (i < arguments.size()) { assert(function->isParam(i)); @@ -166,10 +167,10 @@ private: << printWasmType(arguments[i].type) << "." << std::endl; abort(); } - locals[function->getLocalName(i)] = arguments[i]; + locals[i] = arguments[i]; } else { assert(function->isVar(i)); - locals[function->getLocalName(i)].type = function->getLocalType(i); + locals[i].type = function->getLocalType(i); } } } @@ -358,20 +359,20 @@ private: Flow visitGetLocal(GetLocal *curr) { NOTE_ENTER("GetLocal"); - IString name = scope.function->getLocalName(curr->index); - NOTE_NAME(name); - NOTE_EVAL1(scope.locals[name]); - return scope.locals[name]; + auto index = curr->index; + NOTE_EVAL1(index); + NOTE_EVAL1(scope.locals[index]); + return scope.locals[index]; } Flow visitSetLocal(SetLocal *curr) { NOTE_ENTER("SetLocal"); - IString name = scope.function->getLocalName(curr->index); + auto index = curr->index; Flow flow = visit(curr->value); if (flow.breaking()) return flow; - NOTE_NAME(name); + NOTE_EVAL1(index); NOTE_EVAL1(flow.value); assert(flow.value.type == curr->type); - scope.locals[name] = flow.value; + scope.locals[index] = flow.value; return flow; } Flow visitLoad(Load *curr) { @@ -402,10 +403,10 @@ private: NOTE_EVAL1(value); if (value.type == i32) { switch (curr->op) { - case Clz: return value.countLeadingZeroes(); - case Ctz: return value.countTrailingZeroes(); - case Popcnt: return value.popCount(); - case EqZ: return Literal(int32_t(value == Literal(int32_t(0)))); + case ClzInt32: return value.countLeadingZeroes(); + case CtzInt32: return value.countTrailingZeroes(); + case PopcntInt32: return value.popCount(); + case EqZInt32: return Literal(int32_t(value == Literal(int32_t(0)))); case ReinterpretInt32: return value.castToF32(); case ExtendSInt32: return value.extendToSI64(); case ExtendUInt32: return value.extendToUI64(); @@ -418,10 +419,10 @@ private: } if (value.type == i64) { switch (curr->op) { - case Clz: return value.countLeadingZeroes(); - case Ctz: return value.countTrailingZeroes(); - case Popcnt: return value.popCount(); - case EqZ: return Literal(int32_t(value == Literal(int64_t(0)))); + case ClzInt64: return value.countLeadingZeroes(); + case CtzInt64: return value.countTrailingZeroes(); + case PopcntInt64: return value.popCount(); + case EqZInt64: return Literal(int32_t(value == Literal(int64_t(0)))); case WrapInt64: return value.truncateToI32(); case ReinterpretInt64: return value.castToF64(); case ConvertUInt64ToFloat32: return value.convertUToF32(); @@ -433,13 +434,13 @@ private: } if (value.type == f32) { switch (curr->op) { - case Neg: return value.neg(); - case Abs: return value.abs(); - case Ceil: return value.ceil(); - case Floor: return value.floor(); - case Trunc: return value.trunc(); - case Nearest: return value.nearbyint(); - case Sqrt: return value.sqrt(); + case NegFloat32: return value.neg(); + case AbsFloat32: return value.abs(); + case CeilFloat32: return value.ceil(); + case FloorFloat32: return value.floor(); + case TruncFloat32: return value.trunc(); + case NearestFloat32: return value.nearbyint(); + case SqrtFloat32: return value.sqrt(); case TruncSFloat32ToInt32: case TruncSFloat32ToInt64: return truncSFloat(curr, value); case TruncUFloat32ToInt32: @@ -451,13 +452,13 @@ private: } if (value.type == f64) { switch (curr->op) { - case Neg: return value.neg(); - case Abs: return value.abs(); - case Ceil: return value.ceil(); - case Floor: return value.floor(); - case Trunc: return value.trunc(); - case Nearest: return value.nearbyint(); - case Sqrt: return value.sqrt(); + case NegFloat64: return value.neg(); + case AbsFloat64: return value.abs(); + case CeilFloat64: return value.ceil(); + case FloorFloat64: return value.floor(); + case TruncFloat64: return value.trunc(); + case NearestFloat64: return value.nearbyint(); + case SqrtFloat64: return value.sqrt(); case TruncSFloat64ToInt32: case TruncSFloat64ToInt64: return truncSFloat(curr, value); case TruncUFloat64ToInt32: @@ -489,105 +490,105 @@ private: assert(isConcreteWasmType(curr->right->type) ? right.type == curr->right->type : true); if (left.type == i32) { switch (curr->op) { - case Add: return left.add(right); - case Sub: return left.sub(right); - case Mul: return left.mul(right); - case DivS: { + case AddInt32: return left.add(right); + case SubInt32: return left.sub(right); + case MulInt32: return left.mul(right); + case DivSInt32: { if (right.getInteger() == 0) trap("i32.div_s by 0"); if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow return left.divS(right); } - case DivU: { + case DivUInt32: { if (right.getInteger() == 0) trap("i32.div_u by 0"); return left.divU(right); } - case RemS: { + case RemSInt32: { if (right.getInteger() == 0) trap("i32.rem_s by 0"); if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) return Literal(int32_t(0)); return left.remS(right); } - case RemU: { + case RemUInt32: { if (right.getInteger() == 0) trap("i32.rem_u by 0"); return left.remU(right); } - case And: return left.and_(right); - case Or: return left.or_(right); - case Xor: return left.xor_(right); - case Shl: return left.shl(right.and_(Literal(int32_t(31)))); - case ShrU: return left.shrU(right.and_(Literal(int32_t(31)))); - case ShrS: return left.shrS(right.and_(Literal(int32_t(31)))); - case RotL: return left.rotL(right); - case RotR: return left.rotR(right); - case Eq: return left.eq(right); - case Ne: return left.ne(right); - case LtS: return left.ltS(right); - case LtU: return left.ltU(right); - case LeS: return left.leS(right); - case LeU: return left.leU(right); - case GtS: return left.gtS(right); - case GtU: return left.gtU(right); - case GeS: return left.geS(right); - case GeU: return left.geU(right); + case AndInt32: return left.and_(right); + case OrInt32: return left.or_(right); + case XorInt32: return left.xor_(right); + case ShlInt32: return left.shl(right.and_(Literal(int32_t(31)))); + case ShrUInt32: return left.shrU(right.and_(Literal(int32_t(31)))); + case ShrSInt32: return left.shrS(right.and_(Literal(int32_t(31)))); + case RotLInt32: return left.rotL(right); + case RotRInt32: return left.rotR(right); + case EqInt32: return left.eq(right); + case NeInt32: return left.ne(right); + case LtSInt32: return left.ltS(right); + case LtUInt32: return left.ltU(right); + case LeSInt32: return left.leS(right); + case LeUInt32: return left.leU(right); + case GtSInt32: return left.gtS(right); + case GtUInt32: return left.gtU(right); + case GeSInt32: return left.geS(right); + case GeUInt32: return left.geU(right); default: abort(); } } else if (left.type == i64) { switch (curr->op) { - case Add: return left.add(right); - case Sub: return left.sub(right); - case Mul: return left.mul(right); - case DivS: { + case AddInt64: return left.add(right); + case SubInt64: return left.sub(right); + case MulInt64: return left.mul(right); + case DivSInt64: { if (right.getInteger() == 0) trap("i64.div_s by 0"); if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow return left.divS(right); } - case DivU: { + case DivUInt64: { if (right.getInteger() == 0) trap("i64.div_u by 0"); return left.divU(right); } - case RemS: { + case RemSInt64: { if (right.getInteger() == 0) trap("i64.rem_s by 0"); if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) return Literal(int64_t(0)); return left.remS(right); } - case RemU: { + case RemUInt64: { if (right.getInteger() == 0) trap("i64.rem_u by 0"); return left.remU(right); } - case And: return left.and_(right); - case Or: return left.or_(right); - case Xor: return left.xor_(right); - case Shl: return left.shl(right.and_(Literal(int64_t(63)))); - case ShrU: return left.shrU(right.and_(Literal(int64_t(63)))); - case ShrS: return left.shrS(right.and_(Literal(int64_t(63)))); - case RotL: return left.rotL(right); - case RotR: return left.rotR(right); - case Eq: return left.eq(right); - case Ne: return left.ne(right); - case LtS: return left.ltS(right); - case LtU: return left.ltU(right); - case LeS: return left.leS(right); - case LeU: return left.leU(right); - case GtS: return left.gtS(right); - case GtU: return left.gtU(right); - case GeS: return left.geS(right); - case GeU: return left.geU(right); + case AndInt64: return left.and_(right); + case OrInt64: return left.or_(right); + case XorInt64: return left.xor_(right); + case ShlInt64: return left.shl(right.and_(Literal(int64_t(63)))); + case ShrUInt64: return left.shrU(right.and_(Literal(int64_t(63)))); + case ShrSInt64: return left.shrS(right.and_(Literal(int64_t(63)))); + case RotLInt64: return left.rotL(right); + case RotRInt64: return left.rotR(right); + case EqInt64: return left.eq(right); + case NeInt64: return left.ne(right); + case LtSInt64: return left.ltS(right); + case LtUInt64: return left.ltU(right); + case LeSInt64: return left.leS(right); + case LeUInt64: return left.leU(right); + case GtSInt64: return left.gtS(right); + case GtUInt64: return left.gtU(right); + case GeSInt64: return left.geS(right); + case GeUInt64: return left.geU(right); default: abort(); } } else if (left.type == f32 || left.type == f64) { switch (curr->op) { - case Add: return left.add(right); - case Sub: return left.sub(right); - case Mul: return left.mul(right); - case Div: return left.div(right); - case CopySign: return left.copysign(right); - case Min: return left.min(right); - case Max: return left.max(right); - case Eq: return left.eq(right); - case Ne: return left.ne(right); - case Lt: return left.lt(right); - case Le: return left.le(right); - case Gt: return left.gt(right); - case Ge: return left.ge(right); + case AddFloat32: case AddFloat64: return left.add(right); + case SubFloat32: case SubFloat64: return left.sub(right); + case MulFloat32: case MulFloat64: return left.mul(right); + case DivFloat32: case DivFloat64: return left.div(right); + case CopySignFloat32: case CopySignFloat64: return left.copysign(right); + case MinFloat32: case MinFloat64: return left.min(right); + case MaxFloat32: case MaxFloat64: return left.max(right); + case EqFloat32: case EqFloat64: return left.eq(right); + case NeFloat32: case NeFloat64: return left.ne(right); + case LtFloat32: case LtFloat64: return left.lt(right); + case LeFloat32: case LeFloat64: return left.le(right); + case GtFloat32: case GtFloat64: return left.gt(right); + case GeFloat32: case GeFloat64: return left.ge(right); default: abort(); } } diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp index ea1883072..87059f9be 100644 --- a/src/wasm-js.cpp +++ b/src/wasm-js.cpp @@ -91,7 +91,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) { auto& global = pair.second; if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here double value = EM_ASM_DOUBLE({ return Module['lookupImport'](Pointer_stringify($0), Pointer_stringify($1)) }, global.module.str, global.base.str); - unsigned address = global.address; + uint32_t address = global.address; switch (global.type) { case i32: EM_ASM_({ Module['info'].parent['HEAP32'][$0 >> 2] = $1 }, address, value); break; case f32: EM_ASM_({ Module['info'].parent['HEAPF32'][$0 >> 2] = $1 }, address, value); break; @@ -196,7 +196,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { var source = Module['HEAP8'].subarray($1, $1 + $2); var target = new Int8Array(Module['outside']['newBuffer']); target.set(source, $0); - }, segment.offset, &segment.data[0], segment.data.size()); + }, (uint32_t)segment.offset, &segment.data[0], segment.data.size()); } } @@ -236,7 +236,8 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { } } - Literal load(Load* load, Address addr) override { + Literal load(Load* load, Address address) override { + uint32_t addr = address; if (load->align < load->bytes || (addr & (load->bytes-1))) { int64_t out64; double ret = EM_ASM_DOUBLE({ @@ -325,7 +326,8 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { } } - void store(Store* store_, Address addr, Literal value) override { + void store(Store* store_, Address address, Literal value) override { + uint32_t addr = address; // support int64 stores if (value.type == WasmType::i64) { Store fake = *store_; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 4dfba25f8..b59af7215 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -60,8 +60,6 @@ class Element { IString str_; bool dollared_; - size_t line, col; - public: Element(MixedArena& allocator) : isList_(true), list_(allocator), line(-1), col(-1) {} @@ -69,6 +67,8 @@ public: bool isStr() { return !isList_; } bool dollared() { return dollared_; } + size_t line, col; + // list methods List& list() { @@ -300,11 +300,9 @@ private: if (id == TYPE) return parseType(s); if (id != FUNC) return; size_t i = 1; - Name name; - if (s[i]->isStr()) { - name = s[i]->str(); - i++; - } else { + Name name, exportName; + i = parseFunctionNames(s, name, exportName); + if (!name.is()) { // unnamed, use an index name = Name::fromInt(functionCounter); } @@ -371,21 +369,26 @@ private: wasm.addStart(getFunctionName(*s[1])); } - void parseFunction(Element& s) { + // returns the next index in s + size_t parseFunctionNames(Element& s, Name& name, Name& exportName) { size_t i = 1; - Name name, exportName; - if (s[i]->isStr()) { + while (i < s.size() && s[i]->isStr()) { if (!s[i]->dollared()) { // an export name exportName = s[i]->str(); i++; - } - if (s[i]->isStr()) { - assert(s[i]->dollared()); + } else { name = s[i]->str(); i++; } } + return i; + } + + void parseFunction(Element& s) { + size_t i = 1; + Name name, exportName; + i = parseFunctionNames(s, name, exportName); if (!name.is()) { // unnamed, use an index name = Name::fromInt(functionCounter); @@ -524,18 +527,21 @@ public: enum { maxNameSize = 15 }; char op[maxNameSize + 1] = {'\0'}; strncpy(op, dot + 1, maxNameSize); + #define BINARY_INT_OR_FLOAT(op) (type == i32 ? BinaryOp::op##Int32 : (type == i64 ? BinaryOp::op##Int64 : (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64))) + #define BINARY_INT(op) (type == i32 ? BinaryOp::op##Int32 : BinaryOp::op##Int64) + #define BINARY_FLOAT(op) (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64) switch (op[0]) { case 'a': { - if (op[1] == 'b') return makeUnary(s, UnaryOp::Abs, type); - if (op[1] == 'd') return makeBinary(s, BinaryOp::Add, type); - if (op[1] == 'n') return makeBinary(s, BinaryOp::And, type); + if (op[1] == 'b') return makeUnary(s, type == f32 ? UnaryOp::AbsFloat32 : UnaryOp::AbsFloat64, type); + if (op[1] == 'd') return makeBinary(s, BINARY_INT_OR_FLOAT(Add), type); + if (op[1] == 'n') return makeBinary(s, BINARY_INT(And), type); abort_on(op); } case 'c': { - if (op[1] == 'e') return makeUnary(s, UnaryOp::Ceil, type); - if (op[1] == 'l') return makeUnary(s, UnaryOp::Clz, type); + if (op[1] == 'e') return makeUnary(s, type == f32 ? UnaryOp::CeilFloat32 : UnaryOp::CeilFloat64, type); + if (op[1] == 'l') return makeUnary(s, type == i32 ? UnaryOp::ClzInt32 : UnaryOp::ClzInt64, type); if (op[1] == 'o') { - if (op[2] == 'p') return makeBinary(s, BinaryOp::CopySign, type); + if (op[2] == 'p') return makeBinary(s, BINARY_FLOAT(CopySign), type); if (op[2] == 'n') { if (op[3] == 'v') { if (op[8] == 's') return makeUnary(s, op[11] == '3' ? (type == f32 ? UnaryOp::ConvertSInt32ToFloat32 : UnaryOp::ConvertSInt32ToFloat64) : (type == f32 ? UnaryOp::ConvertSInt64ToFloat32 : UnaryOp::ConvertSInt64ToFloat64), type); @@ -544,92 +550,92 @@ public: if (op[3] == 's') return makeConst(s, type); } } - if (op[1] == 't') return makeUnary(s, UnaryOp::Ctz, type); + if (op[1] == 't') return makeUnary(s, type == i32 ? UnaryOp::CtzInt32 : UnaryOp::CtzInt64, type); abort_on(op); } case 'd': { if (op[1] == 'i') { - if (op[3] == '_') return makeBinary(s, op[4] == 'u' ? BinaryOp::DivU : BinaryOp::DivS, type); - if (op[3] == 0) return makeBinary(s, BinaryOp::Div, type); + if (op[3] == '_') return makeBinary(s, op[4] == 'u' ? BINARY_INT(DivU) : BINARY_INT(DivS), type); + if (op[3] == 0) return makeBinary(s, BINARY_FLOAT(Div), type); } if (op[1] == 'e') return makeUnary(s, UnaryOp::DemoteFloat64, type); abort_on(op); } case 'e': { if (op[1] == 'q') { - if (op[2] == 0) return makeBinary(s, BinaryOp::Eq, type); - if (op[2] == 'z') return makeUnary(s, UnaryOp::EqZ, i32); + if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Eq), type); + if (op[2] == 'z') return makeUnary(s, type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type); } if (op[1] == 'x') return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type); abort_on(op); } case 'f': { - if (op[1] == 'l') return makeUnary(s, UnaryOp::Floor, type); + if (op[1] == 'l') return makeUnary(s, type == f32 ? UnaryOp::FloorFloat32 : UnaryOp::FloorFloat64, type); abort_on(op); } case 'g': { if (op[1] == 't') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::GtU : BinaryOp::GtS, type); - if (op[2] == 0) return makeBinary(s, BinaryOp::Gt, type); + if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GtU) : BINARY_INT(GtS), type); + if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Gt), type); } if (op[1] == 'e') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::GeU : BinaryOp::GeS, type); - if (op[2] == 0) return makeBinary(s, BinaryOp::Ge, type); + if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GeU) : BINARY_INT(GeS), type); + if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Ge), type); } abort_on(op); } case 'l': { if (op[1] == 't') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::LtU : BinaryOp::LtS, type); - if (op[2] == 0) return makeBinary(s, BinaryOp::Lt, type); + if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LtU) : BINARY_INT(LtS), type); + if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Lt), type); } if (op[1] == 'e') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::LeU : BinaryOp::LeS, type); - if (op[2] == 0) return makeBinary(s, BinaryOp::Le, type); + if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LeU) : BINARY_INT(LeS), type); + if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Le), type); } if (op[1] == 'o') return makeLoad(s, type); abort_on(op); } case 'm': { - if (op[1] == 'i') return makeBinary(s, BinaryOp::Min, type); - if (op[1] == 'a') return makeBinary(s, BinaryOp::Max, type); - if (op[1] == 'u') return makeBinary(s, BinaryOp::Mul, type); + if (op[1] == 'i') return makeBinary(s, BINARY_FLOAT(Min), type); + if (op[1] == 'a') return makeBinary(s, BINARY_FLOAT(Max), type); + if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Mul), type); abort_on(op); } case 'n': { if (op[1] == 'e') { - if (op[2] == 0) return makeBinary(s, BinaryOp::Ne, type); - if (op[2] == 'a') return makeUnary(s, UnaryOp::Nearest, type); - if (op[2] == 'g') return makeUnary(s, UnaryOp::Neg, type); + if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Ne), type); + if (op[2] == 'a') return makeUnary(s, type == f32 ? UnaryOp::NearestFloat32 : UnaryOp::NearestFloat64, type); + if (op[2] == 'g') return makeUnary(s, type == f32 ? UnaryOp::NegFloat32 : UnaryOp::NegFloat64, type); } abort_on(op); } case 'o': { - if (op[1] == 'r') return makeBinary(s, BinaryOp::Or, type); + if (op[1] == 'r') return makeBinary(s, BINARY_INT(Or), type); abort_on(op); } case 'p': { if (op[1] == 'r') return makeUnary(s, UnaryOp::PromoteFloat32, type); - if (op[1] == 'o') return makeUnary(s, UnaryOp::Popcnt, type); + if (op[1] == 'o') return makeUnary(s, type == i32 ? UnaryOp::PopcntInt32 : UnaryOp::PopcntInt64, type); abort_on(op); } case 'r': { if (op[1] == 'e') { - if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BinaryOp::RemU : BinaryOp::RemS, type); + if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BINARY_INT(RemU) : BINARY_INT(RemS), type); if (op[2] == 'i') return makeUnary(s, isWasmTypeFloat(type) ? (type == f32 ? UnaryOp::ReinterpretInt32 : UnaryOp::ReinterpretInt64) : (type == i32 ? UnaryOp::ReinterpretFloat32 : UnaryOp::ReinterpretFloat64), type); } if (op[1] == 'o' && op[2] == 't') { - return makeBinary(s, op[3] == 'l' ? BinaryOp::RotL : BinaryOp::RotR, type); + return makeBinary(s, op[3] == 'l' ? BINARY_INT(RotL) : BINARY_INT(RotR), type); } abort_on(op); } case 's': { if (op[1] == 'h') { - if (op[2] == 'l') return makeBinary(s, BinaryOp::Shl, type); - return makeBinary(s, op[4] == 'u' ? BinaryOp::ShrU : BinaryOp::ShrS, type); + if (op[2] == 'l') return makeBinary(s, BINARY_INT(Shl), type); + return makeBinary(s, op[4] == 'u' ? BINARY_INT(ShrU) : BINARY_INT(ShrS), type); } - if (op[1] == 'u') return makeBinary(s, BinaryOp::Sub, type); - if (op[1] == 'q') return makeUnary(s, UnaryOp::Sqrt, type); + if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Sub), type); + if (op[1] == 'q') return makeUnary(s, type == f32 ? UnaryOp::SqrtFloat32 : UnaryOp::SqrtFloat64, type); if (op[1] == 't') return makeStore(s, type); abort_on(op); } @@ -637,7 +643,7 @@ public: if (op[1] == 'r') { if (op[6] == 's') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncSFloat32ToInt32 : UnaryOp::TruncSFloat32ToInt64) : (type == i32 ? UnaryOp::TruncSFloat64ToInt32 : UnaryOp::TruncSFloat64ToInt64), type); if (op[6] == 'u') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncUFloat32ToInt32 : UnaryOp::TruncUFloat32ToInt64) : (type == i32 ? UnaryOp::TruncUFloat64ToInt32 : UnaryOp::TruncUFloat64ToInt64), type); - if (op[2] == 'u') return makeUnary(s, UnaryOp::Trunc, type); + if (op[2] == 'u') return makeUnary(s, type == f32 ? UnaryOp::TruncFloat32 : UnaryOp::TruncFloat64, type); } abort_on(op); } @@ -646,7 +652,7 @@ public: abort_on(op); } case 'x': { - if (op[1] == 'o') return makeBinary(s, BinaryOp::Xor, type); + if (op[1] == 'o') return makeBinary(s, BINARY_INT(Xor), type); abort_on(op); } default: abort_on(op); @@ -736,7 +742,61 @@ private: auto ret = allocator.alloc<Unary>(); ret->op = op; ret->value = parseExpression(s[1]); - ret->type = type; + ret->finalize(); + // type is the reported type, e.g. i64.ctz reports i64 (but has a return type of i32, in this case) + // verify the reported type is correct + switch (op) { + case EqZInt32: + case NegFloat32: + case AbsFloat32: + case CeilFloat32: + case FloorFloat32: + case TruncFloat32: + case NearestFloat32: + case SqrtFloat32: + case ClzInt32: + case CtzInt32: + case PopcntInt32: + case EqZInt64: + case NegFloat64: + case AbsFloat64: + case CeilFloat64: + case FloorFloat64: + case TruncFloat64: + case NearestFloat64: + case SqrtFloat64: + case ClzInt64: + case CtzInt64: + case PopcntInt64: { + if (ret->value->type != unreachable && type != ret->value->type) throw ParseException(std::string("bad type for ") + getExpressionName(ret) + ": " + printWasmType(type) + " vs value type " + printWasmType(ret->value->type), s.line, s.col); + break; + } + case ExtendSInt32: case ExtendUInt32: + case WrapInt64: + case PromoteFloat32: + case DemoteFloat64: + case TruncSFloat32ToInt32: + case TruncUFloat32ToInt32: + case TruncSFloat64ToInt32: + case TruncUFloat64ToInt32: + case ReinterpretFloat32: + case TruncSFloat32ToInt64: + case TruncUFloat32ToInt64: + case TruncSFloat64ToInt64: + case TruncUFloat64ToInt64: + case ReinterpretFloat64: + case ReinterpretInt32: + case ConvertSInt32ToFloat32: + case ConvertUInt32ToFloat32: + case ConvertSInt64ToFloat32: + case ConvertUInt64ToFloat32: + case ReinterpretInt64: + case ConvertSInt32ToFloat64: + case ConvertUInt32ToFloat64: + case ConvertSInt64ToFloat64: + case ConvertUInt64ToFloat64: break; + default: WASM_UNREACHABLE(); + } return ret; } @@ -1149,7 +1209,9 @@ private: if (s.size() == 2) return; size_t i = 2; if (s[i]->isStr()) { - wasm.memory.max = atoi(s[i]->c_str()); + uint64_t max = atoll(s[i]->c_str()); + if (max > Memory::kMaxSize) throw ParseException("total memory must be <= 4GB"); + wasm.memory.max = max; i++; } while (i < s.size()) { diff --git a/src/wasm-validator.h b/src/wasm-validator.h index f93e06524..8e94fd368 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -30,6 +30,7 @@ struct WasmValidator : public PostWalker<WasmValidator, Visitor<WasmValidator>> bool valid; std::map<Name, WasmType> breakTypes; // breaks to a label must all have the same type, and the right type + WasmType returnType = unreachable; // type used in returns public: bool validate(Module& module) { @@ -51,7 +52,7 @@ public: } } void visitIf(If *curr) { - shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "if condition must be i32"); + shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32 || curr->condition->type == i64, curr, "if condition must be valid"); } void visitLoop(Loop *curr) { if (curr->in.is()) { @@ -96,29 +97,39 @@ public: } void visitCall(Call *curr) { auto* target = getModule()->getFunction(curr->target); + shouldBeTrue(curr->operands.size() == target->params.size(), curr, "call param number must match"); for (size_t i = 0; i < curr->operands.size(); i++) { - shouldBeTrue(curr->operands[i]->type == target->params[i], curr, "call param types must match"); + shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, target->params[i], curr, "call param types must match"); } } void visitCallImport(CallImport *curr) { auto* target = getModule()->getImport(curr->target)->type; + shouldBeTrue(curr->operands.size() == target->params.size(), curr, "call param number must match"); for (size_t i = 0; i < curr->operands.size(); i++) { - shouldBeTrue(curr->operands[i]->type == target->params[i], curr, "call param types must match"); + shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, target->params[i], curr, "call param types must match"); + } + } + void visitCallIndirect(CallIndirect *curr) { + auto* type = curr->fullType; + shouldBeEqualOrFirstIsUnreachable(curr->target->type, i32, curr, "indirect call target must be an i32"); + shouldBeTrue(curr->operands.size() == type->params.size(), curr, "call param number must match"); + for (size_t i = 0; i < curr->operands.size(); i++) { + shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, type->params[i], curr, "call param types must match"); } } void visitSetLocal(SetLocal *curr) { if (curr->value->type != unreachable) { - shouldBeEqual(curr->type, curr->value->type, curr, "set_local type must be correct"); + shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "set_local type must be correct"); } } void visitLoad(Load *curr) { validateAlignment(curr->align); - shouldBeEqual(curr->ptr->type, i32, curr, "load pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "load pointer type must be i32"); } void visitStore(Store *curr) { validateAlignment(curr->align); - shouldBeEqual(curr->ptr->type, i32, curr, "store pointer type must be i32"); - shouldBeEqual(curr->value->type, curr->type, curr, "store value type must match"); + shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "store pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "store value type must match"); } void visitBinary(Binary *curr) { if (curr->left->type != unreachable && curr->right->type != unreachable) { @@ -126,24 +137,45 @@ public: } } void visitUnary(Unary *curr) { + shouldBeUnequal(curr->value->type, none, curr, "unaries must not receive a none as their input"); + switch (curr->op) { + case EqZInt32: + case EqZInt64: { + shouldBeEqual(curr->type, i32, curr, "eqz must return i32"); + break; + } + default: {} + } + if (curr->value->type == unreachable) return; switch (curr->op) { - case Clz: - case Ctz: - case Popcnt: - case Neg: - case Abs: - case Ceil: - case Floor: - case Trunc: - case Nearest: - case Sqrt: { + case ClzInt32: + case CtzInt32: + case PopcntInt32: + case NegFloat32: + case AbsFloat32: + case CeilFloat32: + case FloorFloat32: + case TruncFloat32: + case NearestFloat32: + case SqrtFloat32: + case ClzInt64: + case CtzInt64: + case PopcntInt64: + case NegFloat64: + case AbsFloat64: + case CeilFloat64: + case FloorFloat64: + case TruncFloat64: + case NearestFloat64: + case SqrtFloat64: { if (curr->value->type != unreachable) { shouldBeEqual(curr->value->type, curr->type, curr, "non-conversion unaries must return the same type"); } break; } - case EqZ: { - shouldBeEqual(curr->type, i32, curr, "relational unaries must return i32"); + case EqZInt32: + case EqZInt64: { + shouldBeTrue(curr->value->type == i32 || curr->value->type == i64, curr, "eqz input must be i32 or i64"); break; } case ExtendSInt32: shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break; @@ -175,15 +207,42 @@ public: } } + void visitReturn(Return* curr) { + if (curr->value) { + returnType = curr->value->type; + } + } + + void visitHost(Host* curr) { + switch (curr->op) { + case GrowMemory: { + shouldBeEqual(curr->operands.size(), size_t(1), curr, "grow_memory must have 1 operand"); + shouldBeEqualOrFirstIsUnreachable(curr->operands[0]->type, i32, curr, "grow_memory must have i32 operand"); + break; + } + case PageSize: + case CurrentMemory: + case HasFeature: break; + default: WASM_UNREACHABLE(); + } + } + void visitFunction(Function *curr) { // if function has no result, it is ignored // if body is unreachable, it might be e.g. a return - if (curr->result != none && curr->body->type != unreachable) { - shouldBeEqual(curr->result, curr->body->type, curr->body, "function result must match, if function returns"); + if (curr->result != none) { + if (curr->body->type != unreachable) { + shouldBeEqual(curr->result, curr->body->type, curr->body, "function body type must match, if function returns"); + } + if (returnType != unreachable) { + shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function returns"); + } } + returnType = unreachable; } void visitMemory(Memory *curr) { shouldBeFalse(curr->initial > curr->max, "memory", "memory max >= initial"); + shouldBeTrue(curr->max <= Memory::kMaxSize, "memory", "total memory must be <= 4GB"); size_t top = 0; for (auto& segment : curr->segments) { shouldBeFalse(segment.offset < top, "memory", "segment offset is small enough"); @@ -296,6 +355,16 @@ private: } template<typename T, typename S> + bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) { + if (left != unreachable && left != right) { + fail() << "" << left << " != " << right << ": " << text << ", on \n" << curr << std::endl; + valid = false; + return false; + } + return true; + } + + template<typename T, typename S> bool shouldBeUnequal(S left, S right, T curr, const char* text) { if (left == right) { fail() << "" << left << " == " << right << ": " << text << ", on \n" << curr << std::endl; diff --git a/src/wasm.cpp b/src/wasm.cpp index 98268310a..09d9c3c16 100644 --- a/src/wasm.cpp +++ b/src/wasm.cpp @@ -22,66 +22,65 @@ namespace wasm { struct BlockTypeSeeker : public PostWalker<BlockTypeSeeker, Visitor<BlockTypeSeeker>> { Block* target; // look for this one - WasmType type = unreachable; + std::vector<WasmType> types; BlockTypeSeeker(Block* target) : target(target) {} - void noteType(WasmType other) { - // once none, stop. it then indicates a poison value, that must not be consumed - // and ignore unreachable - if (type != none) { - if (other == none) { - type = none; - } else if (other != unreachable) { - if (type == unreachable) { - type = other; - } else if (type != other) { - type = none; // poison value, we saw multiple types; this should not be consumed - } - } - } - } - void visitBreak(Break *curr) { if (curr->name == target->name) { - noteType(curr->value ? curr->value->type : none); + types.push_back(curr->value ? curr->value->type : none); } } void visitSwitch(Switch *curr) { for (auto name : curr->targets) { - if (name == target->name) noteType(curr->value ? curr->value->type : none); + if (name == target->name) types.push_back(curr->value ? curr->value->type : none); } } void visitBlock(Block *curr) { if (curr == target) { - if (curr->list.size() > 0) noteType(curr->list.back()->type); - } else { - type = unreachable; // ignore all breaks til now, they were captured by someone with the same name + if (curr->list.size() > 0) { + types.push_back(curr->list.back()->type); + } else { + types.push_back(none); + } + } else if (curr->name == target->name) { + types.clear(); // ignore all breaks til now, they were captured by someone with the same name } } }; void Block::finalize() { - if (list.size() > 0) { - auto last = list.back()->type; - if (last != unreachable) { - // well that was easy - type = last; - return; - } - } if (!name.is()) { - // that was rather silly - type = unreachable; + // nothing branches here, so this is easy + if (list.size() > 0) { + type = list.back()->type; + } else { + type = unreachable; + } return; } - // oh no this is hard + BlockTypeSeeker seeker(this); Expression* temp = this; seeker.walk(temp); - type = seeker.type; + type = unreachable; + for (auto other : seeker.types) { + // once none, stop. it then indicates a poison value, that must not be consumed + // and ignore unreachable + if (type != none) { + if (other == none) { + type = none; + } else if (other != unreachable) { + if (type == unreachable) { + type = other; + } else if (type != other) { + type = none; // poison value, we saw multiple types; this should not be consumed + } + } + } + } } } // namespace wasm diff --git a/src/wasm.h b/src/wasm.h index f68459401..fc0337700 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -775,10 +775,10 @@ private: // Operators enum UnaryOp { - Clz, Ctz, Popcnt, // int - Neg, Abs, Ceil, Floor, Trunc, Nearest, Sqrt, // float + ClzInt32, ClzInt64, CtzInt32, CtzInt64, PopcntInt32, PopcntInt64, // int + NegFloat32, NegFloat64, AbsFloat32, AbsFloat64, CeilFloat32, CeilFloat64, FloorFloat32, FloorFloat64, TruncFloat32, TruncFloat64, NearestFloat32, NearestFloat64, SqrtFloat32, SqrtFloat64, // float // relational - EqZ, + EqZInt32, EqZInt64, // conversions ExtendSInt32, ExtendUInt32, // extend i32 to i64 WrapInt64, // i64 to i32 @@ -791,13 +791,29 @@ enum UnaryOp { }; enum BinaryOp { - Add, Sub, Mul, // int or float - DivS, DivU, RemS, RemU, And, Or, Xor, Shl, ShrU, ShrS, RotL, RotR, // int - Div, CopySign, Min, Max, // float + AddInt32, SubInt32, MulInt32, // int or float + DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, // int // relational ops - Eq, Ne, // int or float - LtS, LtU, LeS, LeU, GtS, GtU, GeS, GeU, // int - Lt, Le, Gt, Ge // float + EqInt32, NeInt32, // int or float + LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32, // int + + AddInt64, SubInt64, MulInt64, // int or float + DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64, // int + // relational ops + EqInt64, NeInt64, // int or float + LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64, // int + + AddFloat32, SubFloat32, MulFloat32, // int or float + DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32, // float + // relational ops + EqFloat32, NeFloat32, // int or float + LtFloat32, LeFloat32, GtFloat32, GeFloat32, // float + + AddFloat64, SubFloat64, MulFloat64, // int or float + DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64, // float + // relational ops + EqFloat64, NeFloat64, // int or float + LtFloat64, LeFloat64, GtFloat64, GeFloat64, // float }; enum HostOp { @@ -1116,9 +1132,59 @@ public: UnaryOp op; Expression *value; - bool isRelational() { return op == EqZ; } + bool isRelational() { return op == EqZInt32 || op == EqZInt64; } - // no finalize since some opcodes have more than one type, so user must set it anyhow + void finalize() { + switch (op) { + case ClzInt32: + case CtzInt32: + case PopcntInt32: + case NegFloat32: + case AbsFloat32: + case CeilFloat32: + case FloorFloat32: + case TruncFloat32: + case NearestFloat32: + case SqrtFloat32: + case ClzInt64: + case CtzInt64: + case PopcntInt64: + case NegFloat64: + case AbsFloat64: + case CeilFloat64: + case FloorFloat64: + case TruncFloat64: + case NearestFloat64: + case SqrtFloat64: type = value->type; break; + case EqZInt32: + case EqZInt64: type = i32; break; + case ExtendSInt32: case ExtendUInt32: type = i64; break; + case WrapInt64: type = i32; break; + case PromoteFloat32: type = f64; break; + case DemoteFloat64: type = f32; break; + case TruncSFloat32ToInt32: + case TruncUFloat32ToInt32: + case TruncSFloat64ToInt32: + case TruncUFloat64ToInt32: + case ReinterpretFloat32: type = i32; break; + case TruncSFloat32ToInt64: + case TruncUFloat32ToInt64: + case TruncSFloat64ToInt64: + case TruncUFloat64ToInt64: + case ReinterpretFloat64: type = i64; break; + case ReinterpretInt32: + case ConvertSInt32ToFloat32: + case ConvertUInt32ToFloat32: + case ConvertSInt64ToFloat32: + case ConvertUInt64ToFloat32: type = f32; break; + case ReinterpretInt64: + case ConvertSInt32ToFloat64: + case ConvertUInt32ToFloat64: + case ConvertSInt64ToFloat64: + case ConvertUInt64ToFloat64: type = f64; break; + default: std::cerr << "waka " << op << '\n'; WASM_UNREACHABLE(); + } + } }; class Binary : public SpecificExpression<Expression::BinaryId> { @@ -1132,7 +1198,43 @@ public: // the type is always the type of the operands, // except for relationals - bool isRelational() { return op >= Eq; } + bool isRelational() { + switch (op) { + case EqFloat64: + case NeFloat64: + case LtFloat64: + case LeFloat64: + case GtFloat64: + case GeFloat64: + case EqInt32: + case NeInt32: + case LtSInt32: + case LtUInt32: + case LeSInt32: + case LeUInt32: + case GtSInt32: + case GtUInt32: + case GeSInt32: + case GeUInt32: + case EqInt64: + case NeInt64: + case LtSInt64: + case LtUInt64: + case LeSInt64: + case LeUInt64: + case GtSInt64: + case GtUInt64: + case GeSInt64: + case GeUInt64: + case EqFloat32: + case NeFloat32: + case LtFloat32: + case LeFloat32: + case GtFloat32: + case GeFloat32: return true; + default: return false; + } + } void finalize() { if (isRelational()) { |