From 9aa8012d91e5603650532ad1bd90f8d38b3583e2 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 24 May 2023 23:07:36 +0000 Subject: [PATCH] [tint][constant] Add constant::Manager Constructs deduplicated constants, similarly to type::Manager The constant::Manager owns the type::Manager so they can be std::move()'d together without having to risk having the constant::Manager hold a stale pointer to a moved type::Manager. Not currently used. That comes next. Also un-inline type::Manager scalar helpers. Reduces transitive includes. Bug: tint:1935 Change-Id: I28fc74a712f19a171850df5e84433e2d60cba256 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/134360 Reviewed-by: Dan Sinclair Kokoro: Kokoro Reviewed-by: James Price Commit-Queue: Ben Clayton --- src/tint/BUILD.gn | 3 + src/tint/CMakeLists.txt | 3 + src/tint/constant/manager.cc | 105 +++++++++++++++++ src/tint/constant/manager.h | 157 +++++++++++++++++++++++++ src/tint/constant/manager_test.cc | 187 ++++++++++++++++++++++++++++++ src/tint/type/manager.cc | 99 ++++++++++++++++ src/tint/type/manager.h | 73 +++++++----- 7 files changed, 595 insertions(+), 32 deletions(-) create mode 100644 src/tint/constant/manager.cc create mode 100644 src/tint/constant/manager.h create mode 100644 src/tint/constant/manager_test.cc diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 13048870b7..c4e323d652 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -894,6 +894,8 @@ libtint_source_set("libtint_constant_src") { "constant/clone_context.h", "constant/composite.cc", "constant/composite.h", + "constant/manager.cc", + "constant/manager.h", "constant/node.cc", "constant/node.h", "constant/scalar.cc", @@ -1681,6 +1683,7 @@ if (tint_build_unittests) { tint_unittests_source_set("tint_unittests_constant_src") { sources = [ "constant/composite_test.cc", + "constant/manager_test.cc", "constant/scalar_test.cc", "constant/splat_test.cc", ] diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 6fea0a360e..1f7ed9df8a 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -237,6 +237,8 @@ list(APPEND TINT_LIB_SRCS constant/clone_context.h constant/composite.cc constant/composite.h + constant/manager.cc + constant/manager.h constant/scalar.cc constant/scalar.h constant/splat.cc @@ -923,6 +925,7 @@ if(TINT_BUILD_TESTS) ast/workgroup_attribute_test.cc clone_context_test.cc constant/composite_test.cc + constant/manager_test.cc constant/scalar_test.cc constant/splat_test.cc debug_test.cc diff --git a/src/tint/constant/manager.cc b/src/tint/constant/manager.cc new file mode 100644 index 0000000000..829edb274e --- /dev/null +++ b/src/tint/constant/manager.cc @@ -0,0 +1,105 @@ +// Copyright 2023 The Tint Authors. +// +// 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 "src/tint/constant/manager.h" + +#include "src/tint/constant/composite.h" +#include "src/tint/constant/scalar.h" +#include "src/tint/constant/splat.h" +#include "src/tint/type/abstract_float.h" +#include "src/tint/type/abstract_int.h" +#include "src/tint/type/bool.h" +#include "src/tint/type/f16.h" +#include "src/tint/type/f32.h" +#include "src/tint/type/i32.h" +#include "src/tint/type/manager.h" +#include "src/tint/type/u32.h" +#include "src/tint/utils/predicates.h" + +namespace tint::constant { + +Manager::Manager() = default; + +Manager::Manager(Manager&&) = default; + +Manager& Manager::operator=(Manager&& rhs) = default; + +Manager::~Manager() = default; + +const constant::Value* Manager::Composite(const type::Type* type, + utils::VectorRef elements) { + if (elements.IsEmpty()) { + return nullptr; + } + + bool any_zero = false; + bool all_zero = true; + bool all_equal = true; + auto* first = elements.Front(); + for (auto* el : elements) { + if (!el) { + return nullptr; + } + if (!any_zero && el->AnyZero()) { + any_zero = true; + } + if (all_zero && !el->AllZero()) { + all_zero = false; + } + if (all_equal && el != first) { + all_equal = false; + } + } + if (all_equal) { + return Splat(type, elements.Front(), elements.Length()); + } + + return Get(type, std::move(elements), all_zero, any_zero); +} + +const constant::Splat* Manager::Splat(const type::Type* type, + const constant::Value* element, + size_t n) { + return Get(type, element, n); +} + +const Scalar* Manager::Get(i32 value) { + return Get>(types.i32(), value); +} + +const Scalar* Manager::Get(u32 value) { + return Get>(types.u32(), value); +} + +const Scalar* Manager::Get(f32 value) { + return Get>(types.f32(), value); +} + +const Scalar* Manager::Get(f16 value) { + return Get>(types.f16(), value); +} + +const Scalar* Manager::Get(bool value) { + return Get>(types.bool_(), value); +} + +const Scalar* Manager::Get(AFloat value) { + return Get>(types.AFloat(), value); +} + +const Scalar* Manager::Get(AInt value) { + return Get>(types.AInt(), value); +} + +} // namespace tint::constant diff --git a/src/tint/constant/manager.h b/src/tint/constant/manager.h new file mode 100644 index 0000000000..d356988395 --- /dev/null +++ b/src/tint/constant/manager.h @@ -0,0 +1,157 @@ +// Copyright 2023 The Tint Authors. +// +// 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. + +#ifndef SRC_TINT_CONSTANT_MANAGER_H_ +#define SRC_TINT_CONSTANT_MANAGER_H_ + +#include + +#include "src/tint/constant/value.h" +#include "src/tint/number.h" +#include "src/tint/type/manager.h" +#include "src/tint/utils/hash.h" +#include "src/tint/utils/unique_allocator.h" + +namespace tint::constant { +class Splat; + +template +class Scalar; +} // namespace tint::constant + +namespace tint::constant { + +/// The constant manager holds a type manager and all the pointers to the known constant values. +class Manager final { + public: + /// Iterator is the type returned by begin() and end() + using TypeIterator = utils::BlockAllocator::ConstIterator; + + /// Constructor + Manager(); + + /// Move constructor + Manager(Manager&&); + + /// Move assignment operator + /// @param rhs the Manager to move + /// @return this Manager + Manager& operator=(Manager&& rhs); + + /// Destructor + ~Manager(); + + /// Wrap returns a new Manager created with the constants and types of `inner`. + /// The Manager returned by Wrap is intended to temporarily extend the constants and types of an + /// existing immutable Manager. As the copied constants and types are owned by `inner`, `inner` + /// must not be destructed or assigned while using the returned Manager. + /// TODO(bclayton) - Evaluate whether there are safer alternatives to this + /// function. See crbug.com/tint/460. + /// @param inner the immutable Manager to extend + /// @return the Manager that wraps `inner` + static Manager Wrap(const Manager& inner) { + Manager out; + out.values_.Wrap(inner.values_); + out.types = type::Manager::Wrap(inner.types); + return out; + } + + /// @param args the arguments used to construct the type, unique node or node. + /// @return a pointer to an instance of `T` with the provided arguments. + /// If NODE derives from UniqueNode and an existing instance of `T` has been + /// constructed, then the same pointer is returned. + template + NODE* Get(ARGS&&... args) { + return values_.Get(std::forward(args)...); + } + + /// @returns an iterator to the beginning of the types + TypeIterator begin() const { return values_.begin(); } + /// @returns an iterator to the end of the types + TypeIterator end() const { return values_.end(); } + + /// Constructs a constant of a vector, matrix or array type. + /// + /// Examines the element values and will return either a constant::Composite or a + /// constant::Splat, depending on the element types and values. + /// + /// @param type the composite type + /// @param elements the composite elements + /// @returns the value pointer + const constant::Value* Composite(const type::Type* type, + utils::VectorRef elements); + + /// Constructs a splat constant. + /// @param type the splat type + /// @param element the splat element + /// @param n the number of elements + /// @returns the value pointer + const constant::Splat* Splat(const type::Type* type, const constant::Value* element, size_t n); + + /// @param value the constant value + /// @return a Scalar holding the i32 value @p value + const Scalar* Get(i32 value); + + /// @param value the constant value + /// @return a Scalar holding the u32 value @p value + const Scalar* Get(u32 value); + + /// @param value the constant value + /// @return a Scalar holding the f32 value @p value + const Scalar* Get(f32 value); + + /// @param value the constant value + /// @return a Scalar holding the f16 value @p value + const Scalar* Get(f16 value); + + /// @param value the constant value + /// @return a Scalar holding the bool value @p value + const Scalar* Get(bool value); + + /// @param value the constant value + /// @return a Scalar holding the AFloat value @p value + const Scalar* Get(AFloat value); + + /// @param value the constant value + /// @return a Scalar holding the AInt value @p value + const Scalar* Get(AInt value); + + /// The type manager + type::Manager types; + + private: + /// A specialization of utils::Hasher for constant::Value + struct Hasher { + /// @param value the value to hash + /// @returns a hash of the value + size_t operator()(const constant::Value& value) const { return value.Hash(); } + }; + + /// An equality helper for constant::Value + struct Equal { + /// @param a the LHS value + /// @param b the RHS value + /// @returns true if the two constants are equal + bool operator()(const constant::Value& a, const constant::Value& b) const { + return a.Equal(&b); + } + }; + + /// Unique types owned by the manager + utils::UniqueAllocator values_; +}; + +} // namespace tint::constant + +#endif // SRC_TINT_CONSTANT_MANAGER_H_ diff --git a/src/tint/constant/manager_test.cc b/src/tint/constant/manager_test.cc new file mode 100644 index 0000000000..ab6135c4d3 --- /dev/null +++ b/src/tint/constant/manager_test.cc @@ -0,0 +1,187 @@ +// Copyright 2023 The Tint Authors. +// +// 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 "src/tint/constant/manager.h" + +#include "gtest/gtest.h" +#include "src/tint/constant/scalar.h" +#include "src/tint/type/abstract_float.h" +#include "src/tint/type/abstract_int.h" +#include "src/tint/type/bool.h" +#include "src/tint/type/f16.h" +#include "src/tint/type/f32.h" +#include "src/tint/type/i32.h" +#include "src/tint/type/manager.h" +#include "src/tint/type/u32.h" + +namespace tint::constant { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +template +size_t count(const T& range_loopable) { + size_t n = 0; + for (auto it : range_loopable) { + (void)it; + n++; + } + return n; +} + +using ManagerTest = testing::Test; + +TEST_F(ManagerTest, GetUnregistered) { + constant::Manager cm; + + auto* c = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_NE(c, nullptr); +} + +TEST_F(ManagerTest, GetSameConstantReturnsSamePtr) { + constant::Manager cm; + + auto* c1 = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c1)>); + ASSERT_NE(c1, nullptr); + + auto* c2 = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c2)>); + EXPECT_EQ(c1, c2); + EXPECT_EQ(c1->Type(), c2->Type()); +} + +TEST_F(ManagerTest, GetDifferentTypeReturnsDifferentPtr) { + constant::Manager cm; + + auto* c1 = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c1)>); + ASSERT_NE(c1, nullptr); + + auto* c2 = cm.Get(1_u); + static_assert(std::is_same_v*, decltype(c2)>); + EXPECT_NE(static_cast(c1), static_cast(c2)); + EXPECT_NE(c1->Type(), c2->Type()); +} + +TEST_F(ManagerTest, GetDifferentValueReturnsDifferentPtr) { + constant::Manager cm; + + auto* c1 = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c1)>); + ASSERT_NE(c1, nullptr); + + auto* c2 = cm.Get(2_i); + static_assert(std::is_same_v*, decltype(c2)>); + ASSERT_NE(c2, nullptr); + EXPECT_NE(c1, c2); + EXPECT_EQ(c1->Type(), c2->Type()); +} + +TEST_F(ManagerTest, Get_i32) { + constant::Manager cm; + + auto* c = cm.Get(1_i); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1_i); +} + +TEST_F(ManagerTest, Get_u32) { + constant::Manager cm; + + auto* c = cm.Get(1_u); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1_u); +} + +TEST_F(ManagerTest, Get_f32) { + constant::Manager cm; + + auto* c = cm.Get(1_f); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1_f); +} + +TEST_F(ManagerTest, Get_f16) { + constant::Manager cm; + + auto* c = cm.Get(1_h); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1_h); +} + +TEST_F(ManagerTest, Get_bool) { + constant::Manager cm; + + auto* c = cm.Get(true); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, true); +} + +TEST_F(ManagerTest, Get_AFloat) { + constant::Manager cm; + + auto* c = cm.Get(1._a); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1._a); +} + +TEST_F(ManagerTest, Get_AInt) { + constant::Manager cm; + + auto* c = cm.Get(1_a); + static_assert(std::is_same_v*, decltype(c)>); + ASSERT_TRUE(Is(c->Type())); + EXPECT_EQ(c->value, 1_a); +} + +TEST_F(ManagerTest, WrapDoesntAffectInner_Constant) { + Manager inner; + Manager outer = Manager::Wrap(inner); + + inner.Get(1_i); + + EXPECT_EQ(count(inner), 1u); + EXPECT_EQ(count(outer), 0u); + + outer.Get(1_i); + + EXPECT_EQ(count(inner), 1u); + EXPECT_EQ(count(outer), 1u); +} + +TEST_F(ManagerTest, WrapDoesntAffectInner_Types) { + Manager inner; + Manager outer = Manager::Wrap(inner); + + inner.types.Get(); + + EXPECT_EQ(count(inner.types), 1u); + EXPECT_EQ(count(outer.types), 0u); + + outer.types.Get(); + + EXPECT_EQ(count(inner.types), 1u); + EXPECT_EQ(count(outer.types), 1u); +} + +} // namespace +} // namespace tint::constant diff --git a/src/tint/type/manager.cc b/src/tint/type/manager.cc index 666a1d3c71..0782633c90 100644 --- a/src/tint/type/manager.cc +++ b/src/tint/type/manager.cc @@ -14,6 +14,18 @@ #include "src/tint/type/manager.h" +#include "src/tint/type/abstract_float.h" +#include "src/tint/type/abstract_int.h" +#include "src/tint/type/bool.h" +#include "src/tint/type/f16.h" +#include "src/tint/type/f32.h" +#include "src/tint/type/i32.h" +#include "src/tint/type/matrix.h" +#include "src/tint/type/type.h" +#include "src/tint/type/u32.h" +#include "src/tint/type/vector.h" +#include "src/tint/type/void.h" + namespace tint::type { Manager::Manager() = default; @@ -24,4 +36,91 @@ Manager& Manager::operator=(Manager&& rhs) = default; Manager::~Manager() = default; +const type::Void* Manager::void_() { + return Get(); +} + +const type::Bool* Manager::bool_() { + return Get(); +} + +const type::I32* Manager::i32() { + return Get(); +} + +const type::U32* Manager::u32() { + return Get(); +} + +const type::F32* Manager::f32() { + return Get(); +} + +const type::F16* Manager::f16() { + return Get(); +} + +const type::AbstractFloat* Manager::AFloat() { + return Get(); +} + +const type::AbstractInt* Manager::AInt() { + return Get(); +} + +const type::Vector* Manager::vec(const type::Type* inner, uint32_t size) { + return Get(inner, size); +} + +const type::Vector* Manager::vec2(const type::Type* inner) { + return vec(inner, 2); +} + +const type::Vector* Manager::vec3(const type::Type* inner) { + return vec(inner, 3); +} + +const type::Vector* Manager::vec4(const type::Type* inner) { + return vec(inner, 4); +} + +const type::Matrix* Manager::mat(const type::Type* inner, uint32_t cols, uint32_t rows) { + return Get(vec(inner, rows), cols); +} + +const type::Matrix* Manager::mat2x2(const type::Type* inner) { + return mat(inner, 2, 2); +} + +const type::Matrix* Manager::mat2x3(const type::Type* inner) { + return mat(inner, 2, 3); +} + +const type::Matrix* Manager::mat2x4(const type::Type* inner) { + return mat(inner, 2, 4); +} + +const type::Matrix* Manager::mat3x2(const type::Type* inner) { + return mat(inner, 3, 2); +} + +const type::Matrix* Manager::mat3x3(const type::Type* inner) { + return mat(inner, 3, 3); +} + +const type::Matrix* Manager::mat3x4(const type::Type* inner) { + return mat(inner, 3, 4); +} + +const type::Matrix* Manager::mat4x2(const type::Type* inner) { + return mat(inner, 4, 2); +} + +const type::Matrix* Manager::mat4x3(const type::Type* inner) { + return mat(inner, 4, 3); +} + +const type::Matrix* Manager::mat4x4(const type::Type* inner) { + return mat(inner, 4, 4); +} } // namespace tint::type diff --git a/src/tint/type/manager.h b/src/tint/type/manager.h index fbee9be283..b4fa560c47 100644 --- a/src/tint/type/manager.h +++ b/src/tint/type/manager.h @@ -17,18 +17,25 @@ #include -#include "src/tint/type/bool.h" -#include "src/tint/type/f16.h" -#include "src/tint/type/f32.h" -#include "src/tint/type/i32.h" -#include "src/tint/type/matrix.h" #include "src/tint/type/type.h" -#include "src/tint/type/u32.h" -#include "src/tint/type/vector.h" -#include "src/tint/type/void.h" +#include "src/tint/type/unique_node.h" #include "src/tint/utils/hash.h" #include "src/tint/utils/unique_allocator.h" +// Forward declarations +namespace tint::type { +class AbstractFloat; +class AbstractInt; +class Bool; +class F16; +class F32; +class I32; +class Matrix; +class U32; +class Vector; +class Void; +} // namespace tint::type + namespace tint::type { /// The type manager holds all the pointers to the known types. @@ -93,85 +100,87 @@ class Manager final { } /// @returns a void type - const type::Type* void_() { return Get(); } + const type::Void* void_(); /// @returns a bool type - const type::Type* bool_() { return Get(); } + const type::Bool* bool_(); /// @returns an i32 type - const type::Type* i32() { return Get(); } + const type::I32* i32(); /// @returns a u32 type - const type::Type* u32() { return Get(); } + const type::U32* u32(); /// @returns an f32 type - const type::Type* f32() { return Get(); } + const type::F32* f32(); /// @returns an f16 type - const type::Type* f16() { return Get(); } + const type::F16* f16(); + + /// @returns a abstract-float type + const type::AbstractFloat* AFloat(); + + /// @returns a abstract-int type + const type::AbstractInt* AInt(); /// @param inner the inner type /// @param size the vector size /// @returns the vector type - const type::Vector* vec(const type::Type* inner, uint32_t size) { - return Get(inner, size); - } + const type::Vector* vec(const type::Type* inner, uint32_t size); /// @param inner the inner type /// @returns the vector type - const type::Vector* vec2(const type::Type* inner) { return vec(inner, 2); } + const type::Vector* vec2(const type::Type* inner); /// @param inner the inner type /// @returns the vector type - const type::Vector* vec3(const type::Type* inner) { return vec(inner, 3); } + const type::Vector* vec3(const type::Type* inner); /// @param inner the inner type /// @returns the vector type - const type::Vector* vec4(const type::Type* inner) { return vec(inner, 4); } + const type::Vector* vec4(const type::Type* inner); /// @param inner the inner type /// @param cols the number of columns /// @param rows the number of rows /// @returns the matrix type - const type::Matrix* mat(const type::Type* inner, uint32_t cols, uint32_t rows) { - return Get(vec(inner, rows), cols); - } + const type::Matrix* mat(const type::Type* inner, uint32_t cols, uint32_t rows); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat2x2(const type::Type* inner) { return mat(inner, 2, 2); } + const type::Matrix* mat2x2(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat2x3(const type::Type* inner) { return mat(inner, 2, 3); } + const type::Matrix* mat2x3(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat2x4(const type::Type* inner) { return mat(inner, 2, 4); } + const type::Matrix* mat2x4(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat3x2(const type::Type* inner) { return mat(inner, 3, 2); } + const type::Matrix* mat3x2(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat3x3(const type::Type* inner) { return mat(inner, 3, 3); } + const type::Matrix* mat3x3(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat3x4(const type::Type* inner) { return mat(inner, 3, 4); } + const type::Matrix* mat3x4(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat4x2(const type::Type* inner) { return mat(inner, 4, 2); } + const type::Matrix* mat4x2(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat4x3(const type::Type* inner) { return mat(inner, 4, 3); } + const type::Matrix* mat4x3(const type::Type* inner); /// @param inner the inner type /// @returns the matrix type - const type::Matrix* mat4x4(const type::Type* inner) { return mat(inner, 4, 4); } + const type::Matrix* mat4x4(const type::Type* inner); /// @returns an iterator to the beginning of the types TypeIterator begin() const { return types_.begin(); }