blob: 90c6cac510a12e83f1b221f83a54211104bce67b [file] [log] [blame] [edit]
#include "ir/possible-contents.h"
#include "wasm-s-parser.h"
#include "wasm.h"
#include "gtest/gtest.h"
using namespace wasm;
// Asserts a == b, in any order.
template<typename T> void assertEqualSymmetric(const T& a, const T& b) {
EXPECT_EQ(a, b);
EXPECT_EQ(b, a);
EXPECT_PRED2([](const T& a, const T& b) { return !(a != b); }, a, b);
EXPECT_PRED2([](const T& a, const T& b) { return !(b != a); }, a, b);
}
// Asserts a != b, in any order.
template<typename T> void assertNotEqualSymmetric(const T& a, const T& b) {
EXPECT_NE(a, b);
EXPECT_NE(b, a);
EXPECT_PRED2([](const T& a, const T& b) { return !(a == b); }, a, b);
EXPECT_PRED2([](const T& a, const T& b) { return !(b == a); }, a, b);
}
// Asserts a combined with b (in any order) is equal to c.
template<typename T>
void assertCombination(const T& a, const T& b, const T& c) {
T temp1 = a;
temp1.combine(b);
assertEqualSymmetric(temp1, c);
// Also check the type, as nulls will compare equal even if their types
// differ. We want to make sure even the types are identical.
assertEqualSymmetric(temp1.getType(), c.getType());
T temp2 = b;
temp2.combine(a);
assertEqualSymmetric(temp2, c);
assertEqualSymmetric(temp2.getType(), c.getType());
}
// Parse a module from text and return it.
static std::unique_ptr<Module> parse(std::string module) {
auto wasm = std::make_unique<Module>();
wasm->features = FeatureSet::All;
try {
SExpressionParser parser(&module.front());
Element& root = *parser.root;
SExpressionWasmBuilder builder(*wasm, *root[0], IRProfile::Normal);
} catch (ParseException& p) {
p.dump(std::cerr);
Fatal() << "error in parsing wasm text";
}
return wasm;
};
// We want to declare a bunch of globals that are used in various tests. Doing
// so in the global scope is risky, as their constructors use types, and the
// type system itself uses global constructors. Instead, we use a fixture for
// that.
class PossibleContentsTest : public testing::Test {
protected:
void SetUp() override {
// Use nominal typing to test struct types.
wasm::setTypeSystem(TypeSystem::Nominal);
}
Type anyref = Type(HeapType::any, Nullable);
Type funcref = Type(HeapType::func, Nullable);
PossibleContents none = PossibleContents::none();
PossibleContents i32Zero = PossibleContents::literal(Literal(int32_t(0)));
PossibleContents i32One = PossibleContents::literal(Literal(int32_t(1)));
PossibleContents f64One = PossibleContents::literal(Literal(double(1)));
PossibleContents anyNull =
PossibleContents::literal(Literal::makeNull(HeapType::any));
PossibleContents funcNull =
PossibleContents::literal(Literal::makeNull(HeapType::func));
PossibleContents i32Global1 =
PossibleContents::global("i32Global1", Type::i32);
PossibleContents i32Global2 =
PossibleContents::global("i32Global2", Type::i32);
PossibleContents f64Global = PossibleContents::global("f64Global", Type::f64);
PossibleContents anyGlobal = PossibleContents::global("anyGlobal", anyref);
PossibleContents nonNullFunc = PossibleContents::literal(
Literal("func", Signature(Type::none, Type::none)));
PossibleContents exactI32 = PossibleContents::exactType(Type::i32);
PossibleContents exactAnyref = PossibleContents::exactType(anyref);
PossibleContents exactFuncref = PossibleContents::exactType(funcref);
PossibleContents exactNonNullAnyref =
PossibleContents::exactType(Type(HeapType::any, NonNullable));
PossibleContents exactNonNullFuncref =
PossibleContents::exactType(Type(HeapType::func, NonNullable));
PossibleContents exactFuncSignatureType = PossibleContents::exactType(
Type(Signature(Type::none, Type::none), Nullable));
PossibleContents exactNonNullFuncSignatureType = PossibleContents::exactType(
Type(Signature(Type::none, Type::none), NonNullable));
PossibleContents many = PossibleContents::many();
};
TEST_F(PossibleContentsTest, TestComparisons) {
assertEqualSymmetric(none, none);
assertNotEqualSymmetric(none, i32Zero);
assertNotEqualSymmetric(none, i32Global1);
assertNotEqualSymmetric(none, exactI32);
assertNotEqualSymmetric(none, many);
assertEqualSymmetric(i32Zero, i32Zero);
assertNotEqualSymmetric(i32Zero, i32One);
assertNotEqualSymmetric(i32Zero, f64One);
assertNotEqualSymmetric(i32Zero, i32Global1);
assertNotEqualSymmetric(i32Zero, exactI32);
assertNotEqualSymmetric(i32Zero, many);
assertEqualSymmetric(i32Global1, i32Global1);
assertNotEqualSymmetric(i32Global1, i32Global2);
assertNotEqualSymmetric(i32Global1, exactI32);
assertNotEqualSymmetric(i32Global1, many);
assertEqualSymmetric(exactI32, exactI32);
assertNotEqualSymmetric(exactI32, exactAnyref);
assertNotEqualSymmetric(exactI32, many);
assertEqualSymmetric(many, many);
// Nulls
assertNotEqualSymmetric(i32Zero, anyNull);
assertEqualSymmetric(anyNull, anyNull);
assertEqualSymmetric(anyNull, funcNull); // All nulls compare equal.
assertEqualSymmetric(exactNonNullAnyref, exactNonNullAnyref);
assertNotEqualSymmetric(exactNonNullAnyref, exactAnyref);
}
TEST_F(PossibleContentsTest, TestCombinations) {
// None with anything else becomes the other thing.
assertCombination(none, none, none);
assertCombination(none, i32Zero, i32Zero);
assertCombination(none, i32Global1, i32Global1);
assertCombination(none, exactI32, exactI32);
assertCombination(none, many, many);
// i32(0) will become Many, unless the value or the type is identical.
assertCombination(i32Zero, i32Zero, i32Zero);
assertCombination(i32Zero, i32One, exactI32);
assertCombination(i32Zero, f64One, many);
assertCombination(i32Zero, i32Global1, exactI32);
assertCombination(i32Zero, f64Global, many);
assertCombination(i32Zero, exactI32, exactI32);
assertCombination(i32Zero, exactAnyref, many);
assertCombination(i32Zero, many, many);
assertCombination(i32Global1, i32Global1, i32Global1);
assertCombination(i32Global1, i32Global2, exactI32);
assertCombination(i32Global1, f64Global, many);
assertCombination(i32Global1, exactI32, exactI32);
assertCombination(i32Global1, exactAnyref, many);
assertCombination(i32Global1, many, many);
assertCombination(exactI32, exactI32, exactI32);
assertCombination(exactI32, exactAnyref, many);
assertCombination(exactI32, many, many);
assertCombination(many, many, many);
// Exact references: An exact reference only stays exact when combined with
// the same heap type (nullability may be added, but nothing else).
assertCombination(exactFuncref, exactAnyref, many);
assertCombination(exactFuncref, anyGlobal, many);
assertCombination(exactFuncref, nonNullFunc, many);
assertCombination(exactFuncref, exactFuncref, exactFuncref);
assertCombination(exactFuncref, exactNonNullFuncref, exactFuncref);
// Nulls.
assertCombination(anyNull, i32Zero, many);
assertCombination(anyNull, anyNull, anyNull);
assertCombination(anyNull, exactAnyref, exactAnyref);
// Two nulls go to the lub.
assertCombination(anyNull, funcNull, anyNull);
assertCombination(exactNonNullAnyref, exactNonNullAnyref, exactNonNullAnyref);
// If one is a null and the other is not, it makes the one that is not a
// null be a nullable type - but keeps the heap type of the other (since the
// type of the null does not matter, all nulls compare equal).
assertCombination(anyNull, exactNonNullAnyref, exactAnyref);
assertCombination(anyNull, exactNonNullFuncref, exactFuncref);
// Funcrefs
// A function reference + a null becomes an exact type (of that sig), plus
// nullability.
assertCombination(nonNullFunc, anyNull, exactFuncSignatureType);
assertCombination(nonNullFunc, funcNull, exactFuncSignatureType);
assertCombination(exactFuncSignatureType, funcNull, exactFuncSignatureType);
assertCombination(
exactNonNullFuncSignatureType, funcNull, exactFuncSignatureType);
assertCombination(
nonNullFunc, exactFuncSignatureType, exactFuncSignatureType);
assertCombination(
nonNullFunc, exactNonNullFuncSignatureType, exactNonNullFuncSignatureType);
assertCombination(nonNullFunc, exactI32, many);
// Globals vs nulls.
assertCombination(anyGlobal, anyNull, many);
assertCombination(anyGlobal, funcNull, many);
}
TEST_F(PossibleContentsTest, TestOracleMinimal) {
// A minimal test of the public API of PossibleTypesOracle. See the lit test
// for coverage of all the internals (using lit makes the result more
// fuzzable).
auto wasm = parse(R"(
(module
(global $null (ref null any) (ref.null any))
(global $something i32 (i32.const 42))
)
)");
ContentOracle oracle(*wasm);
// This will be a null constant.
EXPECT_TRUE(oracle.getContents(GlobalLocation{"null"}).isNull());
// This will be 42.
EXPECT_EQ(oracle.getContents(GlobalLocation{"something"}).getLiteral(),
Literal(int32_t(42)));
}
TEST_F(PossibleContentsTest, TestOracleManyTypes) {
// Test for a node with many possible types. The pass limits how many it
// notices to not use excessive memory, so even though 4 are possible here,
// we'll just report that more than one is possible ("many").
auto wasm = parse(R"(
(module
(type $A (struct_subtype (field i32) data))
(type $B (struct_subtype (field i64) data))
(type $C (struct_subtype (field f32) data))
(type $D (struct_subtype (field f64) data))
(func $foo (result (ref any))
(select (result (ref any))
(select (result (ref any))
(struct.new $A)
(struct.new $B)
(i32.const 0)
)
(select (result (ref any))
(struct.new $C)
(struct.new $D)
(i32.const 0)
)
(i32.const 0)
)
)
)
)");
ContentOracle oracle(*wasm);
// The function's body should be Many.
EXPECT_TRUE(
oracle.getContents(ResultLocation{wasm->getFunction("foo"), 0}).isMany());
}