// Copyright (c) 2016 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "source/opt/types.h" #include #include #include #include "gtest/gtest.h" #include "source/util/make_unique.h" namespace spvtools { namespace opt { namespace analysis { namespace { // Fixture class providing some element types. class SameTypeTest : public ::testing::Test { protected: void SetUp() override { void_t_ = MakeUnique(); u32_t_ = MakeUnique(32, false); f64_t_ = MakeUnique(64); v3u32_t_ = MakeUnique(u32_t_.get(), 3); image_t_ = MakeUnique(f64_t_.get(), spv::Dim::Dim2D, 1, 1, 0, 0, spv::ImageFormat::R16, spv::AccessQualifier::ReadWrite); } // Element types to be used for constructing other types for testing. std::unique_ptr void_t_; std::unique_ptr u32_t_; std::unique_ptr f64_t_; std::unique_ptr v3u32_t_; std::unique_ptr image_t_; }; #define TestMultipleInstancesOfTheSameTypeQualified(ty, name, ...) \ TEST_F(SameTypeTest, MultiSame##ty##name) { \ std::vector> types; \ for (int i = 0; i < 10; ++i) types.emplace_back(new ty(__VA_ARGS__)); \ for (size_t i = 0; i < types.size(); ++i) { \ for (size_t j = 0; j < types.size(); ++j) { \ EXPECT_TRUE(types[i]->IsSame(types[j].get())) \ << "expected '" << types[i]->str() << "' is the same as '" \ << types[j]->str() << "'"; \ EXPECT_TRUE(*types[i] == *types[j]) \ << "expected '" << types[i]->str() << "' is the same as '" \ << types[j]->str() << "'"; \ } \ } \ } #define TestMultipleInstancesOfTheSameType(ty, ...) \ TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__) // clang-format off TestMultipleInstancesOfTheSameType(Void) TestMultipleInstancesOfTheSameType(Bool) TestMultipleInstancesOfTheSameType(Integer, 32, true) TestMultipleInstancesOfTheSameType(Float, 64) TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3) TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4) TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), spv::Dim::Cube, 0, 0, 1, 1, spv::ImageFormat::Rgb10A2, spv::AccessQualifier::WriteOnly) TestMultipleInstancesOfTheSameType(Sampler) TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get()) // There are three classes of arrays, based on the kinds of length information // they have. // 1. Array length is a constant or spec constant without spec ID, with literals // for the constant value. TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(), Array::LengthInfo{42, { 0, 9999, }}) // 2. Array length is a spec constant with a given spec id. TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(), Array::LengthInfo{42, {1, 99}}) // 3. Array length is an OpSpecConstantOp expression TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(), Array::LengthInfo{42, {2, 42}}) TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get()) TestMultipleInstancesOfTheSameType(Struct, std::vector{ u32_t_.get(), f64_t_.get()}) TestMultipleInstancesOfTheSameType(Opaque, "testing rocks") TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), spv::StorageClass::Input) TestMultipleInstancesOfTheSameType(Function, u32_t_.get(), {f64_t_.get(), f64_t_.get()}) TestMultipleInstancesOfTheSameType(Event) TestMultipleInstancesOfTheSameType(DeviceEvent) TestMultipleInstancesOfTheSameType(ReserveId) TestMultipleInstancesOfTheSameType(Queue) TestMultipleInstancesOfTheSameType(Pipe, spv::AccessQualifier::ReadWrite) TestMultipleInstancesOfTheSameType(ForwardPointer, 10, spv::StorageClass::Uniform) TestMultipleInstancesOfTheSameType(PipeStorage) TestMultipleInstancesOfTheSameType(NamedBarrier) TestMultipleInstancesOfTheSameType(AccelerationStructureNV) #undef TestMultipleInstanceOfTheSameType #undef TestMultipleInstanceOfTheSameTypeQual std::vector> GenerateAllTypes() { // clang-format on // Types in this test case are only equal to themselves, nothing else. std::vector> types; // Forward Pointer types.emplace_back(new ForwardPointer(10000, spv::StorageClass::Input)); types.emplace_back(new ForwardPointer(20000, spv::StorageClass::Input)); // Void, Bool types.emplace_back(new Void()); auto* voidt = types.back().get(); types.emplace_back(new Bool()); auto* boolt = types.back().get(); // Integer types.emplace_back(new Integer(32, true)); auto* s32 = types.back().get(); types.emplace_back(new Integer(32, false)); types.emplace_back(new Integer(64, true)); types.emplace_back(new Integer(64, false)); auto* u64 = types.back().get(); // Float types.emplace_back(new Float(32)); auto* f32 = types.back().get(); types.emplace_back(new Float(64)); // Vector types.emplace_back(new Vector(s32, 2)); types.emplace_back(new Vector(s32, 3)); auto* v3s32 = types.back().get(); types.emplace_back(new Vector(u64, 4)); types.emplace_back(new Vector(f32, 3)); auto* v3f32 = types.back().get(); // Matrix types.emplace_back(new Matrix(v3s32, 3)); types.emplace_back(new Matrix(v3s32, 4)); types.emplace_back(new Matrix(v3f32, 4)); // Images types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 0, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); auto* image1 = types.back().get(); types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 1, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); types.emplace_back(new Image(s32, spv::Dim::Dim3D, 0, 1, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); types.emplace_back(new Image(voidt, spv::Dim::Dim3D, 0, 1, 0, 1, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadWrite)); auto* image2 = types.back().get(); // Sampler types.emplace_back(new Sampler()); // Sampled Image types.emplace_back(new SampledImage(image1)); types.emplace_back(new SampledImage(image2)); // Array // Length is constant with integer bit representation of 42. types.emplace_back(new Array(f32, Array::LengthInfo{99u, {0, 42u}})); auto* a42f32 = types.back().get(); // Differs from previous in length value only. types.emplace_back(new Array(f32, Array::LengthInfo{99u, {0, 44u}})); // Length is 64-bit constant integer value 42. types.emplace_back(new Array(u64, Array::LengthInfo{100u, {0, 42u, 0u}})); // Differs from previous in length value only. types.emplace_back(new Array(u64, Array::LengthInfo{100u, {0, 44u, 0u}})); // Length is spec constant with spec id 18 and default value 44. types.emplace_back(new Array(f32, Array::LengthInfo{99u, { 1, 18u, 44u, }})); // Differs from previous in spec id only. types.emplace_back(new Array(f32, Array::LengthInfo{99u, {1, 19u, 44u}})); // Differs from previous in literal value only. types.emplace_back(new Array(f32, Array::LengthInfo{99u, {1, 19u, 48u}})); // Length is spec constant op with id 42. types.emplace_back(new Array(f32, Array::LengthInfo{42u, {2, 42}})); // Differs from previous in result id only. types.emplace_back(new Array(f32, Array::LengthInfo{43u, {2, 43}})); // RuntimeArray types.emplace_back(new RuntimeArray(v3f32)); types.emplace_back(new RuntimeArray(v3s32)); auto* rav3s32 = types.back().get(); // Struct types.emplace_back(new Struct(std::vector{s32})); types.emplace_back(new Struct(std::vector{s32, f32})); auto* sts32f32 = types.back().get(); types.emplace_back( new Struct(std::vector{u64, a42f32, rav3s32})); // Opaque types.emplace_back(new Opaque("")); types.emplace_back(new Opaque("hello")); types.emplace_back(new Opaque("world")); // Pointer types.emplace_back(new Pointer(f32, spv::StorageClass::Input)); types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(voidt, spv::StorageClass::Function)); // Function types.emplace_back(new Function(voidt, {})); types.emplace_back(new Function(voidt, {boolt})); types.emplace_back(new Function(voidt, {boolt, s32})); types.emplace_back(new Function(s32, {boolt, s32})); // Event, Device Event, Reserve Id, Queue, types.emplace_back(new Event()); types.emplace_back(new DeviceEvent()); types.emplace_back(new ReserveId()); types.emplace_back(new Queue()); // Pipe, Forward Pointer, PipeStorage, NamedBarrier types.emplace_back(new Pipe(spv::AccessQualifier::ReadWrite)); types.emplace_back(new Pipe(spv::AccessQualifier::ReadOnly)); types.emplace_back(new ForwardPointer(1, spv::StorageClass::Input)); types.emplace_back(new ForwardPointer(2, spv::StorageClass::Input)); types.emplace_back(new ForwardPointer(2, spv::StorageClass::Uniform)); types.emplace_back(new PipeStorage()); types.emplace_back(new NamedBarrier()); return types; } TEST(Types, AllTypes) { // Types in this test case are only equal to themselves, nothing else. std::vector> types = GenerateAllTypes(); for (size_t i = 0; i < types.size(); ++i) { for (size_t j = 0; j < types.size(); ++j) { if (i == j) { EXPECT_TRUE(types[i]->IsSame(types[j].get())) << "expected '" << types[i]->str() << "' is the same as '" << types[j]->str() << "'"; } else { EXPECT_FALSE(types[i]->IsSame(types[j].get())) << "entry (" << i << "," << j << ") expected '" << types[i]->str() << "' is different to '" << types[j]->str() << "'"; } } } } TEST(Types, TestNumberOfComponentsOnArrays) { Float f32(32); EXPECT_EQ(f32.NumberOfComponents(), 0); Array array_size_42( &f32, Array::LengthInfo{99u, {Array::LengthInfo::kConstant, 42u}}); EXPECT_EQ(array_size_42.NumberOfComponents(), 42); Array array_size_0xDEADBEEF00C0FFEE( &f32, Array::LengthInfo{ 99u, {Array::LengthInfo::kConstant, 0xC0FFEE, 0xDEADBEEF}}); EXPECT_EQ(array_size_0xDEADBEEF00C0FFEE.NumberOfComponents(), 0xDEADBEEF00C0FFEEull); Array array_size_unknown( &f32, Array::LengthInfo{99u, {Array::LengthInfo::kConstantWithSpecId, 10}}); EXPECT_EQ(array_size_unknown.NumberOfComponents(), UINT64_MAX); RuntimeArray runtime_array(&f32); EXPECT_EQ(runtime_array.NumberOfComponents(), UINT64_MAX); } TEST(Types, TestNumberOfComponentsOnVectors) { Float f32(32); EXPECT_EQ(f32.NumberOfComponents(), 0); for (uint32_t vector_size = 1; vector_size < 4; ++vector_size) { Vector vector(&f32, vector_size); EXPECT_EQ(vector.NumberOfComponents(), vector_size); } } TEST(Types, TestNumberOfComponentsOnMatrices) { Float f32(32); Vector vector(&f32, 2); for (uint32_t number_of_columns = 1; number_of_columns < 4; ++number_of_columns) { Matrix matrix(&vector, number_of_columns); EXPECT_EQ(matrix.NumberOfComponents(), number_of_columns); } } TEST(Types, TestNumberOfComponentsOnStructs) { Float f32(32); Vector vector(&f32, 2); Struct empty_struct({}); EXPECT_EQ(empty_struct.NumberOfComponents(), 0); Struct struct_f32({&f32}); EXPECT_EQ(struct_f32.NumberOfComponents(), 1); Struct struct_f32_vec({&f32, &vector}); EXPECT_EQ(struct_f32_vec.NumberOfComponents(), 2); Struct struct_100xf32(std::vector(100, &f32)); EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100); } TEST(Types, IntSignedness) { std::vector signednesses = {true, false, false, true}; std::vector> types; for (bool s : signednesses) { types.emplace_back(new Integer(32, s)); } for (size_t i = 0; i < signednesses.size(); i++) { EXPECT_EQ(signednesses[i], types[i]->IsSigned()); } } TEST(Types, IntWidth) { std::vector widths = {1, 2, 4, 8, 16, 32, 48, 64, 128}; std::vector> types; for (uint32_t w : widths) { types.emplace_back(new Integer(w, true)); } for (size_t i = 0; i < widths.size(); i++) { EXPECT_EQ(widths[i], types[i]->width()); } } TEST(Types, FloatWidth) { std::vector widths = {1, 2, 4, 8, 16, 32, 48, 64, 128}; std::vector> types; for (uint32_t w : widths) { types.emplace_back(new Float(w)); } for (size_t i = 0; i < widths.size(); i++) { EXPECT_EQ(widths[i], types[i]->width()); } } TEST(Types, VectorElementCount) { auto s32 = MakeUnique(32, true); for (uint32_t c : {2, 3, 4}) { auto s32v = MakeUnique(s32.get(), c); EXPECT_EQ(c, s32v->element_count()); } } TEST(Types, MatrixElementCount) { auto s32 = MakeUnique(32, true); auto s32v4 = MakeUnique(s32.get(), 4); for (uint32_t c : {1, 2, 3, 4, 10, 100}) { auto s32m = MakeUnique(s32v4.get(), c); EXPECT_EQ(c, s32m->element_count()); } } TEST(Types, IsUniqueType) { std::vector> types = GenerateAllTypes(); for (auto& t : types) { bool expectation = true; // Disallowing variable pointers. switch (t->kind()) { case Type::kArray: case Type::kRuntimeArray: case Type::kStruct: expectation = false; break; default: break; } EXPECT_EQ(t->IsUniqueType(false), expectation) << "expected '" << t->str() << "' to be a " << (expectation ? "" : "non-") << "unique type"; // Allowing variables pointers. if (t->AsPointer()) expectation = false; EXPECT_EQ(t->IsUniqueType(true), expectation) << "expected '" << t->str() << "' to be a " << (expectation ? "" : "non-") << "unique type"; } } std::vector> GenerateAllTypesWithDecorations() { std::vector> types = GenerateAllTypes(); uint32_t elems = 1; uint32_t decs = 1; for (auto& t : types) { for (uint32_t i = 0; i < (decs % 10); ++i) { std::vector decoration; for (uint32_t j = 0; j < (elems % 4) + 1; ++j) { decoration.push_back(j); } t->AddDecoration(std::move(decoration)); ++elems; ++decs; } } return types; } TEST(Types, Clone) { std::vector> types = GenerateAllTypesWithDecorations(); for (auto& t : types) { auto clone = t->Clone(); EXPECT_TRUE(*t == *clone); EXPECT_TRUE(t->HasSameDecorations(clone.get())); EXPECT_NE(clone.get(), t.get()); } } TEST(Types, RemoveDecorations) { std::vector> types = GenerateAllTypesWithDecorations(); for (auto& t : types) { auto decorationless = t->RemoveDecorations(); EXPECT_EQ(*t == *decorationless, t->decoration_empty()); EXPECT_EQ(t->HasSameDecorations(decorationless.get()), t->decoration_empty()); EXPECT_NE(t.get(), decorationless.get()); } } } // namespace } // namespace analysis } // namespace opt } // namespace spvtools