| #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(); |
| |
| 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(); |
| } |