summaryrefslogtreecommitdiff
path: root/test/gtest/possible-contents.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/gtest/possible-contents.cpp')
-rw-r--r--test/gtest/possible-contents.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/test/gtest/possible-contents.cpp b/test/gtest/possible-contents.cpp
new file mode 100644
index 000000000..2994a6221
--- /dev/null
+++ b/test/gtest/possible-contents.cpp
@@ -0,0 +1,262 @@
+#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);
+
+ T temp2 = b;
+ temp2.combine(a);
+ assertEqualSymmetric(temp2, c);
+}
+
+// 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);
+ }
+
+ 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", Type::anyref);
+
+ PossibleContents nonNullFunc = PossibleContents::literal(
+ Literal("func", Type(Signature(Type::none, Type::none), NonNullable)));
+
+ PossibleContents exactI32 = PossibleContents::exactType(Type::i32);
+ PossibleContents exactAnyref = PossibleContents::exactType(Type::anyref);
+ PossibleContents exactFuncref = PossibleContents::exactType(Type::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());
+}