| #include <cassert> | |
| #include <iostream> | |
| #include "literal.h" | |
| #include "wasm-builder.h" | |
| #include <ir/match.h> | |
| using namespace wasm; | |
| using namespace wasm::Match; | |
| Module module; | |
| Builder builder(module); | |
| void test_internal_any() { | |
| std::cout << "Testing Internal::Any\n"; | |
| assert(Internal::Any<int32_t>(nullptr).matches(0)); | |
| assert(Internal::Any<int32_t>(nullptr).matches(1)); | |
| assert(Internal::Any<int32_t>(nullptr).matches(-1)); | |
| assert(Internal::Any<int32_t>(nullptr).matches(42LL)); | |
| assert(Internal::Any<int32_t>(nullptr).matches(4.2f)); | |
| assert(Internal::Any<int64_t>(nullptr).matches(0)); | |
| assert(Internal::Any<int64_t>(nullptr).matches(1)); | |
| assert(Internal::Any<int64_t>(nullptr).matches(-1)); | |
| assert(Internal::Any<int64_t>(nullptr).matches(42LL)); | |
| assert(Internal::Any<int64_t>(nullptr).matches(4.2f)); | |
| assert(Internal::Any<float>(nullptr).matches(0)); | |
| assert(Internal::Any<float>(nullptr).matches(1)); | |
| assert(Internal::Any<float>(nullptr).matches(-1)); | |
| assert(Internal::Any<float>(nullptr).matches(42LL)); | |
| assert(Internal::Any<float>(nullptr).matches(4.2f)); | |
| assert(Internal::Any<double>(nullptr).matches(0)); | |
| assert(Internal::Any<double>(nullptr).matches(1)); | |
| assert(Internal::Any<double>(nullptr).matches(-1)); | |
| assert(Internal::Any<double>(nullptr).matches(42LL)); | |
| assert(Internal::Any<double>(nullptr).matches(4.2f)); | |
| // Working as intended: cannot convert `const char [6]' to double | |
| // assert(Internal::Any<double>(nullptr).matches("hello")); | |
| { | |
| int32_t val = 0xffffffff; | |
| assert(Internal::Any<int32_t>(&val).matches(0)); | |
| assert(val == 0); | |
| assert(Internal::Any<int32_t>(&val).matches(1)); | |
| assert(val == 1); | |
| assert(Internal::Any<int32_t>(&val).matches(-1)); | |
| assert(val == -1); | |
| assert(Internal::Any<int32_t>(&val).matches(42LL)); | |
| assert(val == 42); | |
| assert(Internal::Any<int32_t>(&val).matches(4.2f)); | |
| assert(val == 4); | |
| } | |
| { | |
| Expression* expr = nullptr; | |
| Nop* nop = nullptr; | |
| Expression* builtExpr = builder.makeNop(); | |
| Nop* builtNop = builder.makeNop(); | |
| AtomicFence* builtFence = builder.makeAtomicFence(MemoryOrder::SeqCst); | |
| assert(Internal::Any(&expr).matches(builtExpr)); | |
| assert(expr == builtExpr); | |
| assert(Internal::Any(&expr).matches(builtNop)); | |
| assert(expr == builtNop); | |
| assert(Internal::Any(&expr).matches(builtFence)); | |
| assert(expr == builtFence); | |
| assert(Internal::Any(&nop).matches(builtExpr)); | |
| assert(nop == builtExpr); | |
| assert(Internal::Any(&nop).matches(builtNop)); | |
| assert(nop == builtNop); | |
| // Does NOT match sibling expression types. Bound variable unchanged. | |
| assert(!Internal::Any(&nop).matches(builtFence)); | |
| assert(nop == builtNop); | |
| // Working as intended: invalid conversion from Expression** to Nop** | |
| // assert(Internal::Any<Nop*>(&expr).matches(builtExpr)); | |
| } | |
| } | |
| void test_internal_exact() { | |
| std::cout << "Testing Internal::Exact\n"; | |
| assert(Internal::Exact<int32_t>(nullptr, 0).matches(0)); | |
| assert(Internal::Exact<int32_t>(nullptr, 1).matches(1)); | |
| assert(Internal::Exact<int32_t>(nullptr, -1).matches(-1)); | |
| assert(Internal::Exact<int32_t>(nullptr, 42).matches(42LL)); | |
| assert(Internal::Exact<int32_t>(nullptr, 4).matches(4.2f)); | |
| assert(!Internal::Exact<int32_t>(nullptr, 1).matches(0)); | |
| assert(!Internal::Exact<int32_t>(nullptr, -1).matches(1)); | |
| assert(!Internal::Exact<int32_t>(nullptr, 42).matches(-1)); | |
| assert(!Internal::Exact<int32_t>(nullptr, 4).matches(42LL)); | |
| assert(!Internal::Exact<int32_t>(nullptr, 0).matches(4.2f)); | |
| { | |
| Expression* expr = nullptr; | |
| Nop* nop = nullptr; | |
| Nop* builtNop = builder.makeNop(); | |
| Expression* builtExpr = builtNop; | |
| assert(!Internal::Exact(&expr, expr).matches(builtExpr)); | |
| assert(Internal::Exact(&expr, builtExpr).matches(builtExpr)); | |
| assert(expr == builtExpr); | |
| assert(!Internal::Exact(&nop, nop).matches(builtNop)); | |
| assert(Internal::Exact(&nop, builtNop).matches(builtNop)); | |
| assert(nop == builtNop); | |
| nop = nullptr; | |
| assert(Internal::Exact(&nop, builtNop).matches(builtExpr)); | |
| assert(nop == builtNop); | |
| } | |
| } | |
| void test_internal_literal() { | |
| std::cout << "Testing Internal::{Bool,I32,I64,Int,F32,F64,Float}Lit\n"; | |
| Literal i32Zero(int32_t(0)); | |
| Literal i32One(int32_t(1)); | |
| Literal i32Two(int32_t(2)); | |
| Literal f32Zero(float(0)); | |
| Literal f32One(float(1)); | |
| Literal i64Zero(int64_t(0)); | |
| Literal i64One(int64_t(1)); | |
| Literal f64Zero(double(0)); | |
| Literal f64One(double(1)); | |
| auto anyBool = Internal::BoolLit(nullptr, Internal::Any<bool>(nullptr)); | |
| assert(anyBool.matches(i32Zero)); | |
| assert(anyBool.matches(i32One)); | |
| assert(!anyBool.matches(i32Two)); | |
| assert(!anyBool.matches(f32Zero)); | |
| assert(!anyBool.matches(f32One)); | |
| assert(!anyBool.matches(i64Zero)); | |
| assert(!anyBool.matches(i64One)); | |
| assert(!anyBool.matches(f64Zero)); | |
| assert(!anyBool.matches(f64One)); | |
| auto anyi32 = Internal::I32Lit(nullptr, Internal::Any<int32_t>(nullptr)); | |
| assert(anyi32.matches(i32Zero)); | |
| assert(anyi32.matches(i32One)); | |
| assert(!anyi32.matches(f32Zero)); | |
| assert(!anyi32.matches(f32One)); | |
| assert(!anyi32.matches(i64Zero)); | |
| assert(!anyi32.matches(i64One)); | |
| assert(!anyi32.matches(f64Zero)); | |
| assert(!anyi32.matches(f64One)); | |
| auto onei32 = Internal::I32Lit(nullptr, Internal::Exact<int32_t>(nullptr, 1)); | |
| assert(!onei32.matches(i32Zero)); | |
| assert(onei32.matches(i32One)); | |
| assert(!onei32.matches(f32Zero)); | |
| assert(!onei32.matches(f32One)); | |
| assert(!onei32.matches(i64Zero)); | |
| assert(!onei32.matches(i64One)); | |
| assert(!onei32.matches(f64Zero)); | |
| assert(!onei32.matches(f64One)); | |
| auto anyi64 = Internal::I64Lit(nullptr, Internal::Any<int64_t>(nullptr)); | |
| assert(!anyi64.matches(i32Zero)); | |
| assert(!anyi64.matches(i32One)); | |
| assert(!anyi64.matches(f32Zero)); | |
| assert(!anyi64.matches(f32One)); | |
| assert(anyi64.matches(i64Zero)); | |
| assert(anyi64.matches(i64One)); | |
| assert(!anyi64.matches(f64Zero)); | |
| assert(!anyi64.matches(f64One)); | |
| auto onei64 = Internal::I64Lit(nullptr, Internal::Exact<int64_t>(nullptr, 1)); | |
| assert(!onei64.matches(i32Zero)); | |
| assert(!onei64.matches(i32One)); | |
| assert(!onei64.matches(f32Zero)); | |
| assert(!onei64.matches(f32One)); | |
| assert(!onei64.matches(i64Zero)); | |
| assert(onei64.matches(i64One)); | |
| assert(!onei64.matches(f64Zero)); | |
| assert(!onei64.matches(f64One)); | |
| auto anyint = Internal::IntLit(nullptr, Internal::Any<int64_t>(nullptr)); | |
| assert(anyint.matches(i32Zero)); | |
| assert(anyint.matches(i32One)); | |
| assert(!anyint.matches(f32Zero)); | |
| assert(!anyint.matches(f32One)); | |
| assert(anyint.matches(i64Zero)); | |
| assert(anyint.matches(i64One)); | |
| assert(!anyint.matches(f64Zero)); | |
| assert(!anyint.matches(f64One)); | |
| auto oneint = Internal::IntLit(nullptr, Internal::Exact<int64_t>(nullptr, 1)); | |
| assert(!oneint.matches(i32Zero)); | |
| assert(oneint.matches(i32One)); | |
| assert(!oneint.matches(f32Zero)); | |
| assert(!oneint.matches(f32One)); | |
| assert(!oneint.matches(i64Zero)); | |
| assert(oneint.matches(i64One)); | |
| assert(!oneint.matches(f64Zero)); | |
| assert(!oneint.matches(f64One)); | |
| auto anyf32 = Internal::F32Lit(nullptr, Internal::Any<float>(nullptr)); | |
| assert(!anyf32.matches(i32Zero)); | |
| assert(!anyf32.matches(i32One)); | |
| assert(anyf32.matches(f32Zero)); | |
| assert(anyf32.matches(f32One)); | |
| assert(!anyf32.matches(i64Zero)); | |
| assert(!anyf32.matches(i64One)); | |
| assert(!anyf32.matches(f64Zero)); | |
| assert(!anyf32.matches(f64One)); | |
| auto onef32 = Internal::F32Lit(nullptr, Internal::Exact<float>(nullptr, 1)); | |
| assert(!onef32.matches(i32Zero)); | |
| assert(!onef32.matches(i32One)); | |
| assert(!onef32.matches(f32Zero)); | |
| assert(onef32.matches(f32One)); | |
| assert(!onef32.matches(i64Zero)); | |
| assert(!onef32.matches(i64One)); | |
| assert(!onef32.matches(f64Zero)); | |
| assert(!onef32.matches(f64One)); | |
| auto anyf64 = Internal::F64Lit(nullptr, Internal::Any<double>(nullptr)); | |
| assert(!anyf64.matches(i32Zero)); | |
| assert(!anyf64.matches(i32One)); | |
| assert(!anyf64.matches(f32Zero)); | |
| assert(!anyf64.matches(f32One)); | |
| assert(!anyf64.matches(i64Zero)); | |
| assert(!anyf64.matches(i64One)); | |
| assert(anyf64.matches(f64Zero)); | |
| assert(anyf64.matches(f64One)); | |
| auto onef64 = Internal::F64Lit(nullptr, Internal::Exact<double>(nullptr, 1)); | |
| assert(!onef64.matches(i32Zero)); | |
| assert(!onef64.matches(i32One)); | |
| assert(!onef64.matches(f32Zero)); | |
| assert(!onef64.matches(f32One)); | |
| assert(!onef64.matches(i64Zero)); | |
| assert(!onef64.matches(i64One)); | |
| assert(!onef64.matches(f64Zero)); | |
| assert(onef64.matches(f64One)); | |
| auto anyfp = Internal::FloatLit(nullptr, Internal::Any<double>(nullptr)); | |
| assert(!anyfp.matches(i32Zero)); | |
| assert(!anyfp.matches(i32One)); | |
| assert(anyfp.matches(f32Zero)); | |
| assert(anyfp.matches(f32One)); | |
| assert(!anyfp.matches(i64Zero)); | |
| assert(!anyfp.matches(i64One)); | |
| assert(anyfp.matches(f64Zero)); | |
| assert(anyfp.matches(f64One)); | |
| auto onefp = Internal::FloatLit(nullptr, Internal::Exact<double>(nullptr, 1)); | |
| assert(!onefp.matches(i32Zero)); | |
| assert(!onefp.matches(i32One)); | |
| assert(!onefp.matches(f32Zero)); | |
| assert(onefp.matches(f32One)); | |
| assert(!onefp.matches(i64Zero)); | |
| assert(!onefp.matches(i64One)); | |
| assert(!onefp.matches(f64Zero)); | |
| assert(onefp.matches(f64One)); | |
| auto number = Internal::NumberLit(nullptr, 1); | |
| assert(!number.matches(i32Zero)); | |
| assert(number.matches(i32One)); | |
| assert(!number.matches(f32Zero)); | |
| assert(number.matches(f32One)); | |
| assert(!number.matches(i64Zero)); | |
| assert(number.matches(i64One)); | |
| assert(!number.matches(f64Zero)); | |
| assert(number.matches(f64One)); | |
| int64_t x = 0; | |
| Literal xLit; | |
| Literal imatched(int32_t(42)); | |
| assert(Internal::IntLit(&xLit, Internal::Any(&x)).matches(imatched)); | |
| assert(xLit == imatched); | |
| assert(x == 42); | |
| double f = 0; | |
| Literal fLit; | |
| Literal fmatched(double(42)); | |
| assert(Internal::FloatLit(&fLit, Internal::Any(&f)).matches(fmatched)); | |
| assert(fLit == fmatched); | |
| assert(f == 42.0); | |
| Literal numLit; | |
| Literal numMatched(1.0f); | |
| assert(Internal::NumberLit(&numLit, 1).matches(numMatched)); | |
| assert(numLit == numMatched); | |
| } | |
| void test_internal_const() { | |
| std::cout << "Testing Internal::ConstantMatcher\n"; | |
| Const* c = builder.makeConst(Literal(int32_t(42))); | |
| Expression* constExpr = builder.makeConst(Literal(int32_t(43))); | |
| Expression* nop = builder.makeNop(); | |
| Const* extractedConst = nullptr; | |
| Literal extractedLit; | |
| int32_t extractedInt = 0; | |
| auto matcher = Internal::ConstMatcher( | |
| &extractedConst, | |
| Internal::I32Lit(&extractedLit, Internal::Any(&extractedInt))); | |
| assert(matcher.matches(c)); | |
| assert(extractedConst == c); | |
| assert(extractedLit == Literal(int32_t(42))); | |
| assert(extractedInt == 42); | |
| assert(matcher.matches(constExpr)); | |
| assert(extractedConst == constExpr); | |
| assert(extractedLit == Literal(int32_t(43))); | |
| assert(extractedInt == 43); | |
| assert(!matcher.matches(nop)); | |
| } | |
| void test_internal_unary() { | |
| Expression* eqz32 = | |
| builder.makeUnary(EqZInt32, builder.makeConst(Literal(int32_t(0)))); | |
| Expression* eqz64 = | |
| builder.makeUnary(EqZInt64, builder.makeConst(Literal(int64_t(0)))); | |
| Expression* clz = | |
| builder.makeUnary(ClzInt32, builder.makeConst(Literal(int32_t(0)))); | |
| Expression* nop = builder.makeNop(); | |
| std::cout << "Testing Internal::UnaryMatcher\n"; | |
| Unary* out = nullptr; | |
| UnaryOp op; | |
| auto unMatcher = Internal::UnaryMatcher( | |
| &out, Internal::Any<UnaryOp>(&op), Internal::Any<Expression*>(nullptr)); | |
| assert(unMatcher.matches(eqz32)); | |
| assert(out == eqz32); | |
| assert(op == EqZInt32); | |
| assert(unMatcher.matches(eqz64)); | |
| assert(out == eqz64); | |
| assert(op == EqZInt64); | |
| assert(unMatcher.matches(clz)); | |
| assert(out == clz); | |
| assert(op == ClzInt32); | |
| assert(!unMatcher.matches(nop)); | |
| assert(matches(clz, unary(any()))); | |
| assert(matches(eqz64, unary(&out, any()))); | |
| assert(out == eqz64); | |
| assert(matches(eqz32, unary(&op, any()))); | |
| assert(op == EqZInt32); | |
| std::cout << "Testing Internal::UnaryOpMatcher\n"; | |
| out = nullptr; | |
| auto eqz32Matcher = Internal::UnaryOpMatcher( | |
| &out, EqZInt32, Internal::Any<Expression*>(nullptr)); | |
| assert(eqz32Matcher.matches(eqz32)); | |
| assert(out == eqz32); | |
| assert(!eqz32Matcher.matches(eqz64)); | |
| assert(!eqz32Matcher.matches(clz)); | |
| assert(!eqz32Matcher.matches(nop)); | |
| std::cout << "Testing Internal::AbstractUnaryOpMatcher\n"; | |
| out = nullptr; | |
| auto eqzMatcher = Internal::AbstractUnaryOpMatcher( | |
| &out, Abstract::EqZ, Internal::Any<Expression*>(nullptr)); | |
| assert(eqzMatcher.matches(eqz32)); | |
| assert(out == eqz32); | |
| assert(eqzMatcher.matches(eqz64)); | |
| assert(out == eqz64); | |
| assert(!eqzMatcher.matches(clz)); | |
| assert(!eqzMatcher.matches(nop)); | |
| } | |
| void test_internal_binary() { | |
| Expression* eq32 = builder.makeBinary(EqInt32, | |
| builder.makeConst(Literal(int32_t(0))), | |
| builder.makeConst(Literal(int32_t(0)))); | |
| Expression* eq64 = builder.makeBinary(EqInt64, | |
| builder.makeConst(Literal(int64_t(0))), | |
| builder.makeConst(Literal(int64_t(0)))); | |
| Expression* add = builder.makeBinary(AddInt32, | |
| builder.makeConst(Literal(int32_t(0))), | |
| builder.makeConst(Literal(int32_t(0)))); | |
| Expression* nop = builder.makeNop(); | |
| std::cout << "Testing Internal::BinaryMatcher\n"; | |
| Binary* out = nullptr; | |
| BinaryOp op; | |
| auto binMatcher = | |
| Internal::BinaryMatcher(&out, | |
| Internal::Any<BinaryOp>(&op), | |
| Internal::Any<Expression*>(nullptr), | |
| Internal::Any<Expression*>(nullptr)); | |
| assert(binMatcher.matches(eq32)); | |
| assert(out == eq32); | |
| assert(op == EqInt32); | |
| assert(binMatcher.matches(eq64)); | |
| assert(out == eq64); | |
| assert(op == EqInt64); | |
| assert(binMatcher.matches(add)); | |
| assert(out == add); | |
| assert(op == AddInt32); | |
| assert(!binMatcher.matches(nop)); | |
| assert(matches(add, binary(any(), any()))); | |
| assert(matches(eq64, binary(&out, any(), any()))); | |
| assert(out == eq64); | |
| assert(matches(eq32, binary(&op, any(), any()))); | |
| assert(op == EqInt32); | |
| std::cout << "Testing Internal::BinaryOpMatcher\n"; | |
| out = nullptr; | |
| auto eq32Matcher = | |
| Internal::BinaryOpMatcher(&out, | |
| EqInt32, | |
| Internal::Any<Expression*>(nullptr), | |
| Internal::Any<Expression*>(nullptr)); | |
| assert(eq32Matcher.matches(eq32)); | |
| assert(out == eq32); | |
| assert(!eq32Matcher.matches(eq64)); | |
| assert(!eq32Matcher.matches(add)); | |
| assert(!eq32Matcher.matches(nop)); | |
| std::cout << "Testing Internal::AbstractBinaryOpMatcher\n"; | |
| out = nullptr; | |
| auto eqMatcher = | |
| Internal::AbstractBinaryOpMatcher(&out, | |
| Abstract::Eq, | |
| Internal::Any<Expression*>(nullptr), | |
| Internal::Any<Expression*>(nullptr)); | |
| assert(eqMatcher.matches(eq32)); | |
| assert(out == eq32); | |
| assert(eqMatcher.matches(eq64)); | |
| assert(out == eq64); | |
| assert(!eqMatcher.matches(add)); | |
| assert(!eqMatcher.matches(nop)); | |
| } | |
| void test_internal_select() { | |
| std::cout << "Testing Internal::SelectMatcher\n"; | |
| auto zero = [&]() { return builder.makeConst(Literal(int32_t(0))); }; | |
| auto one = [&]() { return builder.makeConst(Literal(int32_t(1))); }; | |
| auto constMatcher = [](int32_t c) { | |
| return Internal::ConstMatcher( | |
| nullptr, Internal::I32Lit(nullptr, Internal::Exact<int32_t>(nullptr, c))); | |
| }; | |
| // NB: `makeSelect` takes the condition first for some reason | |
| Expression* leftOne = builder.makeSelect(zero(), one(), zero()); | |
| Expression* rightOne = builder.makeSelect(zero(), zero(), one()); | |
| Expression* condOne = builder.makeSelect(one(), zero(), zero()); | |
| Select* out = nullptr; | |
| auto zeroesMatcher = Internal::SelectMatcher( | |
| &out, constMatcher(0), constMatcher(0), constMatcher(0)); | |
| assert(!zeroesMatcher.matches(leftOne)); | |
| assert(!zeroesMatcher.matches(rightOne)); | |
| assert(!zeroesMatcher.matches(condOne)); | |
| auto leftMatcher = Internal::SelectMatcher( | |
| &out, constMatcher(1), constMatcher(0), constMatcher(0)); | |
| assert(leftMatcher.matches(leftOne)); | |
| assert(out == leftOne); | |
| assert(!leftMatcher.matches(rightOne)); | |
| assert(!leftMatcher.matches(condOne)); | |
| auto rightMatcher = Internal::SelectMatcher( | |
| &out, constMatcher(0), constMatcher(1), constMatcher(0)); | |
| assert(!rightMatcher.matches(leftOne)); | |
| assert(rightMatcher.matches(rightOne)); | |
| assert(out == rightOne); | |
| assert(!rightMatcher.matches(condOne)); | |
| auto condMatcher = Internal::SelectMatcher( | |
| &out, constMatcher(0), constMatcher(0), constMatcher(1)); | |
| assert(!condMatcher.matches(leftOne)); | |
| assert(!condMatcher.matches(rightOne)); | |
| assert(condMatcher.matches(condOne)); | |
| assert(out == condOne); | |
| } | |
| int main() { | |
| test_internal_any(); | |
| test_internal_exact(); | |
| test_internal_literal(); | |
| test_internal_const(); | |
| test_internal_unary(); | |
| test_internal_binary(); | |
| test_internal_select(); | |
| } |