From 529c3fd385502b63a296ac306284cd6036664099 Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Fri, 6 Jan 2023 02:57:36 +0000 Subject: [PATCH] Add clone into constant/ This Cl adds the ability to clone a constant into a context provided. This allows the IR to clone the constants out of the Program and into the IR. Bug: tint:1718 Change-Id: I78170cdc66b5824a1ab81000976a747b5bffee79 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/116363 Commit-Queue: Dan Sinclair Reviewed-by: Ben Clayton Kokoro: Kokoro --- src/tint/BUILD.gn | 11 ++ src/tint/CMakeLists.txt | 4 + src/tint/constant/clone_context.h | 42 +++++ src/tint/constant/composite.cc | 9 + src/tint/constant/composite.h | 5 + src/tint/constant/composite_test.cc | 112 ++++++++++++ src/tint/constant/scalar.h | 8 + src/tint/constant/scalar_test.cc | 265 ++++++++++++++++++++++++++++ src/tint/constant/splat.cc | 6 + src/tint/constant/splat.h | 5 + src/tint/constant/splat_test.cc | 106 +++++++++++ src/tint/constant/test_helper.h | 36 ++++ src/tint/constant/value.h | 6 + src/tint/ir/builder_impl.cc | 10 +- src/tint/ir/builder_impl.h | 3 +- src/tint/sem/expression_test.cc | 1 + 16 files changed, 624 insertions(+), 5 deletions(-) create mode 100644 src/tint/constant/clone_context.h create mode 100644 src/tint/constant/composite_test.cc create mode 100644 src/tint/constant/scalar_test.cc create mode 100644 src/tint/constant/splat_test.cc create mode 100644 src/tint/constant/test_helper.h diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index c424b5914c..f57a2d73f1 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -366,6 +366,7 @@ libtint_source_set("libtint_core_all_src") { "castable.h", "clone_context.cc", "clone_context.h", + "constant/clone_context.h", "constant/composite.h", "constant/node.h", "constant/scalar.h", @@ -770,6 +771,7 @@ libtint_source_set("libtint_type_src") { libtint_source_set("libtint_constant_src") { sources = [ + "constant/clone_context.h", "constant/composite.cc", "constant/composite.h", "constant/node.cc", @@ -1242,6 +1244,14 @@ if (tint_build_unittests) { ] } + tint_unittests_source_set("tint_unittests_constant_src") { + sources = [ + "constant/composite_test.cc", + "constant/scalar_test.cc", + "constant/splat_test.cc", + ] + } + tint_unittests_source_set("tint_unittests_type_src") { sources = [ "type/array_test.cc", @@ -1738,6 +1748,7 @@ if (tint_build_unittests) { ":libtint_wgsl_reader_src", ":libtint_wgsl_writer_src", ":tint_unittests_ast_src", + ":tint_unittests_constant_src", ":tint_unittests_core_src", ":tint_unittests_diagnostic_src", ":tint_unittests_inspector_src", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 7abc023c59..f35aa5734f 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -254,6 +254,7 @@ list(APPEND TINT_LIB_SRCS castable.h clone_context.cc clone_context.h + constant/clone_context.h constant/composite.cc constant/composite.h constant/scalar.cc @@ -873,6 +874,9 @@ if(TINT_BUILD_TESTS) ast/workgroup_attribute_test.cc castable_test.cc clone_context_test.cc + constant/composite_test.cc + constant/scalar_test.cc + constant/splat_test.cc debug_test.cc demangler_test.cc diagnostic/diagnostic_test.cc diff --git a/src/tint/constant/clone_context.h b/src/tint/constant/clone_context.h new file mode 100644 index 0000000000..3f597ed6b9 --- /dev/null +++ b/src/tint/constant/clone_context.h @@ -0,0 +1,42 @@ +// 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_CLONE_CONTEXT_H_ +#define SRC_TINT_CONSTANT_CLONE_CONTEXT_H_ + +#include "src/tint/type/clone_context.h" +#include "src/tint/utils/block_allocator.h" + +// Forward Declarations +namespace tint::constant { +class Value; +} // namespace tint::constant + +namespace tint::constant { + +/// Context information for cloning of constants +struct CloneContext { + /// The context for cloning type information + type::CloneContext type_ctx; + + /// Destination information + struct { + /// The constant allocator + utils::BlockAllocator* constants; + } dst; +}; + +} // namespace tint::constant + +#endif // SRC_TINT_CONSTANT_CLONE_CONTEXT_H_ diff --git a/src/tint/constant/composite.cc b/src/tint/constant/composite.cc index 76d2adb4ce..5b004e0998 100644 --- a/src/tint/constant/composite.cc +++ b/src/tint/constant/composite.cc @@ -28,4 +28,13 @@ Composite::Composite(const type::Type* t, Composite::~Composite() = default; +Composite* Composite::Clone(CloneContext& ctx) const { + auto* ty = type->Clone(ctx.type_ctx); + utils::Vector els; + for (const auto* el : elements) { + els.Push(el->Clone(ctx)); + } + return ctx.dst.constants->Create(ty, els, all_zero, any_zero); +} + } // namespace tint::constant diff --git a/src/tint/constant/composite.h b/src/tint/constant/composite.h index aacc2ef924..3bd49735f5 100644 --- a/src/tint/constant/composite.h +++ b/src/tint/constant/composite.h @@ -52,6 +52,11 @@ class Composite : public Castable { bool AllEqual() const override { return false; } size_t Hash() const override { return hash; } + /// Clones the constant into the provided context + /// @param ctx the clone context + /// @returns the cloned node + Composite* Clone(CloneContext& ctx) const override; + /// The composite type type::Type const* const type; /// The composite elements diff --git a/src/tint/constant/composite_test.cc b/src/tint/constant/composite_test.cc new file mode 100644 index 0000000000..6fc532d0e3 --- /dev/null +++ b/src/tint/constant/composite_test.cc @@ -0,0 +1,112 @@ +// 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/composite.h" + +#include "src/tint/constant/scalar.h" +#include "src/tint/constant/test_helper.h" + +namespace tint::constant { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +using ConstantTest_Composite = TestHelper; + +TEST_F(ConstantTest_Composite, AllZero) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* compositeAll = create(f32, utils::Vector{fPos0, fPos0}); + auto* compositeAny = create(f32, utils::Vector{fNeg0, fPos1, fPos0}); + auto* compositeNone = create(f32, utils::Vector{fNeg0, fNeg0}); + + EXPECT_TRUE(compositeAll->AllZero()); + EXPECT_FALSE(compositeAny->AllZero()); + EXPECT_FALSE(compositeNone->AllZero()); +} + +TEST_F(ConstantTest_Composite, AnyZero) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* compositeAll = create(f32, utils::Vector{fPos0, fPos0}); + auto* compositeAny = create(f32, utils::Vector{fNeg0, fPos1, fPos0}); + auto* compositeNone = create(f32, utils::Vector{fNeg0, fNeg0}); + + EXPECT_TRUE(compositeAll->AnyZero()); + EXPECT_TRUE(compositeAny->AnyZero()); + EXPECT_FALSE(compositeNone->AnyZero()); +} + +TEST_F(ConstantTest_Composite, AllEqual) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* compositeEq = create(f32, utils::Vector{fPos0, fPos0}); + auto* compositeNe = create(f32, utils::Vector{fNeg0, fPos1, fPos0}); + + EXPECT_TRUE(compositeEq->AllEqual()); + EXPECT_FALSE(compositeNe->AllZero()); +} + +TEST_F(ConstantTest_Composite, Index) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* composite = create(f32, utils::Vector{fPos1, fPos0}); + + ASSERT_NE(composite->Index(0), nullptr); + ASSERT_NE(composite->Index(1), nullptr); + ASSERT_EQ(composite->Index(2), nullptr); + + EXPECT_TRUE(composite->Index(0)->Is>()); + EXPECT_EQ(composite->Index(0)->As>()->ValueOf(), 1.0); + EXPECT_TRUE(composite->Index(1)->Is>()); + EXPECT_EQ(composite->Index(1)->As>()->ValueOf(), 0.0); +} + +TEST_F(ConstantTest_Composite, Clone) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* composite = create(f32, utils::Vector{fPos1, fPos0}); + + type::Manager mgr; + utils::BlockAllocator consts; + constant::CloneContext ctx{type::CloneContext{{nullptr}, {nullptr, &mgr}}, {&consts}}; + + auto* r = composite->As()->Clone(ctx); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(r->type->Is()); + EXPECT_FALSE(r->all_zero); + EXPECT_TRUE(r->any_zero); + ASSERT_EQ(r->elements.Length(), 2u); +} + +} // namespace +} // namespace tint::constant diff --git a/src/tint/constant/scalar.h b/src/tint/constant/scalar.h index 3cf705ecde..a412d910f4 100644 --- a/src/tint/constant/scalar.h +++ b/src/tint/constant/scalar.h @@ -49,6 +49,14 @@ class Scalar : public Castable, constant::Value> { bool AllEqual() const override { return true; } size_t Hash() const override { return utils::Hash(type, ValueOf()); } + /// Clones the constant into the provided context + /// @param ctx the clone context + /// @returns the cloned node + Scalar* Clone(CloneContext& ctx) const override { + auto* ty = type->Clone(ctx.type_ctx); + return ctx.dst.constants->Create>(ty, value); + } + /// @returns `value` if `T` is not a Number, otherwise ValueOf returns the inner value of the /// Number. inline auto ValueOf() const { diff --git a/src/tint/constant/scalar_test.cc b/src/tint/constant/scalar_test.cc new file mode 100644 index 0000000000..e05cca0f52 --- /dev/null +++ b/src/tint/constant/scalar_test.cc @@ -0,0 +1,265 @@ +// 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/scalar.h" + +#include "src/tint/constant/test_helper.h" + +namespace tint::constant { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +using ConstantTest_Scalar = TestHelper; + +TEST_F(ConstantTest_Scalar, AllZero) { + auto* i32 = create(); + auto* u32 = create(); + auto* f16 = create(); + auto* f32 = create(); + auto* bool_ = create(); + + auto* i0 = create>(i32, 0_i); + auto* iPos1 = create>(i32, 1_i); + auto* iNeg1 = create>(i32, -1_i); + + auto* u0 = create>(u32, 0_u); + auto* u1 = create>(u32, 1_u); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + auto* fNeg1 = create>(f32, -1_f); + + auto* f16Pos0 = create>(f16, 0_h); + auto* f16Neg0 = create>(f16, -0_h); + auto* f16Pos1 = create>(f16, 1_h); + auto* f16Neg1 = create>(f16, -1_h); + + auto* bf = create>(bool_, false); + auto* bt = create>(bool_, true); + + auto* afPos0 = create>(f32, 0.0_a); + auto* afNeg0 = create>(f32, -0.0_a); + auto* afPos1 = create>(f32, 1.0_a); + auto* afNeg1 = create>(f32, -1.0_a); + + auto* ai0 = create>(i32, 0_a); + auto* aiPos1 = create>(i32, 1_a); + auto* aiNeg1 = create>(i32, -1_a); + + EXPECT_TRUE(i0->AllZero()); + EXPECT_FALSE(iPos1->AllZero()); + EXPECT_FALSE(iNeg1->AllZero()); + + EXPECT_TRUE(u0->AllZero()); + EXPECT_FALSE(u1->AllZero()); + + EXPECT_TRUE(fPos0->AllZero()); + EXPECT_FALSE(fNeg0->AllZero()); + EXPECT_FALSE(fPos1->AllZero()); + EXPECT_FALSE(fNeg1->AllZero()); + + EXPECT_TRUE(f16Pos0->AllZero()); + EXPECT_FALSE(f16Neg0->AllZero()); + EXPECT_FALSE(f16Pos1->AllZero()); + EXPECT_FALSE(f16Neg1->AllZero()); + + EXPECT_TRUE(bf->AllZero()); + EXPECT_FALSE(bt->AllZero()); + + EXPECT_TRUE(afPos0->AllZero()); + EXPECT_FALSE(afNeg0->AllZero()); + EXPECT_FALSE(afPos1->AllZero()); + EXPECT_FALSE(afNeg1->AllZero()); + + EXPECT_TRUE(ai0->AllZero()); + EXPECT_FALSE(aiPos1->AllZero()); + EXPECT_FALSE(aiNeg1->AllZero()); +} + +TEST_F(ConstantTest_Scalar, AnyZero) { + auto* i32 = create(); + auto* u32 = create(); + auto* f16 = create(); + auto* f32 = create(); + auto* bool_ = create(); + + auto* i0 = create>(i32, 0_i); + auto* iPos1 = create>(i32, 1_i); + auto* iNeg1 = create>(i32, -1_i); + + auto* u0 = create>(u32, 0_u); + auto* u1 = create>(u32, 1_u); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + auto* fNeg1 = create>(f32, -1_f); + + auto* f16Pos0 = create>(f16, 0_h); + auto* f16Neg0 = create>(f16, -0_h); + auto* f16Pos1 = create>(f16, 1_h); + auto* f16Neg1 = create>(f16, -1_h); + + auto* bf = create>(bool_, false); + auto* bt = create>(bool_, true); + + auto* afPos0 = create>(f32, 0.0_a); + auto* afNeg0 = create>(f32, -0.0_a); + auto* afPos1 = create>(f32, 1.0_a); + auto* afNeg1 = create>(f32, -1.0_a); + + auto* ai0 = create>(i32, 0_a); + auto* aiPos1 = create>(i32, 1_a); + auto* aiNeg1 = create>(i32, -1_a); + + EXPECT_TRUE(i0->AnyZero()); + EXPECT_FALSE(iPos1->AnyZero()); + EXPECT_FALSE(iNeg1->AnyZero()); + + EXPECT_TRUE(u0->AnyZero()); + EXPECT_FALSE(u1->AnyZero()); + + EXPECT_TRUE(fPos0->AnyZero()); + EXPECT_FALSE(fNeg0->AnyZero()); + EXPECT_FALSE(fPos1->AnyZero()); + EXPECT_FALSE(fNeg1->AnyZero()); + + EXPECT_TRUE(f16Pos0->AnyZero()); + EXPECT_FALSE(f16Neg0->AnyZero()); + EXPECT_FALSE(f16Pos1->AnyZero()); + EXPECT_FALSE(f16Neg1->AnyZero()); + + EXPECT_TRUE(bf->AnyZero()); + EXPECT_FALSE(bt->AnyZero()); + + EXPECT_TRUE(afPos0->AnyZero()); + EXPECT_FALSE(afNeg0->AnyZero()); + EXPECT_FALSE(afPos1->AnyZero()); + EXPECT_FALSE(afNeg1->AnyZero()); + + EXPECT_TRUE(ai0->AnyZero()); + EXPECT_FALSE(aiPos1->AnyZero()); + EXPECT_FALSE(aiNeg1->AnyZero()); +} + +TEST_F(ConstantTest_Scalar, AllEqual) { + auto* i32 = create(); + auto* u32 = create(); + auto* f16 = create(); + auto* f32 = create(); + auto* bool_ = create(); + + auto* i0 = create>(i32, 0_i); + auto* iPos1 = create>(i32, 1_i); + auto* iNeg1 = create>(i32, -1_i); + + auto* u0 = create>(u32, 0_u); + auto* u1 = create>(u32, 1_u); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + auto* fNeg1 = create>(f32, -1_f); + + auto* f16Pos0 = create>(f16, 0_h); + auto* f16Neg0 = create>(f16, -0_h); + auto* f16Pos1 = create>(f16, 1_h); + auto* f16Neg1 = create>(f16, -1_h); + + auto* bf = create>(bool_, false); + auto* bt = create>(bool_, true); + + auto* afPos0 = create>(f32, 0.0_a); + auto* afNeg0 = create>(f32, -0.0_a); + auto* afPos1 = create>(f32, 1.0_a); + auto* afNeg1 = create>(f32, -1.0_a); + + auto* ai0 = create>(i32, 0_a); + auto* aiPos1 = create>(i32, 1_a); + auto* aiNeg1 = create>(i32, -1_a); + + EXPECT_TRUE(i0->AllEqual()); + EXPECT_TRUE(iPos1->AllEqual()); + EXPECT_TRUE(iNeg1->AllEqual()); + + EXPECT_TRUE(u0->AllEqual()); + EXPECT_TRUE(u1->AllEqual()); + + EXPECT_TRUE(fPos0->AllEqual()); + EXPECT_TRUE(fNeg0->AllEqual()); + EXPECT_TRUE(fPos1->AllEqual()); + EXPECT_TRUE(fNeg1->AllEqual()); + + EXPECT_TRUE(f16Pos0->AllEqual()); + EXPECT_TRUE(f16Neg0->AllEqual()); + EXPECT_TRUE(f16Pos1->AllEqual()); + EXPECT_TRUE(f16Neg1->AllEqual()); + + EXPECT_TRUE(bf->AllEqual()); + EXPECT_TRUE(bt->AllEqual()); + + EXPECT_TRUE(afPos0->AllEqual()); + EXPECT_TRUE(afNeg0->AllEqual()); + EXPECT_TRUE(afPos1->AllEqual()); + EXPECT_TRUE(afNeg1->AllEqual()); + + EXPECT_TRUE(ai0->AllEqual()); + EXPECT_TRUE(aiPos1->AllEqual()); + EXPECT_TRUE(aiNeg1->AllEqual()); +} + +TEST_F(ConstantTest_Scalar, ValueOf) { + auto* i32 = create(); + auto* u32 = create(); + auto* f16 = create(); + auto* f32 = create(); + auto* bool_ = create(); + + auto* i1 = create>(i32, 1_i); + auto* u1 = create>(u32, 1_u); + auto* f1 = create>(f32, 1_f); + auto* f16Pos1 = create>(f16, 1_h); + auto* bf = create>(bool_, false); + auto* bt = create>(bool_, true); + auto* af1 = create>(f32, 1.0_a); + auto* ai1 = create>(i32, 1_a); + + EXPECT_EQ(i1->ValueOf(), 1); + EXPECT_EQ(u1->ValueOf(), 1u); + EXPECT_EQ(f1->ValueOf(), 1.f); + EXPECT_EQ(f16Pos1->ValueOf(), 1.f); + EXPECT_FALSE(bf->ValueOf()); + EXPECT_TRUE(bt->ValueOf()); + EXPECT_EQ(af1->ValueOf(), 1.0); + EXPECT_EQ(ai1->ValueOf(), 1l); +} + +TEST_F(ConstantTest_Scalar, Clone) { + auto* i32 = create(); + auto* val = create>(i32, 12_i); + + type::Manager mgr; + utils::BlockAllocator consts; + constant::CloneContext ctx{type::CloneContext{{nullptr}, {nullptr, &mgr}}, {&consts}}; + + auto* r = val->Clone(ctx); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(r->type->Is()); + EXPECT_EQ(r->value, 12); +} + +} // namespace +} // namespace tint::constant diff --git a/src/tint/constant/splat.cc b/src/tint/constant/splat.cc index 9ef68d46f7..4ebfe5f5f8 100644 --- a/src/tint/constant/splat.cc +++ b/src/tint/constant/splat.cc @@ -22,4 +22,10 @@ Splat::Splat(const type::Type* t, const constant::Value* e, size_t n) : type(t), Splat::~Splat() = default; +Splat* Splat::Clone(CloneContext& ctx) const { + auto* ty = type->Clone(ctx.type_ctx); + auto* element = el->Clone(ctx); + return ctx.dst.constants->Create(ty, element, count); +} + } // namespace tint::constant diff --git a/src/tint/constant/splat.h b/src/tint/constant/splat.h index 9fa7b13b10..5494c8f098 100644 --- a/src/tint/constant/splat.h +++ b/src/tint/constant/splat.h @@ -53,6 +53,11 @@ class Splat : public Castable { /// @returns the hash for the splat size_t Hash() const override { return utils::Hash(type, el->Hash(), count); } + /// Clones the constant into the provided context + /// @param ctx the clone context + /// @returns the cloned node + Splat* Clone(CloneContext& ctx) const override; + /// The type of the splat element type::Type const* const type; /// The element stored in the splat diff --git a/src/tint/constant/splat_test.cc b/src/tint/constant/splat_test.cc new file mode 100644 index 0000000000..5c897cb790 --- /dev/null +++ b/src/tint/constant/splat_test.cc @@ -0,0 +1,106 @@ +// 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/splat.h" + +#include "src/tint/constant/scalar.h" +#include "src/tint/constant/test_helper.h" + +namespace tint::constant { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +using ConstantTest_Splat = TestHelper; + +TEST_F(ConstantTest_Splat, AllZero) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* SpfPos0 = create(f32, fPos0, 2); + auto* SpfNeg0 = create(f32, fNeg0, 2); + auto* SpfPos1 = create(f32, fPos1, 2); + + EXPECT_TRUE(SpfPos0->AllZero()); + EXPECT_FALSE(SpfNeg0->AllZero()); + EXPECT_FALSE(SpfPos1->AllZero()); +} + +TEST_F(ConstantTest_Splat, AnyZero) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* SpfPos0 = create(f32, fPos0, 2); + auto* SpfNeg0 = create(f32, fNeg0, 2); + auto* SpfPos1 = create(f32, fPos1, 2); + + EXPECT_TRUE(SpfPos0->AnyZero()); + EXPECT_FALSE(SpfNeg0->AnyZero()); + EXPECT_FALSE(SpfPos1->AnyZero()); +} + +TEST_F(ConstantTest_Splat, AllEqual) { + auto* f32 = create(); + + auto* fPos0 = create>(f32, 0_f); + auto* fNeg0 = create>(f32, -0_f); + auto* fPos1 = create>(f32, 1_f); + + auto* SpfPos0 = create(f32, fPos0, 2); + auto* SpfNeg0 = create(f32, fNeg0, 2); + auto* SpfPos1 = create(f32, fPos1, 2); + + EXPECT_TRUE(SpfPos0->AllEqual()); + EXPECT_TRUE(SpfNeg0->AllEqual()); + EXPECT_TRUE(SpfPos1->AllEqual()); +} + +TEST_F(ConstantTest_Splat, Index) { + auto* f32 = create(); + + auto* f1 = create>(f32, 1_f); + auto* sp = create(f32, f1, 2); + + ASSERT_NE(sp->Index(0), nullptr); + ASSERT_NE(sp->Index(1), nullptr); + ASSERT_EQ(sp->Index(2), nullptr); + + EXPECT_EQ(sp->Index(0)->As>()->ValueOf(), 1.f); + EXPECT_EQ(sp->Index(1)->As>()->ValueOf(), 1.f); +} + +TEST_F(ConstantTest_Splat, Clone) { + auto* i32 = create(); + auto* val = create>(i32, 12_i); + auto* sp = create(i32, val, 2); + + type::Manager mgr; + utils::BlockAllocator consts; + constant::CloneContext ctx{type::CloneContext{{nullptr}, {nullptr, &mgr}}, {&consts}}; + + auto* r = sp->Clone(ctx); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(r->type->Is()); + EXPECT_TRUE(r->el->Is>()); + EXPECT_EQ(r->count, 2u); +} + +} // namespace +} // namespace tint::constant diff --git a/src/tint/constant/test_helper.h b/src/tint/constant/test_helper.h new file mode 100644 index 0000000000..1c4b928b0c --- /dev/null +++ b/src/tint/constant/test_helper.h @@ -0,0 +1,36 @@ +// 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_TEST_HELPER_H_ +#define SRC_TINT_CONSTANT_TEST_HELPER_H_ + +#include "gtest/gtest.h" +#include "src/tint/program_builder.h" + +namespace tint::constant { + +/// Helper base class for testing +template +class TestHelperBase : public BASE, public ProgramBuilder {}; + +/// Helper class for testing that derives from testing::Test. +using TestHelper = TestHelperBase; + +/// Helper class for testing that derives from `T`. +template +using TestParamHelper = TestHelperBase>; + +} // namespace tint::constant + +#endif // SRC_TINT_CONSTANT_TEST_HELPER_H_ diff --git a/src/tint/constant/value.h b/src/tint/constant/value.h index c16fe7623a..d5b98769ee 100644 --- a/src/tint/constant/value.h +++ b/src/tint/constant/value.h @@ -18,6 +18,7 @@ #include #include "src/tint/castable.h" +#include "src/tint/constant/clone_context.h" #include "src/tint/constant/node.h" #include "src/tint/number.h" #include "src/tint/type/type.h" @@ -75,6 +76,11 @@ class Value : public Castable { /// @returns true if this value is equal to @p b bool Equal(const constant::Value* b) const; + /// Clones the constant into the provided context + /// @param ctx the clone context + /// @returns the cloned node + virtual Value* Clone(CloneContext& ctx) const = 0; + protected: /// @returns the value, if this is of a scalar value or abstract numeric, otherwise /// std::monostate. diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc index 900613017d..7c4c63f12f 100644 --- a/src/tint/ir/builder_impl.cc +++ b/src/tint/ir/builder_impl.cc @@ -85,7 +85,9 @@ bool IsConnected(const FlowNode* b) { BuilderImpl::BuilderImpl(const Program* program) : builder(program), - type_clone_ctx_{{&program->Symbols()}, {&builder.ir.symbols, &builder.ir.types}} {} + clone_ctx_{ + type::CloneContext{{&program->Symbols()}, {&builder.ir.symbols, &builder.ir.types}}, + {&builder.ir.constants}} {} BuilderImpl::~BuilderImpl() = default; @@ -567,7 +569,7 @@ utils::Result BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) } auto* sem = builder.ir.program->Sem().Get(expr); - auto* ty = sem->Type()->Clone(type_clone_ctx_); + auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx); Binary* instr = nullptr; switch (expr->op) { @@ -641,7 +643,7 @@ utils::Result BuilderImpl::EmitBitcast(const ast::BitcastExpression* exp } auto* sem = builder.ir.program->Sem().Get(expr); - auto* ty = sem->Type()->Clone(type_clone_ctx_); + auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx); auto* instr = builder.Bitcast(ty, val.Get()); current_flow_block->instructions.Push(instr); @@ -658,7 +660,7 @@ utils::Result BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit return utils::Failure; } - auto* cv = sem->ConstantValue(); + auto* cv = sem->ConstantValue()->Clone(clone_ctx_); if (!cv) { diagnostics_.add_error( tint::diag::System::IR, diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h index 1227b10eb7..6c858ac838 100644 --- a/src/tint/ir/builder_impl.h +++ b/src/tint/ir/builder_impl.h @@ -19,6 +19,7 @@ #include #include +#include "src/tint/constant/clone_context.h" #include "src/tint/diagnostic/diagnostic.h" #include "src/tint/ir/builder.h" #include "src/tint/ir/flow_node.h" @@ -213,7 +214,7 @@ class BuilderImpl { /// Used for testing purposes. std::unordered_map ast_to_flow_; - type::CloneContext type_clone_ctx_; + constant::CloneContext clone_ctx_; }; } // namespace tint::ir diff --git a/src/tint/sem/expression_test.cc b/src/tint/sem/expression_test.cc index 891400c353..6c49210c21 100644 --- a/src/tint/sem/expression_test.cc +++ b/src/tint/sem/expression_test.cc @@ -33,6 +33,7 @@ class MockConstant : public constant::Value { bool AnyZero() const override { return {}; } bool AllEqual() const override { return {}; } size_t Hash() const override { return 0; } + MockConstant* Clone(constant::CloneContext&) const override { return nullptr; } protected: std::variant InternalValue() const override { return {}; }