Remove unused AST transforms.
Remove AST transforms which are no longer used.
Bug: 421916945
Change-Id: Ic62804a2eb68595f46eabcd20e280a8ceb8f0bf3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/248355
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.bazel b/src/tint/lang/wgsl/ast/transform/BUILD.bazel
index 31ae503..8ac475e 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.bazel
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.bazel
@@ -39,72 +39,23 @@
cc_library(
name = "transform",
srcs = [
- "add_empty_entry_point.cc",
- "array_length_from_uniform.cc",
- "binding_remapper.cc",
- "builtin_polyfill.cc",
- "canonicalize_entry_point_io.cc",
"data.cc",
- "demote_to_helper.cc",
- "direct_variable_access.cc",
- "disable_uniformity_analysis.cc",
- "expand_compound_assignment.cc",
- "first_index_offset.cc",
- "fold_constants.cc",
- "get_insertion_point.cc",
- "hoist_to_decl_before.cc",
"manager.cc",
- "multiplanar_external_texture.cc",
- "promote_initializers_to_let.cc",
- "promote_side_effects_to_decl.cc",
- "remove_continue_in_switch.cc",
- "remove_phonies.cc",
- "renamer.cc",
- "robustness.cc",
"simplify_pointers.cc",
- "single_entry_point.cc",
- "substitute_override.cc",
"transform.cc",
"unshadow.cc",
- "vectorize_scalar_matrix_initializers.cc",
- "zero_init_workgroup_memory.cc",
],
hdrs = [
- "add_empty_entry_point.h",
- "array_length_from_uniform.h",
- "binding_remapper.h",
- "builtin_polyfill.h",
- "canonicalize_entry_point_io.h",
"data.h",
- "demote_to_helper.h",
- "direct_variable_access.h",
- "disable_uniformity_analysis.h",
- "expand_compound_assignment.h",
- "first_index_offset.h",
- "fold_constants.h",
- "get_insertion_point.h",
- "hoist_to_decl_before.h",
"manager.h",
- "multiplanar_external_texture.h",
- "promote_initializers_to_let.h",
- "promote_side_effects_to_decl.h",
- "remove_continue_in_switch.h",
- "remove_phonies.h",
- "renamer.h",
- "robustness.h",
"simplify_pointers.h",
- "single_entry_point.h",
- "substitute_override.h",
"transform.h",
"unshadow.h",
- "vectorize_scalar_matrix_initializers.h",
- "zero_init_workgroup_memory.h",
],
deps = [
"//src/tint/api/common",
"//src/tint/lang/core",
"//src/tint/lang/core/constant",
- "//src/tint/lang/core/ir/transform",
"//src/tint/lang/core/type",
"//src/tint/lang/wgsl",
"//src/tint/lang/wgsl/ast",
@@ -130,42 +81,17 @@
name = "test",
alwayslink = True,
srcs = [
- "add_empty_entry_point_test.cc",
- "array_length_from_uniform_test.cc",
- "binding_remapper_test.cc",
- "builtin_polyfill_test.cc",
- "canonicalize_entry_point_io_test.cc",
- "demote_to_helper_test.cc",
- "direct_variable_access_test.cc",
- "disable_uniformity_analysis_test.cc",
- "expand_compound_assignment_test.cc",
- "first_index_offset_test.cc",
- "fold_constants_test.cc",
- "get_insertion_point_test.cc",
"helper_test.h",
- "hoist_to_decl_before_test.cc",
"manager_test.cc",
- "multiplanar_external_texture_test.cc",
- "promote_initializers_to_let_test.cc",
- "promote_side_effects_to_decl_test.cc",
- "remove_continue_in_switch_test.cc",
- "remove_phonies_test.cc",
- "renamer_test.cc",
- "robustness_test.cc",
"simplify_pointers_test.cc",
- "single_entry_point_test.cc",
- "substitute_override_test.cc",
"transform_test.cc",
"unshadow_test.cc",
- "vectorize_scalar_matrix_initializers_test.cc",
- "zero_init_workgroup_memory_test.cc",
],
deps = [
"//src/tint/api/common",
"//src/tint/lang/core",
"//src/tint/lang/core/constant",
"//src/tint/lang/core/ir",
- "//src/tint/lang/core/ir/transform",
"//src/tint/lang/core/type",
"//src/tint/lang/wgsl",
"//src/tint/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.cmake b/src/tint/lang/wgsl/ast/transform/BUILD.cmake
index ff237f0..eb02cb9 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.cmake
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.cmake
@@ -39,71 +39,22 @@
# Kind: lib
################################################################################
tint_add_target(tint_lang_wgsl_ast_transform lib
- lang/wgsl/ast/transform/add_empty_entry_point.cc
- lang/wgsl/ast/transform/add_empty_entry_point.h
- lang/wgsl/ast/transform/array_length_from_uniform.cc
- lang/wgsl/ast/transform/array_length_from_uniform.h
- lang/wgsl/ast/transform/binding_remapper.cc
- lang/wgsl/ast/transform/binding_remapper.h
- lang/wgsl/ast/transform/builtin_polyfill.cc
- lang/wgsl/ast/transform/builtin_polyfill.h
- lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
- lang/wgsl/ast/transform/canonicalize_entry_point_io.h
lang/wgsl/ast/transform/data.cc
lang/wgsl/ast/transform/data.h
- lang/wgsl/ast/transform/demote_to_helper.cc
- lang/wgsl/ast/transform/demote_to_helper.h
- lang/wgsl/ast/transform/direct_variable_access.cc
- lang/wgsl/ast/transform/direct_variable_access.h
- lang/wgsl/ast/transform/disable_uniformity_analysis.cc
- lang/wgsl/ast/transform/disable_uniformity_analysis.h
- lang/wgsl/ast/transform/expand_compound_assignment.cc
- lang/wgsl/ast/transform/expand_compound_assignment.h
- lang/wgsl/ast/transform/first_index_offset.cc
- lang/wgsl/ast/transform/first_index_offset.h
- lang/wgsl/ast/transform/fold_constants.cc
- lang/wgsl/ast/transform/fold_constants.h
- lang/wgsl/ast/transform/get_insertion_point.cc
- lang/wgsl/ast/transform/get_insertion_point.h
- lang/wgsl/ast/transform/hoist_to_decl_before.cc
- lang/wgsl/ast/transform/hoist_to_decl_before.h
lang/wgsl/ast/transform/manager.cc
lang/wgsl/ast/transform/manager.h
- lang/wgsl/ast/transform/multiplanar_external_texture.cc
- lang/wgsl/ast/transform/multiplanar_external_texture.h
- lang/wgsl/ast/transform/promote_initializers_to_let.cc
- lang/wgsl/ast/transform/promote_initializers_to_let.h
- lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
- lang/wgsl/ast/transform/promote_side_effects_to_decl.h
- lang/wgsl/ast/transform/remove_continue_in_switch.cc
- lang/wgsl/ast/transform/remove_continue_in_switch.h
- lang/wgsl/ast/transform/remove_phonies.cc
- lang/wgsl/ast/transform/remove_phonies.h
- lang/wgsl/ast/transform/renamer.cc
- lang/wgsl/ast/transform/renamer.h
- lang/wgsl/ast/transform/robustness.cc
- lang/wgsl/ast/transform/robustness.h
lang/wgsl/ast/transform/simplify_pointers.cc
lang/wgsl/ast/transform/simplify_pointers.h
- lang/wgsl/ast/transform/single_entry_point.cc
- lang/wgsl/ast/transform/single_entry_point.h
- lang/wgsl/ast/transform/substitute_override.cc
- lang/wgsl/ast/transform/substitute_override.h
lang/wgsl/ast/transform/transform.cc
lang/wgsl/ast/transform/transform.h
lang/wgsl/ast/transform/unshadow.cc
lang/wgsl/ast/transform/unshadow.h
- lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
- lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
- lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
- lang/wgsl/ast/transform/zero_init_workgroup_memory.h
)
tint_target_add_dependencies(tint_lang_wgsl_ast_transform lib
tint_api_common
tint_lang_core
tint_lang_core_constant
- tint_lang_core_ir_transform
tint_lang_core_type
tint_lang_wgsl
tint_lang_wgsl_ast
@@ -133,35 +84,11 @@
# Condition: TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER
################################################################################
tint_add_target(tint_lang_wgsl_ast_transform_test test
- lang/wgsl/ast/transform/add_empty_entry_point_test.cc
- lang/wgsl/ast/transform/array_length_from_uniform_test.cc
- lang/wgsl/ast/transform/binding_remapper_test.cc
- lang/wgsl/ast/transform/builtin_polyfill_test.cc
- lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
- lang/wgsl/ast/transform/demote_to_helper_test.cc
- lang/wgsl/ast/transform/direct_variable_access_test.cc
- lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
- lang/wgsl/ast/transform/expand_compound_assignment_test.cc
- lang/wgsl/ast/transform/first_index_offset_test.cc
- lang/wgsl/ast/transform/fold_constants_test.cc
- lang/wgsl/ast/transform/get_insertion_point_test.cc
lang/wgsl/ast/transform/helper_test.h
- lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
lang/wgsl/ast/transform/manager_test.cc
- lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
- lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
- lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
- lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
- lang/wgsl/ast/transform/remove_phonies_test.cc
- lang/wgsl/ast/transform/renamer_test.cc
- lang/wgsl/ast/transform/robustness_test.cc
lang/wgsl/ast/transform/simplify_pointers_test.cc
- lang/wgsl/ast/transform/single_entry_point_test.cc
- lang/wgsl/ast/transform/substitute_override_test.cc
lang/wgsl/ast/transform/transform_test.cc
lang/wgsl/ast/transform/unshadow_test.cc
- lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
- lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
)
tint_target_add_dependencies(tint_lang_wgsl_ast_transform_test test
@@ -169,7 +96,6 @@
tint_lang_core
tint_lang_core_constant
tint_lang_core_ir
- tint_lang_core_ir_transform
tint_lang_core_type
tint_lang_wgsl
tint_lang_wgsl_ast
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.gn b/src/tint/lang/wgsl/ast/transform/BUILD.gn
index 8b06c3d..73306ea 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.gn
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.gn
@@ -45,71 +45,22 @@
libtint_source_set("transform") {
sources = [
- "add_empty_entry_point.cc",
- "add_empty_entry_point.h",
- "array_length_from_uniform.cc",
- "array_length_from_uniform.h",
- "binding_remapper.cc",
- "binding_remapper.h",
- "builtin_polyfill.cc",
- "builtin_polyfill.h",
- "canonicalize_entry_point_io.cc",
- "canonicalize_entry_point_io.h",
"data.cc",
"data.h",
- "demote_to_helper.cc",
- "demote_to_helper.h",
- "direct_variable_access.cc",
- "direct_variable_access.h",
- "disable_uniformity_analysis.cc",
- "disable_uniformity_analysis.h",
- "expand_compound_assignment.cc",
- "expand_compound_assignment.h",
- "first_index_offset.cc",
- "first_index_offset.h",
- "fold_constants.cc",
- "fold_constants.h",
- "get_insertion_point.cc",
- "get_insertion_point.h",
- "hoist_to_decl_before.cc",
- "hoist_to_decl_before.h",
"manager.cc",
"manager.h",
- "multiplanar_external_texture.cc",
- "multiplanar_external_texture.h",
- "promote_initializers_to_let.cc",
- "promote_initializers_to_let.h",
- "promote_side_effects_to_decl.cc",
- "promote_side_effects_to_decl.h",
- "remove_continue_in_switch.cc",
- "remove_continue_in_switch.h",
- "remove_phonies.cc",
- "remove_phonies.h",
- "renamer.cc",
- "renamer.h",
- "robustness.cc",
- "robustness.h",
"simplify_pointers.cc",
"simplify_pointers.h",
- "single_entry_point.cc",
- "single_entry_point.h",
- "substitute_override.cc",
- "substitute_override.h",
"transform.cc",
"transform.h",
"unshadow.cc",
"unshadow.h",
- "vectorize_scalar_matrix_initializers.cc",
- "vectorize_scalar_matrix_initializers.h",
- "zero_init_workgroup_memory.cc",
- "zero_init_workgroup_memory.h",
]
deps = [
"${dawn_root}/src/utils:utils",
"${tint_src_dir}/api/common",
"${tint_src_dir}/lang/core",
"${tint_src_dir}/lang/core/constant",
- "${tint_src_dir}/lang/core/ir/transform",
"${tint_src_dir}/lang/core/type",
"${tint_src_dir}/lang/wgsl",
"${tint_src_dir}/lang/wgsl/ast",
@@ -132,35 +83,11 @@
if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
tint_unittests_source_set("unittests") {
sources = [
- "add_empty_entry_point_test.cc",
- "array_length_from_uniform_test.cc",
- "binding_remapper_test.cc",
- "builtin_polyfill_test.cc",
- "canonicalize_entry_point_io_test.cc",
- "demote_to_helper_test.cc",
- "direct_variable_access_test.cc",
- "disable_uniformity_analysis_test.cc",
- "expand_compound_assignment_test.cc",
- "first_index_offset_test.cc",
- "fold_constants_test.cc",
- "get_insertion_point_test.cc",
"helper_test.h",
- "hoist_to_decl_before_test.cc",
"manager_test.cc",
- "multiplanar_external_texture_test.cc",
- "promote_initializers_to_let_test.cc",
- "promote_side_effects_to_decl_test.cc",
- "remove_continue_in_switch_test.cc",
- "remove_phonies_test.cc",
- "renamer_test.cc",
- "robustness_test.cc",
"simplify_pointers_test.cc",
- "single_entry_point_test.cc",
- "substitute_override_test.cc",
"transform_test.cc",
"unshadow_test.cc",
- "vectorize_scalar_matrix_initializers_test.cc",
- "zero_init_workgroup_memory_test.cc",
]
deps = [
"${dawn_root}/src/utils:utils",
@@ -169,7 +96,6 @@
"${tint_src_dir}/lang/core",
"${tint_src_dir}/lang/core/constant",
"${tint_src_dir}/lang/core/ir",
- "${tint_src_dir}/lang/core/ir/transform",
"${tint_src_dir}/lang/core/type",
"${tint_src_dir}/lang/wgsl",
"${tint_src_dir}/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc
deleted file mode 100644
index ff63a55..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::AddEmptyEntryPoint);
-
-using namespace tint::core::number_suffixes; // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* func : program.AST().Functions()) {
- if (func->IsEntryPoint()) {
- return false;
- }
- }
- return true;
-}
-
-} // namespace
-
-AddEmptyEntryPoint::AddEmptyEntryPoint() = default;
-
-AddEmptyEntryPoint::~AddEmptyEntryPoint() = default;
-
-Transform::ApplyResult AddEmptyEntryPoint::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- b.Func(b.Symbols().New("unused_entry_point"), {}, b.ty.void_(), {},
- tint::Vector{
- b.Stage(PipelineStage::kCompute),
- b.WorkgroupSize(1_i),
- });
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h
deleted file mode 100644
index 06d5de2..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Add an empty entry point to the module, if no other entry points exist.
-class AddEmptyEntryPoint final : public Castable<AddEmptyEntryPoint, Transform> {
- public:
- /// Constructor
- AddEmptyEntryPoint();
- /// Destructor
- ~AddEmptyEntryPoint() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc
deleted file mode 100644
index fc1c704..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using AddEmptyEntryPointTest = TransformTest;
-
-TEST_F(AddEmptyEntryPointTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_TRUE(ShouldRun<AddEmptyEntryPoint>(src));
-}
-
-TEST_F(AddEmptyEntryPointTest, ShouldRunExistingEntryPoint) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn existing() {}
-)";
-
- EXPECT_FALSE(ShouldRun<AddEmptyEntryPoint>(src));
-}
-
-TEST_F(AddEmptyEntryPointTest, EmptyModule) {
- auto* src = R"()";
-
- auto* expect = R"(
-@compute @workgroup_size(1i)
-fn unused_entry_point() {
-}
-)";
-
- auto got = Run<AddEmptyEntryPoint>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(AddEmptyEntryPointTest, ExistingEntryPoint) {
- auto* src = R"(
-@fragment
-fn main() {
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<AddEmptyEntryPoint>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(AddEmptyEntryPointTest, NameClash) {
- auto* src = R"(var<private> unused_entry_point : f32;)";
-
- auto* expect = R"(
-@compute @workgroup_size(1i)
-fn unused_entry_point_1() {
-}
-
-var<private> unused_entry_point : f32;
-)";
-
- auto got = Run<AddEmptyEntryPoint>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc
deleted file mode 100644
index cea6a86..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/unary_op.h"
-#include "src/tint/lang/wgsl/ast/expression.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/unary_op_expression.h"
-#include "src/tint/lang/wgsl/ast/variable.h"
-#include "src/tint/lang/wgsl/builtin_fn.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/array.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/expression.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/unique_vector.h"
-#include "src/tint/utils/diagnostic/diagnostic.h"
-#include "src/tint/utils/ice/ice.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/text_style.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform::Config);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform::Result);
-
-using namespace tint::core::fluent_types; // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* fn : program.AST().Functions()) {
- if (auto* sem_fn = program.Sem().Get(fn)) {
- for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
- if (builtin->Fn() == wgsl::BuiltinFn::kArrayLength) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-} // namespace
-
-ArrayLengthFromUniform::ArrayLengthFromUniform() = default;
-ArrayLengthFromUniform::~ArrayLengthFromUniform() = default;
-
-/// PIMPL state for the transform
-struct ArrayLengthFromUniform::State {
- /// Constructor
- /// @param program the source program
- /// @param in the input transform data
- /// @param out the output transform data
- State(const Program& program, const DataMap& in, DataMap& out)
- : src(program), outputs(out), cfg(in.Get<Config>()) {}
-
- /// Runs the transform
- /// @returns the new program or SkipTransform if the transform is not required
- ApplyResult Run() {
- if (cfg == nullptr) {
- b.Diagnostics().AddError(Source{}) << "missing transform data for "
- << tint::TypeInfo::Of<ArrayLengthFromUniform>().name;
- return resolver::Resolve(b);
- }
-
- if (cfg->bindpoint_to_size_index.empty() || !ShouldRun(src)) {
- return SkipTransform;
- }
-
- // Create the name of the array lengths uniform variable.
- array_lengths_var = b.Symbols().New("tint_array_lengths");
-
- // Replace all the arrayLength() calls.
- for (auto* fn : src.AST().Functions()) {
- if (auto* sem_fn = sem.Get(fn)) {
- for (auto* call : sem_fn->DirectCalls()) {
- if (auto* target = call->Target()->As<sem::BuiltinFn>()) {
- if (target->Fn() == wgsl::BuiltinFn::kArrayLength) {
- ReplaceArrayLengthCall(call);
- }
- }
- }
- }
- }
-
- // Add the necessary array-length arguments to all the newly created array-length
- // parameters.
- while (!len_params_needing_args.IsEmpty()) {
- AddArrayLengthArguments(len_params_needing_args.Pop());
- }
-
- // Add the tint_array_lengths module-scope uniform variable.
- AddArrayLengthsUniformVar();
-
- outputs.Add<Result>(used_size_indices);
-
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- private:
- // Replaces the arrayLength() builtin call with an array-length expression passed via a uniform
- // buffer.
- void ReplaceArrayLengthCall(const sem::Call* call) {
- if (auto* replacement = ArrayLengthOf(call->Arguments()[0])) {
- ctx.Replace(call->Declaration(), replacement);
- }
- }
-
- /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
- /// accessed by the pointer expression @p expr, or nullptr on error or if the array is not in
- /// the Config::bindpoint_to_size_index map.
- const ast::Expression* ArrayLengthOf(const sem::Expression* expr) {
- const ast::Expression* len = nullptr;
- while (expr) {
- expr = Switch(
- expr, //
- [&](const sem::VariableUser* user) {
- len = ArrayLengthOf(user->Variable());
- return nullptr;
- },
- [&](const sem::MemberAccessorExpression* access) {
- return access->Object(); // Follow the object
- },
- [&](const sem::Expression* e) {
- return Switch(
- e->Declaration(), //
- [&](const ast::UnaryOpExpression* unary) -> const sem::Expression* {
- switch (unary->op) {
- case core::UnaryOp::kAddressOf:
- case core::UnaryOp::kIndirection:
- return sem.Get(unary->expr); // Follow the object
- default:
- TINT_ICE() << "unexpected unary op: " << unary->op;
- }
- },
- TINT_ICE_ON_NO_MATCH);
- },
- TINT_ICE_ON_NO_MATCH);
- }
- return len;
- }
-
- /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
- /// held by the module-scope variable or parameter @p var, or nullptr on error or if the array
- /// is not in the Config::bindpoint_to_size_index map.
- const ast::Expression* ArrayLengthOf(const sem::Variable* var) {
- return Switch(
- var, //
- [&](const sem::GlobalVariable* global) { return ArrayLengthOf(global); },
- [&](const sem::Parameter* param) { return ArrayLengthOf(param); },
- TINT_ICE_ON_NO_MATCH);
- }
-
- /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
- /// held by the module scope variable @p global, or nullptr on error or if the array is not in
- /// the Config::bindpoint_to_size_index map.
- const ast::Expression* ArrayLengthOf(const sem::GlobalVariable* global) {
- auto binding = global->Attributes().binding_point;
- TINT_ASSERT(binding);
-
- auto idx_it = cfg->bindpoint_to_size_index.find(*binding);
- if (idx_it == cfg->bindpoint_to_size_index.end()) {
- // If the bindpoint_to_size_index map does not contain an entry for the storage buffer,
- // then we preserve the arrayLength() call.
- return nullptr;
- }
-
- uint32_t size_index = idx_it->second;
- used_size_indices.insert(size_index);
-
- // Load the total storage buffer size from the UBO.
- uint32_t array_index = size_index / 4;
- auto* vec_expr = b.IndexAccessor(
- b.MemberAccessor(array_lengths_var, kArrayLengthsMemberName), u32(array_index));
- uint32_t vec_index = size_index % 4;
- auto* total_storage_buffer_size = b.IndexAccessor(vec_expr, u32(vec_index));
-
- // Calculate actual array length
- // total_storage_buffer_size - array_offset
- // array_length = ----------------------------------------
- // array_stride
- const Expression* total_size = total_storage_buffer_size;
- if (DAWN_UNLIKELY(global->Type()->Is<core::type::Pointer>())) {
- TINT_ICE() << "storage buffer variable should not be a pointer. "
- "These should have been removed by the SimplifyPointers transform";
- }
- auto* storage_buffer_type = global->Type()->UnwrapRef();
- const core::type::Array* array_type = nullptr;
- if (auto* str = storage_buffer_type->As<core::type::Struct>()) {
- // The variable is a struct, so subtract the byte offset of the
- // array member.
- auto* array_member_sem = str->Members().Back();
- array_type = array_member_sem->Type()->As<core::type::Array>();
- total_size = b.Sub(total_storage_buffer_size, u32(array_member_sem->Offset()));
- } else if (auto* arr = storage_buffer_type->As<core::type::Array>()) {
- array_type = arr;
- } else {
- TINT_ICE() << "expected form of arrayLength argument to be &array_var or "
- "&struct_var.array_member";
- }
- return b.Div(total_size, u32(array_type->Stride()));
- }
-
- /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
- /// held by the object pointed to by the pointer parameter @p param.
- const ast::Expression* ArrayLengthOf(const sem::Parameter* param) {
- // Pointer originates from a parameter.
- // Add a new array length parameter to the function, and use that.
- auto len_name = param_lengths.GetOrAdd(param, [&] {
- auto* fn = param->Owner()->As<sem::Function>();
- auto name = b.Symbols().New(param->Declaration()->name->symbol.Name() + "_length");
- auto* len_param = b.Param(name, b.ty.u32());
- ctx.InsertAfter(fn->Declaration()->params, param->Declaration(), len_param);
- len_params_needing_args.Add(param);
- return name;
- });
- return b.Expr(len_name);
- }
-
- /// Constructs the uniform buffer variable that will hold the array lengths.
- void AddArrayLengthsUniformVar() {
- // Calculate the highest index in the array lengths array
- uint32_t highest_index = 0;
- for (auto idx : used_size_indices) {
- if (idx > highest_index) {
- highest_index = idx;
- }
- }
-
- // Emit an array<vec4<u32>, N>, where N is 1/4 number of elements.
- // We do this because UBOs require an element stride that is 16-byte aligned.
- auto* buffer_size_struct =
- b.Structure(b.Symbols().New("TintArrayLengths"),
- tint::Vector{
- b.Member(kArrayLengthsMemberName,
- b.ty.array(b.ty.vec4<u32>(), u32((highest_index / 4) + 1))),
- });
- b.GlobalVar(array_lengths_var, b.ty.Of(buffer_size_struct), core::AddressSpace::kUniform,
- b.Group(AInt(cfg->ubo_binding.group)),
- b.Binding(AInt(cfg->ubo_binding.binding)));
- }
-
- /// Adds an additional array-length argument to all the calls to the function that owns the
- /// pointer parameter @p param. This may add new entries to #len_params_needing_args.
- void AddArrayLengthArguments(const sem::Parameter* param) {
- auto* fn = param->Owner()->As<sem::Function>();
- for (auto* call : fn->CallSites()) {
- auto* arg = call->Arguments()[param->Index()];
- if (auto* len = ArrayLengthOf(arg); len) {
- ctx.InsertAfter(call->Declaration()->args, arg->Declaration(), len);
- } else {
- // Callee expects an array length, but there's no binding for it.
- // Call arrayLength() at the call-site.
- len = b.Call(wgsl::BuiltinFn::kArrayLength, ctx.Clone(arg->Declaration()));
- ctx.InsertAfter(call->Declaration()->args, arg->Declaration(), len);
- }
- }
- }
-
- /// Name of the array-lengths struct member that holds all the array lengths.
- static constexpr std::string_view kArrayLengthsMemberName = "array_lengths";
-
- /// The source program
- const Program& src;
- /// The transform outputs
- DataMap& outputs;
- /// The transform config
- const Config* const cfg;
- /// The target program builder
- ProgramBuilder b;
- /// The clone context
- program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
- /// Alias to src.Sem()
- const sem::Info& sem = src.Sem();
- /// Name of the uniform buffer variable that holds the array lengths
- Symbol array_lengths_var;
- /// A map of pointer-parameter to the name of the new array-length parameter.
- Hashmap<const sem::Parameter*, Symbol, 8> param_lengths;
- /// Indices into the uniform buffer array indices that are statically used.
- std::unordered_set<uint32_t> used_size_indices;
- /// A vector of array-length parameters which need corresponding array-length arguments for all
- /// callsites.
- UniqueVector<const sem::Parameter*, 8> len_params_needing_args;
-};
-
-Transform::ApplyResult ArrayLengthFromUniform::Apply(const Program& src,
- const DataMap& inputs,
- DataMap& outputs) const {
- return State{src, inputs, outputs}.Run();
-}
-
-ArrayLengthFromUniform::Config::Config() = default;
-ArrayLengthFromUniform::Config::Config(BindingPoint ubo_bp) : ubo_binding(ubo_bp) {}
-ArrayLengthFromUniform::Config::Config(const Config&) = default;
-ArrayLengthFromUniform::Config& ArrayLengthFromUniform::Config::operator=(const Config&) = default;
-ArrayLengthFromUniform::Config::~Config() = default;
-
-ArrayLengthFromUniform::Result::Result(std::unordered_set<uint32_t> used_size_indices_in)
- : used_size_indices(std::move(used_size_indices_in)) {}
-ArrayLengthFromUniform::Result::Result(const Result&) = default;
-ArrayLengthFromUniform::Result::~Result() = default;
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h
deleted file mode 100644
index b8ae52c..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// ArrayLengthFromUniform is a transform that implements calls to arrayLength()
-/// by calculating the length from the total size of the storage buffer, which
-/// is received via a uniform buffer.
-///
-/// The generated uniform buffer will have the form:
-/// ```
-/// struct buffer_size_struct {
-/// buffer_size : array<u32, 8>;
-/// };
-///
-/// @group(0) @binding(30)
-/// var<uniform> buffer_size_ubo : buffer_size_struct;
-/// ```
-/// The binding group and number used for this uniform buffer is provided via
-/// the `Config` transform input. The `Config` struct also defines the mapping
-/// from a storage buffer's `BindingPoint` to the array index that will be used
-/// to get the size of that buffer.
-///
-/// This transform assumes that the `SimplifyPointers`
-/// transforms have been run before it so that arguments to the arrayLength
-/// builtin always have the form `&resource.array`.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * SimplifyPointers
-class ArrayLengthFromUniform final : public Castable<ArrayLengthFromUniform, Transform> {
- public:
- /// Constructor
- ArrayLengthFromUniform();
- /// Destructor
- ~ArrayLengthFromUniform() override;
-
- /// Configuration options for the ArrayLengthFromUniform transform.
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- Config();
-
- /// Constructor
- /// @param ubo_bp the binding point to use for the generated uniform buffer.
- explicit Config(BindingPoint ubo_bp);
-
- /// Copy constructor
- Config(const Config&);
-
- /// Copy assignment
- /// @return this Config
- Config& operator=(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// The binding point to use for the generated uniform buffer.
- BindingPoint ubo_binding;
-
- /// The mapping from binding point to the index for the buffer size lookup.
- std::unordered_map<BindingPoint, uint32_t> bindpoint_to_size_index;
-
- /// Reflection for this class
- TINT_REFLECT(Config, ubo_binding, bindpoint_to_size_index);
- };
-
- /// Information produced about what the transform did.
- /// If there were no calls to the arrayLength() builtin, then no Result will
- /// be emitted.
- struct Result final : public Castable<Result, Data> {
- /// Constructor
- /// @param used_size_indices Indices into the UBO that are statically used.
- explicit Result(std::unordered_set<uint32_t> used_size_indices);
-
- /// Copy constructor
- Result(const Result&);
-
- /// Destructor
- ~Result() override;
-
- /// Indices into the UBO that are statically used.
- std::unordered_set<uint32_t> used_size_indices;
- };
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc
deleted file mode 100644
index 47aedb5..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc
+++ /dev/null
@@ -1,709 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ArrayLengthFromUniformTest = TransformTest;
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunNoArrayLength) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunWithArrayLength) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- EXPECT_TRUE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
- auto* expect = "error: missing transform data for tint::ast::transform::ArrayLengthFromUniform";
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ArrayLengthFromUniformTest, Basic) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = arrayLength(&sb);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = (tint_array_lengths.array_lengths[0u][0u] / 4u);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- auto* val = got.data.Get<ArrayLengthFromUniform::Result>();
- ASSERT_NE(val, nullptr);
- EXPECT_EQ(std::unordered_set<uint32_t>({0}), val->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, BasicInStruct) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB {
- x : i32,
- arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-// Should output the same as BasicInStruct because SimplifyPointers outputs the same AST for
-// explicit and implicit pointer dereference.
-TEST_F(ArrayLengthFromUniformTest, BasicInStruct_ViaPointerDot) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- let p = &sb;
- var len : u32 = arrayLength(&p.arr);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB {
- x : i32,
- arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MultipleStorageBuffers) {
- auto* src = R"(
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-};
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-};
-struct SB4 {
- x : i32,
- arr4 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = arrayLength(&(sb1.arr1));
- var len2 : u32 = arrayLength(&(sb2.arr2));
- var len3 : u32 = arrayLength(&sb3);
- var len4 : u32 = arrayLength(&(sb4.arr4));
- var len5 : u32 = arrayLength(&sb5);
- var x : u32 = (len1 + len2 + len3 + len4 + len5);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 2u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-}
-
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-}
-
-struct SB4 {
- x : i32,
- arr4 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
- var len2 : u32 = ((tint_array_lengths.array_lengths[0u][1u] - 16u) / 16u);
- var len3 : u32 = (tint_array_lengths.array_lengths[0u][2u] / 16u);
- var len4 : u32 = ((tint_array_lengths.array_lengths[0u][3u] - 16u) / 16u);
- var len5 : u32 = (tint_array_lengths.array_lengths[1u][0u] / 16u);
- var x : u32 = ((((len1 + len2) + len3) + len4) + len5);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{1u, 2}, 1);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{2u, 2}, 2);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{3u, 2}, 3);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{4u, 2}, 4);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0, 1, 2, 3, 4}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MultipleUnusedStorageBuffers) {
- auto* src = R"(
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-};
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-};
-struct SB4 {
- x : i32,
- arr4 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = arrayLength(&(sb1.arr1));
- var len3 : u32 = arrayLength(&sb3);
- var x : u32 = (len1 + len3);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-}
-
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-}
-
-struct SB4 {
- x : i32,
- arr4 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
- var len3 : u32 = (tint_array_lengths.array_lengths[0u][2u] / 16u);
- var x : u32 = (len1 + len3);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{1u, 2}, 1);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{2u, 2}, 2);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{3u, 2}, 3);
- cfg.bindpoint_to_size_index.emplace(BindingPoint{4u, 2}, 4);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0, 2}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, NoArrayLengthCalls) {
- auto* src = R"(
-struct SB {
- x : i32,
- arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
- _ = &(sb.arr);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(src, str(got));
- EXPECT_EQ(got.data.Get<ArrayLengthFromUniform::Result>(), nullptr);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MissingBindingPointToIndexMapping) {
- auto* src = R"(
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-};
-
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = arrayLength(&(sb1.arr1));
- var len2 : u32 = arrayLength(&(sb2.arr2));
- var x : u32 = (len1 + len2);
-}
-)";
-
- auto* expect =
- R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
- x : i32,
- arr1 : array<i32>,
-}
-
-struct SB2 {
- x : i32,
- arr2 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@compute @workgroup_size(1)
-fn main() {
- var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
- var len2 : u32 = arrayLength(&(sb2.arr2));
- var x : u32 = (len1 + len2);
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = arrayLength(&sb.arr);
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-struct SB {
- x : i32,
- arr : array<i32>,
-};
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@compute @workgroup_size(1)
-fn main() {
- var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-struct SB {
- x : i32,
- arr : array<i32>,
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({0}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, PtrParam_SingleUse) {
- auto* src = R"(
-@binding(0) @group(0) var<storage, read_write> arr : array<u32>;
-
-fn f2(p : ptr<storage, array<u32>, read_write>) -> u32 {
- return arrayLength(p);
-}
-
-fn f1(p : ptr<storage, array<u32>, read_write>) -> u32 {
- return f2(p);
-}
-
-fn f0(p : ptr<storage, array<u32>, read_write>) -> u32 {
- return f1(p);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- arr[0] = f0(&arr);
-}
-)";
-
- auto* expect =
- R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@binding(0) @group(0) var<storage, read_write> arr : array<u32>;
-
-fn f2(p : ptr<storage, array<u32>, read_write>, p_length : u32) -> u32 {
- return p_length;
-}
-
-fn f1(p : ptr<storage, array<u32>, read_write>, p_length_1 : u32) -> u32 {
- return f2(p, p_length_1);
-}
-
-fn f0(p : ptr<storage, array<u32>, read_write>, p_length_2 : u32) -> u32 {
- return f1(p, p_length_2);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- arr[0] = f0(&(arr), (tint_array_lengths.array_lengths[0u][3u] / 4u));
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 3);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
- EXPECT_EQ(std::unordered_set<uint32_t>({3}),
- got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MissingBindingPoint_PtrParam_MultipleUse) {
- auto* src = R"(
-@binding(0) @group(0) var<storage, read_write> arr_a : array<u32>;
-@binding(0) @group(1) var<storage, read_write> arr_b : array<u32>;
-
-fn f2(p2 : ptr<storage, array<u32>, read_write>) -> u32 {
- return arrayLength(p2);
-}
-
-fn f1(p1 : ptr<storage, array<u32>, read_write>) -> u32 {
- return f2(p1) + arrayLength(p1);
-}
-
-fn f0(p0 : ptr<storage, array<u32>, read_write>) -> u32 {
- return f1(p0) + arrayLength(p0);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- arr_a[0] = f0(&arr_a) + arrayLength(&arr_a);
- arr_b[0] = f0(&arr_b) + arrayLength(&arr_b);
-}
-)";
-
- auto* expect = R"(
-struct TintArrayLengths {
- array_lengths : array<vec4<u32>, 2u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@binding(0) @group(0) var<storage, read_write> arr_a : array<u32>;
-
-@binding(0) @group(1) var<storage, read_write> arr_b : array<u32>;
-
-fn f2(p2 : ptr<storage, array<u32>, read_write>, p2_length : u32) -> u32 {
- return p2_length;
-}
-
-fn f1(p1 : ptr<storage, array<u32>, read_write>, p1_length : u32) -> u32 {
- return (f2(p1, p1_length) + p1_length);
-}
-
-fn f0(p0 : ptr<storage, array<u32>, read_write>, p0_length : u32) -> u32 {
- return (f1(p0, p0_length) + p0_length);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- arr_a[0] = (f0(&(arr_a), arrayLength(&(arr_a))) + arrayLength(&(arr_a)));
- arr_b[0] = (f0(&(arr_b), (tint_array_lengths.array_lengths[1u][1u] / 4u)) + (tint_array_lengths.array_lengths[1u][1u] / 4u));
-}
-)";
-
- ArrayLengthFromUniform::Config cfg({0, 30u});
- cfg.bindpoint_to_size_index.emplace(BindingPoint{1, 0}, 5);
-
- DataMap data;
- data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
- auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper.cc b/src/tint/lang/wgsl/ast/transform/binding_remapper.cc
deleted file mode 100644
index 96316fd..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-
-#include <string>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/ast/disable_validation_attribute.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/text/string.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BindingRemapper);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BindingRemapper::Remappings);
-
-using namespace tint::core::fluent_types; // NOLINT
-
-namespace tint::ast::transform {
-
-BindingRemapper::Remappings::Remappings() = default;
-BindingRemapper::Remappings::Remappings(BindingPoints bp, AccessControls ac, bool may_collide)
- : binding_points(std::move(bp)),
- access_controls(std::move(ac)),
- allow_collisions(may_collide) {}
-
-BindingRemapper::Remappings::Remappings(const Remappings&) = default;
-BindingRemapper::Remappings::~Remappings() = default;
-
-BindingRemapper::BindingRemapper() = default;
-BindingRemapper::~BindingRemapper() = default;
-
-Transform::ApplyResult BindingRemapper::Apply(const Program& src,
- const DataMap& inputs,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- auto* remappings = inputs.Get<Remappings>();
- if (!remappings) {
- b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
- return resolver::Resolve(b);
- }
-
- if (!remappings->allow_collisions && remappings->binding_points.empty() &&
- remappings->access_controls.empty()) {
- return SkipTransform;
- }
-
- for (auto* var : src.AST().Globals<Var>()) {
- if (var->HasBindingPoint()) {
- auto* global_sem = src.Sem().Get<sem::GlobalVariable>(var);
-
- // The original binding point
- BindingPoint from = *global_sem->Attributes().binding_point;
-
- // The binding point after remapping
- BindingPoint bp = from;
-
- // Replace any group or binding attributes.
- // Note: This has to be performed *before* remapping access controls, as
- // `ctx.Clone(var->attributes)` depend on these replacements.
- auto bp_it = remappings->binding_points.find(from);
- if (bp_it != remappings->binding_points.end()) {
- BindingPoint to = bp_it->second;
- auto* new_group = b.Group(AInt(to.group));
- auto* new_binding = b.Binding(AInt(to.binding));
-
- auto* old_group = GetAttribute<GroupAttribute>(var->attributes);
- auto* old_binding = GetAttribute<BindingAttribute>(var->attributes);
-
- ctx.Replace(old_group, new_group);
- ctx.Replace(old_binding, new_binding);
- bp = to;
- }
- // Add `DisableValidationAttribute`s if required
- if (remappings->allow_collisions) {
- auto* attribute = b.Disable(DisabledValidation::kBindingPointCollision);
- ctx.InsertBefore(var->attributes, *var->attributes.begin(), attribute);
- }
-
- // Replace any access controls.
- auto ac_it = remappings->access_controls.find(from);
- if (ac_it != remappings->access_controls.end()) {
- core::Access access = ac_it->second;
- if (access == core::Access::kUndefined) {
- b.Diagnostics().AddError(Source{})
- << "invalid access mode (" << static_cast<uint32_t>(access) << ")";
- return resolver::Resolve(b);
- }
- auto* sem = src.Sem().Get(var);
- if (sem->AddressSpace() != core::AddressSpace::kStorage) {
- b.Diagnostics().AddError(Source{})
- << "cannot apply access control to variable with address space "
- << sem->AddressSpace();
- return resolver::Resolve(b);
- }
- auto* ty = sem->Type()->UnwrapRef();
- auto inner_ty = CreateASTTypeFor(ctx, ty);
- auto* new_var =
- b.create<Var>(ctx.Clone(var->source), // source
- b.Ident(ctx.Clone(var->name->symbol)), // name
- inner_ty, // type
- ctx.Clone(var->declared_address_space), // address space
- b.Expr(access), // access
- ctx.Clone(var->initializer), // initializer
- ctx.Clone(var->attributes)); // attributes
- ctx.Replace(var, new_var);
- }
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper.h b/src/tint/lang/wgsl/ast/transform/binding_remapper.h
deleted file mode 100644
index 2378011..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
-
-#include <unordered_map>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/access.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// BindingPoint is an alias to BindingPoint
-using BindingPoint = BindingPoint;
-
-/// BindingRemapper is a transform used to remap resource binding points and
-/// access controls.
-class BindingRemapper final : public Castable<BindingRemapper, Transform> {
- public:
- /// BindingPoints is a map of old binding point to new binding point
- using BindingPoints = std::unordered_map<BindingPoint, BindingPoint>;
-
- /// AccessControls is a map of old binding point to new access control
- using AccessControls = std::unordered_map<BindingPoint, core::Access>;
-
- /// Remappings is consumed by the BindingRemapper transform.
- /// Data holds information about shader usage and constant buffer offsets.
- struct Remappings final : public Castable<Remappings, Data> {
- /// Constructor
- Remappings();
-
- /// Constructor
- /// @param bp a map of new binding points
- /// @param ac a map of new access controls
- /// @param may_collide If true, then validation will be disabled for
- /// binding point collisions generated by this transform
- Remappings(BindingPoints bp, AccessControls ac, bool may_collide = true);
-
- /// Copy constructor
- Remappings(const Remappings&);
-
- /// Destructor
- ~Remappings() override;
-
- /// A map of old binding point to new binding point
- BindingPoints binding_points;
-
- /// A map of old binding point to new access controls
- AccessControls access_controls;
-
- /// If true, then validation will be disabled for binding point collisions
- /// generated by this transform
- bool allow_collisions = false;
-
- /// Reflection for this class
- TINT_REFLECT(Remappings, binding_points, access_controls, allow_collisions);
- };
-
- /// Constructor
- BindingRemapper();
- ~BindingRemapper() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc b/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc
deleted file mode 100644
index 30697f0..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc
+++ /dev/null
@@ -1,419 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using BindingRemapperTest = TransformTest;
-
-TEST_F(BindingRemapperTest, ShouldRunEmptyRemappings) {
- auto* src = R"()";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{},
- /* allow collisions */ false);
-
- EXPECT_FALSE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunEmptyRemappingsWithCollisions) {
- auto* src = R"()";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{},
- /* allow collisions */ true);
-
- EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunBindingPointRemappings) {
- auto* src = R"()";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{
- {{2, 1}, {1, 2}},
- },
- BindingRemapper::AccessControls{});
-
- EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunAccessControlRemappings) {
- auto* src = R"()";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{
- {{2, 1}, core::Access::kWrite},
- });
-
- EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, NoRemappingsWithoutCollisions) {
- auto* src = R"(
-struct S {
- a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = src;
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{},
- /* allow_collisions */ false);
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, NoRemappingsWithCollisions) {
- auto* src = R"(
-struct S {
- a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(2) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{},
- /* allow_collisions */ true);
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapBindingPoints) {
- auto* src = R"(
-struct S {
- a : f32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(2) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{
- {{2, 1}, {1, 2}}, // Remap
- {{4, 5}, {6, 7}}, // Not found
- // Keep @group(3) @binding(2) as is
- },
- BindingRemapper::AccessControls{});
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapAccessControls) {
- auto* src = R"(
-struct S {
- a : f32,
-};
-
-@group(2) @binding(1) var<storage, read_write> a : S;
-
-@group(3) @binding(2) var<storage, read_write> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(2) @binding(1) var<storage, read_write> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read_write> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(4) @binding(3) var<storage, read> c : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{},
- BindingRemapper::AccessControls{
- {{2, 1}, core::Access::kReadWrite}, // Modify access control
- // Keep @group(3) @binding(2) as is
- {{4, 3}, core::Access::kRead}, // Add access control
- });
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapAll) {
- auto* src = R"(
-struct S {
- a : f32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(4) @binding(5) var<storage, read_write> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(6) @binding(7) var<storage, read_write> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{
- {{2, 1}, {4, 5}},
- {{3, 2}, {6, 7}},
- },
- BindingRemapper::AccessControls{
- {{2, 1}, core::Access::kReadWrite},
- {{3, 2}, core::Access::kReadWrite},
- });
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, BindingCollisionsSameEntryPoint) {
- auto* src = R"(
-struct S {
- i : i32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f() {
- let x : i32 = (((a.i + b.i) + c.i) + d.i);
-}
-)";
-
- auto* expect = R"(
-struct S {
- i : i32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> c : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f() {
- let x : i32 = (((a.i + b.i) + c.i) + d.i);
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{
- {{2, 1}, {1, 1}},
- {{3, 2}, {1, 1}},
- {{4, 3}, {5, 4}},
- },
- BindingRemapper::AccessControls{}, true);
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, BindingCollisionsDifferentEntryPoints) {
- auto* src = R"(
-struct S {
- i : i32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f1() {
- let x : i32 = (a.i + c.i);
-}
-
-@compute @workgroup_size(1)
-fn f2() {
- let x : i32 = (b.i + d.i);
-}
-)";
-
- auto* expect = R"(
-struct S {
- i : i32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> c : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f1() {
- let x : i32 = (a.i + c.i);
-}
-
-@compute @workgroup_size(1)
-fn f2() {
- let x : i32 = (b.i + d.i);
-}
-)";
-
- DataMap data;
- data.Add<BindingRemapper::Remappings>(
- BindingRemapper::BindingPoints{
- {{2, 1}, {1, 1}},
- {{3, 2}, {1, 1}},
- {{4, 3}, {5, 4}},
- },
- BindingRemapper::AccessControls{}, true);
- auto got = Run<BindingRemapper>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, NoData) {
- auto* src = R"(
-struct S {
- a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
- auto* expect = R"(error: missing transform data for tint::ast::transform::BindingRemapper)";
-
- auto got = Run<BindingRemapper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
deleted file mode 100644
index 1b57794..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
+++ /dev/null
@@ -1,1617 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <tuple>
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/type_expression.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types; // NOLINT
-using namespace tint::core::number_suffixes; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BuiltinPolyfill);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BuiltinPolyfill::Config);
-
-namespace tint::ast::transform {
-
-/// BinaryOpSignature is tuple of a binary op, LHS type and RHS type
-using BinaryOpSignature =
- std::tuple<core::BinaryOp, const core::type::Type*, const core::type::Type*>;
-
-/// PIMPL state for the transform
-struct BuiltinPolyfill::State {
- /// Constructor
- /// @param program the source program
- /// @param config the transform config
- State(const Program& program, const Config& config) : src(program), cfg(config) {}
-
- /// Runs the transform
- /// @returns the new program or SkipTransform if the transform is not required
- Transform::ApplyResult Run() {
- for (auto* node : src.ASTNodes().Objects()) {
- Switch(
- node, //
- [&](const CallExpression* expr) { Call(expr); },
- [&](const BinaryExpression* bin_op) {
- auto* s = src.Sem().Get(bin_op);
- if (!s || s->Stage() != core::EvaluationStage::kRuntime) {
- return; // Only polyfill runtime expressions
- }
- switch (bin_op->op) {
- case core::BinaryOp::kShiftLeft:
- case core::BinaryOp::kShiftRight: {
- if (cfg.builtins.bitshift_modulo) {
- ctx.Replace(bin_op,
- [this, bin_op] { return BitshiftModulo(bin_op); });
- made_changes = true;
- }
- break;
- }
- case core::BinaryOp::kDivide: {
- if (cfg.builtins.int_div_mod) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- if (lhs_ty->IsIntegerScalarOrVector()) {
- ctx.Replace(bin_op,
- [this, bin_op] { return IntDivMod(bin_op); });
- made_changes = true;
- }
- }
- break;
- }
- case core::BinaryOp::kModulo: {
- if (cfg.builtins.int_div_mod) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- if (lhs_ty->IsIntegerScalarOrVector()) {
- ctx.Replace(bin_op,
- [this, bin_op] { return IntDivMod(bin_op); });
- made_changes = true;
- }
- }
- if (cfg.builtins.precise_float_mod) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- if (lhs_ty->IsFloatScalarOrVector()) {
- ctx.Replace(bin_op,
- [this, bin_op] { return PreciseFloatMod(bin_op); });
- made_changes = true;
- }
- }
- break;
- }
- default:
- break;
- }
- },
- [&](const Expression* expr) {
- if (cfg.builtins.bgra8unorm) {
- if (auto* ty_expr = src.Sem().Get<sem::TypeExpression>(expr)) {
- if (auto* tex = ty_expr->Type()->As<core::type::StorageTexture>()) {
- if (tex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
- ctx.Replace(expr, [this, tex] {
- return ctx.dst->Expr(ctx.dst->ty.storage_texture(
- tex->Dim(), core::TexelFormat::kRgba8Unorm,
- tex->Access()));
- });
- made_changes = true;
- }
- }
- }
- }
- });
- }
-
- if (!made_changes) {
- return SkipTransform;
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- private:
- /// The source program
- const Program& src;
- /// The transform config
- const Config& cfg;
- /// The destination program builder
- ProgramBuilder b;
- /// The clone context
- program::CloneContext ctx{&b, &src};
- /// The source clone context
- const sem::Info& sem = src.Sem();
- /// Polyfill functions for binary operators.
- Hashmap<BinaryOpSignature, Symbol, 8> binary_op_polyfills;
- /// Polyfill builtins.
- Hashmap<const sem::BuiltinFn*, Symbol, 8> builtin_polyfills;
- /// Polyfill f32 conversion to i32 or u32 (or vectors of)
- Hashmap<const core::type::Type*, Symbol, 2> f32_conv_polyfills;
- /// True if the transform has made changes (i.e. the program needs cloning)
- bool made_changes = false;
-
- ////////////////////////////////////////////////////////////////////////////
- // Function polyfills
- ////////////////////////////////////////////////////////////////////////////
-
- /// Builds the polyfill function for the `acosh` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol acosh(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_acosh");
- uint32_t width = WidthOf(ty);
-
- auto V = [&](AFloat value) -> const Expression* {
- const Expression* expr = b.Expr(value);
- if (width == 1) {
- return expr;
- }
- return b.Call(T(ty), expr);
- };
-
- tint::Vector<const Statement*, 4> body;
- switch (cfg.builtins.acosh) {
- case Level::kFull:
- // return log(x + sqrt(x*x - 1));
- body.Push(b.Return(
- b.Call("log", b.Add("x", b.Call("sqrt", b.Sub(b.Mul("x", "x"), 1_a))))));
- break;
- case Level::kRangeCheck: {
- // return select(acosh(x), 0, x < 1);
- body.Push(b.Return(
- b.Call("select", b.Call("acosh", "x"), V(0.0_a), b.LessThan("x", V(1.0_a)))));
- break;
- }
- default:
- TINT_ICE() << "unhandled polyfill level: " << static_cast<int>(cfg.builtins.acosh);
- }
-
- b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `asinh` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol asinh(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_sinh");
-
- // return log(x + sqrt(x*x + 1));
- b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty),
- tint::Vector{
- b.Return(b.Call("log", b.Add("x", b.Call("sqrt", b.Add(b.Mul("x", "x"), 1_a))))),
- });
-
- return name;
- }
-
- /// Builds the polyfill function for the `atanh` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol atanh(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_atanh");
- uint32_t width = WidthOf(ty);
-
- auto V = [&](AFloat value) -> const Expression* {
- const Expression* expr = b.Expr(value);
- if (width == 1) {
- return expr;
- }
- return b.Call(T(ty), expr);
- };
-
- tint::Vector<const Statement*, 1> body;
- switch (cfg.builtins.atanh) {
- case Level::kFull:
- // return log((1+x) / (1-x)) * 0.5
- body.Push(
- b.Return(b.Mul(b.Call("log", b.Div(b.Add(1_a, "x"), b.Sub(1_a, "x"))), 0.5_a)));
- break;
- case Level::kRangeCheck:
- // return select(atanh(x), 0, x >= 1);
- body.Push(b.Return(b.Call("select", b.Call("atanh", "x"), V(0.0_a),
- b.GreaterThanEqual("x", V(1.0_a)))));
- break;
- default:
- TINT_ICE() << "unhandled polyfill level: " << static_cast<int>(cfg.builtins.acosh);
- }
-
- b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `clamp` builtin when called with integer arguments
- /// (scalar or vector)
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol clampInteger(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_clamp");
-
- b.Func(name,
- tint::Vector{
- b.Param("e", T(ty)),
- b.Param("low", T(ty)),
- b.Param("high", T(ty)),
- },
- T(ty),
- tint::Vector{
- // return min(max(e, low), high);
- b.Return(b.Call("min", b.Call("max", "e", "low"), "high")),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `countLeadingZeros` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol countLeadingZeros(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_count_leading_zeros");
- uint32_t width = WidthOf(ty);
-
- // Returns either u32 or vecN<u32>
- auto U = [&] {
- if (width == 1) {
- return b.ty.u32();
- }
- return b.ty.vec<u32>(width);
- };
- auto V = [&](uint32_t value) -> const Expression* {
- return ScalarOrVector(width, u32(value));
- };
- b.Func(
- name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty),
- tint::Vector{
- // var x = U(v);
- b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
- // let b16 = select(0, 16, x <= 0x0000ffff);
- b.Decl(b.Let("b16",
- b.Call("select", V(0), V(16), b.LessThanEqual("x", V(0x0000ffff))))),
- // x = x << b16;
- b.Assign("x", b.Shl("x", "b16")),
- // let b8 = select(0, 8, x <= 0x00ffffff);
- b.Decl(
- b.Let("b8", b.Call("select", V(0), V(8), b.LessThanEqual("x", V(0x00ffffff))))),
- // x = x << b8;
- b.Assign("x", b.Shl("x", "b8")),
- // let b4 = select(0, 4, x <= 0x0fffffff);
- b.Decl(
- b.Let("b4", b.Call("select", V(0), V(4), b.LessThanEqual("x", V(0x0fffffff))))),
- // x = x << b4;
- b.Assign("x", b.Shl("x", "b4")),
- // let b2 = select(0, 2, x <= 0x3fffffff);
- b.Decl(
- b.Let("b2", b.Call("select", V(0), V(2), b.LessThanEqual("x", V(0x3fffffff))))),
- // x = x << b2;
- b.Assign("x", b.Shl("x", "b2")),
- // let b1 = select(0, 1, x <= 0x7fffffff);
- b.Decl(
- b.Let("b1", b.Call("select", V(0), V(1), b.LessThanEqual("x", V(0x7fffffff))))),
- // let is_zero = select(0, 1, x == 0);
- b.Decl(b.Let("is_zero", b.Call("select", V(0), V(1), b.Equal("x", V(0))))),
- // return R((b16 | b8 | b4 | b2 | b1) + zero);
- b.Return(b.Call(T(ty), b.Add(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"),
- "is_zero"))),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `countTrailingZeros` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol countTrailingZeros(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_count_trailing_zeros");
- uint32_t width = WidthOf(ty);
-
- // Returns either u32 or vecN<u32>
- auto U = [&] {
- if (width == 1) {
- return b.ty.u32();
- }
- return b.ty.vec<u32>(width);
- };
- auto V = [&](uint32_t value) -> const Expression* {
- return ScalarOrVector(width, u32(value));
- };
- auto B = [&](const Expression* value) -> const Expression* {
- if (width == 1) {
- return b.Call<bool>(value);
- }
- return b.Call(b.ty.vec<bool>(width), value);
- };
- b.Func(
- name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty),
- tint::Vector{
- // var x = U(v);
- b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
- // let b16 = select(16, 0, bool(x & 0x0000ffff));
- b.Decl(b.Let("b16", b.Call("select", V(16), V(0), B(b.And("x", V(0x0000ffff)))))),
- // x = x >> b16;
- b.Assign("x", b.Shr("x", "b16")),
- // let b8 = select(8, 0, bool(x & 0x000000ff));
- b.Decl(b.Let("b8", b.Call("select", V(8), V(0), B(b.And("x", V(0x000000ff)))))),
- // x = x >> b8;
- b.Assign("x", b.Shr("x", "b8")),
- // let b4 = select(4, 0, bool(x & 0x0000000f));
- b.Decl(b.Let("b4", b.Call("select", V(4), V(0), B(b.And("x", V(0x0000000f)))))),
- // x = x >> b4;
- b.Assign("x", b.Shr("x", "b4")),
- // let b2 = select(2, 0, bool(x & 0x00000003));
- b.Decl(b.Let("b2", b.Call("select", V(2), V(0), B(b.And("x", V(0x00000003)))))),
- // x = x >> b2;
- b.Assign("x", b.Shr("x", "b2")),
- // let b1 = select(1, 0, bool(x & 0x00000001));
- b.Decl(b.Let("b1", b.Call("select", V(1), V(0), B(b.And("x", V(0x00000001)))))),
- // let is_zero = select(0, 1, x == 0);
- b.Decl(b.Let("is_zero", b.Call("select", V(0), V(1), b.Equal("x", V(0))))),
- // return R((b16 | b8 | b4 | b2 | b1) + zero);
- b.Return(b.Call(T(ty), b.Add(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"),
- "is_zero"))),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `extractBits` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol extractBits(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_extract_bits");
- uint32_t width = WidthOf(ty);
-
- constexpr uint32_t W = 32u; // 32-bit
-
- auto vecN_u32 = [&](const Expression* value) -> const Expression* {
- if (width == 1) {
- return value;
- }
- return b.Call(b.ty.vec<u32>(width), value);
- };
-
- tint::Vector<const Statement*, 8> body{
- b.Decl(b.Let("s", b.Call("min", "offset", u32(W)))),
- b.Decl(b.Let("e", b.Call("min", u32(W), b.Add("s", "count")))),
- };
-
- switch (cfg.builtins.extract_bits) {
- case Level::kFull:
- body.Push(b.Decl(b.Let("shl", b.Sub(u32(W), "e"))));
- body.Push(b.Decl(b.Let("shr", b.Add("shl", "s"))));
- // Here we don't want the shl and shr modulos the rhs, so handle the `rhs >= 32u`
- // cases using `select`. In order to handle the signed shr `lhs >> rhs` corrently,
- // use `(lhs >> 31u) >> 1u` if `rhs >= 32u`.
- body.Push(b.Decl(b.Let("shl_result", b.Call("select", b.Call(T(ty)),
- b.Shl("v", vecN_u32(b.Expr("shl"))),
- b.LessThan("shl", 32_u)))));
- body.Push(b.Return(b.Call(
- "select",
- b.Shr(b.Shr("shl_result", vecN_u32(b.Expr(31_u))), vecN_u32(b.Expr(1_u))),
- b.Shr("shl_result", vecN_u32(b.Expr("shr"))), b.LessThan("shr", 32_u))
-
- ));
- break;
- case Level::kClampParameters:
- body.Push(b.Return(b.Call("extractBits", "v", "s", b.Sub("e", "s"))));
- break;
- default:
- TINT_ICE() << "unhandled polyfill level: "
- << static_cast<int>(cfg.builtins.extract_bits);
- }
-
- b.Func(name,
- tint::Vector{
- b.Param("v", T(ty)),
- b.Param("offset", b.ty.u32()),
- b.Param("count", b.ty.u32()),
- },
- T(ty), std::move(body));
-
- return name;
- }
-
- /// Builds the polyfill function for the `firstLeadingBit` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol firstLeadingBit(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_first_leading_bit");
- uint32_t width = WidthOf(ty);
-
- // Returns either u32 or vecN<u32>
- auto U = [&] {
- if (width == 1) {
- return b.ty.u32();
- }
- return b.ty.vec<u32>(width);
- };
- auto V = [&](uint32_t value) -> const Expression* {
- return ScalarOrVector(width, u32(value));
- };
- auto B = [&](const Expression* value) -> const Expression* {
- if (width == 1) {
- return b.Call<bool>(value);
- }
- return b.Call(b.ty.vec<bool>(width), value);
- };
-
- const Expression* x = nullptr;
- if (ty->IsUnsignedIntegerScalarOrVector()) {
- x = b.Expr("v");
- } else {
- // If ty is signed, then the value is inverted if the sign is negative
- x = b.Call("select", //
- b.Call(U(), "v"), //
- b.Call(U(), b.Complement("v")), //
- b.LessThan("v", ScalarOrVector(width, 0_i)));
- }
-
- b.Func(
- name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty),
- tint::Vector{
- // var x = v; (unsigned)
- // var x = select(U(v), ~U(v), v < 0); (signed)
- b.Decl(b.Var("x", x)),
- // let b16 = select(0, 16, bool(x & 0xffff0000));
- b.Decl(b.Let("b16", b.Call("select", V(0), V(16), B(b.And("x", V(0xffff0000)))))),
- // x = x >> b16;
- b.Assign("x", b.Shr("x", "b16")),
- // let b8 = select(0, 8, bool(x & 0x0000ff00));
- b.Decl(b.Let("b8", b.Call("select", V(0), V(8), B(b.And("x", V(0x0000ff00)))))),
- // x = x >> b8;
- b.Assign("x", b.Shr("x", "b8")),
- // let b4 = select(0, 4, bool(x & 0x000000f0));
- b.Decl(b.Let("b4", b.Call("select", V(0), V(4), B(b.And("x", V(0x000000f0)))))),
- // x = x >> b4;
- b.Assign("x", b.Shr("x", "b4")),
- // let b2 = select(0, 2, bool(x & 0x0000000c));
- b.Decl(b.Let("b2", b.Call("select", V(0), V(2), B(b.And("x", V(0x0000000c)))))),
- // x = x >> b2;
- b.Assign("x", b.Shr("x", "b2")),
- // let b1 = select(0, 1, bool(x & 0x00000002));
- b.Decl(b.Let("b1", b.Call("select", V(0), V(1), B(b.And("x", V(0x00000002)))))),
- // let is_zero = select(0, 0xffffffff, x == 0);
- b.Decl(b.Let("is_zero", b.Call("select", V(0), V(0xffffffff), b.Equal("x", V(0))))),
- // return R(b16 | b8 | b4 | b2 | b1 | zero);
- b.Return(b.Call(
- T(ty), b.Or(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"), "is_zero"))),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `firstTrailingBit` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol firstTrailingBit(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_first_trailing_bit");
- uint32_t width = WidthOf(ty);
-
- // Returns either u32 or vecN<u32>
- auto U = [&] {
- if (width == 1) {
- return b.ty.u32();
- }
- return b.ty.vec<u32>(width);
- };
- auto V = [&](uint32_t value) -> const Expression* {
- return ScalarOrVector(width, u32(value));
- };
- auto B = [&](const Expression* value) -> const Expression* {
- if (width == 1) {
- return b.Call<bool>(value);
- }
- return b.Call(b.ty.vec<bool>(width), value);
- };
- b.Func(
- name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty),
- tint::Vector{
- // var x = U(v);
- b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
- // let b16 = select(16, 0, bool(x & 0x0000ffff));
- b.Decl(b.Let("b16", b.Call("select", V(16), V(0), B(b.And("x", V(0x0000ffff)))))),
- // x = x >> b16;
- b.Assign("x", b.Shr("x", "b16")),
- // let b8 = select(8, 0, bool(x & 0x000000ff));
- b.Decl(b.Let("b8", b.Call("select", V(8), V(0), B(b.And("x", V(0x000000ff)))))),
- // x = x >> b8;
- b.Assign("x", b.Shr("x", "b8")),
- // let b4 = select(4, 0, bool(x & 0x0000000f));
- b.Decl(b.Let("b4", b.Call("select", V(4), V(0), B(b.And("x", V(0x0000000f)))))),
- // x = x >> b4;
- b.Assign("x", b.Shr("x", "b4")),
- // let b2 = select(2, 0, bool(x & 0x00000003));
- b.Decl(b.Let("b2", b.Call("select", V(2), V(0), B(b.And("x", V(0x00000003)))))),
- // x = x >> b2;
- b.Assign("x", b.Shr("x", "b2")),
- // let b1 = select(1, 0, bool(x & 0x00000001));
- b.Decl(b.Let("b1", b.Call("select", V(1), V(0), B(b.And("x", V(0x00000001)))))),
- // let is_zero = select(0, 0xffffffff, x == 0);
- b.Decl(b.Let("is_zero", b.Call("select", V(0), V(0xffffffff), b.Equal("x", V(0))))),
- // return R(b16 | b8 | b4 | b2 | b1 | is_zero);
- b.Return(b.Call(
- T(ty), b.Or(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"), "is_zero"))),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `fwidthFine` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol fwidthFine(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_fwidth_fine");
- // WGSL polyfill function:
- // fn tint_fwidth_fine(v : T) -> T {
- // return abs(dpdxFine(v)) + abs(dpdyFine(v));
- // }
- auto body = tint::Vector{
- b.Return(b.Add(b.Call("abs", b.Call("dpdxFine", "v")),
- b.Call("abs", b.Call("dpdyFine", "v")))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty), body);
- return name;
- }
-
- /// Builds the polyfill function for the `insertBits` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol insertBits(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_insert_bits");
- uint32_t width = WidthOf(ty);
-
- // Currently in WGSL parameters of insertBits must be i32, u32, vecN<i32> or vecN<u32>
- if (DAWN_UNLIKELY(((!ty->DeepestElement()->IsAnyOf<core::type::I32, core::type::U32>())))) {
- TINT_ICE()
- << "insertBits polyfill only support i32, u32, and vector of i32 or u32, got "
- << ty->FriendlyName();
- }
-
- constexpr uint32_t W = 32u; // 32-bit
-
- auto V = [&](auto value) -> const Expression* {
- const Expression* expr = b.Expr(value);
- if (!ty->IsUnsignedIntegerScalarOrVector()) {
- expr = b.Call<i32>(expr);
- }
- if (ty->Is<core::type::Vector>()) {
- expr = b.Call(T(ty), expr);
- }
- return expr;
- };
- auto U = [&](auto value) -> const Expression* {
- if (width == 1) {
- return b.Expr(value);
- }
- return b.vec(b.ty.u32(), width, value);
- };
-
- // Polyfill algorithm:
- // s = min(offset, 32u);
- // e = min(32u, (s + count));
- // mask = (((1u << s) - 1u) ^ ((1u << e) - 1u));
- // return (((n << s) & mask) | (v & ~(mask)));
- // Note that the algorithm above use the left-shifting in C++ manner, but in WGSL, HLSL, MSL
- // the rhs are modulo to bit-width of lhs (that is 32u in this case), and in GLSL the result
- // is undefined if rhs is greater than or equal to bit-width of lhs. The results of `x << y`
- // in C++ and HLSL are different when `y >= 32u`, and the `s` and `e` defined above can be
- // 32u, which are cases we must handle specially. Replace all `(x << y)` to
- // `select(Tx(), x << y, y < 32u)`, in which `Tx` is the type of x, where y can be greater
- // than or equal to 32u.
- // WGSL polyfill function:
- // fn tint_insert_bits(v : T, n : T, offset : u32, count : u32) -> T {
- // let e = offset + count;
- // let mask = (
- // (select(0u, 1u << offset, offset < 32u) - 1u) ^
- // (select(0u, 1u << e, e < 32u) - 1u)
- // );
- // return ((select(T(), n << offset, offset < 32u) & mask) | (v & ~(mask)));
- // }
-
- tint::Vector<const Statement*, 8> body;
-
- switch (cfg.builtins.insert_bits) {
- case Level::kFull:
- // let e = offset + count;
- body.Push(b.Decl(b.Let("e", b.Add("offset", "count"))));
-
- // let mask = (
- // (select(0u, 1u << offset, offset < 32u) - 1u) ^
- // (select(0u, 1u << e, e < 32u) - 1u)
- // );
- body.Push(b.Decl(b.Let(
- "mask",
- b.Xor( //
- b.Sub(
- b.Call("select", 0_u, b.Shl(1_u, "offset"), b.LessThan("offset", 32_u)),
- 1_u),
- b.Sub(b.Call("select", 0_u, b.Shl(1_u, "e"), b.LessThan("e", 32_u)),
- 1_u) //
- ))));
-
- // return ((select(T(), n << offset, offset < 32u) & mask) | (v & ~(mask)));
- body.Push(
- b.Return(b.Or(b.And(b.Call("select", b.Call(T(ty)), b.Shl("n", U("offset")),
- b.LessThan("offset", 32_u)),
- V("mask")),
- b.And("v", V(b.Complement("mask"))))));
-
- break;
- case Level::kClampParameters:
- body.Push(b.Decl(b.Let("s", b.Call("min", "offset", u32(W)))));
- body.Push(b.Decl(b.Let("e", b.Call("min", u32(W), b.Add("s", "count")))));
- body.Push(b.Return(b.Call("insertBits", "v", "n", "s", b.Sub("e", "s"))));
- break;
- default:
- TINT_ICE() << "unhandled polyfill level: "
- << static_cast<int>(cfg.builtins.insert_bits);
- }
-
- b.Func(name,
- tint::Vector{
- b.Param("v", T(ty)),
- b.Param("n", T(ty)),
- b.Param("offset", b.ty.u32()),
- b.Param("count", b.ty.u32()),
- },
- T(ty), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `reflect` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol reflect(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_reflect");
-
- // WGSL polyfill function:
- // fn tint_reflect(e1 : T, e2 : T) -> T {
- // let factor = (-2.0 * dot(e1, e2));
- // return (e1 + (factor * e2));
- // }
- // Using -2.0 instead of 2.0 in factor to prevent the optimization that cause wrong result.
- // See https://crbug.com/tint/1798 for more details.
- auto body = tint::Vector{
- b.Decl(b.Let("factor", b.Mul(-2.0_a, b.Call("dot", "e1", "e2")))),
- b.Return(b.Add("e1", b.Mul("factor", "e2"))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("e1", T(ty)),
- b.Param("e2", T(ty)),
- },
- T(ty), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `saturate` builtin
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol saturate(const core::type::Type* ty) {
- auto name = b.Symbols().New("tint_saturate");
- auto body = tint::Vector{
- b.Return(b.Call("clamp", "v", b.Call(T(ty), 0_a), b.Call(T(ty), 1_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `sign` builtin when the element type is integer
- /// @param ty the parameter and return type for the function
- /// @return the polyfill function name
- Symbol sign_int(const core::type::Type* ty) {
- const uint32_t width = WidthOf(ty);
- auto zero = [&] { return ScalarOrVector(width, 0_a); };
-
- // pos_or_neg_one = (v > 0) ? 1 : -1
- auto pos_or_neg_one = b.Call("select", //
- ScalarOrVector(width, -1_a), //
- ScalarOrVector(width, 1_a), //
- b.GreaterThan("v", zero()));
-
- auto name = b.Symbols().New("tint_sign");
- b.Func(name,
- tint::Vector{
- b.Param("v", T(ty)),
- },
- T(ty),
- tint::Vector{
- b.Return(b.Call("select", pos_or_neg_one, zero(), b.Equal("v", zero()))),
- });
-
- return name;
- }
-
- /// Builds the polyfill function for the `textureSampleBaseClampToEdge` builtin, when the
- /// texture type is texture_2d<f32>.
- /// @return the polyfill function name
- Symbol textureSampleBaseClampToEdge_2d_f32() {
- auto name = b.Symbols().New("tint_textureSampleBaseClampToEdge");
- auto body = tint::Vector{
- b.Decl(b.Let("dims", b.Call(b.ty.vec2<f32>(), b.Call("textureDimensions", "t", 0_a)))),
- b.Decl(b.Let("half_texel", b.Div(b.Call<vec2<f32>>(0.5_a), "dims"))),
- b.Decl(
- b.Let("clamped", b.Call("clamp", "coord", "half_texel", b.Sub(1_a, "half_texel")))),
- b.Return(b.Call("textureSampleLevel", "t", "s", "clamped", 0_a)),
- };
- b.Func(
- name,
- tint::Vector{
- b.Param("t", b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
- b.Param("s", b.ty.sampler(core::type::SamplerKind::kSampler)),
- b.Param("coord", b.ty.vec2<f32>()),
- },
- b.ty.vec4<f32>(), body);
- return name;
- }
-
- /// Builds the polyfill function for the `quantizeToF16` builtin, by replacing the vector form
- /// with scalar calls.
- /// @param vec the vector type
- /// @return the polyfill function name
- Symbol quantizeToF16(const core::type::Vector* vec) {
- auto name = b.Symbols().New("tint_quantizeToF16");
- tint::Vector<const Expression*, 4> args;
- for (uint32_t i = 0; i < vec->Width(); i++) {
- args.Push(b.Call("quantizeToF16", b.IndexAccessor("v", u32(i))));
- }
- b.Func(name,
- tint::Vector{
- b.Param("v", T(vec)),
- },
- T(vec),
- tint::Vector{
- b.Return(b.Call(T(vec), std::move(args))),
- });
- return name;
- }
-
- /// Builds the polyfill function for the `workgroupUniformLoad` builtin.
- /// @param type the type being loaded
- /// @return the polyfill function name
- Symbol workgroupUniformLoad(const core::type::Type* type) {
- auto name = b.Symbols().New("tint_workgroupUniformLoad");
- if (auto* a = type->As<core::type::Atomic>()) {
- b.Func(name,
- tint::Vector{
- b.Param("p", b.ty.ptr<workgroup>(T(type))),
- },
- T(a->Type()),
- tint::Vector{
- b.CallStmt(b.Call("workgroupBarrier")),
- b.Decl(b.Let("result", b.Call("atomicLoad", b.Expr("p")))),
- b.CallStmt(b.Call("workgroupBarrier")),
- b.Return("result"),
- });
- return name;
- }
- b.Func(name,
- tint::Vector{
- b.Param("p", b.ty.ptr<workgroup>(T(type))),
- },
- T(type),
- tint::Vector{
- b.CallStmt(b.Call("workgroupBarrier")),
- b.Decl(b.Let("result", b.Deref("p"))),
- b.CallStmt(b.Call("workgroupBarrier")),
- b.Return("result"),
- });
- return name;
- }
-
- /// Builds the polyfill function to value convert a scalar or vector of f32 to an i32 or u32 (or
- /// vector of).
- /// @param source the type of the value being converted
- /// @param target the target conversion type
- /// @return the polyfill function name
- Symbol ConvF32ToIU32(const core::type::Type* source, const core::type::Type* target) {
- struct Limits {
- AFloat low_condition;
- AInt low_limit;
- AFloat high_condition;
- AInt high_limit;
- };
- const bool is_signed = target->IsSignedIntegerScalarOrVector();
- const uint32_t largest_signed_integer_float = 0x7fffff80;
- const uint32_t largest_unsigned_integer_float = 0xffffff00;
- const Limits limits =
- is_signed ? Limits{
- /* low_condition */ -AFloat(0x80000000),
- /* low_limit */ -AInt(0x80000000),
- /* high_condition */ AFloat(largest_signed_integer_float),
- /* high_limit */ AInt(0x7fffffff),
- }
- : Limits{
- /* low_condition */ AFloat(0),
- /* low_limit */ AInt(0),
- /* high_condition */ AFloat(largest_unsigned_integer_float),
- /* high_limit */ AInt(0xffffffff),
- };
-
- const uint32_t width = WidthOf(target);
-
- // select(target(v), low_limit, v < low_condition)
- auto* select_low = b.Call(wgsl::BuiltinFn::kSelect, //
- b.Call(T(target), "v"), //
- ScalarOrVector(width, limits.low_limit), //
- b.LessThan("v", ScalarOrVector(width, limits.low_condition)));
-
- // select(high_limit, select_low, v <= high_condition)
- // The equality test in the 'LessThanEqual' is used to ensure that the largest integer float
- // will be converted to an integer.
- auto* select_high =
- b.Call(wgsl::BuiltinFn::kSelect, //
- ScalarOrVector(width, limits.high_limit), //
- select_low, //
- b.LessThanEqual("v", ScalarOrVector(width, limits.high_condition)));
-
- auto name = b.Symbols().New(is_signed ? "tint_ftoi" : "tint_ftou");
- b.Func(name, tint::Vector{b.Param("v", T(source))}, T(target),
- tint::Vector{b.Return(select_high)});
- return name;
- }
-
- /// Builds the polyfill function for the `dot4I8Packed` builtin
- /// @return the polyfill function name
- Symbol Dot4I8Packed() {
- using vec4i = vec4<i32>;
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_dot4_i8_packed");
-
- auto body = tint::Vector{
- // const n = vec4u(24, 16, 8, 0);
- // let a_i8 = bitcast<vec4i>(vec4u(a) << n) >> vec4u(24);
- // let b_i8 = bitcast<vec4i>(vec4u(b) << n) >> vec4u(24);
- // return dot(a_i8, b_i8);
- b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
- b.Decl(b.Let("a_i8", b.Shr(b.Bitcast<vec4i>(b.Shl(b.Call<vec4u>("a"), "n")),
- b.Call<vec4u>(24_a)))),
- b.Decl(b.Let("b_i8", b.Shr(b.Bitcast<vec4i>(b.Shl(b.Call<vec4u>("b"), "n")),
- b.Call<vec4u>(24_a)))),
- b.Return(b.Call("dot", "a_i8", "b_i8")),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.u32()),
- b.Param("b", b.ty.u32()),
- },
- b.ty.i32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `dot4U8Packed` builtin
- /// @return the polyfill function name
- Symbol Dot4U8Packed() {
- using vec4u = vec4<u32>;
- auto name = b.Symbols().New("tint_dot4_u8_packed");
-
- auto body = tint::Vector{
- // const n = vec4u(24, 16, 8, 0);
- // let a_u8 = (vec4u(a) >> n) & vec4u(0xff);
- // let b_u8 = (vec4u(b) >> n) & vec4u(0xff);
- // return dot(a_u8, b_u8);
- b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
- b.Decl(b.Let("a_u8", b.And(b.Shr(b.Call<vec4u>("a"), "n"), b.Call<vec4u>(0xff_a)))),
- b.Decl(b.Let("b_u8", b.And(b.Shr(b.Call<vec4u>("b"), "n"), b.Call<vec4u>(0xff_a)))),
- b.Return(b.Call("dot", "a_u8", "b_u8")),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.u32()),
- b.Param("b", b.ty.u32()),
- },
- b.ty.u32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `pack4xI8` builtin
- /// @return the polyfill function name
- Symbol Pack4xI8() {
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_pack_4xi8");
-
- auto body = tint::Vector{
- // const n = vec4u(0, 8, 16, 24);
- // let a_u32 = bitcast<vec4u>(a);
- // let a_u8 = (a_u32 & vec4u(0xff)) << n;
- // return dot(a_u8, vec4u(1));
- b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
- b.Decl(b.Let("a_u32", b.Bitcast<vec4u>("a"))),
- b.Decl(b.Let("a_u8", b.Shl(b.And("a_u32", b.Call<vec4u>(0xff_a)), "n"))),
- b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.vec4<i32>()),
- },
- b.ty.u32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `pack4xU8` builtin
- /// @return the polyfill function name
- Symbol Pack4xU8() {
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_pack_4xu8");
-
- auto body = tint::Vector{
- // const n = vec4u(0, 8, 16, 24);
- // let a_u8 = (a & vec4u(0xff)) << n;
- // return dot(a_u8, vec4u(1));
- b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
- b.Decl(b.Let("a_u8", b.Shl(b.And("a", b.Call<vec4u>(0xff_a)), "n"))),
- b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.vec4<u32>()),
- },
- b.ty.u32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `pack4xI8Clamp` builtin
- /// @return the polyfill function name
- Symbol Pack4xI8Clamp() {
- using vec4i = vec4<i32>;
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_pack_4xi8_clamp");
-
- auto body = tint::Vector{
- // const n = vec4u(0, 8, 16, 24);
- // let a_clamp = clamp(a, vec4i(-128), vec4i(127));
- // let a_u32 = bitcast<vec4u>(a_clamp);
- // let a_u8 = (a_u32 & vec4u(0xff)) << n;
- // return dot(a_u8, vec4u(1));
- b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
- b.Decl(b.Let("a_clamp",
- b.Call("clamp", "a", b.Call<vec4i>(-128_a), b.Call<vec4i>(127_a)))),
- b.Decl(b.Let("a_u32", b.Bitcast<vec4u>("a_clamp"))),
- b.Decl(b.Let("a_u8", b.Shl(b.And("a_u32", b.Call<vec4u>(0xff_a)), "n"))),
- b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.vec4<i32>()),
- },
- b.ty.u32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `pack4xU8Clamp` builtin
- /// @return the polyfill function name
- Symbol Pack4xU8Clamp() {
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_pack_4xu8_clamp");
-
- auto body = tint::Vector{
- // const n = vec4u(0, 8, 16, 24);
- // let a_clamp = clamp(a, vec4u(0), vec4u(255));
- // let a_u8 = a_clamp << n;
- // return dot(a_u8, vec4u(1));
- b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
- b.Decl(
- b.Let("a_clamp", b.Call("clamp", "a", b.Call<vec4u>(0_a), b.Call<vec4u>(255_a)))),
- b.Decl(b.Let("a_u8", b.Call<vec4u>(b.Shl("a_clamp", "n")))),
- b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.vec4<u32>()),
- },
- b.ty.u32(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `unpack4xI8` builtin
- /// @return the polyfill function name
- Symbol Unpack4xI8() {
- using vec4i = vec4<i32>;
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_unpack_4xi8");
-
- auto body = tint::Vector{
- // const n = vec4u(24, 16, 8, 0);
- // let a_vec4u = vec4u(a);
- // let a_vec4i = bitcast<vec4i>(a_vec4u << n);
- // return a_vec4i >> vec4u(24);
- b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
- b.Decl(b.Let("a_vec4u", b.Call<vec4u>("a"))),
- b.Decl(b.Let("a_vec4i", b.Bitcast<vec4i>(b.Shl("a_vec4u", "n")))),
- b.Return(b.Shr("a_vec4i", b.Call<vec4u>(24_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.u32()),
- },
- b.ty.vec4<i32>(), body);
-
- return name;
- }
-
- /// Builds the polyfill function for the `unpack4xU8` builtin
- /// @return the polyfill function name
- Symbol Unpack4xU8() {
- using vec4u = vec4<u32>;
-
- auto name = b.Symbols().New("tint_unpack_4xu8");
-
- auto body = tint::Vector{
- // const n = vec4u(0, 8, 16, 24);
- // const a_vec4u = vec4u(a) >> n;
- // return a_vec4u & vec4u(0xff);
- b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
- b.Decl(b.Let("a_vec4u", b.Shr(b.Call<vec4u>("a"), "n"))),
- b.Return(b.And("a_vec4u", b.Call<vec4u>(0xff_a))),
- };
- b.Func(name,
- tint::Vector{
- b.Param("a", b.ty.u32()),
- },
- b.ty.vec4<u32>(), body);
-
- return name;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Inline polyfills
- ////////////////////////////////////////////////////////////////////////////
-
- /// Builds the polyfill inline expression for a bitshift left or bitshift right, ensuring that
- /// the RHS is modulo the bit-width of the LHS.
- /// @param bin_op the original BinaryExpression
- /// @return the polyfill value for bitshift operation
- const Expression* BitshiftModulo(const BinaryExpression* bin_op) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
- auto* lhs_el_ty = lhs_ty->DeepestElement();
- const Expression* mask = b.Expr(AInt(lhs_el_ty->Size() * 8 - 1));
- if (rhs_ty->Is<core::type::Vector>()) {
- mask = b.Call(CreateASTTypeFor(ctx, rhs_ty), mask);
- }
- auto* lhs = ctx.Clone(bin_op->lhs);
- auto* rhs = b.And(ctx.Clone(bin_op->rhs), mask);
- return b.create<BinaryExpression>(ctx.Clone(bin_op->source), bin_op->op, lhs, rhs);
- }
-
- /// Builds the polyfill inline expression for a integer divide or modulo, preventing DBZs and
- /// integer overflows.
- /// @param bin_op the original BinaryExpression
- /// @return the polyfill divide or modulo
- const Expression* IntDivMod(const BinaryExpression* bin_op) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
- BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
- auto fn = binary_op_polyfills.GetOrAdd(sig, [&] {
- const bool is_div = bin_op->op == core::BinaryOp::kDivide;
-
- const auto [lhs_el_ty, lhs_width] = lhs_ty->Elements(lhs_ty, 1);
- const auto [rhs_el_ty, rhs_width] = rhs_ty->Elements(rhs_ty, 1);
-
- const uint32_t width = std::max(lhs_width, rhs_width);
-
- const char* lhs = "lhs";
- const char* rhs = "rhs";
-
- tint::Vector<const Statement*, 4> body;
-
- if (lhs_width < width) {
- // lhs is scalar, rhs is vector. Convert lhs to vector.
- body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
- lhs = "l";
- }
- if (rhs_width < width) {
- // lhs is vector, rhs is scalar. Convert rhs to vector.
- body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
- rhs = "r";
- }
-
- auto name = b.Symbols().New(is_div ? "tint_div" : "tint_mod");
-
- auto* rhs_is_zero = b.Equal(rhs, ScalarOrVector(width, 0_a));
-
- if (lhs_ty->IsSignedIntegerScalarOrVector()) {
- const auto bits = lhs_el_ty->Size() * 8;
- auto min_int = AInt(AInt::kLowestValue >> (AInt::kNumBits - bits));
- const Expression* lhs_is_min = b.Equal(lhs, ScalarOrVector(width, min_int));
- const Expression* rhs_is_minus_one = b.Equal(rhs, ScalarOrVector(width, -1_a));
- // use_one = rhs_is_zero | ((lhs == MIN_INT) & (rhs == -1))
- auto* use_one = b.Or(rhs_is_zero, b.And(lhs_is_min, rhs_is_minus_one));
-
- // Special handling for mod in case either operand is negative, as negative operands
- // for % is undefined behaviour for most backends (HLSL, MSL, GLSL, SPIR-V).
- if (!is_div) {
- const char* rhs_or_one = "rhs_or_one";
- body.Push(b.Decl(b.Let(
- rhs_or_one, b.Call("select", rhs, ScalarOrVector(width, 1_a), use_one))));
-
- // Is either operand negative?
- // (lhs | rhs) & (1<<31)
- auto sign_bit_mask = ScalarOrVector(width, u32(1 << (bits - 1)));
- auto* lhs_or_rhs = CastScalarOrVector<u32>(width, b.Or(lhs, rhs_or_one));
- auto* lhs_or_rhs_is_neg =
- b.NotEqual(b.And(lhs_or_rhs, sign_bit_mask), ScalarOrVector(width, 0_u));
-
- // lhs - trunc(lhs / rhs) * rhs (note: integral division truncates)
- auto* slow_mod = b.Sub(lhs, b.Mul(b.Div(lhs, rhs_or_one), rhs_or_one));
-
- // lhs % rhs
- auto* fast_mod = b.Mod(lhs, rhs_or_one);
-
- auto* use_slow = b.Call("any", lhs_or_rhs_is_neg);
-
- body.Push(b.If(use_slow, b.Block(b.Return(slow_mod)),
- b.Else(b.Block(b.Return(fast_mod)))));
-
- } else {
- auto* rhs_or_one = b.Call("select", rhs, ScalarOrVector(width, 1_a), use_one);
- body.Push(b.Return(is_div ? b.Div(lhs, rhs_or_one) : b.Mod(lhs, rhs_or_one)));
- }
-
- } else {
- auto* rhs_or_one = b.Call("select", rhs, ScalarOrVector(width, 1_a), rhs_is_zero);
- body.Push(b.Return(is_div ? b.Div(lhs, rhs_or_one) : b.Mod(lhs, rhs_or_one)));
- }
-
- b.Func(name,
- tint::Vector{
- b.Param("lhs", T(lhs_ty)),
- b.Param("rhs", T(rhs_ty)),
- },
- width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width), // return type
- std::move(body));
-
- return name;
- });
- auto* lhs = ctx.Clone(bin_op->lhs);
- auto* rhs = ctx.Clone(bin_op->rhs);
- return b.Call(fn, lhs, rhs);
- }
-
- /// Builds the polyfill inline expression for a precise float modulo, as defined in the spec.
- /// @param bin_op the original BinaryExpression
- /// @return the polyfill divide or modulo
- const Expression* PreciseFloatMod(const BinaryExpression* bin_op) {
- auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
- auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
- BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
- auto fn = binary_op_polyfills.GetOrAdd(sig, [&] {
- const auto [lhs_el_ty, lhs_width] = lhs_ty->Elements(lhs_ty, 1);
- const auto [rhs_el_ty, rhs_width] = rhs_ty->Elements(rhs_ty, 1);
-
- const uint32_t width = std::max(lhs_width, rhs_width);
-
- const char* lhs = "lhs";
- const char* rhs = "rhs";
-
- tint::Vector<const Statement*, 4> body;
-
- if (lhs_width < width) {
- // lhs is scalar, rhs is vector. Convert lhs to vector.
- body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
- lhs = "l";
- }
- if (rhs_width < width) {
- // lhs is vector, rhs is scalar. Convert rhs to vector.
- body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
- rhs = "r";
- }
-
- auto name = b.Symbols().New("tint_float_mod");
-
- // lhs - trunc(lhs / rhs) * rhs
- auto* precise_mod = b.Sub(lhs, b.Mul(b.Call("trunc", b.Div(lhs, rhs)), rhs));
- body.Push(b.Return(precise_mod));
-
- b.Func(name,
- tint::Vector{
- b.Param("lhs", T(lhs_ty)),
- b.Param("rhs", T(rhs_ty)),
- },
- width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width), // return type
- std::move(body));
-
- return name;
- });
- auto* lhs = ctx.Clone(bin_op->lhs);
- auto* rhs = ctx.Clone(bin_op->rhs);
- return b.Call(fn, lhs, rhs);
- }
-
- /// @returns the AST type for the given sem type
- Type T(const core::type::Type* ty) { return CreateASTTypeFor(ctx, ty); }
-
- /// @returns 1 if `ty` is not a vector, otherwise the vector width
- uint32_t WidthOf(const core::type::Type* ty) const {
- if (auto* v = ty->As<core::type::Vector>()) {
- return v->Width();
- }
- return 1;
- }
-
- /// @returns a scalar or vector with the given width, with each element with
- /// the given value.
- template <typename T>
- const Expression* ScalarOrVector(uint32_t width, T value) {
- if (width == 1) {
- return b.Expr(value);
- }
- return b.Call(b.ty.vec<T>(width), value);
- }
-
- template <typename To>
- const Expression* CastScalarOrVector(uint32_t width, const Expression* e) {
- if (width == 1) {
- return b.Call(b.ty.Of<To>(), e);
- }
- return b.Call(b.ty.vec<To>(width), e);
- }
-
- /// Examines the call expression @p expr, applying any necessary polyfill transforms
- void Call(const CallExpression* expr) {
- auto* call = src.Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
- if (!call || call->Stage() != core::EvaluationStage::kRuntime) {
- return; // Only polyfill runtime expressions
- }
- Symbol fn = Switch(
- call->Target(), //
- [&](const sem::BuiltinFn* builtin) {
- switch (builtin->Fn()) {
- case wgsl::BuiltinFn::kAcosh:
- if (cfg.builtins.acosh != Level::kNone) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return acosh(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kAsinh:
- if (cfg.builtins.asinh) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return asinh(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kAtanh:
- if (cfg.builtins.atanh != Level::kNone) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return atanh(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kClamp:
- if (cfg.builtins.clamp_int) {
- auto& sig = builtin->Signature();
- if (sig.parameters[0]->Type()->IsIntegerScalarOrVector()) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return clampInteger(builtin->ReturnType()); });
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kCountLeadingZeros:
- if (cfg.builtins.count_leading_zeros) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return countLeadingZeros(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kCountTrailingZeros:
- if (cfg.builtins.count_trailing_zeros) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return countTrailingZeros(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kExtractBits:
- if (cfg.builtins.extract_bits != Level::kNone) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return extractBits(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kFirstLeadingBit:
- if (cfg.builtins.first_leading_bit) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return firstLeadingBit(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kFirstTrailingBit:
- if (cfg.builtins.first_trailing_bit) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return firstTrailingBit(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kFwidthFine:
- if (cfg.builtins.fwidth_fine) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return fwidthFine(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kInsertBits:
- if (cfg.builtins.insert_bits != Level::kNone) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return insertBits(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kReflect:
- // Only polyfill for vec2<f32>. See https://crbug.com/tint/1798 for
- // more details.
- if (cfg.builtins.reflect_vec2_f32) {
- auto& sig = builtin->Signature();
- auto* vec = sig.return_type->As<core::type::Vector>();
- if (vec && vec->Width() == 2 && vec->Type()->Is<core::type::F32>()) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return reflect(builtin->ReturnType()); });
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kSaturate:
- if (cfg.builtins.saturate) {
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return saturate(builtin->ReturnType()); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kSign:
- if (cfg.builtins.sign_int) {
- auto* ty = builtin->ReturnType();
- if (ty->IsSignedIntegerScalarOrVector()) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return sign_int(ty); });
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kTextureLoad:
- if (cfg.builtins.bgra8unorm) {
- auto& sig = builtin->Signature();
- auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
- if (auto* stex = tex->Type()->As<core::type::StorageTexture>()) {
- if (stex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
- ctx.Replace(expr, [this, expr] {
- return ctx.dst->MemberAccessor(
- ctx.CloneWithoutTransform(expr), "bgra");
- });
- made_changes = true;
- }
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
- if (cfg.builtins.texture_sample_base_clamp_to_edge_2d_f32) {
- auto& sig = builtin->Signature();
- auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
- if (auto* stex = tex->Type()->As<core::type::SampledTexture>()) {
- if (stex->Type()->Is<core::type::F32>()) {
- return builtin_polyfills.GetOrAdd(builtin, [&] {
- return textureSampleBaseClampToEdge_2d_f32();
- });
- }
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kTextureStore:
- if (cfg.builtins.bgra8unorm) {
- auto& sig = builtin->Signature();
- auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
- if (auto* stex = tex->Type()->As<core::type::StorageTexture>()) {
- if (stex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
- size_t value_idx = static_cast<size_t>(
- sig.IndexOf(core::ParameterUsage::kValue));
- ctx.Replace(expr, [this, expr, value_idx] {
- tint::Vector<const Expression*, 3> args;
- for (auto* arg : expr->args) {
- arg = ctx.Clone(arg);
- if (args.Length() == value_idx) { // value
- arg = ctx.dst->MemberAccessor(arg, "bgra");
- }
- args.Push(arg);
- }
- return ctx.dst->Call(
- tint::ToString(wgsl::BuiltinFn::kTextureStore),
- std::move(args));
- });
- made_changes = true;
- }
- }
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kWorkgroupUniformLoad:
- if (cfg.builtins.workgroup_uniform_load) {
- auto* param_type = builtin->Parameters()[0]->Type();
- TINT_ASSERT(param_type->Is<core::type::Pointer>());
- auto* ty = param_type->As<core::type::Pointer>()->StoreType();
- return builtin_polyfills.GetOrAdd(
- builtin, [&] { return workgroupUniformLoad(ty); });
- }
- return Symbol{};
-
- case wgsl::BuiltinFn::kDot4I8Packed: {
- if (cfg.builtins.dot_4x8_packed) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Dot4I8Packed(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kDot4U8Packed: {
- if (cfg.builtins.dot_4x8_packed) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Dot4U8Packed(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kPack4XI8: {
- if (cfg.builtins.pack_unpack_4x8) {
- return builtin_polyfills.GetOrAdd(builtin, [&] { return Pack4xI8(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kPack4XU8: {
- if (cfg.builtins.pack_unpack_4x8) {
- return builtin_polyfills.GetOrAdd(builtin, [&] { return Pack4xU8(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kPack4XI8Clamp: {
- if (cfg.builtins.pack_unpack_4x8) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Pack4xI8Clamp(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kPack4XU8Clamp: {
- if (cfg.builtins.pack_4xu8_clamp) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Pack4xU8Clamp(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kUnpack4XI8: {
- if (cfg.builtins.pack_unpack_4x8) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Unpack4xI8(); });
- }
- return Symbol{};
- }
-
- case wgsl::BuiltinFn::kUnpack4XU8: {
- if (cfg.builtins.pack_unpack_4x8) {
- return builtin_polyfills.GetOrAdd(builtin,
- [&] { return Unpack4xU8(); });
- }
- return Symbol{};
- }
-
- default:
- return Symbol{};
- }
- },
- [&](const sem::ValueConversion* conv) {
- if (cfg.builtins.conv_f32_to_iu32) {
- auto* src_ty = conv->Source();
- if (tint::Is<core::type::F32>(src_ty->Elements(src_ty).type)) {
- auto* dst_ty = conv->Target();
- if (tint::IsAnyOf<core::type::I32, core::type::U32>(
- dst_ty->Elements(dst_ty).type)) {
- return f32_conv_polyfills.GetOrAdd(dst_ty, [&] { //
- return ConvF32ToIU32(src_ty, dst_ty);
- });
- }
- }
- }
- return Symbol{};
- });
-
- if (fn.IsValid()) {
- ctx.Replace(call->Declaration(),
- [this, fn, expr] { return ctx.dst->Call(fn, ctx.Clone(expr->args)); });
- made_changes = true;
- }
- }
-};
-
-BuiltinPolyfill::BuiltinPolyfill() = default;
-
-BuiltinPolyfill::~BuiltinPolyfill() = default;
-
-Transform::ApplyResult BuiltinPolyfill::Apply(const Program& src,
- const DataMap& data,
- DataMap&) const {
- auto* cfg = data.Get<Config>();
- if (!cfg) {
- return SkipTransform;
- }
- return State{src, *cfg}.Run();
-}
-
-BuiltinPolyfill::Config::Config() = default;
-BuiltinPolyfill::Config::Config(const Builtins& b) : builtins(b) {}
-BuiltinPolyfill::Config::Config(const Config&) = default;
-BuiltinPolyfill::Config::~Config() = default;
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
deleted file mode 100644
index 9c01106..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Implements builtins for backends that do not have a native implementation.
-class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
- public:
- /// Constructor
- BuiltinPolyfill();
- /// Destructor
- ~BuiltinPolyfill() override;
-
- /// Enumerator of polyfill levels
- enum class Level {
- /// No polyfill needed, supported by the backend.
- kNone,
- /// Clamp the parameters to the inner implementation.
- kClampParameters,
- /// Range check the input.
- kRangeCheck,
- /// Polyfill the entire function
- kFull,
- };
-
- /// Specifies the builtins that should be polyfilled by the transform.
- struct Builtins {
- /// What level should `acosh` be polyfilled?
- Level acosh = Level::kNone;
- /// Should `asinh` be polyfilled?
- bool asinh = false;
- /// What level should `atanh` be polyfilled?
- Level atanh = Level::kNone;
- /// Should storage textures of format 'bgra8unorm' be replaced with 'rgba8unorm'?
- bool bgra8unorm = false;
- /// Should the RHS of `<<` and `>>` be wrapped in a modulo bit-width of LHS?
- bool bitshift_modulo = false;
- /// Should `clamp()` be polyfilled for integer values (scalar or vector)?
- bool clamp_int = false;
- /// Should `countLeadingZeros()` be polyfilled?
- bool count_leading_zeros = false;
- /// Should `countTrailingZeros()` be polyfilled?
- bool count_trailing_zeros = false;
- /// Should converting f32 to i32 or u32 be polyfilled?
- bool conv_f32_to_iu32 = false;
- /// What level should `extractBits()` be polyfilled?
- Level extract_bits = Level::kNone;
- /// Should `firstLeadingBit()` be polyfilled?
- bool first_leading_bit = false;
- /// Should `firstTrailingBit()` be polyfilled?
- bool first_trailing_bit = false;
- /// Should `fwidthFine()` be polyfilled?
- bool fwidth_fine = false;
- /// Should `insertBits()` be polyfilled?
- Level insert_bits = Level::kNone;
- /// Should integer scalar / vector divides and modulos be polyfilled to avoid DBZ and
- /// integer overflows?
- bool int_div_mod = false;
- /// Should float modulos be polyfilled to emit a precise modulo operation as per the spec?
- bool precise_float_mod = false;
- /// Should `reflect()` be polyfilled for vec2<f32>?
- bool reflect_vec2_f32 = false;
- /// Should `saturate()` be polyfilled?
- bool saturate = false;
- /// Should `sign()` be polyfilled for integer types?
- bool sign_int = false;
- /// Should `textureSampleBaseClampToEdge()` be polyfilled for texture_2d<f32> textures?
- bool texture_sample_base_clamp_to_edge_2d_f32 = false;
- /// Should `workgroupUniformLoad()` be polyfilled?
- bool workgroup_uniform_load = false;
- /// Should `dot4I8Packed()` and `dot4U8Packed()` be polyfilled?
- bool dot_4x8_packed = false;
- /// Should `pack4xI8()`, `pack4xU8()`, `pack4xI8Clamp()`, `unpack4xI8()` and `unpack4xU8()`
- /// be polyfilled?
- bool pack_unpack_4x8 = false;
- /// Should `pack4xU8Clamp()` be polyfilled?
- /// TODO(tint:1497): remove the option once the bug in DXC is fixed.
- bool pack_4xu8_clamp = false;
-
- /// Reflection for this struct
- TINT_REFLECT(Builtins,
- acosh,
- asinh,
- atanh,
- bgra8unorm,
- bitshift_modulo,
- clamp_int,
- count_leading_zeros,
- count_trailing_zeros,
- conv_f32_to_iu32,
- extract_bits,
- first_leading_bit,
- first_trailing_bit,
- fwidth_fine,
- insert_bits,
- int_div_mod,
- precise_float_mod,
- reflect_vec2_f32,
- saturate,
- sign_int,
- texture_sample_base_clamp_to_edge_2d_f32,
- workgroup_uniform_load,
- dot_4x8_packed,
- pack_unpack_4x8,
- pack_4xu8_clamp);
- };
-
- /// Config is consumed by the BuiltinPolyfill transform.
- /// Config specifies the builtins that should be polyfilled.
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- Config();
-
- /// Constructor
- /// @param b the list of builtins to polyfill
- explicit Config(const Builtins& b);
-
- /// Copy constructor
- Config(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// The builtins to polyfill
- Builtins builtins;
-
- /// Reflection for this struct
- TINT_REFLECT(Config, builtins);
- };
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-namespace tint {
-
-/// Level reflection information
-TINT_REFLECT_ENUM_RANGE(ast::transform::BuiltinPolyfill::Level, kNone, kFull);
-
-} // namespace tint
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
deleted file mode 100644
index 4aa809d..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
+++ /dev/null
@@ -1,4340 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using Level = BuiltinPolyfill::Level;
-
-using BuiltinPolyfillTest = TransformTest;
-
-TEST_F(BuiltinPolyfillTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRun_OverrideCall) {
- auto* src = R"(
-override x = 42.123;
-override y = saturate(x);
-)";
-
- DataMap data;
- BuiltinPolyfill::Builtins builtins;
- builtins.saturate = true;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, data));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRun_OverrideBinary) {
- auto* src = R"(
-override v = 10i;
-override x = 20i / v;
-)";
-
- DataMap data;
- BuiltinPolyfill::Builtins builtins;
- builtins.int_div_mod = true;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, data));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// acosh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillAcosh(Level level) {
- BuiltinPolyfill::Builtins builtins;
- builtins.acosh = level;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAcosh) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- _ = acosh(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kNone)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : f32 = acosh(1.0);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Full_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : f32 = acosh(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_acosh(x : f32) -> f32 {
- return log((x + sqrt(((x * x) - 1))));
-}
-
-fn f() {
- let v = 1.0;
- let r : f32 = tint_acosh(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Full_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = acosh(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
- return log((x + sqrt(((x * x) - 1))));
-}
-
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = tint_acosh(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Range_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : f32 = acosh(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_acosh(x : f32) -> f32 {
- return select(acosh(x), 0.0, (x < 1.0));
-}
-
-fn f() {
- let v = 1.0;
- let r : f32 = tint_acosh(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Range_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = acosh(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
- return select(acosh(x), vec3<f32>(0.0), (x < vec3<f32>(1.0)));
-}
-
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = tint_acosh(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// asinh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSinh() {
- BuiltinPolyfill::Builtins builtins;
- builtins.asinh = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAsinh) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- _ = asinh(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : f32 = asinh(1.0);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : f32 = asinh(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_sinh(x : f32) -> f32 {
- return log((x + sqrt(((x * x) + 1))));
-}
-
-fn f() {
- let v = 1.0;
- let r : f32 = tint_sinh(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = asinh(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_sinh(x : vec3<f32>) -> vec3<f32> {
- return log((x + sqrt(((x * x) + 1))));
-}
-
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = tint_sinh(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// atanh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillAtanh(Level level) {
- BuiltinPolyfill::Builtins builtins;
- builtins.atanh = level;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAtanh) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- _ = atanh(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kNone)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : f32 = atanh(0.23);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Full_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : f32 = atanh(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_atanh(x : f32) -> f32 {
- return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-fn f() {
- let v = 1.0;
- let r : f32 = tint_atanh(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Full_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = atanh(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
- return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = tint_atanh(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Range_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : f32 = atanh(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_atanh(x : f32) -> f32 {
- return select(atanh(x), 0.0, (x >= 1.0));
-}
-
-fn f() {
- let v = 1.0;
- let r : f32 = tint_atanh(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Range_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = atanh(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
- return select(atanh(x), vec3<f32>(0.0), (x >= vec3<f32>(1.0)));
-}
-
-fn f() {
- let v = 1.0;
- let r : vec3<f32> = tint_atanh(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// bgra8unorm
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillBgra8unorm() {
- BuiltinPolyfill::Builtins builtins;
- builtins.bgra8unorm = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureVar) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureParam) {
- auto* src = R"(
-fn f(tex : texture_storage_3d<bgra8unorm, write>) {
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureVar) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<rgba8unorm, write>;
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureParam) {
- auto* src = R"(
-fn f(tex : texture_storage_3d<bgra8unorm, write>) {
-}
-)";
-
- auto* expect = R"(
-fn f(tex : texture_storage_3d<rgba8unorm, write>) {
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureLoad) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, read>;
-
-fn f(coords : vec2<i32>) -> vec4<f32> {
- return textureLoad(tex, coords);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, read>;
-
-fn f(coords : vec2<i32>) -> vec4<f32> {
- return textureLoad(tex, coords).bgra;
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, value);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, value.bgra);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_Param) {
- auto* src = R"(
-fn f(tex : texture_storage_2d<bgra8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, value);
-}
-)";
-
- auto* expect = R"(
-fn f(tex : texture_storage_2d<rgba8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, value.bgra);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_WithAtanh) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, atanh(value));
-}
-)";
-
- auto* expect = R"(
-fn tint_atanh(x : vec4<f32>) -> vec4<f32> {
- return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
- textureStore(tex, coords, tint_atanh(value).bgra);
-}
-)";
-
- BuiltinPolyfill::Builtins builtins;
- builtins.atanh = BuiltinPolyfill::Level::kFull;
- builtins.bgra8unorm = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- auto got = Run<BuiltinPolyfill>(src, std::move(data));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureLoadAndStore) {
- auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, read_write>;
-
-fn f(coords : vec2<i32>) {
- textureStore(tex, coords, textureLoad(tex, coords));
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, read_write>;
-
-fn f(coords : vec2<i32>) {
- textureStore(tex, coords, textureLoad(tex, coords).bgra.bgra);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// bitshiftModulo
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillBitshiftModulo() {
- BuiltinPolyfill::Builtins builtins;
- builtins.bitshift_modulo = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_scalar) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = 1i << v;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_vector) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = vec3(1i) << vec3(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_scalar) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = 1i >> v;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_vector) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = vec3(1i) >> vec3(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_scalar) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = 1i << v;
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let v = 15u;
- let r = (1i << (v & 31));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_vector) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = vec3(1i) << vec3(v);
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let v = 15u;
- let r = (vec3(1i) << (vec3(v) & vec3<u32>(31)));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_scalar) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = 1i >> v;
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let v = 15u;
- let r = (1i >> (v & 31));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_vector) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r = vec3(1i) >> vec3(v);
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let v = 15u;
- let r = (vec3(1i) >> (vec3(v) & vec3<u32>(31)));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// clampInteger
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillClampInteger() {
- BuiltinPolyfill::Builtins builtins;
- builtins.clamp_int = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- _ = clamp(v, 2i, 3i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_u32) {
- auto* src = R"(
-fn f() {
- let v = 1u;
- _ = clamp(v, 2u, 3u);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f32) {
- auto* src = R"(
-fn f() {
- let v = 1f;
- _ = clamp(v, 2f, 3f);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let v = 1h;
- _ = clamp(v, 2h, 3h);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = clamp(1i, 2i, 3i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- let r : i32 = clamp(v, 2i, 3i);
-}
-)";
-
- auto* expect = R"(
-fn tint_clamp(e : i32, low : i32, high : i32) -> i32 {
- return min(max(e, low), high);
-}
-
-fn f() {
- let v = 1i;
- let r : i32 = tint_clamp(v, 2i, 3i);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- let r : vec3<i32> = clamp(vec3(v), vec3(2i), vec3(3i));
-}
-)";
-
- auto* expect =
- R"(
-fn tint_clamp(e : vec3<i32>, low : vec3<i32>, high : vec3<i32>) -> vec3<i32> {
- return min(max(e, low), high);
-}
-
-fn f() {
- let v = 1i;
- let r : vec3<i32> = tint_clamp(vec3(v), vec3(2i), vec3(3i));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_u32) {
- auto* src = R"(
-fn f() {
- let r : u32 = clamp(1u, 2u, 3u);
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let r : u32 = clamp(1u, 2u, 3u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 1u;
- let r : vec3<u32> = clamp(vec3(v), vec3(2u), vec3(3u));
-}
-)";
-
- auto* expect =
- R"(
-fn tint_clamp(e : vec3<u32>, low : vec3<u32>, high : vec3<u32>) -> vec3<u32> {
- return min(max(e, low), high);
-}
-
-fn f() {
- let v = 1u;
- let r : vec3<u32> = tint_clamp(vec3(v), vec3(2u), vec3(3u));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// conv_f32_to_iu32
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillConvF32ToIU32() {
- BuiltinPolyfill::Builtins builtins;
- builtins.conv_f32_to_iu32 = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToI32) {
- auto* src = R"(
-fn f() {
- let f = 42.0;
- _ = i32(f);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToU32) {
- auto* src = R"(
-fn f() {
- let f = 42.0;
- _ = u32(f);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3I32) {
- auto* src = R"(
-fn f() {
- let f = vec3(42.0);
- _ = vec3<i32>(f);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3U32) {
- auto* src = R"(
-fn f() {
- let f = vec3(42.0);
- _ = vec3<u32>(f);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvF32ToI32) {
- auto* src = R"(
-fn f() {
- let f = 42.0;
- _ = i32(f);
-}
-)";
- auto* expect = R"(
-fn tint_ftoi(v : f32) -> i32 {
- return select(2147483647, select(i32(v), -2147483648, (v < -2147483648.0)), (v <= 2147483520.0));
-}
-
-fn f() {
- let f = 42.0;
- _ = tint_ftoi(f);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvF32ToU32) {
- auto* src = R"(
-fn f() {
- let f = 42.0;
- _ = u32(f);
-}
-)";
- auto* expect = R"(
-fn tint_ftou(v : f32) -> u32 {
- return select(4294967295, select(u32(v), 0, (v < 0.0)), (v <= 4294967040.0));
-}
-
-fn f() {
- let f = 42.0;
- _ = tint_ftou(f);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3I32) {
- auto* src = R"(
-fn f() {
- let f = vec3(42.0);
- _ = vec3<i32>(f);
-}
-)";
- auto* expect = R"(
-fn tint_ftoi(v : vec3<f32>) -> vec3<i32> {
- return select(vec3(2147483647), select(vec3<i32>(v), vec3(-2147483648), (v < vec3(-2147483648.0))), (v <= vec3(2147483520.0)));
-}
-
-fn f() {
- let f = vec3(42.0);
- _ = tint_ftoi(f);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3U32) {
- auto* src = R"(
-fn f() {
- let f = vec3(42.0);
- _ = vec3<u32>(f);
-}
-)";
- auto* expect = R"(
-fn tint_ftou(v : vec3<f32>) -> vec3<u32> {
- return select(vec3(4294967295), select(vec3<u32>(v), vec3(0), (v < vec3(0.0))), (v <= vec3(4294967040.0)));
-}
-
-fn f() {
- let f = vec3(42.0);
- _ = tint_ftou(f);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// countLeadingZeros
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillCountLeadingZeros() {
- BuiltinPolyfill::Builtins builtins;
- builtins.count_leading_zeros = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunCountLeadingZeros) {
- auto* src = R"(
-fn f() {
- let v = 15;
- _ = countLeadingZeros(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountLeadingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = countLeadingZeros(15i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillCountLeadingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : i32 = countLeadingZeros(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_count_leading_zeros(v : i32) -> i32 {
- var x = u32(v);
- let b16 = select(0u, 16u, (x <= 65535u));
- x = (x << b16);
- let b8 = select(0u, 8u, (x <= 16777215u));
- x = (x << b8);
- let b4 = select(0u, 4u, (x <= 268435455u));
- x = (x << b4);
- let b2 = select(0u, 2u, (x <= 1073741823u));
- x = (x << b2);
- let b1 = select(0u, 1u, (x <= 2147483647u));
- let is_zero = select(0u, 1u, (x == 0u));
- return i32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : i32 = tint_count_leading_zeros(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : u32 = countLeadingZeros(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_count_leading_zeros(v : u32) -> u32 {
- var x = u32(v);
- let b16 = select(0u, 16u, (x <= 65535u));
- x = (x << b16);
- let b8 = select(0u, 8u, (x <= 16777215u));
- x = (x << b8);
- let b4 = select(0u, 4u, (x <= 268435455u));
- x = (x << b4);
- let b2 = select(0u, 2u, (x <= 1073741823u));
- x = (x << b2);
- let b1 = select(0u, 1u, (x <= 2147483647u));
- let is_zero = select(0u, 1u, (x == 0u));
- return u32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : u32 = tint_count_leading_zeros(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : vec3<i32> = countLeadingZeros(vec3<i32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_count_leading_zeros(v : vec3<i32>) -> vec3<i32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(0u), vec3<u32>(16u), (x <= vec3<u32>(65535u)));
- x = (x << b16);
- let b8 = select(vec3<u32>(0u), vec3<u32>(8u), (x <= vec3<u32>(16777215u)));
- x = (x << b8);
- let b4 = select(vec3<u32>(0u), vec3<u32>(4u), (x <= vec3<u32>(268435455u)));
- x = (x << b4);
- let b2 = select(vec3<u32>(0u), vec3<u32>(2u), (x <= vec3<u32>(1073741823u)));
- x = (x << b2);
- let b1 = select(vec3<u32>(0u), vec3<u32>(1u), (x <= vec3<u32>(2147483647u)));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
- return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : vec3<i32> = tint_count_leading_zeros(vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : vec3<u32> = countLeadingZeros(vec3<u32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_count_leading_zeros(v : vec3<u32>) -> vec3<u32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(0u), vec3<u32>(16u), (x <= vec3<u32>(65535u)));
- x = (x << b16);
- let b8 = select(vec3<u32>(0u), vec3<u32>(8u), (x <= vec3<u32>(16777215u)));
- x = (x << b8);
- let b4 = select(vec3<u32>(0u), vec3<u32>(4u), (x <= vec3<u32>(268435455u)));
- x = (x << b4);
- let b2 = select(vec3<u32>(0u), vec3<u32>(2u), (x <= vec3<u32>(1073741823u)));
- x = (x << b2);
- let b1 = select(vec3<u32>(0u), vec3<u32>(1u), (x <= vec3<u32>(2147483647u)));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
- return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : vec3<u32> = tint_count_leading_zeros(vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// countTrailingZeros
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillCountTrailingZeros() {
- BuiltinPolyfill::Builtins builtins;
- builtins.count_trailing_zeros = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunCountTrailingZeros) {
- auto* src = R"(
-fn f() {
- let v = 15;
- _ = countTrailingZeros(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountTrailingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = countTrailingZeros(15i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillCountTrailingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : i32 = countTrailingZeros(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_count_trailing_zeros(v : i32) -> i32 {
- var x = u32(v);
- let b16 = select(16u, 0u, bool((x & 65535u)));
- x = (x >> b16);
- let b8 = select(8u, 0u, bool((x & 255u)));
- x = (x >> b8);
- let b4 = select(4u, 0u, bool((x & 15u)));
- x = (x >> b4);
- let b2 = select(2u, 0u, bool((x & 3u)));
- x = (x >> b2);
- let b1 = select(1u, 0u, bool((x & 1u)));
- let is_zero = select(0u, 1u, (x == 0u));
- return i32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : i32 = tint_count_trailing_zeros(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : u32 = countTrailingZeros(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_count_trailing_zeros(v : u32) -> u32 {
- var x = u32(v);
- let b16 = select(16u, 0u, bool((x & 65535u)));
- x = (x >> b16);
- let b8 = select(8u, 0u, bool((x & 255u)));
- x = (x >> b8);
- let b4 = select(4u, 0u, bool((x & 15u)));
- x = (x >> b4);
- let b2 = select(2u, 0u, bool((x & 3u)));
- x = (x >> b2);
- let b1 = select(1u, 0u, bool((x & 1u)));
- let is_zero = select(0u, 1u, (x == 0u));
- return u32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : u32 = tint_count_trailing_zeros(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : vec3<i32> = countTrailingZeros(vec3<i32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_count_trailing_zeros(v : vec3<i32>) -> vec3<i32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
- return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : vec3<i32> = tint_count_trailing_zeros(vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : vec3<u32> = countTrailingZeros(vec3<u32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_count_trailing_zeros(v : vec3<u32>) -> vec3<u32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
- return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : vec3<u32> = tint_count_trailing_zeros(vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// extractBits
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillExtractBits(Level level) {
- BuiltinPolyfill::Builtins builtins;
- builtins.extract_bits = level;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunExtractBits) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- _ = extractBits(v, 5u, 6u);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kNone)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = countTrailingZeros(15i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : i32 = extractBits(v, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : i32, offset : u32, count : u32) -> i32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- let shl = (32u - e);
- let shr = (shl + s);
- let shl_result = select(i32(), (v << shl), (shl < 32u));
- return select(((shl_result >> 31u) >> 1u), (shl_result >> shr), (shr < 32u));
-}
-
-fn f() {
- let v = 1234i;
- let r : i32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : u32 = extractBits(v, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : u32, offset : u32, count : u32) -> u32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- let shl = (32u - e);
- let shr = (shl + s);
- let shl_result = select(u32(), (v << shl), (shl < 32u));
- return select(((shl_result >> 31u) >> 1u), (shl_result >> shr), (shr < 32u));
-}
-
-fn f() {
- let v = 1234u;
- let r : u32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = extractBits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- let shl = (32u - e);
- let shr = (shl + s);
- let shl_result = select(vec3<i32>(), (v << vec3<u32>(shl)), (shl < 32u));
- return select(((shl_result >> vec3<u32>(31u)) >> vec3<u32>(1u)), (shl_result >> vec3<u32>(shr)), (shr < 32u));
-}
-
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = tint_extract_bits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = extractBits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- let shl = (32u - e);
- let shr = (shl + s);
- let shl_result = select(vec3<u32>(), (v << vec3<u32>(shl)), (shl < 32u));
- return select(((shl_result >> vec3<u32>(31u)) >> vec3<u32>(1u)), (shl_result >> vec3<u32>(shr)), (shr < 32u));
-}
-
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = tint_extract_bits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : i32 = extractBits(v, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : i32, offset : u32, count : u32) -> i32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return extractBits(v, s, (e - s));
-}
-
-fn f() {
- let v = 1234i;
- let r : i32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : u32 = extractBits(v, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : u32, offset : u32, count : u32) -> u32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return extractBits(v, s, (e - s));
-}
-
-fn f() {
- let v = 1234u;
- let r : u32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = extractBits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return extractBits(v, s, (e - s));
-}
-
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = tint_extract_bits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = extractBits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_extract_bits(v : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return extractBits(v, s, (e - s));
-}
-
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = tint_extract_bits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// firstLeadingBit
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFirstLeadingBit() {
- BuiltinPolyfill::Builtins builtins;
- builtins.first_leading_bit = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFirstLeadingBit) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- _ = firstLeadingBit(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstLeadingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = firstLeadingBit(15i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstLeadingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : i32 = firstLeadingBit(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_first_leading_bit(v : i32) -> i32 {
- var x = select(u32(v), u32(~(v)), (v < 0i));
- let b16 = select(0u, 16u, bool((x & 4294901760u)));
- x = (x >> b16);
- let b8 = select(0u, 8u, bool((x & 65280u)));
- x = (x >> b8);
- let b4 = select(0u, 4u, bool((x & 240u)));
- x = (x >> b4);
- let b2 = select(0u, 2u, bool((x & 12u)));
- x = (x >> b2);
- let b1 = select(0u, 1u, bool((x & 2u)));
- let is_zero = select(0u, 4294967295u, (x == 0u));
- return i32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : i32 = tint_first_leading_bit(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : u32 = firstLeadingBit(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_first_leading_bit(v : u32) -> u32 {
- var x = v;
- let b16 = select(0u, 16u, bool((x & 4294901760u)));
- x = (x >> b16);
- let b8 = select(0u, 8u, bool((x & 65280u)));
- x = (x >> b8);
- let b4 = select(0u, 4u, bool((x & 240u)));
- x = (x >> b4);
- let b2 = select(0u, 2u, bool((x & 12u)));
- x = (x >> b2);
- let b1 = select(0u, 1u, bool((x & 2u)));
- let is_zero = select(0u, 4294967295u, (x == 0u));
- return u32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : u32 = tint_first_leading_bit(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : vec3<i32> = firstLeadingBit(vec3<i32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_first_leading_bit(v : vec3<i32>) -> vec3<i32> {
- var x = select(vec3<u32>(v), vec3<u32>(~(v)), (v < vec3<i32>(0i)));
- let b16 = select(vec3<u32>(0u), vec3<u32>(16u), vec3<bool>((x & vec3<u32>(4294901760u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(0u), vec3<u32>(8u), vec3<bool>((x & vec3<u32>(65280u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(0u), vec3<u32>(4u), vec3<bool>((x & vec3<u32>(240u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(0u), vec3<u32>(2u), vec3<bool>((x & vec3<u32>(12u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(0u), vec3<u32>(1u), vec3<bool>((x & vec3<u32>(2u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
- return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : vec3<i32> = tint_first_leading_bit(vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : vec3<u32> = firstLeadingBit(vec3<u32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_first_leading_bit(v : vec3<u32>) -> vec3<u32> {
- var x = v;
- let b16 = select(vec3<u32>(0u), vec3<u32>(16u), vec3<bool>((x & vec3<u32>(4294901760u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(0u), vec3<u32>(8u), vec3<bool>((x & vec3<u32>(65280u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(0u), vec3<u32>(4u), vec3<bool>((x & vec3<u32>(240u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(0u), vec3<u32>(2u), vec3<bool>((x & vec3<u32>(12u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(0u), vec3<u32>(1u), vec3<bool>((x & vec3<u32>(2u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
- return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : vec3<u32> = tint_first_leading_bit(vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// firstTrailingBit
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFirstTrailingBit() {
- BuiltinPolyfill::Builtins builtins;
- builtins.first_trailing_bit = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFirstTrailingBit) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- _ = firstTrailingBit(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstTrailingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = firstTrailingBit(15i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstTrailingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : i32 = firstTrailingBit(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_first_trailing_bit(v : i32) -> i32 {
- var x = u32(v);
- let b16 = select(16u, 0u, bool((x & 65535u)));
- x = (x >> b16);
- let b8 = select(8u, 0u, bool((x & 255u)));
- x = (x >> b8);
- let b4 = select(4u, 0u, bool((x & 15u)));
- x = (x >> b4);
- let b2 = select(2u, 0u, bool((x & 3u)));
- x = (x >> b2);
- let b1 = select(1u, 0u, bool((x & 1u)));
- let is_zero = select(0u, 4294967295u, (x == 0u));
- return i32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : i32 = tint_first_trailing_bit(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : u32 = firstTrailingBit(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_first_trailing_bit(v : u32) -> u32 {
- var x = u32(v);
- let b16 = select(16u, 0u, bool((x & 65535u)));
- x = (x >> b16);
- let b8 = select(8u, 0u, bool((x & 255u)));
- x = (x >> b8);
- let b4 = select(4u, 0u, bool((x & 15u)));
- x = (x >> b4);
- let b2 = select(2u, 0u, bool((x & 3u)));
- x = (x >> b2);
- let b1 = select(1u, 0u, bool((x & 1u)));
- let is_zero = select(0u, 4294967295u, (x == 0u));
- return u32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : u32 = tint_first_trailing_bit(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 15i;
- let r : vec3<i32> = firstTrailingBit(vec3<i32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_first_trailing_bit(v : vec3<i32>) -> vec3<i32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
- return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15i;
- let r : vec3<i32> = tint_first_trailing_bit(vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 15u;
- let r : vec3<u32> = firstTrailingBit(vec3<u32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_first_trailing_bit(v : vec3<u32>) -> vec3<u32> {
- var x = vec3<u32>(v);
- let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
- x = (x >> b16);
- let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
- x = (x >> b8);
- let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
- x = (x >> b4);
- let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
- x = (x >> b2);
- let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
- let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
- return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
- let v = 15u;
- let r : vec3<u32> = tint_first_trailing_bit(vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// fwidthFine
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFwidthFine() {
- BuiltinPolyfill::Builtins builtins;
- builtins.fwidth_fine = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFwidthFine) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- _ = fwidthFine(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFwidthFine()));
-}
-
-TEST_F(BuiltinPolyfillTest, FwidthFine_f32) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- let r : f32 = fwidthFine(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_fwidth_fine(v : f32) -> f32 {
- return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
-}
-
-fn f() {
- let v = 0.5f;
- let r : f32 = tint_fwidth_fine(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FwidthFine_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- let r : vec3<f32> = fwidthFine(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_fwidth_fine(v : vec3<f32>) -> vec3<f32> {
- return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
-}
-
-fn f() {
- let v = 0.5f;
- let r : vec3<f32> = tint_fwidth_fine(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// insertBits
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillInsertBits(Level level) {
- BuiltinPolyfill::Builtins builtins;
- builtins.insert_bits = level;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunInsertBits) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- _ = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kNone)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters)));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = insertBits(1234i, 5678i, 5u, 6u);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : i32 = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : i32, n : i32, offset : u32, count : u32) -> i32 {
- let e = (offset + count);
- let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
- return ((select(i32(), (n << offset), (offset < 32u)) & i32(mask)) | (v & i32(~(mask))));
-}
-
-fn f() {
- let v = 1234i;
- let r : i32 = tint_insert_bits(v, 5678, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : u32 = insertBits(v, 5678u, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : u32, n : u32, offset : u32, count : u32) -> u32 {
- let e = (offset + count);
- let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
- return ((select(u32(), (n << offset), (offset < 32u)) & mask) | (v & ~(mask)));
-}
-
-fn f() {
- let v = 1234u;
- let r : u32 = tint_insert_bits(v, 5678u, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = insertBits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : vec3<i32>, n : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
- let e = (offset + count);
- let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
- return ((select(vec3<i32>(), (n << vec3<u32>(offset)), (offset < 32u)) & vec3<i32>(i32(mask))) | (v & vec3<i32>(i32(~(mask)))));
-}
-
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = tint_insert_bits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = insertBits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : vec3<u32>, n : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
- let e = (offset + count);
- let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
- return ((select(vec3<u32>(), (n << vec3<u32>(offset)), (offset < 32u)) & vec3<u32>(mask)) | (v & vec3<u32>(~(mask))));
-}
-
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = tint_insert_bits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : i32 = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : i32, n : i32, offset : u32, count : u32) -> i32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
- let v = 1234i;
- let r : i32 = tint_insert_bits(v, 5678, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : u32 = insertBits(v, 5678u, 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : u32, n : u32, offset : u32, count : u32) -> u32 {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
- let v = 1234u;
- let r : u32 = tint_insert_bits(v, 5678u, 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = insertBits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : vec3<i32>, n : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
- let v = 1234i;
- let r : vec3<i32> = tint_insert_bits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = insertBits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
- auto* expect = R"(
-fn tint_insert_bits(v : vec3<u32>, n : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
- let s = min(offset, 32u);
- let e = min(32u, (s + count));
- return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
- let v = 1234u;
- let r : vec3<u32> = tint_insert_bits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// precise_float_mod
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillPreciseFloatMod() {
- BuiltinPolyfill::Builtins builtins;
- builtins.precise_float_mod = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunPreciseFloatMod) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = 20f % v;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillPreciseFloatMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_af_f32) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = 20.0 % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
- return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(20.0, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_af) {
- auto* src = R"(
-fn f() {
- let v = 10.0;
- let x = 20f % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
- return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
- let v = 10.0;
- let x = tint_float_mod(20.0f, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_f32) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = 20f % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
- return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(20.0f, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_Overloads) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = 20f % v;
- let w = 10i;
- let y = 20i % w;
- let u = 10u;
- let z = 20u % u;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
- return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(20.0f, v);
- let w = 10i;
- let y = (20i % w);
- let u = 10u;
- let z = (20u % u);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_af_f32) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = vec3(20.0) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
- let r = vec3<f32>(rhs);
- return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(vec3(20.0), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_af) {
- auto* src = R"(
-fn f() {
- let v = 10.0;
- let x = vec3(20f) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
- let r = vec3<f32>(rhs);
- return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
- let v = 10.0;
- let x = tint_float_mod(vec3(20.0f), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_f32) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = vec3(20f) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
- let r = vec3<f32>(rhs);
- return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(vec3(20.0f), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 10f;
- let x = vec3<f32>(20f) % vec3<f32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : vec3<f32>) -> vec3<f32> {
- return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
- let v = 10.0f;
- let x = tint_float_mod(vec3<f32>(20.0f), vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// int_div_mod
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillIntDivMod() {
- BuiltinPolyfill::Builtins builtins;
- builtins.int_div_mod = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunIntDiv) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i / v;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunIntMod) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i % v;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20 / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
- return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(20, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20 % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
- let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
- if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(20, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = v / 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
- return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(v, 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = v % 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
- let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
- if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(v, 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
- return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(20i, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
- let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
- if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(20i, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20 / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
- return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(20, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20 % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
- return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(20, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = v / 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
- return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(v, 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = v % 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
- return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(v, 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20u / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
- return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(20u, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20u % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
- return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(20u, v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_ai_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3(20) / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(vec3(20), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_ai_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3(20) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
- if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(vec3(20), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3(v) / 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(vec3(v), 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_ai) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3(v) % 20;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
- if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(vec3(v), 20);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3<i32>(20i) / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(vec3<i32>(20i), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3<i32>(20i) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
- let r = vec3<i32>(rhs);
- let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
- if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(vec3<i32>(20i), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = vec3<u32>(20u) / v;
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
- let r = vec3<u32>(rhs);
- return (lhs / select(r, vec3(1), (r == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(vec3<u32>(20u), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = vec3<u32>(20u) % v;
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
- let r = vec3<u32>(rhs);
- return (lhs % select(r, vec3(1), (r == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(vec3<u32>(20u), v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20 / vec3(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
- let l = vec3<i32>(lhs);
- return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(20, vec3(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20 % vec3(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
- let l = vec3<i32>(lhs);
- let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1)))));
- if (any(((vec3<u32>((l | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (l - ((l / rhs_or_one) * rhs_or_one));
- } else {
- return (l % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(20, vec3(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i / vec3<i32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
- let l = vec3<i32>(lhs);
- return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(20i, vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = 20i % vec3<i32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
- let l = vec3<i32>(lhs);
- let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1)))));
- if (any(((vec3<u32>((l | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (l - ((l / rhs_or_one) * rhs_or_one));
- } else {
- return (l % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(20i, vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20u / vec3<u32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
- let l = vec3<u32>(lhs);
- return (l / select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(20u, vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = 20u % vec3<u32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
- let l = vec3<u32>(lhs);
- return (l % select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(20u, vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3<i32>(20i) / vec3<i32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
- return (lhs / select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
- let v = 10i;
- let x = tint_div(vec3<i32>(20i), vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 10i;
- let x = vec3<i32>(20i) % vec3<i32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
- let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1)))));
- if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
- return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
- } else {
- return (lhs % rhs_or_one);
- }
-}
-
-fn f() {
- let v = 10i;
- let x = tint_mod(vec3<i32>(20i), vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = vec3<u32>(20u) / vec3<u32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_div(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
- return (lhs / select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_div(vec3<u32>(20u), vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_vec3_u32) {
- auto* src = R"(
-fn f() {
- let v = 10u;
- let x = vec3<u32>(20u) % vec3<u32>(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
- return (lhs % select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
- let v = 10u;
- let x = tint_mod(vec3<u32>(20u), vec3<u32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// reflect for vec2<f32>
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillReflectVec2F32() {
- BuiltinPolyfill::Builtins builtins;
- builtins.reflect_vec2_f32 = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec2_f32) {
- auto* src = R"(
-fn f() {
- let e1 = vec2<f32>(1.0f);
- let e2 = vec2<f32>(1.0f);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec2_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let e1 = vec2<f16>(1.0h);
- let e2 = vec2<f16>(1.0h);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec3_f32) {
- auto* src = R"(
-fn f() {
- let e1 = vec3<f32>(1.0f);
- let e2 = vec3<f32>(1.0f);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec3_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let e1 = vec3<f16>(1.0h);
- let e2 = vec3<f16>(1.0h);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec4_f32) {
- auto* src = R"(
-fn f() {
- let e1 = vec3<f32>(1.0f);
- let e2 = vec3<f32>(1.0f);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec4_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let e1 = vec3<f16>(1.0h);
- let e2 = vec3<f16>(1.0h);
- let x = reflect(e1, e2);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : vec2<f32> = reflect(vec2<f32>(1.0), vec2<f32>(1.0));
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_vec2_f32) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- let r : vec2<f32> = reflect(vec2<f32>(v), vec2<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_reflect(e1 : vec2<f32>, e2 : vec2<f32>) -> vec2<f32> {
- let factor = (-2.0 * dot(e1, e2));
- return (e1 + (factor * e2));
-}
-
-fn f() {
- let v = 0.5f;
- let r : vec2<f32> = tint_reflect(vec2<f32>(v), vec2<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillReflectVec2F32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_multiple_types) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let in_f32 = 0.5f;
- let out_f32_vec2 : vec2<f32> = reflect(vec2<f32>(in_f32), vec2<f32>(in_f32));
- let out_f32_vec3 : vec3<f32> = reflect(vec3<f32>(in_f32), vec3<f32>(in_f32));
- let out_f32_vec4 : vec4<f32> = reflect(vec4<f32>(in_f32), vec4<f32>(in_f32));
- let in_f16 = 0.5h;
- let out_f16_vec2 : vec2<f16> = reflect(vec2<f16>(in_f16), vec2<f16>(in_f16));
- let out_f16_vec3 : vec3<f16> = reflect(vec3<f16>(in_f16), vec3<f16>(in_f16));
- let out_f16_vec4 : vec4<f16> = reflect(vec4<f16>(in_f16), vec4<f16>(in_f16));
-}
-)";
-
- auto* expect = R"(
-enable f16;
-
-fn tint_reflect(e1 : vec2<f32>, e2 : vec2<f32>) -> vec2<f32> {
- let factor = (-2.0 * dot(e1, e2));
- return (e1 + (factor * e2));
-}
-
-fn f() {
- let in_f32 = 0.5f;
- let out_f32_vec2 : vec2<f32> = tint_reflect(vec2<f32>(in_f32), vec2<f32>(in_f32));
- let out_f32_vec3 : vec3<f32> = reflect(vec3<f32>(in_f32), vec3<f32>(in_f32));
- let out_f32_vec4 : vec4<f32> = reflect(vec4<f32>(in_f32), vec4<f32>(in_f32));
- let in_f16 = 0.5h;
- let out_f16_vec2 : vec2<f16> = reflect(vec2<f16>(in_f16), vec2<f16>(in_f16));
- let out_f16_vec3 : vec3<f16> = reflect(vec3<f16>(in_f16), vec3<f16>(in_f16));
- let out_f16_vec4 : vec4<f16> = reflect(vec4<f16>(in_f16), vec4<f16>(in_f16));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillReflectVec2F32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// saturate
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSaturate() {
- BuiltinPolyfill::Builtins builtins;
- builtins.saturate = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSaturate) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- _ = saturate(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSaturate()));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : f32 = saturate(0.5);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSaturate()));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_f32) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- let r : f32 = saturate(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_saturate(v : f32) -> f32 {
- return clamp(v, f32(0), f32(1));
-}
-
-fn f() {
- let v = 0.5f;
- let r : f32 = tint_saturate(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let v = 0.5h;
- let r : f16 = saturate(v);
-}
-)";
-
- auto* expect = R"(
-enable f16;
-
-fn tint_saturate(v : f16) -> f16 {
- return clamp(v, f16(0), f16(1));
-}
-
-fn f() {
- let v = 0.5h;
- let r : f16 = tint_saturate(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_vec3_f32) {
- auto* src = R"(
-fn f() {
- let v = 0.5f;
- let r : vec3<f32> = saturate(vec3<f32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_saturate(v : vec3<f32>) -> vec3<f32> {
- return clamp(v, vec3<f32>(0), vec3<f32>(1));
-}
-
-fn f() {
- let v = 0.5f;
- let r : vec3<f32> = tint_saturate(vec3<f32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_vec3_f16) {
- auto* src = R"(
-enable f16;
-
-fn f() {
- let v = 0.5h;
- let r : vec3<f16> = saturate(vec3<f16>(v));
-}
-)";
-
- auto* expect = R"(
-enable f16;
-
-fn tint_saturate(v : vec3<f16>) -> vec3<f16> {
- return clamp(v, vec3<f16>(0), vec3<f16>(1));
-}
-
-fn f() {
- let v = 0.5h;
- let r : vec3<f16> = tint_saturate(vec3<f16>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sign_int
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSignInt() {
- BuiltinPolyfill::Builtins builtins;
- builtins.sign_int = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSign_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- _ = sign(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSign_f32) {
- auto* src = R"(
-fn f() {
- let v = 1f;
- _ = sign(v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_ConstantExpression) {
- auto* src = R"(
-fn f() {
- let r : i32 = sign(1i);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- let r : i32 = sign(v);
-}
-)";
-
- auto* expect = R"(
-fn tint_sign(v : i32) -> i32 {
- return select(select(-1, 1, (v > 0)), 0, (v == 0));
-}
-
-fn f() {
- let v = 1i;
- let r : i32 = tint_sign(v);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSignInt());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_vec3_i32) {
- auto* src = R"(
-fn f() {
- let v = 1i;
- let r : vec3<i32> = sign(vec3<i32>(v));
-}
-)";
-
- auto* expect = R"(
-fn tint_sign(v : vec3<i32>) -> vec3<i32> {
- return select(select(vec3(-1), vec3(1), (v > vec3(0))), vec3(0), (v == vec3(0)));
-}
-
-fn f() {
- let v = 1i;
- let r : vec3<i32> = tint_sign(vec3<i32>(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillSignInt());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// textureSampleBaseClampToEdge
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillTextureSampleBaseClampToEdge_2d_f32() {
- BuiltinPolyfill::Builtins builtins;
- builtins.texture_sample_base_clamp_to_edge_2d_f32 = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunTextureSampleBaseClampToEdge_2d_f32) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunTextureSampleBaseClampToEdge_external) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32()));
-}
-
-TEST_F(BuiltinPolyfillTest, TextureSampleBaseClampToEdge_2d_f32_f32) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
- let r = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
- auto* expect = R"(
-fn tint_textureSampleBaseClampToEdge(t : texture_2d<f32>, s : sampler, coord : vec2<f32>) -> vec4<f32> {
- let dims = vec2<f32>(textureDimensions(t, 0));
- let half_texel = (vec2<f32>(0.5) / dims);
- let clamped = clamp(coord, half_texel, (1 - half_texel));
- return textureSampleLevel(t, s, clamped, 0);
-}
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
- let r = tint_textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// workgroupUniformLoad
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillWorkgroupUniformLoad() {
- BuiltinPolyfill::Builtins builtins;
- builtins.workgroup_uniform_load = true;
-
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- return data;
-}
-
-DataMap polyfillWorkgroupUniformLoadWithDirectVariableAccess() {
- DataMap data;
-
- BuiltinPolyfill::Builtins builtins;
- builtins.workgroup_uniform_load = true;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- DirectVariableAccess::Options options;
- data.Add<DirectVariableAccess::Config>(options);
-
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunWorkgroupUniformLoad) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-fn f() {
- _ = workgroupUniformLoad(&v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunWorkgroupUniformLoadAtomic) {
- auto* src = R"(
-var<workgroup> v : atomic<i32>;
-
-fn f() {
- _ = workgroupUniformLoad(&v);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
- EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad()));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_i32) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-fn f() {
- let r = workgroupUniformLoad(&v);
-}
-)";
-
- auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
- workgroupBarrier();
- let result = *(p);
- workgroupBarrier();
- return result;
-}
-
-var<workgroup> v : i32;
-
-fn f() {
- let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoadAtomic_i32) {
- auto* src = R"(
-var<workgroup> v : atomic<i32>;
-
-fn f() {
- let r = workgroupUniformLoad(&v);
-}
-)";
-
- auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, atomic<i32>>) -> i32 {
- workgroupBarrier();
- let result = atomicLoad(p);
- workgroupBarrier();
- return result;
-}
-
-var<workgroup> v : atomic<i32>;
-
-fn f() {
- let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_ComplexType) {
- auto* src = R"(
-struct Inner {
- b : bool,
- v : vec4<i32>,
- m : mat3x3<f32>,
-}
-
-struct Outer {
- a : array<Inner, 4>,
-}
-
-var<workgroup> v : Outer;
-
-fn f() {
- let r = workgroupUniformLoad(&v);
-}
-)";
-
- auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, Outer>) -> Outer {
- workgroupBarrier();
- let result = *(p);
- workgroupBarrier();
- return result;
-}
-
-struct Inner {
- b : bool,
- v : vec4<i32>,
- m : mat3x3<f32>,
-}
-
-struct Outer {
- a : array<Inner, 4>,
-}
-
-var<workgroup> v : Outer;
-
-fn f() {
- let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_AvoidDuplicateEnables) {
- auto* src = R"(
-var<workgroup> a : i32;
-var<workgroup> b : u32;
-var<workgroup> c : f32;
-
-fn f() {
- let ra = workgroupUniformLoad(&a);
- let rb = workgroupUniformLoad(&b);
- let rc = workgroupUniformLoad(&c);
-}
-)";
-
- auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
- workgroupBarrier();
- let result = *(p);
- workgroupBarrier();
- return result;
-}
-
-fn tint_workgroupUniformLoad_1(p : ptr<workgroup, u32>) -> u32 {
- workgroupBarrier();
- let result = *(p);
- workgroupBarrier();
- return result;
-}
-
-fn tint_workgroupUniformLoad_2(p : ptr<workgroup, f32>) -> f32 {
- workgroupBarrier();
- let result = *(p);
- workgroupBarrier();
- return result;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : u32;
-
-var<workgroup> c : f32;
-
-fn f() {
- let ra = tint_workgroupUniformLoad(&(a));
- let rb = tint_workgroupUniformLoad_1(&(b));
- let rc = tint_workgroupUniformLoad_2(&(c));
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_DirectVariableAccess) {
- auto* src = R"(
-var<workgroup> v : i32;
-var<workgroup> v2 : i32;
-
-fn f() {
- let r = workgroupUniformLoad(&v);
- let s = workgroupUniformLoad(&v2);
-}
-)";
-
- auto* expect = R"(
-fn tint_workgroupUniformLoad_v() -> i32 {
- workgroupBarrier();
- let result = v;
- workgroupBarrier();
- return result;
-}
-
-fn tint_workgroupUniformLoad_v2() -> i32 {
- workgroupBarrier();
- let result = v2;
- workgroupBarrier();
- return result;
-}
-
-var<workgroup> v : i32;
-
-var<workgroup> v2 : i32;
-
-fn f() {
- let r = tint_workgroupUniformLoad_v();
- let s = tint_workgroupUniformLoad_v2();
-}
-)";
-
- auto got = Run<BuiltinPolyfill, DirectVariableAccess>(
- src, polyfillWorkgroupUniformLoadWithDirectVariableAccess());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Built-in functions in packed_4x8_integer_dot_product
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillPacked4x8IntegerDotProduct() {
- BuiltinPolyfill::Builtins builtins;
- builtins.dot_4x8_packed = true;
- builtins.pack_unpack_4x8 = true;
- builtins.pack_4xu8_clamp = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
- return data;
-}
-
-TEST_F(BuiltinPolyfillTest, Dot4I8Packed) {
- auto* src = R"(
-fn f() {
- let v1 = 0x01020304u;
- let v2 = 0xF1F2F3F4u;
- _ = dot4I8Packed(v1, v2);
-}
-)";
-
- auto* expect = R"(
-fn tint_dot4_i8_packed(a : u32, b : u32) -> i32 {
- const n = vec4<u32>(24, 16, 8, 0);
- let a_i8 = (bitcast<vec4<i32>>((vec4<u32>(a) << n)) >> vec4<u32>(24));
- let b_i8 = (bitcast<vec4<i32>>((vec4<u32>(b) << n)) >> vec4<u32>(24));
- return dot(a_i8, b_i8);
-}
-
-fn f() {
- let v1 = 16909060u;
- let v2 = 4059231220u;
- _ = tint_dot4_i8_packed(v1, v2);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Dot4U8Packed) {
- auto* src = R"(
-fn f() {
- let v1 = 0x01020304u;
- let v2 = 0xF1F2F3F4u;
- _ = dot4U8Packed(v1, v2);
-}
-)";
-
- auto* expect = R"(
-fn tint_dot4_u8_packed(a : u32, b : u32) -> u32 {
- const n = vec4<u32>(24, 16, 8, 0);
- let a_u8 = ((vec4<u32>(a) >> n) & vec4<u32>(255));
- let b_u8 = ((vec4<u32>(b) >> n) & vec4<u32>(255));
- return dot(a_u8, b_u8);
-}
-
-fn f() {
- let v1 = 16909060u;
- let v2 = 4059231220u;
- _ = tint_dot4_u8_packed(v1, v2);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xI8) {
- auto* src = R"(
-fn f() {
- let v1 = vec4i(127, 128, -128, -129);
- _ = pack4xI8(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_pack_4xi8(a : vec4<i32>) -> u32 {
- const n = vec4<u32>(0, 8, 16, 24);
- let a_u32 = bitcast<vec4<u32>>(a);
- let a_u8 = ((a_u32 & vec4<u32>(255)) << n);
- return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
- let v1 = vec4i(127, 128, -(128), -(129));
- _ = tint_pack_4xi8(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xU8) {
- auto* src = R"(
-fn f() {
- let v1 = vec4u(0, 254, 255, 256);
- _ = pack4xU8(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_pack_4xu8(a : vec4<u32>) -> u32 {
- const n = vec4<u32>(0, 8, 16, 24);
- let a_u8 = ((a & vec4<u32>(255)) << n);
- return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
- let v1 = vec4u(0, 254, 255, 256);
- _ = tint_pack_4xu8(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xI8Clamp) {
- auto* src = R"(
-fn f() {
- let v1 = vec4i(127, 128, -128, -129);
- _ = pack4xI8Clamp(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_pack_4xi8_clamp(a : vec4<i32>) -> u32 {
- const n = vec4<u32>(0, 8, 16, 24);
- let a_clamp = clamp(a, vec4<i32>(-128), vec4<i32>(127));
- let a_u32 = bitcast<vec4<u32>>(a_clamp);
- let a_u8 = ((a_u32 & vec4<u32>(255)) << n);
- return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
- let v1 = vec4i(127, 128, -(128), -(129));
- _ = tint_pack_4xi8_clamp(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xU8Clamp) {
- auto* src = R"(
-fn f() {
- let v1 = vec4u(0, 254, 255, 256);
- _ = pack4xU8Clamp(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_pack_4xu8_clamp(a : vec4<u32>) -> u32 {
- const n = vec4<u32>(0, 8, 16, 24);
- let a_clamp = clamp(a, vec4<u32>(0), vec4<u32>(255));
- let a_u8 = vec4<u32>((a_clamp << n));
- return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
- let v1 = vec4u(0, 254, 255, 256);
- _ = tint_pack_4xu8_clamp(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Unpack4xI8) {
- auto* src = R"(
-fn f() {
- let v1 = u32(0x01FF02FE);
- _ = unpack4xI8(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_unpack_4xi8(a : u32) -> vec4<i32> {
- const n = vec4<u32>(24, 16, 8, 0);
- let a_vec4u = vec4<u32>(a);
- let a_vec4i = bitcast<vec4<i32>>((a_vec4u << n));
- return (a_vec4i >> vec4<u32>(24));
-}
-
-fn f() {
- let v1 = u32(33489662);
- _ = tint_unpack_4xi8(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Unpack4xU8) {
- auto* src = R"(
-fn f() {
- let v1 = u32(0xFF01FE02);
- _ = unpack4xU8(v1);
-}
-)";
-
- auto* expect = R"(
-fn tint_unpack_4xu8(a : u32) -> vec4<u32> {
- const n = vec4<u32>(0, 8, 16, 24);
- let a_vec4u = (vec4<u32>(a) >> n);
- return (a_vec4u & vec4<u32>(255));
-}
-
-fn f() {
- let v1 = u32(4278320642);
- _ = tint_unpack_4xu8(v1);
-}
-)";
-
- auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Polyfill combinations
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_F(BuiltinPolyfillTest, BitshiftAndModulo) {
- auto* src = R"(
-fn f(x : i32, y : u32, z : u32) {
- let l = x << (y % z);
-}
-)";
-
- auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
- return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f(x : i32, y : u32, z : u32) {
- let l = (x << (tint_mod(y, z) & 31));
-}
-)";
-
- BuiltinPolyfill::Builtins builtins;
- builtins.bitshift_modulo = true;
- builtins.int_div_mod = true;
- DataMap data;
- data.Add<BuiltinPolyfill::Config>(builtins);
-
- auto got = Run<BuiltinPolyfill>(src, std::move(data));
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
deleted file mode 100644
index 7696ef2..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
+++ /dev/null
@@ -1,826 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
-
-#include <algorithm>
-#include <string>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/ast/disable_validation_attribute.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/utils/containers/transform.h"
-
-using namespace tint::core::number_suffixes; // NOLINT
-using namespace tint::core::fluent_types; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLClipDistance1);
-
-namespace tint::ast::transform {
-
-CanonicalizeEntryPointIO::CanonicalizeEntryPointIO() = default;
-CanonicalizeEntryPointIO::~CanonicalizeEntryPointIO() = default;
-
-namespace {
-
-/// Info for a struct member
-struct MemberInfo {
- /// The struct member item
- const StructMember* member;
- /// The struct member location if provided
- std::optional<uint32_t> location;
- /// The struct member blend_src if provided
- std::optional<uint32_t> blend_src;
- /// The struct member color if provided
- std::optional<uint32_t> color;
-};
-
-/// FXC is sensitive to field order in structures, this is used by StructMemberComparator to ensure
-/// that FXC is happy with the order of emitted fields.
-uint32_t BuiltinOrder(core::BuiltinValue builtin) {
- switch (builtin) {
- case core::BuiltinValue::kPosition:
- return 1;
- case core::BuiltinValue::kVertexIndex:
- return 2;
- case core::BuiltinValue::kInstanceIndex:
- return 3;
- case core::BuiltinValue::kFrontFacing:
- return 4;
- case core::BuiltinValue::kFragDepth:
- return 5;
- case core::BuiltinValue::kLocalInvocationId:
- return 6;
- case core::BuiltinValue::kLocalInvocationIndex:
- return 7;
- case core::BuiltinValue::kGlobalInvocationId:
- return 8;
- case core::BuiltinValue::kWorkgroupId:
- return 9;
- case core::BuiltinValue::kNumWorkgroups:
- return 10;
- case core::BuiltinValue::kSampleIndex:
- return 11;
- case core::BuiltinValue::kSampleMask:
- return 12;
- case core::BuiltinValue::kPointSize:
- return 13;
- case core::BuiltinValue::kClipDistances:
- return 14;
- default:
- break;
- }
- TINT_UNREACHABLE();
-}
-
-// Returns true if `attr` is a shader IO attribute.
-bool IsShaderIOAttribute(const Attribute* attr) {
- return attr->IsAnyOf<BuiltinAttribute, InterpolateAttribute, InvariantAttribute,
- LocationAttribute, ColorAttribute, BlendSrcAttribute>();
-}
-
-} // namespace
-
-/// PIMPL state for the transform
-struct CanonicalizeEntryPointIO::State {
- /// OutputValue represents a shader result that the wrapper function produces.
- struct OutputValue {
- /// The name of the output value.
- std::string name;
- /// The type of the output value.
- Type type;
- /// The shader IO attributes.
- tint::Vector<const Attribute*, 8> attributes;
- /// The value itself.
- const Expression* value;
- /// The output location.
- std::optional<uint32_t> location;
- /// The output blend_src.
- std::optional<uint32_t> blend_src;
- };
-
- /// The clone context.
- program::CloneContext& ctx;
- /// The program builder
- ProgramBuilder& b;
- /// The entry point function (AST).
- const Function* func_ast;
- /// The entry point function (SEM).
- const sem::Function* func_sem;
-
- /// The new entry point wrapper function's parameters.
- tint::Vector<const Parameter*, 8> wrapper_ep_parameters;
-
- /// The members of the wrapper function's struct parameter.
- tint::Vector<MemberInfo, 8> wrapper_struct_param_members;
- /// The name of the wrapper function's struct parameter.
- Symbol wrapper_struct_param_name;
- /// The parameters that will be passed to the original function.
- tint::Vector<const Expression*, 8> inner_call_parameters;
- /// The members of the wrapper function's struct return type.
- tint::Vector<MemberInfo, 8> wrapper_struct_output_members;
- /// The wrapper function output values.
- tint::Vector<OutputValue, 8> wrapper_output_values;
- /// The body of the wrapper function.
- tint::Vector<const Statement*, 8> wrapper_body;
- /// Input names used by the entrypoint
- std::unordered_set<std::string> input_names;
- /// A map of cloned attribute to builtin value
- Hashmap<const BuiltinAttribute*, core::BuiltinValue, 16> builtin_attrs;
- /// A map of builtin values to HLSL wave intrinsic functions.
- Hashmap<core::BuiltinValue, Symbol, 2> wave_intrinsics;
- /// The array length of the struct member with builtin attribute `clip_distances`. Now it is
- /// only set and used on HLSL and MSL backends.
- uint32_t clip_distances_size = 0;
-
- /// Constructor
- /// @param context the clone context
- /// @param builder the program builder
- /// @param function the entry point function
- State(program::CloneContext& context, ProgramBuilder& builder, const Function* function)
- : ctx(context), b(builder), func_ast(function), func_sem(ctx.src->Sem().Get(function)) {}
-
- /// Clones the attributes from @p in and adds it to @p out. If @p in is a builtin attribute,
- /// then builtin_attrs is updated with the builtin information.
- /// @param in the attribute to clone
- /// @param out the output Attributes
- template <size_t N>
- void CloneAttribute(const Attribute* in, tint::Vector<const Attribute*, N>& out) {
- auto* cloned = ctx.Clone(in);
- out.Push(cloned);
- if (auto* builtin = in->As<BuiltinAttribute>()) {
- builtin_attrs.Add(cloned->As<BuiltinAttribute>(), builtin->builtin);
- }
- }
-
- /// Clones the shader IO and internal attributes from @p in.
- /// @param in the attributes to clone
- /// @param do_interpolate whether to clone InterpolateAttribute
- /// @return the cloned attributes
- auto CloneShaderIOAttributes(tint::VectorRef<const Attribute*> in, bool do_interpolate) {
- tint::Vector<const Attribute*, 8> out;
- for (auto* attr : in) {
- if ((IsShaderIOAttribute(attr) &&
- (do_interpolate || !attr->template Is<InterpolateAttribute>())) ||
- attr->Is<ast::InternalAttribute>()) {
- CloneAttribute(attr, out);
- }
- }
- return out;
- }
-
- /// @param attr the input attribute
- /// @returns the builtin value of the attribute
- core::BuiltinValue BuiltinOf(const BuiltinAttribute* attr) {
- if (attr->generation_id == b.ID()) {
- // attr belongs to the target program.
- // Obtain the builtin value from #builtin_attrs.
- if (auto builtin = builtin_attrs.Get(attr)) {
- return *builtin;
- }
- } else {
- // attr belongs to the source program.
- // Obtain the builtin value from the semantic info.
- return attr->builtin;
- }
- TINT_ICE() << "could not obtain builtin value from attribute";
- }
-
- /// @param attrs the input attribute list
- /// @returns the builtin value if any of the attributes in @p attrs is a builtin attribute,
- /// otherwise core::BuiltinValue::kUndefined
- core::BuiltinValue BuiltinOf(VectorRef<const Attribute*> attrs) {
- if (auto* builtin = GetAttribute<BuiltinAttribute>(attrs)) {
- return BuiltinOf(builtin);
- }
- return core::BuiltinValue::kUndefined;
- }
-
- /// Create or return a symbol for the wrapper function's struct parameter.
- /// @returns the symbol for the struct parameter
- Symbol InputStructSymbol() {
- if (!wrapper_struct_param_name.IsValid()) {
- wrapper_struct_param_name = b.Sym();
- }
- return wrapper_struct_param_name;
- }
-
- /// Call a wave intrinsic function for the subgroup builtin contained in @p attrs, if any.
- /// @param attrs the attribute list that may contain a subgroup builtin
- /// @returns an expression that calls a HLSL wave intrinsic, or nullptr
- const ast::CallExpression* CallWaveIntrinsic(VectorRef<const Attribute*> attrs) {
- // Helper to make a wave intrinsic.
- auto make_intrinsic = [&](const char* name, HLSLWaveIntrinsic::Op op) {
- auto symbol = b.Symbols().New(name);
- b.Func(symbol, Empty, b.ty.u32(), nullptr,
- Vector{b.ASTNodes().Create<HLSLWaveIntrinsic>(b.ID(), b.AllocateNodeID(), op),
- b.Disable(DisabledValidation::kFunctionHasNoBody)});
- return symbol;
- };
-
- // Get or create the intrinsic function.
- auto builtin = BuiltinOf(attrs);
- auto intrinsic = wave_intrinsics.GetOrAdd(builtin, [&] {
- if (builtin == core::BuiltinValue::kSubgroupInvocationId) {
- return make_intrinsic("__WaveGetLaneIndex",
- HLSLWaveIntrinsic::Op::kWaveGetLaneIndex);
- }
- if (builtin == core::BuiltinValue::kSubgroupSize) {
- return make_intrinsic("__WaveGetLaneCount",
- HLSLWaveIntrinsic::Op::kWaveGetLaneCount);
- }
- return Symbol();
- });
- if (!intrinsic) {
- return nullptr;
- }
-
- // Call the intrinsic function.
- return b.Call(intrinsic);
- }
-
- /// Add a shader input to the entry point.
- /// @param name the name of the shader input
- /// @param type the type of the shader input
- /// @param location the location if provided
- /// @param color the color if provided
- /// @param attrs the attributes to apply to the shader input
- /// @returns an expression which evaluates to the value of the shader input
- const Expression* AddInput(std::string name,
- const core::type::Type* type,
- std::optional<uint32_t> location,
- std::optional<uint32_t> color,
- tint::Vector<const Attribute*, 8> attrs) {
- auto ast_type = CreateASTTypeFor(ctx, type);
-
- auto builtin_attr = BuiltinOf(attrs);
-
- // Move it to the new structure member list.
- Symbol symbol =
- input_names.emplace(name).second ? b.Symbols().Register(name) : b.Symbols().New(name);
- wrapper_struct_param_members.Push({b.Member(symbol, ast_type, std::move(attrs)), location,
- /* blend_src */ std::nullopt, color});
- const Expression* expr = b.MemberAccessor(InputStructSymbol(), symbol);
-
- // If this is a fragment position builtin, we need to invert the 'w' component of the
- // vector.
- if (builtin_attr == core::BuiltinValue::kPosition) {
- auto* xyz = b.MemberAccessor(expr, "xyz");
- auto* w = b.MemberAccessor(b.MemberAccessor(InputStructSymbol(), symbol), "w");
- expr = b.Call<vec4<f32>>(xyz, b.Div(1_a, w));
- }
-
- return expr;
- }
-
- /// Add a shader output to the entry point.
- /// @param name the name of the shader output
- /// @param type the type of the shader output
- /// @param location the location if provided
- /// @param blend_src the blend_src if provided
- /// @param attrs the attributes to apply to the shader output
- /// @param value the value of the shader output
- void AddOutput(std::string name,
- const core::type::Type* type,
- std::optional<uint32_t> location,
- std::optional<uint32_t> blend_src,
- tint::Vector<const Attribute*, 8> attrs,
- const Expression* value) {
- auto builtin_attr = BuiltinOf(attrs);
-
- if (func_ast->PipelineStage() == PipelineStage::kVertex &&
- builtin_attr == core::BuiltinValue::kClipDistances) {
- const auto* arrayType = type->As<core::type::Array>();
- if (DAWN_UNLIKELY(arrayType == nullptr || !arrayType->ConstantCount().has_value())) {
- TINT_ICE() << "The type of `clip_distances` is not a sized array";
- } else {
- clip_distances_size = *arrayType->ConstantCount();
- }
- }
-
- auto ast_type = CreateASTTypeFor(ctx, type);
-
- OutputValue output;
- output.name = name;
- output.type = ast_type;
- output.attributes = std::move(attrs);
- output.value = value;
- output.location = location;
- output.blend_src = blend_src;
- wrapper_output_values.Push(output);
- }
-
- /// Process a non-struct parameter.
- /// This creates a new object for the shader input, moving the shader IO
- /// attributes to it. It also adds an expression to the list of parameters
- /// that will be passed to the original function.
- /// @param param the original function parameter
- void ProcessNonStructParameter(const sem::Parameter* param) {
- if (auto* wave_intrinsic_call = CallWaveIntrinsic(param->Declaration()->attributes)) {
- inner_call_parameters.Push(wave_intrinsic_call);
- for (auto* attr : param->Declaration()->attributes) {
- ctx.Remove(param->Declaration()->attributes, attr);
- }
- return;
- }
-
- // Do not add interpolation attributes on vertex input
- bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kVertex;
- // Remove the shader IO attributes from the inner function parameter, and attach them to the
- // new object instead.
- tint::Vector<const Attribute*, 8> attributes;
- for (auto* attr : param->Declaration()->attributes) {
- if (IsShaderIOAttribute(attr)) {
- ctx.Remove(param->Declaration()->attributes, attr);
- if ((do_interpolate || !attr->Is<InterpolateAttribute>())) {
- CloneAttribute(attr, attributes);
- }
- }
- }
-
- auto name = param->Declaration()->name->symbol.Name();
- auto* input_expr = AddInput(name, param->Type(), param->Attributes().location,
- param->Attributes().color, std::move(attributes));
- inner_call_parameters.Push(input_expr);
- }
-
- /// Process a struct parameter.
- /// This creates new objects for each struct member, moving the shader IO
- /// attributes to them. It also creates the structure that will be passed to
- /// the original function.
- /// @param param the original function parameter
- void ProcessStructParameter(const sem::Parameter* param) {
- // Do not add interpolation attributes on vertex input
- bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kVertex;
-
- auto* str = param->Type()->As<sem::Struct>();
-
- // Recreate struct members in the outer entry point and build an initializer
- // list to pass them through to the inner function.
- tint::Vector<const Expression*, 8> inner_struct_values;
- for (auto* member : str->Members()) {
- if (DAWN_UNLIKELY(member->Type()->Is<core::type::Struct>())) {
- TINT_ICE() << "nested IO struct";
- }
-
- if (auto* wave_intrinsic_call = CallWaveIntrinsic(member->Declaration()->attributes)) {
- inner_struct_values.Push(wave_intrinsic_call);
- continue;
- }
-
- auto name = member->Name().Name();
-
- auto attributes =
- CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
- auto* input_expr = AddInput(name, member->Type(), member->Attributes().location,
- member->Attributes().color, std::move(attributes));
- inner_struct_values.Push(input_expr);
- }
-
- // Construct the original structure using the new shader input objects.
- inner_call_parameters.Push(
- b.Call(ctx.Clone(param->Declaration()->type), inner_struct_values));
- }
-
- /// Process the entry point return type.
- /// This generates a list of output values that are returned by the original
- /// function.
- /// @param inner_ret_type the original function return type
- /// @param original_result the result object produced by the original function
- void ProcessReturnType(const core::type::Type* inner_ret_type, Symbol original_result) {
- // Do not add interpolation attributes on fragment output
- bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kFragment;
- if (auto* str = inner_ret_type->As<sem::Struct>()) {
- for (auto* member : str->Members()) {
- if (DAWN_UNLIKELY(member->Type()->Is<core::type::Struct>())) {
- TINT_ICE() << "nested IO struct";
- }
-
- auto name = member->Name().Name();
- auto attributes =
- CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
-
- // Extract the original structure member.
- AddOutput(name, member->Type(), member->Attributes().location,
- member->Attributes().blend_src, std::move(attributes),
- b.MemberAccessor(original_result, name));
- }
- } else if (!inner_ret_type->Is<core::type::Void>()) {
- auto attributes =
- CloneShaderIOAttributes(func_ast->return_type_attributes, do_interpolate);
-
- // Propagate the non-struct return value as is.
- AddOutput("value", func_sem->ReturnType(), func_sem->ReturnLocation(),
- func_sem->ReturnIndex(), std::move(attributes), b.Expr(original_result));
- }
- }
-
- /// Comparison function used to reorder struct members such that all members with
- /// color attributes appear first (ordered by color slot), then location attributes (ordered by
- /// location slot), then blend_src attributes (ordered by blend_src slot), followed by those
- /// with builtin attributes (ordered by BuiltinOrder).
- /// @param x a struct member
- /// @param y another struct member
- /// @returns true if a comes before b
- bool StructMemberComparator(const MemberInfo& x, const MemberInfo& y) {
- if (x.color.has_value() && y.color.has_value() && x.color != y.color) {
- // Both have color attributes: smallest goes first.
- return x.color < y.color;
- } else if (x.color.has_value() != y.color.has_value()) {
- // The member with the color goes first
- return x.color.has_value();
- }
-
- if (x.location.has_value() && y.location.has_value() && x.location != y.location) {
- // Both have location attributes: smallest goes first.
- return x.location < y.location;
- } else if (x.location.has_value() != y.location.has_value()) {
- // The member with the location goes first
- return x.location.has_value();
- }
-
- if (x.blend_src.has_value() && y.blend_src.has_value() && x.blend_src != y.blend_src) {
- // Both have blend_src attributes: smallest goes first.
- return x.blend_src < y.blend_src;
- } else if (x.blend_src.has_value() != y.blend_src.has_value()) {
- // The member with the blend_src goes first
- return x.blend_src.has_value();
- }
-
- {
- auto* x_blt = GetAttribute<BuiltinAttribute>(x.member->attributes);
- auto* y_blt = GetAttribute<BuiltinAttribute>(y.member->attributes);
- if (x_blt && y_blt) {
- // Both are builtins: order matters for FXC.
- auto builtin_a = BuiltinOf(x_blt);
- auto builtin_b = BuiltinOf(y_blt);
- auto order_a = BuiltinOrder(builtin_a);
- auto order_b = BuiltinOrder(builtin_b);
- if (order_a != order_b) {
- return order_a < order_b;
- }
- } else if ((x_blt != nullptr) != (y_blt != nullptr)) {
- // The member with the builtin goes first
- return x_blt != nullptr;
- }
- }
-
- // Control flow reaches here if x is the same as y.
- // Sort algorithms sometimes do that.
- return false;
- }
-
- /// Create the wrapper function's struct parameter and type objects.
- void CreateInputStruct() {
- // Sort the struct members to satisfy HLSL interfacing matching rules.
- std::sort(wrapper_struct_param_members.begin(), wrapper_struct_param_members.end(),
- [&](auto& x, auto& y) { return StructMemberComparator(x, y); });
-
- auto members =
- tint::Transform(wrapper_struct_param_members, [](auto& info) { return info.member; });
-
- // Create the new struct type.
- auto struct_name = b.Sym();
- auto* in_struct = b.create<Struct>(b.Ident(struct_name), std::move(members), Empty);
- ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, in_struct);
-
- // Create a new function parameter using this struct type.
- auto* param = b.Param(InputStructSymbol(), b.ty(struct_name));
- wrapper_ep_parameters.Push(param);
- }
-
- /// Get an existing member symbol from a set of member names or create a new symbol
- /// @param member_names the set of the existing member names
- /// @param symbol_name the name of the symbol
- /// @returns the symbol that represents the member name we want
- Symbol GetOrCreateMemberName(std::unordered_set<std::string>* member_names,
- std::string symbol_name) {
- Symbol member_name;
- if (member_names->count(symbol_name)) {
- member_name = b.Symbols().New(symbol_name);
- } else {
- member_name = b.Symbols().Register(symbol_name);
- }
- member_names->insert(member_name.Name());
- return member_name;
- }
-
- /// Handle `clip_distances` in HLSL
- /// @param assignments the assignments to the members of the output wrapper struct
- /// @param member_names the set that contains all the member names in the output wrapper struct
- /// @param clip_distances_inner_array_name the `clip_distances` member in inner struct
- /// @param wrapper_struct_name the symbol of the output wrapper struct
- /// @param old_struct_member_attributes the attributes of the old `clip_distances` member
- void HandleClipDistancesInHLSL(
- tint::Vector<const Statement*, 8>* assignments,
- std::unordered_set<std::string>* member_names,
- const Symbol& clip_distances_inner_array_name,
- const Symbol& wrapper_struct_name,
- tint::Vector<const Attribute*, 8>&& old_struct_member_attributes) {
- auto TranslateClipDistancesIntoF32 =
- [&](const Symbol& new_member, core::u32 inner_array_index,
- tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
- assignments->Push(
- b.Assign(b.MemberAccessor(wrapper_struct_name, new_member),
- b.IndexAccessor(clip_distances_inner_array_name, inner_array_index)));
- wrapper_struct_output_members.Push({
- /* member */ b.Member(new_member, b.ty.f32(), std::move(new_member_attributes)),
- /* location */ std::nullopt,
- /* blend_src */ std::nullopt,
- /* color */ std::nullopt,
- });
- };
-
- auto TranslateClipDistancesIntoVector =
- [&](const Symbol& new_member, uint32_t vector_size, uint32_t inner_array_offset,
- tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
- for (uint32_t i = 0; i < vector_size; ++i) {
- assignments->Push(b.Assign(
- b.IndexAccessor(b.MemberAccessor(wrapper_struct_name, new_member), u32(i)),
- b.IndexAccessor(clip_distances_inner_array_name,
- u32(inner_array_offset + i))));
- }
- wrapper_struct_output_members.Push({
- /* member */ b.Member(new_member, b.ty.vec(b.ty.f32(), vector_size),
- std::move(new_member_attributes)),
- /* location */ std::nullopt,
- /* blend_src */ std::nullopt,
- /* color */ std::nullopt,
- });
- };
-
- // float clip_distance_0 : SV_ClipDistance0;
- auto attribute_vector_0 = std::move(old_struct_member_attributes);
- attribute_vector_0.Push(b.Disable(ast::DisabledValidation::kIgnoreClipDistancesType));
- auto new_member_0 = GetOrCreateMemberName(member_names, "clip_distance_0");
- if (clip_distances_size == 1u) {
- TranslateClipDistancesIntoF32(new_member_0, 0_u, std::move(attribute_vector_0));
- return;
- }
-
- // floatN clip_distance_0 : SV_ClipDistance0;
- uint32_t clip_distance0_size = std::min(clip_distances_size, 4u);
- TranslateClipDistancesIntoVector(new_member_0, clip_distance0_size, 0,
- std::move(attribute_vector_0));
-
- // It is enough to just generate SV_ClipDistance0.
- if (clip_distances_size <= 4u) {
- return;
- }
-
- // float clip_distance_1 : SV_ClipDistance1;
- auto attribute_vector_1 =
- Vector{b.ASTNodes().Create<HLSLClipDistance1>(b.ID(), b.AllocateNodeID())};
- auto new_member_1 = GetOrCreateMemberName(member_names, "clip_distance_1");
-
- if (clip_distances_size == 5u) {
- TranslateClipDistancesIntoF32(new_member_1, 4_u, std::move(attribute_vector_1));
- return;
- }
-
- // floatN clip_distance_1 : SV_ClipDistance1;
- uint32_t clip_distances1_size = clip_distances_size - 4u;
- TranslateClipDistancesIntoVector(new_member_1, clip_distances1_size, 4u,
- std::move(attribute_vector_1));
- }
-
- /// Create and return the wrapper function's struct result object.
- /// @returns the struct type
- Struct* CreateOutputStruct() {
- tint::Vector<const Statement*, 8> assignments;
-
- auto wrapper_result = b.Symbols().New("wrapper_result");
-
- // Create the struct members and their corresponding assignment statements.
- std::unordered_set<std::string> member_names;
- for (auto& outval : wrapper_output_values) {
- auto* builtin_attribute = GetAttribute<BuiltinAttribute>(outval.attributes);
- if (builtin_attribute != nullptr &&
- builtin_attribute->builtin == core::BuiltinValue::kClipDistances) {
- Symbol clip_distances_inner_array = b.Symbols().New("tmp_inner_clip_distances");
-
- // Consume outval.type in the let statement as in HLSL `SV_ClipDistance` always has
- // different type.
- assignments.Push(
- b.Decl(b.Let(clip_distances_inner_array, outval.type, outval.value)));
- HandleClipDistancesInHLSL(&assignments, &member_names, clip_distances_inner_array,
- wrapper_result, std::move(outval.attributes));
- continue;
- }
-
- // Use the original output name, unless that is already taken.
- Symbol name = GetOrCreateMemberName(&member_names, outval.name);
-
- assignments.Push(b.Assign(b.MemberAccessor(wrapper_result, name), outval.value));
-
- wrapper_struct_output_members.Push({
- /* member */ b.Member(name, outval.type, std::move(outval.attributes)),
- /* location */ outval.location,
- /* blend_src */ outval.blend_src,
- /* color */ std::nullopt,
- });
- }
-
- // Sort the struct members to satisfy HLSL interfacing matching rules.
- std::sort(wrapper_struct_output_members.begin(), wrapper_struct_output_members.end(),
- [&](auto& x, auto& y) { return StructMemberComparator(x, y); });
-
- tint::Vector<const StructMember*, 8> members;
- for (auto& mem : wrapper_struct_output_members) {
- members.Push(mem.member);
- }
-
- // Create the new struct type.
- auto* out_struct = b.create<Struct>(b.Ident(b.Sym()), std::move(members), Empty);
- ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, out_struct);
-
- // Create the output struct object, assign its members, and return it.
- auto* result_object = b.Var(wrapper_result, b.ty(out_struct->name->symbol));
- wrapper_body.Push(b.Decl(result_object));
- for (auto* assignment : assignments) {
- wrapper_body.Push(assignment);
- }
- wrapper_body.Push(b.Return(wrapper_result));
-
- return out_struct;
- }
-
- // Recreate the original function without entry point attributes and call it.
- /// @returns the inner function call expression
- const CallExpression* CallInnerFunction() {
- // Add a suffix to the function name, as the wrapper function will take the original entry
- // point name.
- auto ep_name = func_ast->name->symbol.Name();
- auto inner_name = b.Symbols().New(ep_name + "_inner");
-
- // Clone everything, dropping the function and return type attributes.
- // The parameter attributes will have already been stripped during
- // processing.
- auto* inner_function = b.create<Function>(b.Ident(inner_name), ctx.Clone(func_ast->params),
- ctx.Clone(func_ast->return_type),
- ctx.Clone(func_ast->body), Empty, Empty);
- ctx.Replace(func_ast, inner_function);
-
- // Call the function.
- return b.Call(inner_function->name->symbol, inner_call_parameters);
- }
-
- /// Process the entry point function.
- void Process() {
- // Exit early if there is no shader IO to handle.
- if (func_sem->Parameters().Length() == 0 &&
- func_sem->ReturnType()->Is<core::type::Void>()) {
- return;
- }
-
- // Process the entry point parameters, collecting those that need to be
- // aggregated into a single structure.
- if (!func_sem->Parameters().IsEmpty()) {
- for (auto* param : func_sem->Parameters()) {
- if (param->Type()->Is<core::type::Struct>()) {
- ProcessStructParameter(param);
- } else {
- ProcessNonStructParameter(param);
- }
- }
-
- // Create a structure parameter for the outer entry point if necessary.
- if (!wrapper_struct_param_members.IsEmpty()) {
- CreateInputStruct();
- }
- }
-
- // Recreate the original function and call it.
- auto* call_inner = CallInnerFunction();
-
- // Process the return type, and start building the wrapper function body.
- std::function<Type()> wrapper_ret_type = [&] { return b.ty.void_(); };
- if (func_sem->ReturnType()->Is<core::type::Void>()) {
- // The function call is just a statement with no result.
- wrapper_body.Push(b.CallStmt(call_inner));
- } else {
- // Capture the result of calling the original function.
- auto* inner_result = b.Let(b.Symbols().New("inner_result"), call_inner);
- wrapper_body.Push(b.Decl(inner_result));
-
- // Process the original return type to determine the outputs that the
- // outer function needs to produce.
- ProcessReturnType(func_sem->ReturnType(), inner_result->name->symbol);
- }
-
- // Produce the entry point outputs, if necessary.
- if (!wrapper_output_values.IsEmpty()) {
- auto* output_struct = CreateOutputStruct();
- wrapper_ret_type = [&, output_struct] { return b.ty(output_struct->name->symbol); };
- }
-
- // Create the wrapper entry point function.
- Symbol name = ctx.Clone(func_ast->name->symbol);
- auto* wrapper_func =
- b.create<Function>(b.Ident(name), wrapper_ep_parameters, b.ty(wrapper_ret_type()),
- b.Block(wrapper_body), ctx.Clone(func_ast->attributes), Empty);
- ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), func_ast, wrapper_func);
- }
-};
-
-Transform::ApplyResult CanonicalizeEntryPointIO::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- // Remove entry point IO attributes from struct declarations.
- // New structures will be created for each entry point, as necessary.
- for (auto* ty : src.AST().TypeDecls()) {
- if (auto* struct_ty = ty->As<Struct>()) {
- for (auto* member : struct_ty->members) {
- for (auto* attr : member->attributes) {
- if (IsShaderIOAttribute(attr)) {
- ctx.Remove(member->attributes, attr);
- }
- }
- }
- }
- }
-
- for (auto* func_ast : src.AST().Functions()) {
- if (!func_ast->IsEntryPoint()) {
- continue;
- }
-
- State state(ctx, b, func_ast);
- state.Process();
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::HLSLWaveIntrinsic(GenerationID pid, NodeID nid, Op o)
- : Base(pid, nid, Empty), op(o) {}
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::~HLSLWaveIntrinsic() = default;
-std::string CanonicalizeEntryPointIO::HLSLWaveIntrinsic::InternalName() const {
- StringStream ss;
- switch (op) {
- case Op::kWaveGetLaneCount:
- return "intrinsic_wave_get_lane_count";
- case Op::kWaveGetLaneIndex:
- return "intrinsic_wave_get_lane_index";
- }
- return ss.str();
-}
-
-const CanonicalizeEntryPointIO::HLSLWaveIntrinsic*
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Clone(ast::CloneContext& ctx) const {
- return ctx.dst->ASTNodes().Create<CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
- ctx.dst->ID(), ctx.dst->AllocateNodeID(), op);
-}
-
-CanonicalizeEntryPointIO::HLSLClipDistance1::HLSLClipDistance1(GenerationID pid, NodeID nid)
- : Base(pid, nid, Empty) {}
-
-CanonicalizeEntryPointIO::HLSLClipDistance1::~HLSLClipDistance1() = default;
-
-std::string CanonicalizeEntryPointIO::HLSLClipDistance1::InternalName() const {
- return "SV_ClipDistance1";
-}
-
-const CanonicalizeEntryPointIO::HLSLClipDistance1*
-CanonicalizeEntryPointIO::HLSLClipDistance1::Clone(ast::CloneContext& ctx) const {
- return ctx.dst->ASTNodes().Create<CanonicalizeEntryPointIO::HLSLClipDistance1>(
- ctx.dst->ID(), ctx.dst->AllocateNodeID());
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
deleted file mode 100644
index 39eb99b..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// CanonicalizeEntryPointIO is a transform used to rewrite shader entry point
-/// interfaces into a form that the generators can handle. Each entry point
-/// function is stripped of all shader IO attributes and wrapped in a function
-/// that provides the shader interface.
-/// The transform config determines whether to use global variables, structures,
-/// or parameters for the shader inputs and outputs, and optionally adds
-/// additional builtins to the shader interface.
-///
-/// Before:
-/// ```
-/// struct Locations{
-/// @location(1) loc1 : f32;
-/// @location(2) loc2 : vec4<u32>;
-/// };
-///
-/// @fragment
-/// fn frag_main(@builtin(position) coord : vec4<f32>,
-/// locations : Locations) -> @location(0) f32 {
-/// if (coord.w > 1.0) {
-/// return 0.0;
-/// }
-/// var col : f32 = (coord.x * locations.loc1);
-/// return col;
-/// }
-/// ```
-///
-/// After (using structures for all parameters):
-/// ```
-/// struct Locations{
-/// loc1 : f32;
-/// loc2 : vec4<u32>;
-/// };
-///
-/// struct frag_main_in {
-/// @builtin(position) coord : vec4<f32>;
-/// @location(1) loc1 : f32;
-/// @location(2) loc2 : vec4<u32>
-/// };
-///
-/// struct frag_main_out {
-/// @location(0) loc0 : f32;
-/// };
-///
-/// fn frag_main_inner(coord : vec4<f32>,
-/// locations : Locations) -> f32 {
-/// if (coord.w > 1.0) {
-/// return 0.0;
-/// }
-/// var col : f32 = (coord.x * locations.loc1);
-/// return col;
-/// }
-///
-/// @fragment
-/// fn frag_main(in : frag_main_in) -> frag_main_out {
-/// let inner_retval = frag_main_inner(in.coord, Locations(in.loc1, in.loc2));
-/// var wrapper_result : frag_main_out;
-/// wrapper_result.loc0 = inner_retval;
-/// return wrapper_result;
-/// }
-/// ```
-///
-/// @note Depends on the following transforms to have been run first:
-/// * Unshadow
-class CanonicalizeEntryPointIO final : public Castable<CanonicalizeEntryPointIO, Transform> {
- public:
- /// HLSLWaveIntrinsic is an InternalAttribute that is used to decorate a stub function so that
- /// the HLSL backend transforms this into calls to Wave* intrinsic functions.
- class HLSLWaveIntrinsic final : public Castable<HLSLWaveIntrinsic, InternalAttribute> {
- public:
- /// Wave intrinsic op
- enum class Op {
- kWaveGetLaneIndex,
- kWaveGetLaneCount,
- };
-
- /// Constructor
- /// @param pid the identifier of the program that owns this node
- /// @param nid the unique node identifier
- /// @param o the op of the wave intrinsic
- HLSLWaveIntrinsic(GenerationID pid, NodeID nid, Op o);
- /// Destructor
- ~HLSLWaveIntrinsic() override;
-
- /// @copydoc InternalAttribute::InternalName
- std::string InternalName() const override;
-
- /// Performs a deep clone of this object using the program::CloneContext `ctx`.
- /// @param ctx the clone context
- /// @return the newly cloned object
- const HLSLWaveIntrinsic* Clone(CloneContext& ctx) const override;
-
- /// The op of the intrinsic
- const Op op;
- };
-
- /// HLSLClipDistance1 is an InternalAttribute that is used to represent `SV_ClipDistance1`.
- class HLSLClipDistance1 final : public Castable<HLSLClipDistance1, InternalAttribute> {
- public:
- /// Constructor
- /// @param pid the identifier of the program that owns this node
- /// @param nid the unique node identifier
- HLSLClipDistance1(GenerationID pid, NodeID nid);
- /// Destructor
- ~HLSLClipDistance1() override;
-
- /// @copydoc InternalAttribute::InternalName
- std::string InternalName() const override;
-
- /// Performs a deep clone of this object using the program::CloneContext `ctx`.
- /// @param ctx the clone context
- /// @return the newly cloned object
- const HLSLClipDistance1* Clone(CloneContext& ctx) const override;
- };
-
- /// Constructor
- CanonicalizeEntryPointIO();
- ~CanonicalizeEntryPointIO() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_H_
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
deleted file mode 100644
index 8808eaf..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
+++ /dev/null
@@ -1,1716 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using CanonicalizeEntryPointIOTest = TransformTest;
-
-TEST_F(CanonicalizeEntryPointIOTest, NoShaderIO) {
- // Test that we do not introduce wrapper functions when there is no shader IO
- // to process.
- auto* src = R"(
-@fragment
-fn frag_main() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main() {
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameters_Hlsl) {
- auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-@fragment
-fn frag_main(@location(1) loc1 : f32,
- @location(2) @interpolate(flat) loc2 : vec4<u32>,
- @builtin(position) coord : vec4<f32>,
- @color(3) color : vec4<f32>) {
- var col : f32 = (coord.x * loc1) + color.g;
-}
-)";
-
- auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct tint_symbol_1 {
- @color(3)
- color : vec4<f32>,
- @location(1)
- loc1 : f32,
- @location(2) @interpolate(flat)
- loc2 : vec4<u32>,
- @builtin(position)
- coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>, color : vec4<f32>) {
- var col : f32 = ((coord.x * loc1) + color.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(tint_symbol.loc1, tint_symbol.loc2, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.color);
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias) {
- auto* src = R"(
-alias myf32 = f32;
-
-@fragment
-fn frag_main(@location(1) loc1 : myf32) {
- var x : myf32 = loc1;
-}
-)";
-
- auto* expect = R"(
-alias myf32 = f32;
-
-struct tint_symbol_1 {
- @location(1)
- loc1 : f32,
-}
-
-fn frag_main_inner(loc1 : myf32) {
- var x : myf32 = loc1;
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(tint_symbol.loc1);
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main(@location(1) loc1 : myf32) {
- var x : myf32 = loc1;
-}
-
-alias myf32 = f32;
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(1)
- loc1 : f32,
-}
-
-fn frag_main_inner(loc1 : myf32) {
- var x : myf32 = loc1;
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(tint_symbol.loc1);
-}
-
-alias myf32 = f32;
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl) {
- auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct FragBuiltins {
- @builtin(position) coord : vec4<f32>,
-};
-struct FragLocations {
- @location(1) loc1 : f32,
- @location(2) @interpolate(flat) loc2 : vec4<u32>,
-};
-struct FragColors {
- @color(3) col3 : vec4<f32>,
- @color(1) col1 : vec4<u32>,
- @color(2) col2 : vec4<i32>,
-};
-
-@fragment
-fn frag_main(@location(0) loc0 : f32,
- locations : FragLocations,
- builtins : FragBuiltins,
- colors : FragColors) {
- var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-)";
-
- auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct FragBuiltins {
- coord : vec4<f32>,
-}
-
-struct FragLocations {
- loc1 : f32,
- loc2 : vec4<u32>,
-}
-
-struct FragColors {
- col3 : vec4<f32>,
- col1 : vec4<u32>,
- col2 : vec4<i32>,
-}
-
-struct tint_symbol_1 {
- @color(1)
- col1 : vec4<u32>,
- @color(2)
- col2 : vec4<i32>,
- @color(3)
- col3 : vec4<f32>,
- @location(0)
- loc0 : f32,
- @location(1)
- loc1 : f32,
- @location(2) @interpolate(flat)
- loc2 : vec4<u32>,
- @builtin(position)
- coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins, colors : FragColors) {
- var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w))), FragColors(tint_symbol.col3, tint_symbol.col1, tint_symbol.col2));
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl_OutOfOrder) {
- auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-@fragment
-fn frag_main(@location(0) loc0 : f32,
- locations : FragLocations,
- builtins : FragBuiltins,
- colors : FragColors) {
- var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-struct FragBuiltins {
- @builtin(position) coord : vec4<f32>,
-};
-struct FragLocations {
- @location(1) loc1 : f32,
- @location(2) @interpolate(flat) loc2 : vec4<u32>,
-};
-struct FragColors {
- @color(3) col3 : vec4<f32>,
- @color(1) col1 : vec4<u32>,
- @color(2) col2 : vec4<i32>,
-};
-)";
-
- auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct tint_symbol_1 {
- @color(1)
- col1 : vec4<u32>,
- @color(2)
- col2 : vec4<i32>,
- @color(3)
- col3 : vec4<f32>,
- @location(0)
- loc0 : f32,
- @location(1)
- loc1 : f32,
- @location(2) @interpolate(flat)
- loc2 : vec4<u32>,
- @builtin(position)
- coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins, colors : FragColors) {
- var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w))), FragColors(tint_symbol.col3, tint_symbol.col1, tint_symbol.col2));
-}
-
-struct FragBuiltins {
- coord : vec4<f32>,
-}
-
-struct FragLocations {
- loc1 : f32,
- loc2 : vec4<u32>,
-}
-
-struct FragColors {
- col3 : vec4<f32>,
- col1 : vec4<u32>,
- col2 : vec4<i32>,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Hlsl) {
- auto* src = R"(
-@fragment
-fn frag_main() -> @builtin(frag_depth) f32 {
- return 1.0;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- @builtin(frag_depth)
- value : f32,
-}
-
-fn frag_main_inner() -> f32 {
- return 1.0;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
- let inner_result = frag_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.value = inner_result;
- return wrapper_result;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl) {
- auto* src = R"(
-struct FragOutput {
- @location(0) color : vec4<f32>,
- @builtin(frag_depth) depth : f32,
- @builtin(sample_mask) mask : u32,
-};
-
-@fragment
-fn frag_main() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-)";
-
- auto* expect = R"(
-struct FragOutput {
- color : vec4<f32>,
- depth : f32,
- mask : u32,
-}
-
-struct tint_symbol {
- @location(0)
- color : vec4<f32>,
- @builtin(frag_depth)
- depth : f32,
- @builtin(sample_mask)
- mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
- let inner_result = frag_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.color = inner_result.color;
- wrapper_result.depth = inner_result.depth;
- wrapper_result.mask = inner_result.mask;
- return wrapper_result;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-
-struct FragOutput {
- @location(0) color : vec4<f32>,
- @builtin(frag_depth) depth : f32,
- @builtin(sample_mask) mask : u32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- @location(0)
- color : vec4<f32>,
- @builtin(frag_depth)
- depth : f32,
- @builtin(sample_mask)
- mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
- let inner_result = frag_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.color = inner_result.color;
- wrapper_result.depth = inner_result.depth;
- wrapper_result.mask = inner_result.mask;
- return wrapper_result;
-}
-
-struct FragOutput {
- color : vec4<f32>,
- depth : f32,
- mask : u32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Hlsl) {
- auto* src = R"(
-struct FragmentInput {
- @location(0) value : f32,
- @location(1) mul : f32,
-};
-
-fn foo(x : FragmentInput) -> f32 {
- return x.value * x.mul;
-}
-
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-)";
-
- auto* expect = R"(
-struct FragmentInput {
- value : f32,
- mul : f32,
-}
-
-fn foo(x : FragmentInput) -> f32 {
- return (x.value * x.mul);
-}
-
-struct tint_symbol_1 {
- @location(0)
- value : f32,
- @location(1)
- mul : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
- frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
-}
-
-struct tint_symbol_3 {
- @location(0)
- value : f32,
- @location(1)
- mul : f32,
-}
-
-fn frag_main2_inner(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(tint_symbol_2 : tint_symbol_3) {
- frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Hlsl_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-fn foo(x : FragmentInput) -> f32 {
- return x.value * x.mul;
-}
-
-struct FragmentInput {
- @location(0) value : f32,
- @location(1) mul : f32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(0)
- value : f32,
- @location(1)
- mul : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
- frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
-}
-
-struct tint_symbol_3 {
- @location(0)
- value : f32,
- @location(1)
- mul : f32,
-}
-
-fn frag_main2_inner(inputs : FragmentInput) {
- var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(tint_symbol_2 : tint_symbol_3) {
- frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
-}
-
-fn foo(x : FragmentInput) -> f32 {
- return (x.value * x.mul);
-}
-
-struct FragmentInput {
- value : f32,
- mul : f32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) {
- auto* src = R"(
-struct FragmentInput {
- @location(0) col1 : f32,
- @location(1) col2 : f32,
-};
-
-var<private> global_inputs : FragmentInput;
-
-fn foo() -> f32 {
- return global_inputs.col1 * 0.5;
-}
-
-fn bar() -> f32 {
- return global_inputs.col2 * 2.0;
-}
-
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-)";
-
- auto* expect = R"(
-struct FragmentInput {
- col1 : f32,
- col2 : f32,
-}
-
-var<private> global_inputs : FragmentInput;
-
-fn foo() -> f32 {
- return (global_inputs.col1 * 0.5);
-}
-
-fn bar() -> f32 {
- return (global_inputs.col2 * 2.0);
-}
-
-struct tint_symbol_1 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
- frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-
-fn foo() -> f32 {
- return global_inputs.col1 * 0.5;
-}
-
-fn bar() -> f32 {
- return global_inputs.col2 * 2.0;
-}
-
-var<private> global_inputs : FragmentInput;
-
-struct FragmentInput {
- @location(0) col1 : f32,
- @location(1) col2 : f32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
- frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
-}
-
-fn foo() -> f32 {
- return (global_inputs.col1 * 0.5);
-}
-
-fn bar() -> f32 {
- return (global_inputs.col2 * 2.0);
-}
-
-var<private> global_inputs : FragmentInput;
-
-struct FragmentInput {
- col1 : f32,
- col2 : f32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) {
- auto* src = R"(
-alias myf32 = f32;
-
-struct FragmentInput {
- @location(0) col1 : myf32,
- @location(1) col2 : myf32,
-};
-
-struct FragmentOutput {
- @location(0) col1 : myf32,
- @location(1) col2 : myf32,
-};
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
- return x.col1;
-}
-
-@fragment
-fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
- var x : myf32 = foo(inputs);
- return MyFragmentOutput(x, inputs.col2);
-}
-)";
-
- auto* expect = R"(
-alias myf32 = f32;
-
-struct FragmentInput {
- col1 : myf32,
- col2 : myf32,
-}
-
-struct FragmentOutput {
- col1 : myf32,
- col2 : myf32,
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
- return x.col1;
-}
-
-struct tint_symbol_1 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-struct tint_symbol_2 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
- var x : myf32 = foo(inputs);
- return MyFragmentOutput(x, inputs.col2);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
- let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
- var wrapper_result : tint_symbol_2;
- wrapper_result.col1 = inner_result.col1;
- wrapper_result.col2 = inner_result.col2;
- return wrapper_result;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
- var x : myf32 = foo(inputs);
- return MyFragmentOutput(x, inputs.col2);
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
- return x.col1;
-}
-
-struct FragmentInput {
- @location(0) col1 : myf32,
- @location(1) col2 : myf32,
-};
-
-struct FragmentOutput {
- @location(0) col1 : myf32,
- @location(1) col2 : myf32,
-};
-
-alias myf32 = f32;
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-struct tint_symbol_2 {
- @location(0)
- col1 : f32,
- @location(1)
- col2 : f32,
-}
-
-fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
- var x : myf32 = foo(inputs);
- return MyFragmentOutput(x, inputs.col2);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
- let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
- var wrapper_result : tint_symbol_2;
- wrapper_result.col1 = inner_result.col1;
- wrapper_result.col2 = inner_result.col2;
- return wrapper_result;
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
- return x.col1;
-}
-
-struct FragmentInput {
- col1 : myf32,
- col2 : myf32,
-}
-
-struct FragmentOutput {
- col1 : myf32,
- col2 : myf32,
-}
-
-alias myf32 = f32;
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) {
- auto* src = R"(
-struct VertexOut {
- @builtin(position) pos : vec4<f32>,
- @location(1) @interpolate(flat) loc1 : f32,
- @location(2) @interpolate(linear, sample) loc2 : f32,
- @location(3) @interpolate(perspective, centroid) loc3 : f32,
-};
-
-struct FragmentIn {
- @location(1) @interpolate(flat) loc1 : f32,
- @location(2) @interpolate(linear, sample) loc2 : f32,
-};
-
-@vertex
-fn vert_main() -> VertexOut {
- return VertexOut();
-}
-
-@fragment
-fn frag_main(inputs : FragmentIn,
- @location(3) @interpolate(perspective, centroid) loc3 : f32) {
- let x = inputs.loc1 + inputs.loc2 + loc3;
-}
-)";
-
- auto* expect = R"(
-struct VertexOut {
- pos : vec4<f32>,
- loc1 : f32,
- loc2 : f32,
- loc3 : f32,
-}
-
-struct FragmentIn {
- loc1 : f32,
- loc2 : f32,
-}
-
-struct tint_symbol {
- @location(1) @interpolate(flat)
- loc1 : f32,
- @location(2) @interpolate(linear, sample)
- loc2 : f32,
- @location(3) @interpolate(perspective, centroid)
- loc3 : f32,
- @builtin(position)
- pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
- let inner_result = vert_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.pos = inner_result.pos;
- wrapper_result.loc1 = inner_result.loc1;
- wrapper_result.loc2 = inner_result.loc2;
- wrapper_result.loc3 = inner_result.loc3;
- return wrapper_result;
-}
-
-struct tint_symbol_2 {
- @location(1) @interpolate(flat)
- loc1 : f32,
- @location(2) @interpolate(linear, sample)
- loc2 : f32,
- @location(3) @interpolate(perspective, centroid)
- loc3 : f32,
-}
-
-fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
- let x = ((inputs.loc1 + inputs.loc2) + loc3);
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
- frag_main_inner(FragmentIn(tint_symbol_1.loc1, tint_symbol_1.loc2), tint_symbol_1.loc3);
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main(inputs : FragmentIn,
- @location(3) @interpolate(perspective, centroid) loc3 : f32) {
- let x = inputs.loc1 + inputs.loc2 + loc3;
-}
-
-@vertex
-fn vert_main() -> VertexOut {
- return VertexOut();
-}
-
-struct VertexOut {
- @builtin(position) pos : vec4<f32>,
- @location(1) @interpolate(flat) loc1 : f32,
- @location(2) @interpolate(linear, sample) loc2 : f32,
- @location(3) @interpolate(perspective, centroid) loc3 : f32,
-};
-
-struct FragmentIn {
- @location(1) @interpolate(flat) loc1: f32,
- @location(2) @interpolate(linear, sample) loc2 : f32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(1) @interpolate(flat)
- loc1 : f32,
- @location(2) @interpolate(linear, sample)
- loc2 : f32,
- @location(3) @interpolate(perspective, centroid)
- loc3 : f32,
-}
-
-fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
- let x = ((inputs.loc1 + inputs.loc2) + loc3);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
- frag_main_inner(FragmentIn(tint_symbol.loc1, tint_symbol.loc2), tint_symbol.loc3);
-}
-
-struct tint_symbol_2 {
- @location(1) @interpolate(flat)
- loc1 : f32,
- @location(2) @interpolate(linear, sample)
- loc2 : f32,
- @location(3) @interpolate(perspective, centroid)
- loc3 : f32,
- @builtin(position)
- pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn vert_main() -> tint_symbol_2 {
- let inner_result = vert_main_inner();
- var wrapper_result : tint_symbol_2;
- wrapper_result.pos = inner_result.pos;
- wrapper_result.loc1 = inner_result.loc1;
- wrapper_result.loc2 = inner_result.loc2;
- wrapper_result.loc3 = inner_result.loc3;
- return wrapper_result;
-}
-
-struct VertexOut {
- pos : vec4<f32>,
- loc1 : f32,
- loc2 : f32,
- loc3 : f32,
-}
-
-struct FragmentIn {
- loc1 : f32,
- loc2 : f32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) {
- auto* src = R"(
-struct VertexOut {
- @builtin(position) @invariant pos : vec4<f32>,
-};
-
-@vertex
-fn main1() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn main2() -> @builtin(position) @invariant vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct VertexOut {
- pos : vec4<f32>,
-}
-
-struct tint_symbol {
- @builtin(position) @invariant
- pos : vec4<f32>,
-}
-
-fn main1_inner() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn main1() -> tint_symbol {
- let inner_result = main1_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.pos = inner_result.pos;
- return wrapper_result;
-}
-
-struct tint_symbol_1 {
- @builtin(position) @invariant
- value : vec4<f32>,
-}
-
-fn main2_inner() -> vec4<f32> {
- return vec4<f32>();
-}
-
-@vertex
-fn main2() -> tint_symbol_1 {
- let inner_result_1 = main2_inner();
- var wrapper_result_1 : tint_symbol_1;
- wrapper_result_1.value = inner_result_1;
- return wrapper_result_1;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn main1() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn main2() -> @builtin(position) @invariant vec4<f32> {
- return vec4<f32>();
-}
-
-struct VertexOut {
- @builtin(position) @invariant pos : vec4<f32>,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- @builtin(position) @invariant
- pos : vec4<f32>,
-}
-
-fn main1_inner() -> VertexOut {
- return VertexOut();
-}
-
-@vertex
-fn main1() -> tint_symbol {
- let inner_result = main1_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.pos = inner_result.pos;
- return wrapper_result;
-}
-
-struct tint_symbol_1 {
- @builtin(position) @invariant
- value : vec4<f32>,
-}
-
-fn main2_inner() -> vec4<f32> {
- return vec4<f32>();
-}
-
-@vertex
-fn main2() -> tint_symbol_1 {
- let inner_result_1 = main2_inner();
- var wrapper_result_1 : tint_symbol_1;
- wrapper_result_1.value = inner_result_1;
- return wrapper_result_1;
-}
-
-struct VertexOut {
- pos : vec4<f32>,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes) {
- auto* src = R"(
-struct FragmentInput {
- @size(16) @location(1) value : f32,
- @builtin(position) @align(32) coord : vec4<f32>,
- @location(0) @interpolate(linear, sample) @align(128) loc0 : f32,
-};
-
-struct FragmentOutput {
- @size(16) @location(1) @interpolate(flat) value : f32,
-};
-
-@fragment
-fn frag_main(inputs : FragmentInput) -> FragmentOutput {
- return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
-}
-)";
-
- auto* expect = R"(
-struct FragmentInput {
- @size(16)
- value : f32,
- @align(32)
- coord : vec4<f32>,
- @align(128)
- loc0 : f32,
-}
-
-struct FragmentOutput {
- @size(16)
- value : f32,
-}
-
-struct tint_symbol_1 {
- @location(0) @interpolate(linear, sample)
- loc0 : f32,
- @location(1)
- value : f32,
- @builtin(position)
- coord : vec4<f32>,
-}
-
-struct tint_symbol_2 {
- @location(1)
- value : f32,
-}
-
-fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
- return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
- let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.loc0));
- var wrapper_result : tint_symbol_2;
- wrapper_result.value = inner_result.value;
- return wrapper_result;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn frag_main(inputs : FragmentInput) -> FragmentOutput {
- return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
-}
-
-struct FragmentInput {
- @size(16) @location(1) value : f32,
- @builtin(position) @align(32) coord : vec4<f32>,
- @location(0) @interpolate(linear, sample) @align(128) loc0 : f32,
-};
-
-struct FragmentOutput {
- @size(16) @location(1) @interpolate(flat) value : f32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol_1 {
- @location(0) @interpolate(linear, sample)
- loc0 : f32,
- @location(1)
- value : f32,
- @builtin(position)
- coord : vec4<f32>,
-}
-
-struct tint_symbol_2 {
- @location(1)
- value : f32,
-}
-
-fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
- return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
- let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.loc0));
- var wrapper_result : tint_symbol_2;
- wrapper_result.value = inner_result.value;
- return wrapper_result;
-}
-
-struct FragmentInput {
- @size(16)
- value : f32,
- @align(32)
- coord : vec4<f32>,
- @align(128)
- loc0 : f32,
-}
-
-struct FragmentOutput {
- @size(16)
- value : f32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
- auto* src = R"(
-struct VertexOutput {
- @location(1) @interpolate(flat) b : u32,
- @builtin(position) pos : vec4<f32>,
- @location(3) @interpolate(flat) d : u32,
- @location(0) a : f32,
- @location(2) @interpolate(flat) c : i32,
-};
-
-struct FragmentInputExtra {
- @location(3) @interpolate(flat) d : u32,
- @builtin(position) pos : vec4<f32>,
- @location(0) a : f32,
-};
-
-@vertex
-fn vert_main() -> VertexOutput {
- return VertexOutput();
-}
-
-@fragment
-fn frag_main(@builtin(front_facing) ff : bool,
- @location(2) @interpolate(flat) c : i32,
- inputs : FragmentInputExtra,
- @location(1) @interpolate(flat) b : u32) {
-}
-)";
-
- auto* expect = R"(
-struct VertexOutput {
- b : u32,
- pos : vec4<f32>,
- d : u32,
- a : f32,
- c : i32,
-}
-
-struct FragmentInputExtra {
- d : u32,
- pos : vec4<f32>,
- a : f32,
-}
-
-struct tint_symbol {
- @location(0)
- a : f32,
- @location(1) @interpolate(flat)
- b : u32,
- @location(2) @interpolate(flat)
- c : i32,
- @location(3) @interpolate(flat)
- d : u32,
- @builtin(position)
- pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOutput {
- return VertexOutput();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
- let inner_result = vert_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.b = inner_result.b;
- wrapper_result.pos = inner_result.pos;
- wrapper_result.d = inner_result.d;
- wrapper_result.a = inner_result.a;
- wrapper_result.c = inner_result.c;
- return wrapper_result;
-}
-
-struct tint_symbol_2 {
- @location(0)
- a : f32,
- @location(1) @interpolate(flat)
- b : u32,
- @location(2) @interpolate(flat)
- c : i32,
- @location(3) @interpolate(flat)
- d : u32,
- @builtin(position)
- pos : vec4<f32>,
- @builtin(front_facing)
- ff : bool,
-}
-
-fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
- frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, vec4<f32>(tint_symbol_1.pos.xyz, (1 / tint_symbol_1.pos.w)), tint_symbol_1.a), tint_symbol_1.b);
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SortedMembers_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn vert_main() -> VertexOutput {
- return VertexOutput();
-}
-
-@fragment
-fn frag_main(@builtin(front_facing) ff : bool,
- @location(2) @interpolate(flat) c : i32,
- inputs : FragmentInputExtra,
- @location(1) @interpolate(flat) b : u32) {
-}
-
-struct VertexOutput {
- @location(1) @interpolate(flat) b : u32,
- @builtin(position) pos : vec4<f32>,
- @location(3) @interpolate(flat) d : u32,
- @location(0) a : f32,
- @location(2) @interpolate(flat) c : i32,
-};
-
-struct FragmentInputExtra {
- @location(3) @interpolate(flat) d : u32,
- @builtin(position) pos : vec4<f32>,
- @location(0) a : f32,
-};
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- @location(0)
- a : f32,
- @location(1) @interpolate(flat)
- b : u32,
- @location(2) @interpolate(flat)
- c : i32,
- @location(3) @interpolate(flat)
- d : u32,
- @builtin(position)
- pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOutput {
- return VertexOutput();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
- let inner_result = vert_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.b = inner_result.b;
- wrapper_result.pos = inner_result.pos;
- wrapper_result.d = inner_result.d;
- wrapper_result.a = inner_result.a;
- wrapper_result.c = inner_result.c;
- return wrapper_result;
-}
-
-struct tint_symbol_2 {
- @location(0)
- a : f32,
- @location(1) @interpolate(flat)
- b : u32,
- @location(2) @interpolate(flat)
- c : i32,
- @location(3) @interpolate(flat)
- d : u32,
- @builtin(position)
- pos : vec4<f32>,
- @builtin(front_facing)
- ff : bool,
-}
-
-fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
- frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, vec4<f32>(tint_symbol_1.pos.xyz, (1 / tint_symbol_1.pos.w)), tint_symbol_1.a), tint_symbol_1.b);
-}
-
-struct VertexOutput {
- b : u32,
- pos : vec4<f32>,
- d : u32,
- a : f32,
- c : i32,
-}
-
-struct FragmentInputExtra {
- d : u32,
- pos : vec4<f32>,
- a : f32,
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) {
- auto* src = R"(
-@fragment
-fn tint_symbol_1(@location(0) col : f32) {
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol_2 {
- @location(0)
- col : f32,
-}
-
-fn tint_symbol_1_inner(col : f32) {
-}
-
-@fragment
-fn tint_symbol_1(tint_symbol : tint_symbol_2) {
- tint_symbol_1_inner(tint_symbol.col);
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Index_Attribute_Hlsl) {
- auto* src = R"(
-enable dual_source_blending;
-
-struct FragOutput {
- @location(0) @blend_src(0) color : vec4<f32>,
- @location(0) @blend_src(1) blend : vec4<f32>,
- @builtin(frag_depth) depth : f32,
- @builtin(sample_mask) mask : u32,
-};
-
-@fragment
-fn frag_main() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- output.blend = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-)";
-
- auto* expect = R"(
-enable dual_source_blending;
-
-struct FragOutput {
- color : vec4<f32>,
- blend : vec4<f32>,
- depth : f32,
- mask : u32,
-}
-
-struct tint_symbol {
- @location(0) @blend_src(0)
- color : vec4<f32>,
- @location(0) @blend_src(1)
- blend : vec4<f32>,
- @builtin(frag_depth)
- depth : f32,
- @builtin(sample_mask)
- mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
- var output : FragOutput;
- output.depth = 1.0;
- output.mask = 7u;
- output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- output.blend = vec4<f32>(0.5, 0.5, 0.5, 1.0);
- return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
- let inner_result = frag_main_inner();
- var wrapper_result : tint_symbol;
- wrapper_result.color = inner_result.color;
- wrapper_result.blend = inner_result.blend;
- wrapper_result.depth = inner_result.depth;
- wrapper_result.mask = inner_result.mask;
- return wrapper_result;
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SubgroupBuiltins_Hlsl) {
- auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn frag_main(@builtin(subgroup_invocation_id) id : u32,
- @builtin(subgroup_size) size : u32) {
- let x = size - id;
-}
-)";
-
- auto* expect = R"(
-enable subgroups;
-
-@internal(intrinsic_wave_get_lane_index) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneIndex() -> u32
-
-@internal(intrinsic_wave_get_lane_count) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneCount() -> u32
-
-fn frag_main_inner(id : u32, size : u32) {
- let x = (size - id);
-}
-
-@compute @workgroup_size(64)
-fn frag_main() {
- frag_main_inner(__WaveGetLaneIndex(), __WaveGetLaneCount());
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SubgroupBuiltinsStruct_Hlsl) {
- auto* src = R"(
-enable subgroups;
-
-struct Inputs {
- @builtin(subgroup_invocation_id) id : u32,
- @builtin(subgroup_size) size : u32,
-}
-
-@compute @workgroup_size(64)
-fn frag_main(inputs : Inputs) {
- let x = inputs.size - inputs.id;
-}
-)";
-
- auto* expect = R"(
-enable subgroups;
-
-@internal(intrinsic_wave_get_lane_index) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneIndex() -> u32
-
-@internal(intrinsic_wave_get_lane_count) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneCount() -> u32
-
-struct Inputs {
- id : u32,
- size : u32,
-}
-
-fn frag_main_inner(inputs : Inputs) {
- let x = (inputs.size - inputs.id);
-}
-
-@compute @workgroup_size(64)
-fn frag_main() {
- frag_main_inner(Inputs(__WaveGetLaneIndex(), __WaveGetLaneCount()));
-}
-)";
-
- auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc b/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc
deleted file mode 100644
index 5f8de69..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DemoteToHelper);
-
-using namespace tint::core::number_suffixes; // NOLINT
-
-namespace tint::ast::transform {
-
-DemoteToHelper::DemoteToHelper() = default;
-
-DemoteToHelper::~DemoteToHelper() = default;
-
-Transform::ApplyResult DemoteToHelper::Apply(const Program& src, const DataMap&, DataMap&) const {
- auto& sem = src.Sem();
-
- // Collect the set of functions that need to be processed.
- // A function needs to be processed if it is reachable by a shader that contains a discard at
- // any point in its call hierarchy.
- std::unordered_set<const sem::Function*> functions_to_process;
- for (auto* func : src.AST().Functions()) {
- if (!func->IsEntryPoint()) {
- continue;
- }
-
- // Determine whether this entry point and its callees need to be transformed.
- bool needs_transform = false;
- if (sem.Get(func)->DiscardStatement()) {
- needs_transform = true;
- } else {
- for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
- if (callee->DiscardStatement()) {
- needs_transform = true;
- break;
- }
- }
- }
- if (!needs_transform) {
- continue;
- }
-
- // Process the entry point and its callees.
- functions_to_process.insert(sem.Get(func));
- for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
- functions_to_process.insert(callee);
- }
- }
-
- if (functions_to_process.empty()) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- // Create a module-scope flag that indicates whether the current invocation has been discarded.
- auto flag = b.Symbols().New("tint_discarded");
- b.GlobalVar(flag, core::AddressSpace::kPrivate, b.Expr(false));
-
- // Replace all discard statements with a statement that marks the invocation as discarded.
- ctx.ReplaceAll(
- [&](const DiscardStatement*) -> const Statement* { return b.Assign(flag, b.Expr(true)); });
-
- // Insert a conditional discard at the end of each entry point that does not end with a return.
- for (auto* func : functions_to_process) {
- if (func->Declaration()->IsEntryPoint()) {
- auto* sem_body = sem.Get(func->Declaration()->body);
- if (sem_body->Behaviors().Contains(sem::Behavior::kNext)) {
- ctx.InsertBack(func->Declaration()->body->statements,
- b.If(flag, b.Block(b.Discard())));
- }
- }
- }
-
- HoistToDeclBefore hoist_to_decl_before(ctx);
-
- // Mask all writes to host-visible memory using the discarded flag.
- // We also insert a discard statement before all return statements in entry points for shaders
- // that discard.
- std::unordered_map<const core::type::Type*, Symbol> atomic_cmpxchg_result_types;
- for (auto* node : src.ASTNodes().Objects()) {
- Switch(
- node,
-
- // Mask assignments to storage buffer variables.
- [&](const AssignmentStatement* assign) {
- // Skip writes in functions that are not called from shaders that discard.
- auto* func = sem.Get(assign)->Function();
- if (functions_to_process.count(func) == 0) {
- return;
- }
-
- // Skip phony assignments.
- if (assign->lhs->Is<PhonyExpression>()) {
- return;
- }
-
- // Skip writes to invocation-private address spaces.
- auto* ref = sem.GetVal(assign->lhs)->Type()->As<core::type::Reference>();
- switch (ref->AddressSpace()) {
- case core::AddressSpace::kStorage:
- // Need to mask these.
- break;
- case core::AddressSpace::kFunction:
- case core::AddressSpace::kPrivate:
- case core::AddressSpace::kOut:
- // Skip these.
- return;
- default:
- TINT_UNREACHABLE()
- << "write to unhandled address space: " << ref->AddressSpace();
- }
-
- // If the RHS has side effects (which may contain derivative operations), we need to
- // hoist it out to a separate declaration so that it does not get masked.
- auto* rhs = sem.GetVal(assign->rhs);
- if (rhs->HasSideEffects()) {
- hoist_to_decl_before.Add(rhs, assign->rhs,
- HoistToDeclBefore::VariableKind::kLet);
- }
-
- // Mask the assignment using the invocation-discarded flag.
- ctx.Replace(assign, b.If(b.Not(flag), b.Block(ctx.Clone(assign))));
- },
-
- // Mask builtins that write to host-visible memory.
- [&](const CallExpression* call) {
- auto* sem_call = sem.Get<sem::Call>(call);
- auto* stmt = sem_call ? sem_call->Stmt() : nullptr;
- auto* func = stmt ? stmt->Function() : nullptr;
- auto* builtin = sem_call ? sem_call->Target()->As<sem::BuiltinFn>() : nullptr;
- if (functions_to_process.count(func) == 0 || !builtin) {
- return;
- }
-
- if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
- // A call to textureStore() will always be a statement.
- // Wrap it inside a conditional block.
- auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(stmt->Declaration())));
- ctx.Replace(stmt->Declaration(), masked_call);
- } else if (builtin->IsAtomic() && builtin->Fn() != wgsl::BuiltinFn::kAtomicLoad) {
- // A call to an atomic builtin can be a statement or an expression.
- if (auto* call_stmt = stmt->Declaration()->As<CallStatement>();
- call_stmt && call_stmt->expr == call) {
- // This call is a statement.
- // Wrap it inside a conditional block.
- auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(call_stmt)));
- ctx.Replace(stmt->Declaration(), masked_call);
- } else {
- // This call is an expression.
- // We transform:
- // let y = x + atomicAdd(&p, 1);
- // Into:
- // var tmp : i32;
- // if (!tint_discarded) {
- // tmp = atomicAdd(&p, 1);
- // }
- // let y = x + tmp;
- auto result = b.Sym();
- auto result_ty = CreateASTTypeFor(ctx, sem_call->Type());
- auto* masked_call =
- b.If(b.Not(flag),
- b.Block(b.Assign(result, ctx.CloneWithoutTransform(call))));
- auto* result_decl = b.Decl(b.Var(result, result_ty));
- hoist_to_decl_before.Prepare(sem_call);
- hoist_to_decl_before.InsertBefore(stmt, result_decl);
- hoist_to_decl_before.InsertBefore(stmt, masked_call);
- ctx.Replace(call, b.Expr(result));
- }
- }
- },
-
- // Insert a conditional discard before all return statements in entry points.
- [&](const ReturnStatement* ret) {
- auto* func = sem.Get(ret)->Function();
- if (func->Declaration()->IsEntryPoint() && functions_to_process.count(func)) {
- auto* discard = b.If(flag, b.Block(b.Discard()));
- ctx.InsertBefore(sem.Get(ret)->Block()->Declaration()->statements, ret,
- discard);
- }
- });
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper.h b/src/tint/lang/wgsl/ast/transform/demote_to_helper.h
deleted file mode 100644
index fcc1b99..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Implement demote-to-helper semantics for discard statements.
-///
-/// For backend targets that implement discard by terminating the invocation, we need to change the
-/// program to ensure that discarding the fragment does not affect uniformity with respect to
-/// derivative operations. We do this by setting a global flag and masking all writes to storage
-/// buffers and textures.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * PromoteSideEffectsToDecl
-/// * ExpandCompoundAssignment
-class DemoteToHelper final : public Castable<DemoteToHelper, Transform> {
- public:
- /// Constructor
- DemoteToHelper();
- /// Destructor
- ~DemoteToHelper() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc b/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc
deleted file mode 100644
index ce447c0..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc
+++ /dev/null
@@ -1,1399 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using DemoteToHelperTest = TransformTest;
-
-TEST_F(DemoteToHelperTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunNoDiscard) {
- auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-@fragment
-fn foo() {
- v = 42;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunDiscardInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-@fragment
-fn foo() {
- discard;
- v = 42;
-}
-)";
-
- EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunDiscardInHelper) {
- auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-fn bar() {
- discard;
-}
-
-@fragment
-fn foo() {
- bar();
- v = 42;
-}
-)";
-
- EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, EmptyModule) {
- auto* src = R"()";
-
- auto* expect = src;
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- v = ret.x;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- v = ret.x;
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInHelper) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
- discard;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- bar();
- }
- let ret = textureSample(t, s, coord);
- v = ret.x;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
- tint_discarded = true;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- bar();
- }
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- v = ret.x;
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(coord : vec2<f32>) {
- let ret = textureSample(t, s, coord);
- v = ret.x;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- bar(coord);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(coord : vec2<f32>) {
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- v = ret.x;
- }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- bar(coord);
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInHelper) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(in : f32, coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- v = ret.x;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- bar(in, coord);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(in : f32, coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- v = ret.x;
- }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- bar(in, coord);
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_NoDiscard) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- let ret = textureSample(t, s, coord);
- v = ret.x;
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that write via sugared pointer also discards
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInEntryPoint_ViaPointerDot) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- let p = &v;
- p.x = ret.x;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- let p = &(v);
- if (!(tint_discarded)) {
- p.x = ret.x;
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that no additional discards are inserted when the function unconditionally returns in a
-// nested block.
-TEST_F(DemoteToHelperTest, EntryPointReturn_NestedInBlock) {
- auto* src = R"(
-@fragment
-fn foo() {
- {
- discard;
- return;
- }
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@fragment
-fn foo() {
- {
- tint_discarded = true;
- if (tint_discarded) {
- discard;
- }
- return;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that a discard statement is inserted before every return statement in an entry point that
-// contains a discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- if (in < 1.0) {
- return ret.x;
- }
- return 2.0;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- if ((in < 1.0)) {
- if (tint_discarded) {
- discard;
- }
- return ret.x;
- }
- if (tint_discarded) {
- discard;
- }
- return 2.0;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that a discard statement is inserted before every return statement in an entry point that
-// calls a function that contains a discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInHelper) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
- discard;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
- if (in == 0.0) {
- bar();
- }
- let ret = textureSample(t, s, coord);
- if (in < 1.0) {
- return ret.x;
- }
- return 2.0;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
- tint_discarded = true;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
- if ((in == 0.0)) {
- bar();
- }
- let ret = textureSample(t, s, coord);
- if ((in < 1.0)) {
- if (tint_discarded) {
- discard;
- }
- return ret.x;
- }
- if (tint_discarded) {
- discard;
- }
- return 2.0;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that no return statements are modified in an entry point that does not discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_NoDiscard) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
- if ((in == 0.0)) {
- bar();
- }
- let ret = textureSample(t, s, coord);
- if ((in < 1.0)) {
- return ret.x;
- }
- return 2.0;
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that only functions that are part of a shader that discards are transformed.
-// Functions in non-discarding stages should not have their writes masked, and non-discarding entry
-// points should not have their return statements replaced.
-TEST_F(DemoteToHelperTest, MultipleShaders) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v1 : f32;
-
-@group(0) @binding(3) var<storage, read_write> v2 : f32;
-
-fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
- let ret = textureSample(t, s, coord);
- v1 = ret.x * 2.0;
- return ret.y * 2.0;
-}
-
-fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
- let ret = textureSample(t, s, coord);
- v1 = ret.x * 2.0;
- return ret.y * 2.0;
-}
-
-@fragment
-fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = bar_discard(in, coord);
- v2 = ret;
-}
-
-@fragment
-fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- let ret = bar_no_discard(in, coord);
- if (in == 0.0) {
- return;
- }
- v2 = ret;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v1 : f32;
-
-@group(0) @binding(3) var<storage, read_write> v2 : f32;
-
-fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- v1 = (ret.x * 2.0);
- }
- return (ret.y * 2.0);
-}
-
-fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
- let ret = textureSample(t, s, coord);
- v1 = (ret.x * 2.0);
- return (ret.y * 2.0);
-}
-
-@fragment
-fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = bar_discard(in, coord);
- if (!(tint_discarded)) {
- v2 = ret;
- }
- if (tint_discarded) {
- discard;
- }
-}
-
-@fragment
-fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- let ret = bar_no_discard(in, coord);
- if ((in == 0.0)) {
- return;
- }
- v2 = ret;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not mask writes to invocation-private address spaces.
-TEST_F(DemoteToHelperTest, InvocationPrivateWrites) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- var vf : f32;
- vf = ret.x;
- vp = ret.y;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- var vf : f32;
- vf = ret.x;
- vp = ret.y;
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not mask writes to invocation-private address spaces via a sugared pointer write
-TEST_F(DemoteToHelperTest, InvocationPrivateWritesViaPointerDot) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- var vf : f32;
- vf = ret.x;
- let p = &vp;
- p.x = ret.x;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- var vf : f32;
- vf = ret.x;
- let p = &(vp);
- p.x = ret.x;
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStoreInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- textureStore(t2, vec2<u32>(coord), ret);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- textureStore(t2, vec2<u32>(coord), ret);
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStoreInHelper) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-fn bar(coord : vec2<f32>, value : vec4<f32>) {
- textureStore(t2, vec2<u32>(coord), value);
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- bar(coord, ret);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-fn bar(coord : vec2<f32>, value : vec4<f32>) {
- if (!(tint_discarded)) {
- textureStore(t2, vec2<u32>(coord), value);
- }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- bar(coord, ret);
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStore_NoDiscard) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- let ret = textureSample(t, s, coord);
- textureStore(t2, vec2<u32>(coord), ret);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStoreInEntryPoint) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- atomicStore(&a, i32(ret.x));
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- if (!(tint_discarded)) {
- atomicStore(&(a), i32(ret.x));
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStoreInHelper) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-fn bar(value : vec4<f32>) {
- atomicStore(&a, i32(value.x));
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if (in == 0.0) {
- discard;
- }
- let ret = textureSample(t, s, coord);
- bar(ret);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-fn bar(value : vec4<f32>) {
- if (!(tint_discarded)) {
- atomicStore(&(a), i32(value.x));
- }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let ret = textureSample(t, s, coord);
- bar(ret);
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStore_NoDiscard) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
- let ret = textureSample(t, s, coord);
- atomicStore(&(a), i32(ret.x));
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicBuiltinExpression) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if (in == 0.0) {
- discard;
- }
- let v = i32(textureSample(t, s, coord).x);
- let result = v + atomicAdd(&a, v);
- return result;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let v = i32(textureSample(t, s, coord).x);
- var tint_symbol : i32;
- if (!(tint_discarded)) {
- tint_symbol = atomicAdd(&(a), v);
- }
- let result = (v + tint_symbol);
- if (tint_discarded) {
- discard;
- }
- return result;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicBuiltinExpression_InForLoopContinuing) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if (in == 0.0) {
- discard;
- }
- var result = 0;
- for (var i = 0; i < 10; i = atomicAdd(&a, 1)) {
- result += i;
- }
- result += i32(textureSample(t, s, coord).x);
- return result;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- var result = 0;
- {
- var i = 0;
- loop {
- if (!((i < 10))) {
- break;
- }
- {
- result += i;
- }
-
- continuing {
- var tint_symbol : i32;
- if (!(tint_discarded)) {
- tint_symbol = atomicAdd(&(a), 1);
- }
- i = tint_symbol;
- }
- }
- }
- result += i32(textureSample(t, s, coord).x);
- if (tint_discarded) {
- discard;
- }
- return result;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicCompareExchangeWeak) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if (in == 0.0) {
- discard;
- }
- var result = 0;
- if (!atomicCompareExchangeWeak(&a, i32(in), 42).exchanged) {
- let xchg = atomicCompareExchangeWeak(&a, i32(in), 42);
- result = xchg.old_value;
- }
- result += i32(textureSample(t, s, coord).x);
- return result;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- var result = 0;
- var tint_symbol : __atomic_compare_exchange_result_i32;
- if (!(tint_discarded)) {
- tint_symbol = atomicCompareExchangeWeak(&(a), i32(in), 42);
- }
- if (!(tint_symbol.exchanged)) {
- var tint_symbol_1 : __atomic_compare_exchange_result_i32;
- if (!(tint_discarded)) {
- tint_symbol_1 = atomicCompareExchangeWeak(&(a), i32(in), 42);
- }
- let xchg = tint_symbol_1;
- result = xchg.old_value;
- }
- result += i32(textureSample(t, s, coord).x);
- if (tint_discarded) {
- discard;
- }
- return result;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that no masking is generated for calls to `atomicLoad()`.
-TEST_F(DemoteToHelperTest, AtomicLoad) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if (in == 0.0) {
- discard;
- }
- let v = i32(textureSample(t, s, coord).x);
- let result = v + atomicLoad(&a);
- return result;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let v = i32(textureSample(t, s, coord).x);
- let result = (v + atomicLoad(&(a)));
- if (tint_discarded) {
- discard;
- }
- return result;
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, PhonyAssignment) {
- auto* src = R"(
-@fragment
-fn foo(@location(0) in : f32) {
- if (in == 0.0) {
- discard;
- }
- _ = in;
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@fragment
-fn foo(@location(0) in : f32) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- _ = in;
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, Assignment_HoistExplicitDerivative) {
- auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> output : array<f32, 4>;
-
-@fragment
-fn foo(@location(0) in : f32) {
- if (in == 0.0) {
- discard;
- }
- output[u32(in)] = dpdx(in);
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var<storage, read_write> output : array<f32, 4>;
-
-@fragment
-fn foo(@location(0) in : f32) {
- if ((in == 0.0)) {
- tint_discarded = true;
- }
- let tint_symbol : f32 = dpdx(in);
- if (!(tint_discarded)) {
- output[u32(in)] = tint_symbol;
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, Assignment_HoistImplicitDerivative) {
- auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> output : array<vec4f, 4>;
-
-@group(0) @binding(1)
-var t : texture_2d<f32>;
-
-@group(0) @binding(2)
-var s : sampler;
-
-@fragment
-fn foo(@interpolate(flat) @location(0) in : u32) {
- if (in == 0) {
- discard;
- }
- output[in] = textureSample(t, s, vec2());
-}
-)";
-
- auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var<storage, read_write> output : array<vec4f, 4>;
-
-@group(0) @binding(1) var t : texture_2d<f32>;
-
-@group(0) @binding(2) var s : sampler;
-
-@fragment
-fn foo(@interpolate(flat) @location(0) in : u32) {
- if ((in == 0)) {
- tint_discarded = true;
- }
- let tint_symbol : vec4<f32> = textureSample(t, s, vec2());
- if (!(tint_discarded)) {
- output[in] = tint_symbol;
- }
- if (tint_discarded) {
- discard;
- }
-}
-)";
-
- auto got = Run<DemoteToHelper>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
deleted file mode 100644
index 0918238..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
+++ /dev/null
@@ -1,1223 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/abstract_int.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/reverse.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-#include "src/tint/utils/text/string_stream.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DirectVariableAccess);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DirectVariableAccess::Config);
-
-using namespace tint::core::number_suffixes; // NOLINT
-using namespace tint::core::fluent_types; // NOLINT
-
-namespace tint::ast::transform {
-
-namespace {
-
-/// AccessRoot describes the root of an AccessShape.
-struct AccessRoot {
- /// The pointer-unwrapped type of the *transformed* variable.
- /// This may be different for pointers in 'private' and 'function' address space, as the pointer
- /// parameter type is to the *base object* instead of the input pointer type.
- tint::core::type::Type const* type = nullptr;
- /// The originating module-scope variable ('private', 'storage', 'uniform', 'workgroup'),
- /// function-scope variable ('function'), or pointer parameter in the source program.
- tint::sem::Variable const* variable = nullptr;
- /// The address space of the variable or pointer type.
- tint::core::AddressSpace address_space = tint::core::AddressSpace::kUndefined;
-
- /// @return a hash code for this object
- tint::HashCode HashCode() const { return Hash(type, variable); }
-};
-
-/// Inequality operator for AccessRoot
-bool operator!=(const AccessRoot& a, const AccessRoot& b) {
- return a.type != b.type || a.variable != b.variable;
-}
-
-/// DynamicIndex is used by DirectVariableAccess::State::AccessOp to indicate an array, matrix or
-/// vector index.
-struct DynamicIndex {
- /// @return a hash code for this object
- tint::HashCode HashCode() const { return 42 /* empty struct: any number will do */; }
-};
-
-/// Inequality operator for DynamicIndex
-bool operator!=(const DynamicIndex&, const DynamicIndex&) {
- return false; // empty struct: two DynamicIndex objects are always equal
-}
-
-/// AccessOp describes a single access in an access chain.
-/// The access is one of:
-/// Symbol - a struct member access.
-/// DynamicIndex - a runtime index on an array, matrix column, or vector element.
-using AccessOp = std::variant<tint::Symbol, DynamicIndex>;
-
-/// A vector of AccessOp. Describes the static "path" from a root variable to an element
-/// within the variable. Array accessors index expressions are held externally to the
-/// AccessShape, so AccessShape will be considered equal even if the array, matrix or vector
-/// index values differ.
-///
-/// For example, consider the following:
-///
-/// ```
-/// struct A {
-/// x : array<i32, 8>,
-/// y : u32,
-/// };
-/// struct B {
-/// x : i32,
-/// y : array<A, 4>
-/// };
-/// var<workgroup> C : B;
-/// ```
-///
-/// The following AccessShape would describe the following:
-///
-/// +==============================+===============+=================================+
-/// | AccessShape | Type | Expression |
-/// +==============================+===============+=================================+
-/// | [ Variable 'C', Symbol 'x' ] | i32 | C.x |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y' ] | array<A, 4> | C.y |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y', | A | C.y[dyn_idx[0]] |
-/// | DynamicIndex ] | | |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y', | array<i32, 8> | C.y[dyn_idx[0]].x |
-/// | DynamicIndex, Symbol 'x' ] | | |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y', | i32 | C.y[dyn_idx[0]].x[dyn_idx[1]] |
-/// | DynamicIndex, Symbol 'x', | | |
-/// | DynamicIndex ] | | |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y', | u32 | C.y[dyn_idx[0]].y |
-/// | DynamicIndex, Symbol 'y' ] | | |
-/// +------------------------------+---------------+---------------------------------+
-///
-/// Where: `dyn_idx` is the AccessChain::dynamic_indices.
-struct AccessShape {
- // The originating variable.
- AccessRoot root;
- /// The chain of access ops.
- tint::Vector<AccessOp, 8> ops;
-
- /// @returns the number of DynamicIndex operations in #ops.
- uint32_t NumDynamicIndices() const {
- uint32_t count = 0;
- for (auto& op : ops) {
- if (std::holds_alternative<DynamicIndex>(op)) {
- count++;
- }
- }
- return count;
- }
-
- /// @return a hash code for this object
- tint::HashCode HashCode() const { return Hash(root, ops); }
-};
-
-/// Equality operator for AccessShape
-bool operator==(const AccessShape& a, const AccessShape& b) {
- return !(a.root != b.root) && a.ops == b.ops;
-}
-
-/// Inequality operator for AccessShape
-bool operator!=(const AccessShape& a, const AccessShape& b) {
- return !(a == b);
-}
-
-/// AccessChain describes a chain of access expressions originating from a variable.
-struct AccessChain : AccessShape {
- /// The array accessor index expressions. This vector is indexed by the `DynamicIndex`s in
- /// #indices.
- Vector<const sem::ValueExpression*, 8> dynamic_indices;
- /// If true, then this access chain is used as an argument to call a variant.
- bool used_in_call = false;
-};
-
-} // namespace
-
-/// The PIMPL state for the DirectVariableAccess transform
-struct DirectVariableAccess::State {
- /// Constructor
- /// @param src the source Program
- /// @param options the transform options
- State(const Program& src, const Options& options)
- : ctx{&b, &src, /* auto_clone_symbols */ true}, opts(options) {}
-
- /// The main function for the transform.
- /// @returns the ApplyResult
- ApplyResult Run() {
- // If there are no functions with pointer parameters, then this transform can be skipped.
- if (!AnyPointerParameters()) {
- return SkipTransform;
- }
-
- // Stage 1:
- // Walk all the expressions of the program, starting with the expression leaves.
- // Whenever we find an identifier resolving to a var, pointer parameter or pointer let to
- // another chain, start constructing an access chain. When chains are accessed, these chains
- // are grown and moved up the expression tree. After this stage, we are left with all the
- // expression access chains to variables that we may need to transform.
- for (auto* node : ctx.src->ASTNodes().Objects()) {
- if (auto* expr = sem.GetVal(node)) {
- AppendAccessChain(expr);
- }
- }
-
- // Stage 2:
- // Walk the functions in dependency order, starting with the entry points.
- // Construct the set of function 'variants' by examining the calls made by each function to
- // their call target. Each variant holds a map of pointer parameter to access chains, and
- // will have the pointer parameters replaced with an array of u32s, used to perform the
- // pointer indexing in the variant.
- // Function call pointer arguments are replaced with an array of these dynamic indices.
- auto decls = sem.Module()->DependencyOrderedDeclarations();
- for (auto* decl : tint::Reverse(decls)) {
- if (auto* fn = sem.Get<sem::Function>(decl)) {
- auto* fn_info = FnInfoFor(fn);
- ProcessFunction(fn, fn_info);
- TransformFunction(fn, fn_info);
- }
- }
-
- // Stage 3:
- // Filter out access chains that do not need transforming.
- // Ensure that chain dynamic index expressions are evaluated once at the correct place
- ProcessAccessChains();
-
- // Stage 4:
- // Replace all the access chain expressions in all functions with reconstructed expression
- // using the originating global variable, and any dynamic indices passed in to the function
- // variant.
- TransformAccessChainExpressions();
-
- // Stage 5:
- // Actually kick the clone.
- CloneState state;
- clone_state = &state;
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- private:
- /// Holds symbols of the transformed pointer parameter.
- /// If both symbols are valid, then #base_ptr and #indices are both program-unique symbols
- /// derived from the original parameter name.
- /// If only one symbol is valid, then this is the original parameter symbol.
- struct PtrParamSymbols {
- /// The symbol of the base pointer parameter.
- Symbol base_ptr;
- /// The symbol of the dynamic indicies parameter.
- Symbol indices;
- };
-
- /// FnVariant describes a unique variant of a function, specialized by the AccessShape of the
- /// pointer arguments - also known as the variant's "signature".
- ///
- /// To help understand what a variant is, consider the following WGSL:
- ///
- /// ```
- /// fn F(a : ptr<storage, u32>, b : u32, c : ptr<storage, u32>) {
- /// return *a + b + *c;
- /// }
- ///
- /// @group(0) @binding(0) var<storage> S0 : u32;
- /// @group(0) @binding(0) var<storage> S1 : array<u32, 64>;
- ///
- /// fn x() {
- /// F(&S0, 0, &S0); // (A)
- /// F(&S0, 0, &S0); // (B)
- /// F(&S1[0], 1, &S0); // (C)
- /// F(&S1[5], 2, &S0); // (D)
- /// F(&S1[5], 3, &S1[3]); // (E)
- /// F(&S1[7], 4, &S1[2]); // (F)
- /// }
- /// ```
- ///
- /// Given the calls in x(), function F() will have 3 variants:
- /// (1) F<S0,S0> - called by (A) and (B).
- /// Note that only 'uniform', 'storage' and 'workgroup' pointer
- /// parameters are considered for a variant signature, and so
- /// the argument for parameter 'b' is not included in the
- /// signature.
- /// (2) F<S1[dyn_idx],S0> - called by (C) and (D).
- /// Note that the array index value is external to the
- /// AccessShape, and so is not part of the variant signature.
- /// (3) F<S1[dyn_idx],S1[dyn_idx]> - called by (E) and (F).
- ///
- /// Each variant of the function will be emitted as a separate function by the transform, and
- /// would look something like:
- ///
- /// ```
- /// // variant F<S0,S0> (1)
- /// fn F_S0_S0(b : u32) {
- /// return S0 + b + S0;
- /// }
- ///
- /// type S1_X = array<u32, 1>;
- ///
- /// // variant F<S1[dyn_idx],S0> (2)
- /// fn F_S1_X_S0(a : S1_X, b : u32) {
- /// return S1[a[0]] + b + S0;
- /// }
- ///
- /// // variant F<S1[dyn_idx],S1[dyn_idx]> (3)
- /// fn F_S1_X_S1_X(a : S1_X, b : u32, c : S1_X) {
- /// return S1[a[0]] + b + S1[c[0]];
- /// }
- ///
- /// @group(0) @binding(0) var<storage> S0 : u32;
- /// @group(0) @binding(0) var<storage> S1 : array<u32, 64>;
- ///
- /// fn x() {
- /// F_S0_S0(0); // (A)
- /// F(&S0, 0, &S0); // (B)
- /// F_S1_X_S0(S1_X(0), 1); // (C)
- /// F_S1_X_S0(S1_X(5), 2); // (D)
- /// F_S1_X_S1_X(S1_X(5), 3, S1_X(3)); // (E)
- /// F_S1_X_S1_X(S1_X(7), 4, S1_X(2)); // (F)
- /// }
- /// ```
- struct FnVariant {
- /// The signature of the variant is a map of each of the function's 'uniform', 'storage' and
- /// 'workgroup' pointer parameters to the caller's AccessShape.
- using Signature = Hashmap<const sem::Parameter*, AccessShape, 4>;
-
- /// The unique name of the variant.
- /// The symbol is in the `ctx.dst` program namespace.
- Symbol name;
-
- /// A map of direct calls made by this variant to the name of other function variants.
- Hashmap<const sem::Call*, Symbol, 4> calls;
-
- /// A map of input program parameter to output parameter symbols.
- Hashmap<const sem::Parameter*, PtrParamSymbols, 4> ptr_param_symbols;
-
- /// The declaration order of the variant, in relation to other variants of the same
- /// function. Used to ensure deterministic ordering of the transform, as map iteration is
- /// not deterministic between compilers.
- size_t order = 0;
- };
-
- /// FnInfo holds information about a function in the input program.
- struct FnInfo {
- /// A map of variant signature to the variant data.
- Hashmap<FnVariant::Signature, FnVariant, 8> variants;
- /// A map of expressions that have been hoisted to a 'let' declaration in the function.
- Hashmap<const sem::ValueExpression*, Symbol, 8> hoisted_exprs;
-
- /// @returns the variants of the function in a deterministically ordered vector.
- tint::Vector<std::pair<const FnVariant::Signature*, FnVariant*>, 8> SortedVariants() {
- tint::Vector<std::pair<const FnVariant::Signature*, FnVariant*>, 8> out;
- out.Reserve(variants.Count());
- for (auto& it : variants) {
- out.Push({&it.key.Value(), &it.value});
- }
- out.Sort([&](auto& va, auto& vb) { return va.second->order < vb.second->order; });
- return out;
- }
- };
-
- /// The program builder
- ProgramBuilder b;
- /// The clone context
- program::CloneContext ctx;
- /// The transform options
- const Options& opts;
- /// Alias to the semantic info in ctx.src
- const sem::Info& sem = ctx.src->Sem();
- /// Alias to the symbols in ctx.src
- const SymbolTable& sym = ctx.src->Symbols();
- /// Map of semantic function to the function info
- Hashmap<const sem::Function*, FnInfo*, 8> fns;
- /// Map of AccessShape to the name of a type alias for the an array<u32, N> used for the
- /// dynamic indices of an access chain, passed down as the transformed type of a variant's
- /// pointer parameter.
- Hashmap<AccessShape, Symbol, 8> dynamic_index_array_aliases;
- /// Map of semantic expression to AccessChain
- Hashmap<const sem::ValueExpression*, AccessChain*, 32> access_chains;
- /// Allocator for FnInfo
- BlockAllocator<FnInfo> fn_info_allocator;
- /// Allocator for AccessChain
- BlockAllocator<AccessChain> access_chain_allocator;
- /// Helper used for hoisting expressions to lets
- HoistToDeclBefore hoist{ctx};
- /// Map of string to unique symbol (no collisions in output program).
- Hashmap<std::string, Symbol, 8> unique_symbols;
-
- /// CloneState holds pointers to the current function, variant and variant's parameters.
- struct CloneState {
- /// The current function being cloned
- FnInfo* current_function = nullptr;
- /// The current function variant being built
- FnVariant* current_variant = nullptr;
- /// The signature of the current function variant being built
- const FnVariant::Signature* current_variant_sig = nullptr;
- };
-
- /// The clone state.
- /// Only valid during the lifetime of the program::CloneContext::Clone().
- CloneState* clone_state = nullptr;
-
- /// @returns true if any user functions have parameters of a pointer type.
- bool AnyPointerParameters() const {
- for (auto* fn : ctx.src->AST().Functions()) {
- for (auto* param : fn->params) {
- if (sem.Get(param)->Type()->Is<core::type::Pointer>()) {
- return true;
- }
- }
- }
- return false;
- }
-
- /// AppendAccessChain creates or extends an existing AccessChain for the given expression,
- /// modifying the #access_chains map.
- void AppendAccessChain(const sem::ValueExpression* expr) {
- // take_chain moves the AccessChain from the expression `from` to the expression `expr`.
- // Returns nullptr if `from` did not hold an access chain.
- auto take_chain = [&](const sem::ValueExpression* from) -> AccessChain* {
- if (auto* chain = AccessChainFor(from)) {
- access_chains.Remove(from);
- access_chains.Add(expr, chain);
- return chain;
- }
- return nullptr;
- };
-
- Switch(
- expr,
- [&](const sem::VariableUser* user) {
- // Expression resolves to a variable.
- auto* variable = user->Variable();
-
- auto create_new_chain = [&] {
- auto* chain = access_chain_allocator.Create();
- chain->root.variable = variable;
- chain->root.type = variable->Type();
- chain->root.address_space = variable->AddressSpace();
- if (auto* ptr = chain->root.type->As<core::type::Pointer>()) {
- chain->root.address_space = ptr->AddressSpace();
- }
- access_chains.Add(expr, chain);
- };
-
- Switch(
- variable->Declaration(),
- [&](const Var*) {
- if (variable->AddressSpace() != core::AddressSpace::kHandle) {
- // Start a new access chain for the non-handle 'var' access
- create_new_chain();
- }
- },
- [&](const Parameter*) {
- if (variable->Type()->Is<core::type::Pointer>()) {
- // Start a new access chain for the pointer parameter access
- create_new_chain();
- }
- },
- [&](const Let*) {
- if (variable->Type()->Is<core::type::Pointer>()) {
- // variable is a pointer-let.
- auto* init = sem.GetVal(variable->Declaration()->initializer);
- // Note: We do not use take_chain() here, as we need to preserve the
- // AccessChain on the let's initializer, as the let needs its
- // initializer updated, and the let may be used multiple times. Instead
- // we copy the let's AccessChain into a a new AccessChain.
- if (auto* init_chain = AccessChainFor(init)) {
- access_chains.Add(expr, access_chain_allocator.Create(*init_chain));
- }
- }
- });
- },
- [&](const sem::StructMemberAccess* a) {
- // Structure member access.
- // Append the Symbol of the member name to the chain, and move the chain to the
- // member access expression.
- if (auto* chain = take_chain(a->Object())) {
- chain->ops.Push(a->Member()->Name());
- }
- },
- [&](const sem::IndexAccessorExpression* a) {
- // Array, matrix or vector index.
- // Store the index expression into AccessChain::dynamic_indices, append a
- // DynamicIndex to the chain, and move the chain to the index accessor expression.
- if (auto* chain = take_chain(a->Object())) {
- chain->ops.Push(DynamicIndex{});
- chain->dynamic_indices.Push(a->Index());
- }
- },
- [&](const sem::ValueExpression* e) {
- if (auto* unary = e->Declaration()->As<UnaryOpExpression>()) {
- // Unary op.
- // If this is a '&' or '*', simply move the chain to the unary op expression.
- if (unary->op == core::UnaryOp::kAddressOf ||
- unary->op == core::UnaryOp::kIndirection) {
- take_chain(sem.GetVal(unary->expr));
- }
- }
- });
- }
-
- /// MaybeHoistDynamicIndices examines the AccessChain::dynamic_indices member of @p chain,
- /// hoisting all expressions to their own uniquely named 'let' if none of the following are
- /// true:
- /// 1. The index expression is a constant value.
- /// 2. The index expression's statement is the same as @p usage.
- /// 3. The index expression is an identifier resolving to a 'let', 'const' or parameter, AND
- /// that identifier resolves to the same variable at @p usage.
- ///
- /// A dynamic index will only be hoisted once. The hoisting applies to all variants of the
- /// function that holds the dynamic index expression.
- void MaybeHoistDynamicIndices(AccessChain* chain, const sem::Statement* usage) {
- for (auto& idx : chain->dynamic_indices) {
- if (idx->ConstantValue()) {
- // Dynamic index is constant.
- continue; // Hoisting not required.
- }
-
- if (idx->Stmt() == usage) {
- // The index expression is owned by the statement of usage.
- continue; // Hoisting not required
- }
-
- if (auto* idx_variable_user = idx->UnwrapMaterialize()->As<sem::VariableUser>()) {
- auto* idx_variable = idx_variable_user->Variable();
- if (idx_variable->Declaration()->IsAnyOf<Let, Parameter>()) {
- // Dynamic index is an immutable variable
- continue; // Hoisting not required.
- }
- }
-
- // The dynamic index needs to be hoisted (if it hasn't been already).
- auto fn = FnInfoFor(idx->Stmt()->Function());
- fn->hoisted_exprs.GetOrAdd(idx, [this, idx] {
- // Create a name for the new 'let'
- auto name = b.Symbols().New("ptr_index_save");
- // Insert a new 'let' just above the dynamic index statement.
- hoist.InsertBefore(idx->Stmt(), [this, idx, name] {
- return b.Decl(b.Let(name, ctx.CloneWithoutTransform(idx->Declaration())));
- });
- return name;
- });
- }
- }
-
- /// BuildDynamicIndex builds the AST expression node for the dynamic index expression used in an
- /// AccessChain. This is similar to just cloning the expression, but BuildDynamicIndex()
- /// also:
- /// * Collapses constant value index expressions down to the computed value. This acts as an
- /// constant folding optimization and reduces noise from the transform.
- /// * Casts the resulting expression to a u32 if @p cast_to_u32 is true, and the expression type
- /// isn't implicitly usable as a u32. This is to help feed the expression into a
- /// `array<u32, N>` argument passed to a callee variant function.
- const Expression* BuildDynamicIndex(const sem::ValueExpression* idx, bool cast_to_u32) {
- if (auto* val = idx->ConstantValue()) {
- // Expression evaluated to a constant value. Just emit that constant.
- return b.Expr(val->ValueAs<AInt>());
- }
-
- // Expression is not a constant, clone the expression.
- // Note: If the dynamic index expression was hoisted to a let, then cloning will return an
- // identifier expression to the hoisted let.
- auto* expr = ctx.Clone(idx->Declaration());
-
- if (cast_to_u32) {
- // The index may be fed to a dynamic index array<u32, N> argument, so the index
- // expression may need casting to u32.
- if (!idx->UnwrapMaterialize()
- ->Type()
- ->UnwrapRef()
- ->IsAnyOf<core::type::U32, core::type::AbstractInt>()) {
- expr = b.Call<u32>(expr);
- }
- }
-
- return expr;
- }
-
- /// ProcessFunction scans the direct calls made by the function @p fn, adding new variants to
- /// the callee functions and transforming the call expression to pass dynamic indices instead of
- /// true pointers.
- /// If the function @p fn has pointer parameters that must be transformed to a caller variant,
- /// and the function is not called, then the function is dropped from the output of the
- /// transform, as it cannot be generated.
- /// @note ProcessFunction must be called in dependency order for the program, starting with the
- /// entry points.
- void ProcessFunction(const sem::Function* fn, FnInfo* fn_info) {
- if (fn_info->variants.IsEmpty()) {
- // Function has no variants pre-generated by callers.
- if (MustBeCalled(fn)) {
- // Drop the function, as it wasn't called and cannot be generated.
- ctx.Remove(ctx.src->AST().GlobalDeclarations(), fn->Declaration());
- return;
- }
-
- // Function was not called. Create a single variant with an empty signature.
- FnVariant variant;
- variant.name = ctx.Clone(fn->Declaration()->name->symbol);
- variant.order = 0; // Unaltered comes first.
- fn_info->variants.Add(FnVariant::Signature{}, std::move(variant));
- }
-
- // Process each of the direct calls made by this function.
- for (auto* call : fn->DirectCalls()) {
- ProcessCall(fn_info, call);
- }
- }
-
- /// ProcessCall creates new variants of the callee function by permuting the call for each of
- /// the variants of @p caller. ProcessCall also registers the clone callback to transform the
- /// call expression to pass dynamic indices instead of true pointers.
- void ProcessCall(FnInfo* caller, const sem::Call* call) {
- auto* target = call->Target()->As<sem::Function>();
- if (!target) {
- // Call target is not a user-declared function.
- return; // Not interested in this call.
- }
-
- if (!HasPointerParameter(target)) {
- return; // Not interested in this call.
- }
-
- bool call_needs_transforming = false;
-
- // Build the call target function variant for each variant of the caller.
- for (auto caller_variant_it : caller->SortedVariants()) {
- auto& caller_signature = *caller_variant_it.first;
- auto& caller_variant = *caller_variant_it.second;
-
- // Build the target variant's signature.
- FnVariant::Signature target_signature;
- for (size_t i = 0; i < call->Arguments().Length(); i++) {
- const auto* arg = call->Arguments()[i];
- const auto* param = target->Parameters()[i];
- const auto* param_ty = param->Type()->As<core::type::Pointer>();
- if (!param_ty) {
- continue; // Parameter type is not a pointer.
- }
-
- // Fetch the access chain for the argument.
- auto* arg_chain = AccessChainFor(arg);
- if (!arg_chain) {
- continue; // Argument does not have an access chain
- }
-
- // Construct the absolute AccessShape by considering the AccessShape of the caller
- // variant's argument. This will propagate back through pointer parameters, to the
- // outermost caller.
- auto absolute = AbsoluteAccessShape(caller_signature, *arg_chain);
-
- // If the address space of the root variable of the access chain does not require
- // transformation, then there's nothing to do.
- if (!AddressSpaceRequiresTransform(absolute.root.address_space)) {
- continue;
- }
-
- // Record that this chain was used in a function call.
- // This preserves the chain during the access chain filtering stage.
- arg_chain->used_in_call = true;
-
- if (IsPrivateOrFunction(absolute.root.address_space)) {
- // Pointers in 'private' and 'function' address spaces need to be passed by
- // pointer argument.
- absolute.root.variable = param;
- }
-
- // Add the parameter's absolute AccessShape to the target's signature.
- target_signature.Add(param, std::move(absolute));
- }
-
- // Construct a new FnVariant if this is the first caller of the target signature
- auto* target_info = FnInfoFor(target);
- auto& target_variant = target_info->variants.GetOrAdd(target_signature, [&] {
- if (target_signature.IsEmpty()) {
- // Call target does not require any argument changes.
- FnVariant variant;
- variant.name = ctx.Clone(target->Declaration()->name->symbol);
- variant.order = 0; // Unaltered comes first.
- return variant;
- }
-
- // Build an appropriate variant function name.
- // This is derived from the original function name and the pointer parameter
- // chains.
- StringStream ss;
- ss << target->Declaration()->name->symbol.Name();
- for (auto* param : target->Parameters()) {
- if (auto indices = target_signature.Get(param)) {
- ss << "_" << AccessShapeName(*indices);
- }
- }
-
- // Build the pointer parameter symbols.
- Hashmap<const sem::Parameter*, PtrParamSymbols, 4> ptr_param_symbols;
- for (auto& param_it : target_signature) {
- auto* param = param_it.key.Value();
- auto& shape = param_it.value;
-
- // Parameter needs replacing with either zero, one or two parameters:
- // If the parameter is in the 'private' or 'function' address space, then the
- // originating pointer is always passed down. This always comes first.
- // If the access chain has dynamic indices, then we create an array<u32, N>
- // parameter to hold the dynamic indices.
- bool requires_base_ptr_param = IsPrivateOrFunction(shape.root.address_space);
- bool requires_indices_param = shape.NumDynamicIndices() > 0;
-
- PtrParamSymbols symbols;
- if (requires_base_ptr_param && requires_indices_param) {
- auto original_name = param->Declaration()->name->symbol;
- symbols.base_ptr = UniqueSymbolWithSuffix(original_name, "_base");
- symbols.indices = UniqueSymbolWithSuffix(original_name, "_indices");
- } else if (requires_base_ptr_param) {
- symbols.base_ptr = ctx.Clone(param->Declaration()->name->symbol);
- } else if (requires_indices_param) {
- symbols.indices = ctx.Clone(param->Declaration()->name->symbol);
- }
-
- // Remember this base pointer name.
- ptr_param_symbols.Add(param, symbols);
- }
-
- // Build the variant.
- FnVariant variant;
- variant.name = b.Symbols().New(ss.str());
- variant.order = target_info->variants.Count() + 1;
- variant.ptr_param_symbols = std::move(ptr_param_symbols);
- return variant;
- });
-
- // Record the call made by caller variant to the target variant.
- caller_variant.calls.Add(call, target_variant.name);
- if (!target_signature.IsEmpty()) {
- // The call expression will need transforming for at least one caller variant.
- call_needs_transforming = true;
- }
- }
-
- if (call_needs_transforming) {
- // Register the clone callback to correctly transform the call expression into the
- // appropriate variant calls.
- TransformCall(call);
- }
- }
-
- /// @returns true if the address space @p address_space requires transforming given the
- /// transform's options.
- bool AddressSpaceRequiresTransform(core::AddressSpace address_space) const {
- switch (address_space) {
- case core::AddressSpace::kUniform:
- case core::AddressSpace::kStorage:
- case core::AddressSpace::kWorkgroup:
- return true;
- case core::AddressSpace::kPrivate:
- return opts.transform_private;
- case core::AddressSpace::kFunction:
- return opts.transform_function;
- default:
- return false;
- }
- }
-
- /// @returns the AccessChain for the expression @p expr, or nullptr if the expression does
- /// not hold an access chain.
- AccessChain* AccessChainFor(const sem::ValueExpression* expr) const {
- if (auto chain = access_chains.Get(expr)) {
- return *chain;
- }
- return nullptr;
- }
-
- /// @returns the absolute AccessShape for @p indices, by replacing the originating pointer
- /// parameter with the AccessChain of variant's signature.
- AccessShape AbsoluteAccessShape(const FnVariant::Signature& signature,
- const AccessShape& shape) const {
- if (auto* root_param = shape.root.variable->As<sem::Parameter>()) {
- if (auto incoming_chain = signature.Get(root_param)) {
- // Access chain originates from a parameter, which will be transformed into an array
- // of dynamic indices. Concatenate the signature's AccessShape for the parameter
- // to the chain's indices, skipping over the chain's initial parameter index.
- auto absolute = *incoming_chain;
- for (auto& op : shape.ops) {
- absolute.ops.Push(op);
- }
- return absolute;
- }
- }
-
- // Chain does not originate from a parameter, so is already absolute.
- return shape;
- }
-
- /// TransformFunction registers the clone callback to transform the function @p fn into the
- /// (potentially multiple) function's variants. TransformFunction will assign the current
- /// function and variant to #clone_state, which can be used by the other clone callbacks.
- void TransformFunction(const sem::Function* fn, FnInfo* fn_info) {
- // Register a custom handler for the specific function
- ctx.Replace(fn->Declaration(), [this, fn, fn_info] {
- // For the scope of this lambda, assign current_function to fn_info.
- TINT_SCOPED_ASSIGNMENT(clone_state->current_function, fn_info);
-
- // This callback expects a single function returned. As we're generating potentially
- // many variant functions, keep a record of the last created variant, and explicitly add
- // this to the module if it isn't the last. We'll return the last created variant,
- // taking the place of the original function.
- const Function* pending_variant = nullptr;
-
- // For each variant of fn...
- for (auto variant_it : fn_info->SortedVariants()) {
- if (pending_variant) {
- b.AST().AddFunction(pending_variant);
- }
-
- auto& variant_sig = *variant_it.first;
- auto& variant = *variant_it.second;
-
- // For the rest of this scope, assign the current variant and variant signature.
- TINT_SCOPED_ASSIGNMENT(clone_state->current_variant_sig, &variant_sig);
- TINT_SCOPED_ASSIGNMENT(clone_state->current_variant, &variant);
-
- // Build the variant's parameters.
- // Pointer parameters in the 'uniform', 'storage' or 'workgroup' address space are
- // either replaced with an array of dynamic indices, or are dropped (if there are no
- // dynamic indices).
- tint::Vector<const Parameter*, 8> params;
- for (auto* param : fn->Parameters()) {
- if (auto incoming_shape = variant_sig.Get(param)) {
- auto& symbols = *variant.ptr_param_symbols.Get(param);
- if (symbols.base_ptr.IsValid()) {
- auto base_ptr_ty = b.ty.ptr(
- incoming_shape->root.address_space,
- CreateASTTypeFor(ctx, incoming_shape->root.type->UnwrapPtrOrRef()));
- params.Push(b.Param(symbols.base_ptr, base_ptr_ty));
- }
- if (symbols.indices.IsValid()) {
- // Variant has dynamic indices for this variant, replace it.
- auto dyn_idx_arr_type = DynamicIndexArrayType(*incoming_shape);
- params.Push(b.Param(symbols.indices, dyn_idx_arr_type));
- }
- } else {
- // Just a regular parameter. Just clone the original parameter.
- params.Push(ctx.Clone(param->Declaration()));
- }
- }
-
- // Build the variant by cloning the source function. The other clone callbacks will
- // use clone_state->current_variant and clone_state->current_variant_sig to produce
- // the variant.
- auto ret_ty = ctx.Clone(fn->Declaration()->return_type);
- auto body = ctx.Clone(fn->Declaration()->body);
- auto attrs = ctx.Clone(fn->Declaration()->attributes);
- auto ret_attrs = ctx.Clone(fn->Declaration()->return_type_attributes);
- pending_variant =
- b.create<Function>(b.Ident(variant.name), std::move(params), ret_ty, body,
- std::move(attrs), std::move(ret_attrs));
- }
-
- return pending_variant;
- });
- }
-
- /// TransformCall registers the clone callback to transform the call expression @p call to call
- /// the correct target variant, and to replace pointers arguments with an array of dynamic
- /// indices.
- void TransformCall(const sem::Call* call) {
- // Register a custom handler for the specific call expression
- ctx.Replace(call->Declaration(), [this, call] {
- auto target_variant = clone_state->current_variant->calls.Get(call);
- if (!target_variant) {
- // The current variant does not need to transform this call.
- return ctx.CloneWithoutTransform(call->Declaration());
- }
-
- // Build the new call expressions's arguments.
- tint::Vector<const Expression*, 8> new_args;
- for (size_t arg_idx = 0; arg_idx < call->Arguments().Length(); arg_idx++) {
- auto* arg = call->Arguments()[arg_idx];
- auto* param = call->Target()->Parameters()[arg_idx];
- auto* param_ty = param->Type()->As<core::type::Pointer>();
- if (!param_ty) {
- // Parameter is not a pointer.
- // Just clone the unaltered argument.
- new_args.Push(ctx.Clone(arg->Declaration()));
- continue; // Parameter is not a pointer
- }
-
- auto* chain = AccessChainFor(arg);
- if (!chain) {
- // No access chain means the argument is not a pointer that needs transforming.
- // Just clone the unaltered argument.
- new_args.Push(ctx.Clone(arg->Declaration()));
- continue;
- }
-
- // Construct the absolute AccessShape by considering the AccessShape of the caller
- // variant's argument. This will propagate back through pointer parameters, to the
- // outermost caller.
- auto full_indices = AbsoluteAccessShape(*clone_state->current_variant_sig, *chain);
-
- // If the parameter is a pointer in the 'private' or 'function' address space, then
- // we need to pass an additional pointer argument to the base object.
- if (IsPrivateOrFunction(param_ty->AddressSpace())) {
- auto* root_expr = BuildAccessRootExpr(chain->root, /* deref */ false);
- if (!chain->root.variable->Is<sem::Parameter>()) {
- root_expr = b.AddressOf(root_expr);
- }
- new_args.Push(root_expr);
- }
-
- // Get or create the dynamic indices array.
- if (auto dyn_idx_arr_ty = DynamicIndexArrayType(full_indices)) {
- // Build an array of dynamic indices to pass as the replacement for the pointer.
- tint::Vector<const Expression*, 8> dyn_idx_args;
- if (auto* root_param = chain->root.variable->As<sem::Parameter>()) {
- // Access chain originates from a pointer parameter.
- if (auto incoming_chain =
- clone_state->current_variant_sig->Get(root_param)) {
- auto indices =
- clone_state->current_variant->ptr_param_symbols.Get(root_param)
- ->indices;
-
- // This pointer parameter will have been replaced with a array<u32, N>
- // holding the variant's dynamic indices for the pointer. Unpack these
- // directly into the array constructor's arguments.
- auto N = incoming_chain->NumDynamicIndices();
- for (uint32_t i = 0; i < N; i++) {
- dyn_idx_args.Push(b.IndexAccessor(indices, u32(i)));
- }
- }
- }
- // Pass the dynamic indices of the access chain into the array constructor.
- for (auto& dyn_idx : chain->dynamic_indices) {
- dyn_idx_args.Push(BuildDynamicIndex(dyn_idx, /* cast_to_u32 */ true));
- }
- // Construct the dynamic index array, and push as an argument.
- new_args.Push(b.Call(dyn_idx_arr_ty, std::move(dyn_idx_args)));
- }
- }
-
- // Make the call to the target's variant.
- return b.Call(*target_variant, std::move(new_args));
- });
- }
-
- /// ProcessAccessChains performs the following:
- /// * Removes all AccessChains from expressions that are not either used as a pointer argument
- /// in a call, or originates from a pointer parameter.
- /// * Hoists the dynamic index expressions of AccessChains to 'let' statements, to prevent
- /// multiple evaluation of the expressions, and avoid expressions resolving to different
- /// variables based on lexical scope.
- void ProcessAccessChains() {
- auto chain_exprs = access_chains.Keys();
- chain_exprs.Sort([](const auto& expr_a, const auto& expr_b) {
- return expr_a->Declaration()->node_id.value < expr_b->Declaration()->node_id.value;
- });
-
- for (auto* expr : chain_exprs) {
- auto* chain = *access_chains.Get(expr);
- if (!chain->used_in_call && !chain->root.variable->Is<sem::Parameter>()) {
- // Chain was not used in a function call, and does not originate from a
- // parameter. This chain does not need transforming. Drop it.
- access_chains.Remove(expr);
- continue;
- }
-
- // Chain requires transforming.
-
- // We need to be careful that the chain does not use expressions with side-effects which
- // cannot be repeatedly evaluated. In this situation we can hoist the dynamic index
- // expressions to their own uniquely named lets (if required).
- MaybeHoistDynamicIndices(chain, expr->Stmt());
- }
- }
-
- /// TransformAccessChainExpressions registers the clone callback to:
- /// * Transform all expressions that have an AccessChain (which aren't arguments to function
- /// calls, these are handled by TransformCall()), into the equivalent expression using a
- /// module-scope variable.
- /// * Replace expressions that have been hoisted to a let, with an identifier expression to that
- /// let.
- void TransformAccessChainExpressions() {
- // Register a custom handler for all non-function call expressions
- ctx.ReplaceAll([this](const Expression* ast_expr) -> const Expression* {
- if (!clone_state->current_variant) {
- // Expression does not belong to a function variant.
- return nullptr; // Just clone the expression.
- }
-
- auto* expr = sem.GetVal(ast_expr);
- if (!expr) {
- // No semantic node for the expression.
- return nullptr; // Just clone the expression.
- }
-
- // If the expression has been hoisted to a 'let', then replace the expression with an
- // identifier to the hoisted let.
- if (auto hoisted = clone_state->current_function->hoisted_exprs.Get(expr)) {
- return b.Expr(*hoisted);
- }
-
- auto* chain = AccessChainFor(expr);
- if (!chain) {
- // The expression does not have an AccessChain.
- return nullptr; // Just clone the expression.
- }
-
- auto* root_param = chain->root.variable->As<sem::Parameter>();
- if (!root_param) {
- // The expression has an access chain, but does not originate with a pointer
- // parameter. We don't need to change anything here.
- return nullptr; // Just clone the expression.
- }
-
- auto incoming_shape = clone_state->current_variant_sig->Get(root_param);
- if (!incoming_shape) {
- // The root parameter of the access chain is not part of the variant's signature.
- return nullptr; // Just clone the expression.
- }
-
- // Expression holds an access chain to a pointer parameter that needs transforming.
- // Reconstruct the expression using the variant's incoming shape.
-
- auto* chain_expr = BuildAccessRootExpr(incoming_shape->root, /* deref */ true);
-
- // Chain starts with a pointer parameter.
- // Replace this with the variant's incoming shape. This will bring the expression up to
- // the incoming pointer.
- size_t next_dyn_idx_from_indices = 0;
- auto& indices =
- clone_state->current_variant->ptr_param_symbols.Get(root_param)->indices;
- for (auto param_access : incoming_shape->ops) {
- chain_expr = BuildAccessExpr(chain_expr, param_access, [&] {
- return b.IndexAccessor(indices, AInt(next_dyn_idx_from_indices++));
- });
- }
-
- // Now build the expression chain within the function.
-
- // For each access in the chain (excluding the pointer parameter)...
- size_t next_dyn_idx_from_chain = 0;
- for (auto& op : chain->ops) {
- chain_expr = BuildAccessExpr(chain_expr, op, [&] {
- return BuildDynamicIndex(chain->dynamic_indices[next_dyn_idx_from_chain++],
- false);
- });
- }
-
- // BuildAccessExpr() always returns a non-pointer.
- // If the expression we're replacing is a pointer, take the address.
- if (expr->Type()->Is<core::type::Pointer>()) {
- chain_expr = b.AddressOf(chain_expr);
- }
-
- return chain_expr;
- });
- }
-
- /// @returns the FnInfo for the given function, constructing a new FnInfo if @p fn doesn't
- /// already have one.
- FnInfo* FnInfoFor(const sem::Function* fn) {
- return fns.GetOrAdd(fn, [this] { return fn_info_allocator.Create(); });
- }
-
- /// @returns the type alias used to hold the dynamic indices for @p shape, declaring a new alias
- /// if this is the first call for the given shape.
- Type DynamicIndexArrayType(const AccessShape& shape) {
- auto name = dynamic_index_array_aliases.GetOrAdd(shape, [&] {
- // Count the number of dynamic indices
- uint32_t num_dyn_indices = shape.NumDynamicIndices();
- if (num_dyn_indices == 0) {
- return Symbol{};
- }
- auto symbol = b.Symbols().New(AccessShapeName(shape));
- b.Alias(symbol, b.ty.array(b.ty.u32(), u32(num_dyn_indices)));
- return symbol;
- });
- return name.IsValid() ? b.ty(name) : Type{};
- }
-
- /// @returns a name describing the given shape
- std::string AccessShapeName(const AccessShape& shape) {
- StringStream ss;
-
- if (IsPrivateOrFunction(shape.root.address_space)) {
- ss << "F";
- } else {
- ss << shape.root.variable->Declaration()->name->symbol.Name();
- }
-
- for (auto& op : shape.ops) {
- ss << "_";
-
- if (std::holds_alternative<DynamicIndex>(op)) {
- /// The op uses a dynamic (runtime-expression) index.
- ss << "X";
- continue;
- }
-
- auto* member = std::get_if<Symbol>(&op);
- if (DAWN_LIKELY(member)) {
- ss << member->Name();
- continue;
- }
-
- TINT_ICE() << "unhandled variant for access chain";
- }
- return ss.str();
- }
-
- /// Builds an expresion to the root of an access, returning the new expression.
- /// @param root the AccessRoot
- /// @param deref if true, the returned expression will always be a reference type.
- const Expression* BuildAccessRootExpr(const AccessRoot& root, bool deref) {
- if (auto* param = root.variable->As<sem::Parameter>()) {
- if (auto symbols = clone_state->current_variant->ptr_param_symbols.Get(param)) {
- if (deref) {
- return b.Deref(b.Expr(symbols->base_ptr));
- }
- return b.Expr(symbols->base_ptr);
- }
- }
-
- const Expression* expr = b.Expr(ctx.Clone(root.variable->Declaration()->name->symbol));
- if (deref) {
- if (root.variable->Type()->Is<core::type::Pointer>()) {
- expr = b.Deref(expr);
- }
- }
- return expr;
- }
-
- /// Builds a single access in an access chain, returning the new expression.
- /// The returned expression will always be of a reference type.
- /// @param expr the input expression
- /// @param access the access to perform on the current expression
- /// @param dynamic_index a function that obtains the next dynamic index
- const Expression* BuildAccessExpr(const Expression* expr,
- const AccessOp& access,
- std::function<const Expression*()> dynamic_index) {
- if (std::holds_alternative<DynamicIndex>(access)) {
- /// The access uses a dynamic (runtime-expression) index.
- auto* idx = dynamic_index();
- return b.IndexAccessor(expr, idx);
- }
-
- auto* member = std::get_if<Symbol>(&access);
- if (DAWN_LIKELY(member)) {
- /// The access is a member access.
- return b.MemberAccessor(expr, ctx.Clone(*member));
- }
-
- TINT_ICE() << "unhandled variant type for access chain";
- }
-
- /// @returns a new Symbol starting with @p symbol concatenated with @p suffix, and possibly an
- /// underscore and number, if the symbol is already taken.
- Symbol UniqueSymbolWithSuffix(Symbol symbol, const std::string& suffix) {
- auto str = symbol.Name() + suffix;
- return unique_symbols.GetOrAdd(str, [&] { return b.Symbols().New(str); });
- }
-
- /// @returns true if the function @p fn has at least one pointer parameter.
- static bool HasPointerParameter(const sem::Function* fn) {
- for (auto* param : fn->Parameters()) {
- if (param->Type()->Is<core::type::Pointer>()) {
- return true;
- }
- }
- return false;
- }
-
- /// @returns true if the function @p fn has at least one pointer parameter in an address space
- /// that must be replaced. If this function is not called, then the function cannot be sensibly
- /// generated, and must be stripped.
- static bool MustBeCalled(const sem::Function* fn) {
- for (auto* param : fn->Parameters()) {
- if (auto* ptr = param->Type()->As<core::type::Pointer>()) {
- switch (ptr->AddressSpace()) {
- case core::AddressSpace::kUniform:
- case core::AddressSpace::kStorage:
- case core::AddressSpace::kWorkgroup:
- return true;
- default:
- return false;
- }
- }
- }
- return false;
- }
-
- /// @returns true if the given address space is 'private' or 'function'.
- static bool IsPrivateOrFunction(const core::AddressSpace sc) {
- return sc == core::AddressSpace::kPrivate || sc == core::AddressSpace::kFunction;
- }
-};
-
-DirectVariableAccess::Config::Config() = default;
-DirectVariableAccess::Config::Config(const Options& opt) : options(opt) {}
-
-DirectVariableAccess::Config::~Config() = default;
-
-DirectVariableAccess::DirectVariableAccess() = default;
-
-DirectVariableAccess::~DirectVariableAccess() = default;
-
-Transform::ApplyResult DirectVariableAccess::Apply(const Program& program,
- const DataMap& inputs,
- DataMap&) const {
- Options options;
- if (auto* cfg = inputs.Get<Config>()) {
- options = cfg->options;
- }
- return State(program, options).Run();
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h b/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
deleted file mode 100644
index 0d2771d..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// DirectVariableAccess is a transform that allows usage of pointer parameters in the 'storage',
-/// 'uniform' and 'workgroup' address space, and passing of pointers to sub-objects. These pointers
-/// are allowed with the `unrestricted_pointer_parameters` WGSL feature.
-///
-/// DirectVariableAccess works by creating specializations of functions that have pointer
-/// parameters, one specialization for each pointer argument's unique access chain 'shape' from a
-/// unique variable. Calls to specialized functions are transformed so that the pointer arguments
-/// are replaced with an array of access-chain indicies, and if the pointer is in the 'function' or
-/// 'private' address space, also with a pointer to the root object. For more information, see the
-/// comments in src/tint/lang/wgsl/ast/transform/direct_variable_access.cc.
-///
-/// @note DirectVariableAccess requires the transform::Unshadow transform to have been run first.
-class DirectVariableAccess final : public Castable<DirectVariableAccess, Transform> {
- public:
- /// Constructor
- DirectVariableAccess();
- /// Destructor
- ~DirectVariableAccess() override;
-
- /// Options adjusts the behaviour of the transform.
- struct Options {
- /// If true, then 'private' sub-object pointer arguments will be transformed.
- bool transform_private = false;
- /// If true, then 'function' sub-object pointer arguments will be transformed.
- bool transform_function = false;
-
- /// Reflection for this struct
- TINT_REFLECT(Options, transform_private, transform_function);
- };
-
- /// Config is consumed by the DirectVariableAccess transform.
- /// Config specifies the behavior of the transform.
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- Config();
-
- /// Constructor
- /// @param options behavior of the transform
- explicit Config(const Options& options);
-
- /// Destructor
- ~Config() override;
-
- /// The transform behavior options
- Options options;
-
- /// Reflection for this struct
- TINT_REFLECT(Config, options);
- };
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
deleted file mode 100644
index 9b18a40..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
+++ /dev/null
@@ -1,3576 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
-
-#include <memory>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-/// @returns a DataMap with DirectVariableAccess::Config::transform_private enabled.
-static DataMap EnablePrivate() {
- DirectVariableAccess::Options opts;
- opts.transform_private = true;
-
- DataMap inputs;
- inputs.Add<DirectVariableAccess::Config>(opts);
- return inputs;
-}
-
-/// @returns a DataMap with DirectVariableAccess::Config::transform_function enabled.
-static DataMap EnableFunction() {
- DirectVariableAccess::Options opts;
- opts.transform_function = true;
-
- DataMap inputs;
- inputs.Add<DirectVariableAccess::Config>(opts);
- return inputs;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ShouldRun tests
-////////////////////////////////////////////////////////////////////////////////
-namespace should_run {
-
-using DirectVariableAccessShouldRunTest = TransformTest;
-
-TEST_F(DirectVariableAccessShouldRunTest, EmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<DirectVariableAccess>(src));
-}
-
-} // namespace should_run
-
-////////////////////////////////////////////////////////////////////////////////
-// remove uncalled
-////////////////////////////////////////////////////////////////////////////////
-namespace remove_uncalled {
-
-using DirectVariableAccessRemoveUncalledTest = TransformTest;
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrUniform) {
- auto* src = R"(
-var<private> keep_me = 42;
-
-fn u(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-)";
-
- auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrStorage) {
- auto* src = R"(
-var<private> keep_me = 42;
-
-fn s(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
- return *(p);
-}
-)";
-
- auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrWorkgroup) {
- auto* src = R"(
-var<private> keep_me = 42;
-
-fn w(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-)";
-
- auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Disabled) {
- auto* src = R"(
-var<private> keep_me = 42;
-
-fn f(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Enabled) {
- auto* src = R"(
-var<private> keep_me = 42;
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Disabled) {
- auto* src = R"(
-var<private> keep_me = 42;
-
-fn f(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *(p);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Enabled) {
- auto* src = R"(
-var<private> keep_me = 42;
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace remove_uncalled
-
-////////////////////////////////////////////////////////////////////////////////
-// pointer chains
-////////////////////////////////////////////////////////////////////////////////
-namespace pointer_chains_tests {
-
-using DirectVariableAccessPtrChainsTest = TransformTest;
-
-TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p0 = &U;
- let p1 = &(*p0)[1];
- let p2 = &(*p1)[1+1];
- let p3 = &(*p2)[2*2 - 1];
- a(10, p3, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
- let p0 = p;
- let p1 = &(*p0)[1];
- let p2 = &(*p1)[1+1];
- let p3 = &(*p2)[2*2 - 1];
- a(10, p3, 20);
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
- let p0 = &(U);
- let p1 = &((*(p0))[1]);
- let p2 = &((*(p1))[(1 + 1)]);
- let p3 = &((*(p2))[((2 * 2) - 1)]);
- a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn c_U() {
- let p0 = &(U);
- let p1 = &(U[1]);
- let p2 = &(U[1][2]);
- let p3 = &(U[1][2][3]);
- a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices_ViaPointerIndex) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p0 = &U;
- let p1 = &p0[1];
- let p2 = &p1[1+1];
- let p3 = &p2[2*2 - 1];
- a(10, p3, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
- let p0 = p;
- let p1 = &p0[1];
- let p2 = &p1[1+1];
- let p3 = &p2[2*2 - 1];
- a(10, p3, 20);
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
- let p0 = &(U);
- let p1 = &(p0[1]);
- let p2 = &(p1[(1 + 1)]);
- let p3 = &(p2[((2 * 2) - 1)]);
- a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn c_U() {
- let p0 = &(U);
- let p1 = &(U[1]);
- let p2 = &(U[1][2]);
- let p3 = &(U[1][2][3]);
- a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistIndices) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
- i++;
- return i;
-}
-fn second() -> i32 {
- i++;
- return i;
-}
-fn third() -> i32 {
- i++;
- return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p0 = &U;
- let p1 = &(*p0)[first()];
- let p2 = &(*p1)[second()][third()];
- a(10, p2, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
- let p0 = &U;
- let p1 = &(*p0)[first()];
- let p2 = &(*p1)[second()][third()];
- a(10, p2, 20);
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
- i++;
- return i;
-}
-
-fn second() -> i32 {
- i++;
- return i;
-}
-
-fn third() -> i32 {
- i++;
- return i;
-}
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
- let p0 = &(U);
- let ptr_index_save = first();
- let p1 = &((*(p0))[ptr_index_save]);
- let ptr_index_save_1 = second();
- let ptr_index_save_2 = third();
- let p2 = &((*(p1))[ptr_index_save_1][ptr_index_save_2]);
- a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save), u32(ptr_index_save_1), u32(ptr_index_save_2)), 20);
-}
-
-fn c_U() {
- let p0 = &(U);
- let ptr_index_save_3 = first();
- let p1 = &((*(p0))[ptr_index_save_3]);
- let ptr_index_save_4 = second();
- let ptr_index_save_5 = third();
- let p2 = &((*(p1))[ptr_index_save_4][ptr_index_save_5]);
- a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save_3), u32(ptr_index_save_4), u32(ptr_index_save_5)), 20);
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopInit) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
- i++;
- return i;
-}
-fn second() -> i32 {
- i++;
- return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- for (let p1 = &U[first()]; true; ) {
- a(10, &(*p1)[second()], 20);
- }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
- for (let p1 = &(*p)[first()]; true; ) {
- a(10, &(*p1)[second()], 20);
- }
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
- i++;
- return i;
-}
-
-fn second() -> i32 {
- i++;
- return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]];
-}
-
-fn b() {
- {
- let ptr_index_save = first();
- let p1 = &(U[ptr_index_save]);
- loop {
- if (!(true)) {
- break;
- }
- {
- a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(second())), 20);
- }
- }
- }
-}
-
-fn c_U() {
- {
- let ptr_index_save_1 = first();
- let p1 = &(U[ptr_index_save_1]);
- loop {
- if (!(true)) {
- break;
- }
- {
- a_U_X_X(10, U_X_X(u32(ptr_index_save_1), u32(second())), 20);
- }
- }
- }
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCond) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
- i++;
- return i;
-}
-fn second() -> i32 {
- i++;
- return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p = &U[first()][second()];
- for (; a(10, p, 20).x < 4; ) {
- let body = 1;
- }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
- let p2 = &(*p)[first()][second()];
- for (; a(10, p2, 20).x < 4; ) {
- let body = 1;
- }
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
- i++;
- return i;
-}
-
-fn second() -> i32 {
- i++;
- return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]];
-}
-
-fn b() {
- let ptr_index_save = first();
- let ptr_index_save_1 = second();
- let p = &(U[ptr_index_save][ptr_index_save_1]);
- for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4); ) {
- let body = 1;
- }
-}
-
-fn c_U() {
- let ptr_index_save_2 = first();
- let ptr_index_save_3 = second();
- let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
- for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4); ) {
- let body = 1;
- }
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCont) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
- i++;
- return i;
-}
-fn second() -> i32 {
- i++;
- return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p = &U[first()][second()];
- for (var i = 0; i < 3; a(10, p, 20)) {
- i++;
- }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
- let p2 = &(*p)[first()][second()];
- for (var i = 0; i < 3; a(10, p2, 20)) {
- i++;
- }
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
- i++;
- return i;
-}
-
-fn second() -> i32 {
- i++;
- return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]];
-}
-
-fn b() {
- let ptr_index_save = first();
- let ptr_index_save_1 = second();
- let p = &(U[ptr_index_save][ptr_index_save_1]);
- for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20)) {
- i++;
- }
-}
-
-fn c_U() {
- let ptr_index_save_2 = first();
- let ptr_index_save_3 = second();
- let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
- for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20)) {
- i++;
- }
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInWhileCond) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
- i++;
- return i;
-}
-fn second() -> i32 {
- i++;
- return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let p = &U[first()][second()];
- while (a(10, p, 20).x < 4) {
- let body = 1;
- }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
- let p2 = &(*p)[first()][second()];
- while (a(10, p2, 20).x < 4) {
- let body = 1;
- }
-}
-
-fn d() {
- c(&U);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
- i++;
- return i;
-}
-
-fn second() -> i32 {
- i++;
- return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
- return U[p[0]][p[1]];
-}
-
-fn b() {
- let ptr_index_save = first();
- let ptr_index_save_1 = second();
- let p = &(U[ptr_index_save][ptr_index_save_1]);
- while((a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4)) {
- let body = 1;
- }
-}
-
-fn c_U() {
- let ptr_index_save_2 = first();
- let ptr_index_save_3 = second();
- let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
- while((a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4)) {
- let body = 1;
- }
-}
-
-fn d() {
- c_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace pointer_chains_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'uniform' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace uniform_as_tests {
-
-using DirectVariableAccessUniformASTest = TransformTest;
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_i32_read) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : i32;
-
-fn a(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
- return *p;
-}
-
-fn b() {
- a(10, &U, 20);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : i32;
-
-fn a_U(pre : i32, post : i32) -> i32 {
- return U;
-}
-
-fn b() {
- a_U(10, 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let I = 3;
- a(10, &U[I], 20);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-alias U_X = array<u32, 1u>;
-
-fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4<i32> {
- return U[p[0]];
-}
-
-fn b() {
- let I = 3;
- a_U_X(10, U_X(u32(I)), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead_ViaPointerDot) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- var I = vec2<i32>(3, 3);
- let p = &I;
- a(10, &U[p.x], 20);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-alias U_X = array<u32, 1u>;
-
-fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4<i32> {
- return U[p[0]];
-}
-
-fn b() {
- var I = vec2<i32>(3, 3);
- let p = &(I);
- a_U_X(10, U_X(u32(p.x)), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-fn f0(p : ptr<uniform, vec4<f32>>) -> f32 {
- return (*p).x;
-}
-
-fn f1(p : ptr<uniform, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&(*p)[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &(*p)[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of U
- res += f0(&U.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of U
- let p_vec = &U.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<uniform, Inner>) -> f32 {
- let p_mat = &(*p).mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<uniform, InnerArr>, p1 : ptr<uniform, mat3x4<f32>>) -> f32 {
- let p0_inner = &(*p0)[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<uniform, Outer>) -> f32 {
- return f3(&(*p).arr, &U.mat);
-}
-
-fn b() {
- f4(&U);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-alias U_mat_X = array<u32, 1u>;
-
-fn f0_U_mat_X(p : U_mat_X) -> f32 {
- return U.mat[p[0]].x;
-}
-
-alias U_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 {
- return U.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_U_mat() -> f32 {
- var res : f32;
- {
- res += f0_U_mat_X(U_mat_X(1));
- }
- {
- let p_vec = &(U.mat[1]);
- res += f0_U_mat_X(U_mat_X(1));
- }
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(U.arr[2].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias U_arr_X_mat = array<u32, 1u>;
-
-fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(U.arr[p[0]].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(U.arr[2].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn f2_U_arr_X(p : U_arr_X) -> f32 {
- let p_mat = &(U.arr[p[0]].mat);
- return f1_U_arr_X_mat(U_arr_X_mat(p[0u]));
-}
-
-fn f3_U_arr_U_mat() -> f32 {
- let p0_inner = &(U.arr[3]);
- return (f2_U_arr_X(U_arr_X(3)) + f1_U_mat());
-}
-
-fn f4_U() -> f32 {
- return f3_U_arr_U_mat();
-}
-
-fn b() {
- f4_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining_ViaPointerDotOrIndex) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-fn f0(p : ptr<uniform, vec4<f32>>) -> f32 {
- return p.x;
-}
-
-fn f1(p : ptr<uniform, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&p[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &p[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of U
- res += f0(&U.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of U
- let p_vec = &U.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<uniform, Inner>) -> f32 {
- let p_mat = &p.mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<uniform, InnerArr>, p1 : ptr<uniform, mat3x4<f32>>) -> f32 {
- let p0_inner = &(*p0)[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<uniform, Outer>) -> f32 {
- return f3(&p.arr, &U.mat);
-}
-
-fn b() {
- f4(&U);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-alias U_mat_X = array<u32, 1u>;
-
-fn f0_U_mat_X(p : U_mat_X) -> f32 {
- return (&(U.mat[p[0]])).x;
-}
-
-alias U_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 {
- return (&(U.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_U_mat() -> f32 {
- var res : f32;
- {
- res += f0_U_mat_X(U_mat_X(1));
- }
- {
- let p_vec = &(U.mat[1]);
- res += f0_U_mat_X(U_mat_X(1));
- }
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(U.arr[2].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias U_arr_X_mat = array<u32, 1u>;
-
-fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(U.arr[p[0]].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(U.arr[2].mat[1]);
- res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn f2_U_arr_X(p : U_arr_X) -> f32 {
- let p_mat = &(U.arr[p[0]].mat);
- return f1_U_arr_X_mat(U_arr_X_mat(p[0u]));
-}
-
-fn f3_U_arr_U_mat() -> f32 {
- let p0_inner = &(U.arr[3]);
- return (f2_U_arr_X(U_arr_X(3)) + f1_U_mat());
-}
-
-fn f4_U() -> f32 {
- return f3_U_arr_U_mat();
-}
-
-fn b() {
- f4_U();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining2) {
- auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<uniform> input : T;
-
-fn f2(p : ptr<uniform, T2>) -> T3 {
- return (*p)[3];
-}
-
-fn f1(p : ptr<uniform, T1>) -> T3 {
- return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<uniform, T>) -> T3 {
- return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- f0(&input);
-}
-)";
-
- auto* expect =
- R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<uniform> input : T;
-
-alias input_X_X = array<u32, 2u>;
-
-fn f2_input_X_X(p : input_X_X) -> T3 {
- return input[p[0]][p[1]][3];
-}
-
-alias input_X = array<u32, 1u>;
-
-fn f1_input_X(p : input_X) -> T3 {
- return f2_input_X_X(input_X_X(p[0u], 2));
-}
-
-fn f0_input() -> T3 {
- return f1_input_X(input_X(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
- f0_input();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace uniform_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'storage' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace storage_as_tests {
-
-using DirectVariableAccessStorageASTest = TransformTest;
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_i32_Via_struct_read) {
- auto* src = R"(
-struct str {
- i : i32,
-};
-
-@group(0) @binding(0) var<storage> S : str;
-
-fn a(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
- return *p;
-}
-
-fn b() {
- a(10, &S.i, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : i32,
-}
-
-@group(0) @binding(0) var<storage> S : str;
-
-fn a_S_i(pre : i32, post : i32) -> i32 {
- return S.i;
-}
-
-fn b() {
- a_S_i(10, 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_arr_i32_Via_struct_write) {
- auto* src = R"(
-struct str {
- arr : array<i32, 4>,
-};
-
-@group(0) @binding(0) var<storage, read_write> S : str;
-
-fn a(pre : i32, p : ptr<storage, array<i32, 4>, read_write>, post : i32) {
- *p = array<i32, 4>();
-}
-
-fn b() {
- a(10, &S.arr, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- arr : array<i32, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> S : str;
-
-fn a_S_arr(pre : i32, post : i32) {
- S.arr = array<i32, 4>();
-}
-
-fn b() {
- a_S_arr(10, 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_vec4i32_Via_array_DynamicWrite) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<storage, vec4<i32>, read_write>, post : i32) {
- *p = vec4<i32>();
-}
-
-fn b() {
- let I = 3;
- a(10, &S[I], 20);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
-
-alias S_X = array<u32, 1u>;
-
-fn a_S_X(pre : i32, p : S_X, post : i32) {
- S[p[0]] = vec4<i32>();
-}
-
-fn b() {
- let I = 3;
- a_S_X(10, S_X(u32(I)), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-fn f0(p : ptr<storage, vec4<f32>>) -> f32 {
- return (*p).x;
-}
-
-fn f1(p : ptr<storage, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&(*p)[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &(*p)[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of S
- res += f0(&S.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of S
- let p_vec = &S.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<storage, Inner>) -> f32 {
- let p_mat = &(*p).mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<storage, InnerArr>, p1 : ptr<storage, mat3x4<f32>>) -> f32 {
- let p0_inner = &(*p0)[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<storage, Outer>) -> f32 {
- return f3(&(*p).arr, &S.mat);
-}
-
-fn b() {
- f4(&S);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-alias S_mat_X = array<u32, 1u>;
-
-fn f0_S_mat_X(p : S_mat_X) -> f32 {
- return S.mat[p[0]].x;
-}
-
-alias S_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 {
- return S.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_S_mat() -> f32 {
- var res : f32;
- {
- res += f0_S_mat_X(S_mat_X(1));
- }
- {
- let p_vec = &(S.mat[1]);
- res += f0_S_mat_X(S_mat_X(1));
- }
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(S.arr[2].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias S_arr_X_mat = array<u32, 1u>;
-
-fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(S.arr[p[0]].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(S.arr[2].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn f2_S_arr_X(p : S_arr_X) -> f32 {
- let p_mat = &(S.arr[p[0]].mat);
- return f1_S_arr_X_mat(S_arr_X_mat(p[0u]));
-}
-
-fn f3_S_arr_S_mat() -> f32 {
- let p0_inner = &(S.arr[3]);
- return (f2_S_arr_X(S_arr_X(3)) + f1_S_mat());
-}
-
-fn f4_S() -> f32 {
- return f3_S_arr_S_mat();
-}
-
-fn b() {
- f4_S();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining_ViaPointerDotOrIndex) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-fn f0(p : ptr<storage, vec4<f32>>) -> f32 {
- return p.x;
-}
-
-fn f1(p : ptr<storage, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&p[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &p[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of S
- res += f0(&S.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of S
- let p_vec = &S.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<storage, Inner>) -> f32 {
- let p_mat = &p.mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<storage, InnerArr>, p1 : ptr<storage, mat3x4<f32>>) -> f32 {
- let p0_inner = &p0[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<storage, Outer>) -> f32 {
- return f3(&p.arr, &S.mat);
-}
-
-fn b() {
- f4(&S);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-alias S_mat_X = array<u32, 1u>;
-
-fn f0_S_mat_X(p : S_mat_X) -> f32 {
- return (&(S.mat[p[0]])).x;
-}
-
-alias S_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 {
- return (&(S.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_S_mat() -> f32 {
- var res : f32;
- {
- res += f0_S_mat_X(S_mat_X(1));
- }
- {
- let p_vec = &(S.mat[1]);
- res += f0_S_mat_X(S_mat_X(1));
- }
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(S.arr[2].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias S_arr_X_mat = array<u32, 1u>;
-
-fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(S.arr[p[0]].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(S.arr[2].mat[1]);
- res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn f2_S_arr_X(p : S_arr_X) -> f32 {
- let p_mat = &(S.arr[p[0]].mat);
- return f1_S_arr_X_mat(S_arr_X_mat(p[0u]));
-}
-
-fn f3_S_arr_S_mat() -> f32 {
- let p0_inner = &(S.arr[3]);
- return (f2_S_arr_X(S_arr_X(3)) + f1_S_mat());
-}
-
-fn f4_S() -> f32 {
- return f3_S_arr_S_mat();
-}
-
-fn b() {
- f4_S();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining2) {
- auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage> input : T;
-
-fn f2(p : ptr<storage, T2>) -> T3 {
- return (*p)[3];
-}
-
-fn f1(p : ptr<storage, T1>) -> T3 {
- return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<storage, T>) -> T3 {
- return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- f0(&input);
-}
-)";
-
- auto* expect =
- R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage> input : T;
-
-alias input_X_X = array<u32, 2u>;
-
-fn f2_input_X_X(p : input_X_X) -> T3 {
- return input[p[0]][p[1]][3];
-}
-
-alias input_X = array<u32, 1u>;
-
-fn f1_input_X(p : input_X) -> T3 {
- return f2_input_X_X(input_X_X(p[0u], 2));
-}
-
-fn f0_input() -> T3 {
- return f1_input_X(input_X(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
- f0_input();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace storage_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'workgroup' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace workgroup_as_tests {
-
-using DirectVariableAccessWorkgroupASTest = TransformTest;
-
-TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticRead) {
- auto* src = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- a(10, &W[3], 20);
-}
-)";
-
- auto* expect = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-alias W_X = array<u32, 1u>;
-
-fn a_W_X(pre : i32, p : W_X, post : i32) -> vec4<i32> {
- return W[p[0]];
-}
-
-fn b() {
- a_W_X(10, W_X(3), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticWrite) {
- auto* src = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) {
- *p = vec4<i32>();
-}
-
-fn b() {
- a(10, &W[3], 20);
-}
-)";
-
- auto* expect = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-alias W_X = array<u32, 1u>;
-
-fn a_W_X(pre : i32, p : W_X, post : i32) {
- W[p[0]] = vec4<i32>();
-}
-
-fn b() {
- a_W_X(10, W_X(3), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-var<workgroup> W : Outer;
-
-fn f0(p : ptr<workgroup, vec4<f32>>) -> f32 {
- return (*p).x;
-}
-
-fn f1(p : ptr<workgroup, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&(*p)[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &(*p)[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of W
- res += f0(&W.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of W
- let p_vec = &W.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<workgroup, Inner>) -> f32 {
- let p_mat = &(*p).mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<workgroup, InnerArr>, p1 : ptr<workgroup, mat3x4<f32>>) -> f32 {
- let p0_inner = &(*p0)[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<workgroup, Outer>) -> f32 {
- return f3(&(*p).arr, &W.mat);
-}
-
-fn b() {
- f4(&W);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-var<workgroup> W : Outer;
-
-alias W_mat_X = array<u32, 1u>;
-
-fn f0_W_mat_X(p : W_mat_X) -> f32 {
- return W.mat[p[0]].x;
-}
-
-alias W_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 {
- return W.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_W_mat() -> f32 {
- var res : f32;
- {
- res += f0_W_mat_X(W_mat_X(1));
- }
- {
- let p_vec = &(W.mat[1]);
- res += f0_W_mat_X(W_mat_X(1));
- }
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(W.arr[2].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias W_arr_X_mat = array<u32, 1u>;
-
-fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(W.arr[p[0]].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(W.arr[2].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn f2_W_arr_X(p : W_arr_X) -> f32 {
- let p_mat = &(W.arr[p[0]].mat);
- return f1_W_arr_X_mat(W_arr_X_mat(p[0u]));
-}
-
-fn f3_W_arr_W_mat() -> f32 {
- let p0_inner = &(W.arr[3]);
- return (f2_W_arr_X(W_arr_X(3)) + f1_W_mat());
-}
-
-fn f4_W() -> f32 {
- return f3_W_arr_W_mat();
-}
-
-fn b() {
- f4_W();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining_ViaPointerDotOrIndex) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-var<workgroup> W : Outer;
-
-fn f0(p : ptr<workgroup, vec4<f32>>) -> f32 {
- return p.x;
-}
-
-fn f1(p : ptr<workgroup, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&p[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &p[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of W
- res += f0(&W.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of W
- let p_vec = &W.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<workgroup, Inner>) -> f32 {
- let p_mat = &p.mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<workgroup, InnerArr>, p1 : ptr<workgroup, mat3x4<f32>>) -> f32 {
- let p0_inner = &p0[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<workgroup, Outer>) -> f32 {
- return f3(&p.arr, &W.mat);
-}
-
-fn b() {
- f4(&W);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-var<workgroup> W : Outer;
-
-alias W_mat_X = array<u32, 1u>;
-
-fn f0_W_mat_X(p : W_mat_X) -> f32 {
- return (&(W.mat[p[0]])).x;
-}
-
-alias W_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 {
- return (&(W.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_W_mat() -> f32 {
- var res : f32;
- {
- res += f0_W_mat_X(W_mat_X(1));
- }
- {
- let p_vec = &(W.mat[1]);
- res += f0_W_mat_X(W_mat_X(1));
- }
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(W.arr[2].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias W_arr_X_mat = array<u32, 1u>;
-
-fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
- }
- {
- let p_vec = &(W.arr[p[0]].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
- }
- {
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- {
- let p_vec = &(W.arr[2].mat[1]);
- res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
- }
- return res;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn f2_W_arr_X(p : W_arr_X) -> f32 {
- let p_mat = &(W.arr[p[0]].mat);
- return f1_W_arr_X_mat(W_arr_X_mat(p[0u]));
-}
-
-fn f3_W_arr_W_mat() -> f32 {
- let p0_inner = &(W.arr[3]);
- return (f2_W_arr_X(W_arr_X(3)) + f1_W_mat());
-}
-
-fn f4_W() -> f32 {
- return f3_W_arr_W_mat();
-}
-
-fn b() {
- f4_W();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining2) {
- auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage, read> input : T;
-
-fn f2(p : ptr<workgroup, T2>) -> T3 {
- return (*p)[3];
-}
-
-fn f1(p : ptr<workgroup, T1>) -> T3 {
- return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<workgroup, T>) -> T3 {
- return f1(&(*p)[1]);
-}
-
-var<workgroup> W : T;
-@compute @workgroup_size(1)
-fn main() {
- W = input;
- f0(&W);
-}
-)";
-
- auto* expect =
- R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage, read> input : T;
-
-alias W_X_X = array<u32, 2u>;
-
-fn f2_W_X_X(p : W_X_X) -> T3 {
- return W[p[0]][p[1]][3];
-}
-
-alias W_X = array<u32, 1u>;
-
-fn f1_W_X(p : W_X) -> T3 {
- return f2_W_X_X(W_X_X(p[0u], 2));
-}
-
-fn f0_W() -> T3 {
- return f1_W_X(W_X(1));
-}
-
-var<workgroup> W : T;
-
-@compute @workgroup_size(1)
-fn main() {
- W = input;
- f0_W();
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace workgroup_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'private' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace private_as_tests {
-
-using DirectVariableAccessPrivateASTest = TransformTest;
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_read) {
- auto* src = R"(
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-var<private> P : i32;
-
-fn b() {
- a(10, &(P), 20);
-}
-)";
-
- auto* expect = R"(
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-var<private> P : i32;
-
-fn b() {
- a_F(10, &(P), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_write) {
- auto* src = R"(
-fn a(pre : i32, p : ptr<private, i32>, post : i32) {
- *(p) = 42;
-}
-
-var<private> P : i32;
-
-fn b() {
- a(10, &(P), 20);
-}
-)";
-
- auto* expect = R"(
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) {
- *(p) = 42;
-}
-
-var<private> P : i32;
-
-fn b() {
- a_F(10, &(P), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_Via_struct_read) {
- auto* src = R"(
-struct str {
- i : i32,
-};
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *p;
-}
-
-var<private> P : str;
-
-fn b() {
- a(10, &P.i, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : i32,
-}
-
-fn a_F_i(pre : i32, p : ptr<private, str>, post : i32) -> i32 {
- return (*(p)).i;
-}
-
-var<private> P : str;
-
-fn b() {
- a_F_i(10, &(P), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_Via_struct_read) {
- auto* src = R"(
-struct str {
- i : i32,
-}
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-var<private> P : str;
-
-fn b() {
- a(10, &(P.i), 20);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
- auto* src = R"(
-struct str {
- arr : array<i32, 4>,
-};
-
-fn a(pre : i32, p : ptr<private, array<i32, 4>>, post : i32) {
- *p = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
- a(10, &P.arr, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- arr : array<i32, 4>,
-}
-
-fn a_F_arr(pre : i32, p : ptr<private, str>, post : i32) {
- (*(p)).arr = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
- a_F_arr(10, &(P), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
- auto* src = R"(
-struct str {
- arr : array<i32, 4>,
-}
-
-fn a(pre : i32, p : ptr<private, array<i32, 4>>, post : i32) {
- *(p) = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
- a(10, &(P.arr), 20);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_mixed) {
- auto* src = R"(
-struct str {
- i : i32,
-};
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *p;
-}
-
-var<private> Pi : i32;
-var<private> Ps : str;
-var<private> Pa : array<i32, 4>;
-
-fn b() {
- a(10, &Pi, 20);
- a(30, &Ps.i, 40);
- a(50, &Pa[2], 60);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : i32,
-}
-
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-fn a_F_i(pre : i32, p : ptr<private, str>, post : i32) -> i32 {
- return (*(p)).i;
-}
-
-alias F_X = array<u32, 1u>;
-
-fn a_F_X(pre : i32, p_base : ptr<private, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
- return (*(p_base))[p_indices[0]];
-}
-
-var<private> Pi : i32;
-
-var<private> Ps : str;
-
-var<private> Pa : array<i32, 4>;
-
-alias F_X_1 = array<u32, 1u>;
-
-fn b() {
- a_F(10, &(Pi), 20);
- a_F_i(30, &(Ps), 40);
- a_F_X(50, &(Pa), F_X_1(2), 60);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_mixed) {
- auto* src = R"(
-struct str {
- i : i32,
-}
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-var<private> Pi : i32;
-
-var<private> Ps : str;
-
-var<private> Pa : array<i32, 4>;
-
-fn b() {
- a(10, &(Pi), 20);
- a(10, &(Ps.i), 20);
- a(10, &(Pa[2]), 20);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-var<private> P : Outer;
-
-fn f0(p : ptr<private, vec4<f32>>) -> f32 {
- return (*p).x;
-}
-
-fn f1(p : ptr<private, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&(*p)[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &(*p)[1];
- res += f0(p_vec);
- }
- {
- // call f0() with inline usage of P
- res += f0(&P.arr[2].mat[1]);
- }
- {
- // call f0() with pointer-let usage of P
- let p_vec = &P.arr[2].mat[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<private, Inner>) -> f32 {
- let p_mat = &(*p).mat;
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<private, InnerArr>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
- let p0_inner = &(*p0)[3];
- return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<private, Outer>) -> f32 {
- return f3(&(*p).arr, &P.mat);
-}
-
-fn b() {
- f4(&P);
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-var<private> P : Outer;
-
-alias F_mat_X = array<u32, 1u>;
-
-fn f0_F_mat_X(p_base : ptr<private, Outer>, p_indices : F_mat_X) -> f32 {
- return (*(p_base)).mat[p_indices[0]].x;
-}
-
-alias F_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_F_arr_X_mat_X(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat_X) -> f32 {
- return (*(p_base)).arr[p_indices[0]].mat[p_indices[1]].x;
-}
-
-alias F_mat_X_1 = array<u32, 1u>;
-
-alias F_arr_X_mat_X_1 = array<u32, 2u>;
-
-fn f1_F_mat(p : ptr<private, Outer>) -> f32 {
- var res : f32;
- {
- res += f0_F_mat_X(p, F_mat_X_1(1));
- }
- {
- let p_vec = &((*(p)).mat[1]);
- res += f0_F_mat_X(p, F_mat_X_1(1));
- }
- {
- res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
- }
- {
- let p_vec = &(P.arr[2].mat[1]);
- res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
- }
- return res;
-}
-
-alias F_arr_X_mat = array<u32, 1u>;
-
-alias F_arr_X_mat_X_2 = array<u32, 2u>;
-
-fn f1_F_arr_X_mat(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat) -> f32 {
- var res : f32;
- {
- res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_2(p_indices[0u], 1));
- }
- {
- let p_vec = &((*(p_base)).arr[p_indices[0]].mat[1]);
- res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_2(p_indices[0u], 1));
- }
- {
- res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
- }
- {
- let p_vec = &(P.arr[2].mat[1]);
- res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
- }
- return res;
-}
-
-alias F_arr_X = array<u32, 1u>;
-
-alias F_arr_X_mat_1 = array<u32, 1u>;
-
-fn f2_F_arr_X(p_base : ptr<private, Outer>, p_indices : F_arr_X) -> f32 {
- let p_mat = &((*(p_base)).arr[p_indices[0]].mat);
- return f1_F_arr_X_mat(p_base, F_arr_X_mat_1(p_indices[0u]));
-}
-
-alias F_arr_X_1 = array<u32, 1u>;
-
-fn f3_F_arr_F_mat(p0 : ptr<private, Outer>, p1 : ptr<private, Outer>) -> f32 {
- let p0_inner = &((*(p0)).arr[3]);
- return (f2_F_arr_X(p0, F_arr_X_1(3)) + f1_F_mat(p1));
-}
-
-fn f4_F(p : ptr<private, Outer>) -> f32 {
- return f3_F_arr_F_mat(p, &(P));
-}
-
-fn b() {
- f4_F(&(P));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining2) {
- auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-fn f2(p : ptr<private, T2>) -> T3 {
- return (*p)[3];
-}
-
-fn f1(p : ptr<private, T1>) -> T3 {
- return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<private, T>) -> T3 {
- return f1(&(*p)[1]);
-}
-
-var<private> P : T;
-
-@compute @workgroup_size(1)
-fn main() {
- f0(&P);
-}
-)";
-
- auto* expect =
- R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-alias F_X_X = array<u32, 2u>;
-
-fn f2_F_X_X(p_base : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X_X) -> T3 {
- return (*(p_base))[p_indices[0]][p_indices[1]][3];
-}
-
-alias F_X = array<u32, 1u>;
-
-alias F_X_X_1 = array<u32, 2u>;
-
-fn f1_F_X(p_base : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X) -> T3 {
- return f2_F_X_X(p_base, F_X_X_1(p_indices[0u], 2));
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn f0_F(p : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>) -> T3 {
- return f1_F_X(p, F_X_1(1));
-}
-
-var<private> P : T;
-
-@compute @workgroup_size(1)
-fn main() {
- f0_F(&(P));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-var<private> P : Outer;
-
-fn f0(p : ptr<private, vec4<f32>>) -> f32 {
- return (*(p)).x;
-}
-
-fn f1(p : ptr<private, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- res += f0(&((*(p))[1]));
- }
- {
- let p_vec = &((*(p))[1]);
- res += f0(p_vec);
- }
- {
- res += f0(&(P.arr[2].mat[1]));
- }
- {
- let p_vec = &(P.arr[2].mat[1]);
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<private, Inner>) -> f32 {
- let p_mat = &((*(p)).mat);
- return f1(p_mat);
-}
-
-fn f3(p0 : ptr<private, InnerArr>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
- let p0_inner = &((*(p0))[3]);
- return (f2(p0_inner) + f1(p1));
-}
-
-fn f4(p : ptr<private, Outer>) -> f32 {
- return f3(&((*(p)).arr), &(P.mat));
-}
-
-fn b() {
- f4(&(P));
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace private_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'function' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace function_as_tests {
-
-using DirectVariableAccessFunctionASTest = TransformTest;
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_LocalPtr) {
- auto* src = R"(
-fn f() {
- var v : i32;
- let p : ptr<function, i32> = &(v);
- var x : i32 = *(p);
-}
-)";
-
- auto* expect = src; // Nothing changes
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_read) {
- auto* src = R"(
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-fn b() {
- var F : i32;
- a(10, &(F), 20);
-}
-)";
-
- auto* expect = R"(
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-fn b() {
- var F : i32;
- a_F(10, &(F), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_write) {
- auto* src = R"(
-fn a(pre : i32, p : ptr<function, i32>, post : i32) {
- *(p) = 42;
-}
-
-fn b() {
- var F : i32;
- a(10, &(F), 20);
-}
-)";
-
- auto* expect = R"(
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) {
- *(p) = 42;
-}
-
-fn b() {
- var F : i32;
- a_F(10, &(F), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_Via_struct_read) {
- auto* src = R"(
-struct str {
- i : i32,
-};
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *p;
-}
-
-fn b() {
- var F : str;
- a(10, &F.i, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : i32,
-}
-
-fn a_F_i(pre : i32, p : ptr<function, str>, post : i32) -> i32 {
- return (*(p)).i;
-}
-
-fn b() {
- var F : str;
- a_F_i(10, &(F), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
- auto* src = R"(
-struct str {
- arr : array<i32, 4>,
-};
-
-fn a(pre : i32, p : ptr<function, array<i32, 4>>, post : i32) {
- *p = array<i32, 4>();
-}
-
-fn b() {
- var F : str;
- a(10, &F.arr, 20);
-}
-)";
-
- auto* expect = R"(
-struct str {
- arr : array<i32, 4>,
-}
-
-fn a_F_arr(pre : i32, p : ptr<function, str>, post : i32) {
- (*(p)).arr = array<i32, 4>();
-}
-
-fn b() {
- var F : str;
- a_F_arr(10, &(F), 20);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_mixed) {
- auto* src = R"(
-struct str {
- i : i32,
-};
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *p;
-}
-
-fn b() {
- var Fi : i32;
- var Fs : str;
- var Fa : array<i32, 4>;
-
- a(10, &Fi, 20);
- a(30, &Fs.i, 40);
- a(50, &Fa[2], 60);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : i32,
-}
-
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-fn a_F_i(pre : i32, p : ptr<function, str>, post : i32) -> i32 {
- return (*(p)).i;
-}
-
-alias F_X = array<u32, 1u>;
-
-fn a_F_X(pre : i32, p_base : ptr<function, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
- return (*(p_base))[p_indices[0]];
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn b() {
- var Fi : i32;
- var Fs : str;
- var Fa : array<i32, 4>;
- a_F(10, &(Fi), 20);
- a_F_i(30, &(Fs), 40);
- a_F_X(50, &(Fa), F_X_1(2), 60);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining) {
- auto* src = R"(
-struct Inner {
- mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-};
-
-fn f0(p : ptr<function, vec4<f32>>) -> f32 {
- return (*p).x;
-}
-
-fn f1(p : ptr<function, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- // call f0() with inline usage of p
- res += f0(&(*p)[1]);
- }
- {
- // call f0() with pointer-let usage of p
- let p_vec = &(*p)[1];
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<function, Inner>) -> f32 {
- let p_mat = &(*p).mat;
- return f1(p_mat);
-}
-
-fn f3(p : ptr<function, InnerArr>) -> f32 {
- let p_inner = &(*p)[3];
- return f2(p_inner);
-}
-
-fn f4(p : ptr<function, Outer>) -> f32 {
- return f3(&(*p).arr);
-}
-
-fn b() {
- var S : Outer;
- f4(&S);
-}
-)";
-
- auto* expect =
- R"(
-struct Inner {
- mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
- arr : InnerArr,
- mat : mat3x4<f32>,
-}
-
-fn f0(p : ptr<function, vec4<f32>>) -> f32 {
- return (*(p)).x;
-}
-
-fn f1(p : ptr<function, mat3x4<f32>>) -> f32 {
- var res : f32;
- {
- res += f0(&((*(p))[1]));
- }
- {
- let p_vec = &((*(p))[1]);
- res += f0(p_vec);
- }
- return res;
-}
-
-fn f2(p : ptr<function, Inner>) -> f32 {
- let p_mat = &((*(p)).mat);
- return f1(p_mat);
-}
-
-fn f3(p : ptr<function, InnerArr>) -> f32 {
- let p_inner = &((*(p))[3]);
- return f2(p_inner);
-}
-
-fn f4(p : ptr<function, Outer>) -> f32 {
- return f3(&((*(p)).arr));
-}
-
-fn b() {
- var S : Outer;
- f4(&(S));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining2) {
- auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-fn f2(p : ptr<function, T2>) -> T3 {
- return (*p)[3];
-}
-
-fn f1(p : ptr<function, T1>) -> T3 {
- return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<function, T>) -> T3 {
- return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
- var v : T;
- f0(&v);
-}
-)";
-
- auto* expect =
- R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-alias F_X_X = array<u32, 2u>;
-
-fn f2_F_X_X(p_base : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X_X) -> T3 {
- return (*(p_base))[p_indices[0]][p_indices[1]][3];
-}
-
-alias F_X = array<u32, 1u>;
-
-alias F_X_X_1 = array<u32, 2u>;
-
-fn f1_F_X(p_base : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X) -> T3 {
- return f2_F_X_X(p_base, F_X_X_1(p_indices[0u], 2));
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn f0_F(p : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>) -> T3 {
- return f1_F_X(p, F_X_1(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
- var v : T;
- f0_F(&(v));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_i32_Via_struct_read) {
- auto* src = R"(
-struct str {
- i : i32,
-}
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return *(p);
-}
-
-fn b() {
- var F : str;
- a(10, &(F.i), 20);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
- auto* src = R"(
-struct str {
- arr : array<i32, 4>,
-}
-
-fn a(pre : i32, p : ptr<function, array<i32, 4>>, post : i32) {
- *(p) = array<i32, 4>();
-}
-
-fn b() {
- var F : str;
- a(10, &(F.arr), 20);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, PointerForwarding_NoUse) {
- auto* src = R"(
-fn a(p : ptr<function, i32>) -> i32 {
- return *p;
-}
-
-fn b(p : ptr<function, i32>) -> i32 {
- return a(p);
-}
-)";
-
- auto* expect =
- R"(
-fn a_F(p : ptr<function, i32>) -> i32 {
- return *(p);
-}
-
-fn b(p : ptr<function, i32>) -> i32 {
- return a_F(p);
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace function_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// complex tests
-////////////////////////////////////////////////////////////////////////////////
-namespace complex_tests {
-
-using DirectVariableAccessComplexTest = TransformTest;
-
-TEST_F(DirectVariableAccessComplexTest, Param_ptr_mixed_vec4i32_ViaMultiple) {
- auto* src = R"(
-struct str {
- i : vec4<i32>,
-};
-
-@group(0) @binding(0) var<uniform> U : vec4<i32>;
-@group(0) @binding(1) var<uniform> U_str : str;
-@group(0) @binding(2) var<uniform> U_arr : array<vec4<i32>, 8>;
-@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-@group(1) @binding(0) var<storage> S : vec4<i32>;
-@group(1) @binding(1) var<storage> S_str : str;
-@group(1) @binding(2) var<storage> S_arr : array<vec4<i32>, 8>;
-@group(1) @binding(3) var<storage> S_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
- var<workgroup> W : vec4<i32>;
- var<workgroup> W_str : str;
- var<workgroup> W_arr : array<vec4<i32>, 8>;
- var<workgroup> W_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-fn fn_u(p : ptr<uniform, vec4<i32>>) -> vec4<i32> {
- return *p;
-}
-
-fn fn_s(p : ptr<storage, vec4<i32>>) -> vec4<i32> {
- return *p;
-}
-
-fn fn_w(p : ptr<workgroup, vec4<i32>>) -> vec4<i32> {
- return *p;
-}
-
-fn b() {
- let I = 3;
- let J = 4;
-
- let u = fn_u(&U);
- let u_str = fn_u(&U_str.i);
- let u_arr0 = fn_u(&U_arr[0]);
- let u_arr1 = fn_u(&U_arr[1]);
- let u_arrI = fn_u(&U_arr[I]);
- let u_arr1_arr0 = fn_u(&U_arr_arr[1][0]);
- let u_arr2_arrI = fn_u(&U_arr_arr[2][I]);
- let u_arrI_arr2 = fn_u(&U_arr_arr[I][2]);
- let u_arrI_arrJ = fn_u(&U_arr_arr[I][J]);
-
- let s = fn_s(&S);
- let s_str = fn_s(&S_str.i);
- let s_arr0 = fn_s(&S_arr[0]);
- let s_arr1 = fn_s(&S_arr[1]);
- let s_arrI = fn_s(&S_arr[I]);
- let s_arr1_arr0 = fn_s(&S_arr_arr[1][0]);
- let s_arr2_arrI = fn_s(&S_arr_arr[2][I]);
- let s_arrI_arr2 = fn_s(&S_arr_arr[I][2]);
- let s_arrI_arrJ = fn_s(&S_arr_arr[I][J]);
-
- let w = fn_w(&W);
- let w_str = fn_w(&W_str.i);
- let w_arr0 = fn_w(&W_arr[0]);
- let w_arr1 = fn_w(&W_arr[1]);
- let w_arrI = fn_w(&W_arr[I]);
- let w_arr1_arr0 = fn_w(&W_arr_arr[1][0]);
- let w_arr2_arrI = fn_w(&W_arr_arr[2][I]);
- let w_arrI_arr2 = fn_w(&W_arr_arr[I][2]);
- let w_arrI_arrJ = fn_w(&W_arr_arr[I][J]);
-}
-)";
-
- auto* expect = R"(
-struct str {
- i : vec4<i32>,
-}
-
-@group(0) @binding(0) var<uniform> U : vec4<i32>;
-
-@group(0) @binding(1) var<uniform> U_str : str;
-
-@group(0) @binding(2) var<uniform> U_arr : array<vec4<i32>, 8>;
-
-@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-@group(1) @binding(0) var<storage> S : vec4<i32>;
-
-@group(1) @binding(1) var<storage> S_str : str;
-
-@group(1) @binding(2) var<storage> S_arr : array<vec4<i32>, 8>;
-
-@group(1) @binding(3) var<storage> S_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-var<workgroup> W : vec4<i32>;
-
-var<workgroup> W_str : str;
-
-var<workgroup> W_arr : array<vec4<i32>, 8>;
-
-var<workgroup> W_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-fn fn_u_U() -> vec4<i32> {
- return U;
-}
-
-fn fn_u_U_str_i() -> vec4<i32> {
- return U_str.i;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn fn_u_U_arr_X(p : U_arr_X) -> vec4<i32> {
- return U_arr[p[0]];
-}
-
-alias U_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_u_U_arr_arr_X_X(p : U_arr_arr_X_X) -> vec4<i32> {
- return U_arr_arr[p[0]][p[1]];
-}
-
-fn fn_s_S() -> vec4<i32> {
- return S;
-}
-
-fn fn_s_S_str_i() -> vec4<i32> {
- return S_str.i;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn fn_s_S_arr_X(p : S_arr_X) -> vec4<i32> {
- return S_arr[p[0]];
-}
-
-alias S_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_s_S_arr_arr_X_X(p : S_arr_arr_X_X) -> vec4<i32> {
- return S_arr_arr[p[0]][p[1]];
-}
-
-fn fn_w_W() -> vec4<i32> {
- return W;
-}
-
-fn fn_w_W_str_i() -> vec4<i32> {
- return W_str.i;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn fn_w_W_arr_X(p : W_arr_X) -> vec4<i32> {
- return W_arr[p[0]];
-}
-
-alias W_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_w_W_arr_arr_X_X(p : W_arr_arr_X_X) -> vec4<i32> {
- return W_arr_arr[p[0]][p[1]];
-}
-
-fn b() {
- let I = 3;
- let J = 4;
- let u = fn_u_U();
- let u_str = fn_u_U_str_i();
- let u_arr0 = fn_u_U_arr_X(U_arr_X(0));
- let u_arr1 = fn_u_U_arr_X(U_arr_X(1));
- let u_arrI = fn_u_U_arr_X(U_arr_X(u32(I)));
- let u_arr1_arr0 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(1, 0));
- let u_arr2_arrI = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(2, u32(I)));
- let u_arrI_arr2 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), 2));
- let u_arrI_arrJ = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), u32(J)));
- let s = fn_s_S();
- let s_str = fn_s_S_str_i();
- let s_arr0 = fn_s_S_arr_X(S_arr_X(0));
- let s_arr1 = fn_s_S_arr_X(S_arr_X(1));
- let s_arrI = fn_s_S_arr_X(S_arr_X(u32(I)));
- let s_arr1_arr0 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(1, 0));
- let s_arr2_arrI = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(2, u32(I)));
- let s_arrI_arr2 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), 2));
- let s_arrI_arrJ = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), u32(J)));
- let w = fn_w_W();
- let w_str = fn_w_W_str_i();
- let w_arr0 = fn_w_W_arr_X(W_arr_X(0));
- let w_arr1 = fn_w_W_arr_X(W_arr_X(1));
- let w_arrI = fn_w_W_arr_X(W_arr_X(u32(I)));
- let w_arr1_arr0 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(1, 0));
- let w_arr2_arrI = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(2, u32(I)));
- let w_arrI_arr2 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), 2));
- let w_arrI_arrJ = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), u32(J)));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, Indexing) {
- auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 { return i; }
-
-fn b(p : ptr<storage, array<array<array<i32, 9>, 9>, 9>>) -> i32 {
- return (*p) [ a( (*p)[0][1][2] )]
- [ a( (*p)[a(3)][4][5] )]
- [ a( (*p)[6][a(7)][8] )];
-}
-
-fn c() {
- let v = b(&S[42]);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 {
- return i;
-}
-
-alias S_X = array<u32, 1u>;
-
-fn b_S_X(p : S_X) -> i32 {
- return S[p[0]][a(S[p[0]][0][1][2])][a(S[p[0]][a(3)][4][5])][a(S[p[0]][6][a(7)][8])];
-}
-
-fn c() {
- let v = b_S_X(S_X(42));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, IndexingInPtrCall) {
- auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(pre : i32, i : ptr<storage, i32>, post : i32) -> i32 {
- return *i;
-}
-
-fn b(p : ptr<storage, array<array<array<i32, 9>, 9>, 9>>) -> i32 {
- return a(10, &(*p)[ a( 20, &(*p)[0][1][2], 30 )]
- [ a( 40, &(*p)[3][4][5], 50 )]
- [ a( 60, &(*p)[6][7][8], 70 )], 80);
-}
-
-fn c() {
- let v = b(&S[42]);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-alias S_X_X_X_X = array<u32, 4u>;
-
-fn a_S_X_X_X_X(pre : i32, i : S_X_X_X_X, post : i32) -> i32 {
- return S[i[0]][i[1]][i[2]][i[3]];
-}
-
-alias S_X = array<u32, 1u>;
-
-fn b_S_X(p : S_X) -> i32 {
- return a_S_X_X_X_X(10, S_X_X_X_X(p[0u], u32(a_S_X_X_X_X(20, S_X_X_X_X(p[0u], 0, 1, 2), 30)), u32(a_S_X_X_X_X(40, S_X_X_X_X(p[0u], 3, 4, 5), 50)), u32(a_S_X_X_X_X(60, S_X_X_X_X(p[0u], 6, 7, 8), 70))), 80);
-}
-
-fn c() {
- let v = b_S_X(S_X(42));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, IndexingDualPointers) {
- auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 { return i; }
-
-fn b(s : ptr<storage, array<array<i32, 9>, 9>>,
- u : ptr<uniform, array<array<vec4<i32>, 9>, 9>>) -> i32 {
- return (*s) [ a( (*u)[0][1].x )]
- [ a( (*u)[a(3)][4].y )];
-}
-
-fn c() {
- let v = b(&S[42], &U[24]);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
-
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 {
- return i;
-}
-
-alias S_X = array<u32, 1u>;
-
-alias U_X = array<u32, 1u>;
-
-fn b_S_X_U_X(s : S_X, u : U_X) -> i32 {
- return S[s[0]][a(U[u[0]][0][1].x)][a(U[u[0]][a(3)][4].y)];
-}
-
-fn c() {
- let v = b_S_X_U_X(S_X(42), U_X(24));
-}
-)";
-
- auto got = Run<DirectVariableAccess>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace complex_tests
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc
deleted file mode 100644
index 0481710..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DisableUniformityAnalysis);
-
-namespace tint::ast::transform {
-
-DisableUniformityAnalysis::DisableUniformityAnalysis() = default;
-
-DisableUniformityAnalysis::~DisableUniformityAnalysis() = default;
-
-Transform::ApplyResult DisableUniformityAnalysis::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- if (src.Sem().Module()->Extensions().Contains(
- wgsl::Extension::kChromiumDisableUniformityAnalysis)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
- b.Enable(wgsl::Extension::kChromiumDisableUniformityAnalysis);
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h
deleted file mode 100644
index 052f59e..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Disable uniformity analysis for the program.
-class DisableUniformityAnalysis final : public Castable<DisableUniformityAnalysis, Transform> {
- public:
- /// Constructor
- DisableUniformityAnalysis();
- /// Destructor
- ~DisableUniformityAnalysis() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
deleted file mode 100644
index 09c00b6..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using DisableUniformityAnalysisTest = TransformTest;
-
-TEST_F(DisableUniformityAnalysisTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_TRUE(ShouldRun<DisableUniformityAnalysis>(src));
-}
-
-TEST_F(DisableUniformityAnalysisTest, ShouldRunExtensionAlreadyPresent) {
- auto* src = R"(
-enable chromium_disable_uniformity_analysis;
-)";
-
- EXPECT_FALSE(ShouldRun<DisableUniformityAnalysis>(src));
-}
-
-TEST_F(DisableUniformityAnalysisTest, EmptyModule) {
- auto* src = R"()";
-
- auto* expect = R"(
-enable chromium_disable_uniformity_analysis;
-)";
-
- auto got = Run<DisableUniformityAnalysis>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DisableUniformityAnalysisTest, NonEmptyModule) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> global : i32;
-
-@compute @workgroup_size(64)
-fn main() {
- if ((global == 42)) {
- workgroupBarrier();
- }
-}
-)";
-
- auto expect = "\nenable chromium_disable_uniformity_analysis;\n" + std::string(src);
-
- auto got = Run<DisableUniformityAnalysis>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
deleted file mode 100644
index d4c66c1..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/compound_assignment_statement.h"
-#include "src/tint/lang/wgsl/ast/increment_decrement_statement.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ExpandCompoundAssignment);
-
-using namespace tint::core::number_suffixes; // NOLINT
-
-namespace tint::ast::transform {
-
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* node : program.ASTNodes().Objects()) {
- if (node->IsAnyOf<CompoundAssignmentStatement, IncrementDecrementStatement>()) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-/// PIMPL state for the transform
-struct ExpandCompoundAssignment::State {
- /// Constructor
- /// @param context the clone context
- explicit State(program::CloneContext& context)
- : ctx(context), b(*ctx.dst), hoist_to_decl_before(ctx) {}
-
- /// Replace `stmt` with a regular assignment statement of the form:
- /// lhs = lhs op rhs
- /// The LHS expression will only be evaluated once, and any side effects will
- /// be hoisted to `let` declarations above the assignment statement.
- /// @param stmt the statement to replace
- /// @param lhs the lhs expression from the source statement
- /// @param rhs the rhs expression in the destination module
- /// @param op the binary operator
- void Expand(const Statement* stmt,
- const Expression* lhs,
- const Expression* rhs,
- core::BinaryOp op) {
- // Helper function to create the new LHS expression. This will be called
- // twice when building the non-compound assignment statement, so must
- // not produce expressions that cause side effects.
- std::function<const Expression*()> new_lhs;
-
- // Helper function to create a variable that is a pointer to `expr`.
- auto hoist_pointer_to = [&](const Expression* expr) {
- // Lhs may already be a pointer, in which case we don't take it's address
- bool is_pointer = ctx.src->Sem().GetVal(expr)->Type()->Is<core::type::Pointer>();
- auto name = b.Sym();
- auto* ptr = is_pointer ? ctx.Clone(expr) : b.AddressOf(ctx.Clone(expr));
- auto* decl = b.Decl(b.Let(name, ptr));
- hoist_to_decl_before.InsertBefore(ctx.src->Sem().Get(stmt), decl);
- return name;
- };
-
- // Helper function to hoist `expr` to a let declaration.
- auto hoist_expr_to_let = [&](const Expression* expr) {
- auto name = b.Sym();
- auto* decl = b.Decl(b.Let(name, ctx.Clone(expr)));
- hoist_to_decl_before.InsertBefore(ctx.src->Sem().Get(stmt), decl);
- return name;
- };
-
- // Helper function that returns `true` if the type of `expr` is a vector.
- auto is_vec = [&](const Expression* expr) {
- if (auto* val_expr = ctx.src->Sem().GetVal(expr)) {
- return val_expr->Type()->UnwrapPtrOrRef()->Is<core::type::Vector>();
- }
- return false;
- };
-
- // Hoist the LHS expression subtree into local constants to produce a new
- // LHS that we can evaluate twice.
- // We need to special case compound assignments to vector components since
- // we cannot take the address of a vector component.
- auto* index_accessor = lhs->As<IndexAccessorExpression>();
- auto* member_accessor = lhs->As<MemberAccessorExpression>();
- if (lhs->Is<IdentifierExpression>() ||
- (member_accessor && member_accessor->object->Is<IdentifierExpression>())) {
- // TODO(crbug.com/tint/2115): This branch should also handle (recursive) deref'd
- // identifiers (e.g. (*p).bar += rhs)).
-
- // This is the simple case with no side effects, so we can just use
- // the original LHS expression directly. Before:
- // foo.bar += rhs;
- // After:
- // foo.bar = foo.bar + rhs;
- new_lhs = [&] { return ctx.Clone(lhs); };
- } else if (index_accessor && is_vec(index_accessor->object)) {
- // This is the case for vector component via an array accessor. We need
- // to capture a pointer to the vector and also the index value.
- // Before:
- // v[idx()] += rhs;
- // After:
- // let vec_ptr = &v;
- // let index = idx();
- // (*vec_ptr)[index] = (*vec_ptr)[index] + rhs;
- auto lhs_ptr = hoist_pointer_to(index_accessor->object);
- auto index = hoist_expr_to_let(index_accessor->index);
- new_lhs = [&, lhs_ptr, index] { return b.IndexAccessor(b.Deref(lhs_ptr), index); };
- } else if (member_accessor && is_vec(member_accessor->object)) {
- // This is the case for vector component via a member accessor. We just
- // need to capture a pointer to the vector.
- // Before:
- // a[idx()].y += rhs;
- // After:
- // let vec_ptr = &a[idx()];
- // (*vec_ptr).y = (*vec_ptr).y + rhs;
- auto lhs_ptr = hoist_pointer_to(member_accessor->object);
- new_lhs = [&, lhs_ptr] {
- return b.MemberAccessor(b.Deref(lhs_ptr), ctx.Clone(member_accessor->member));
- };
- } else {
- // For all other statements that may have side-effecting expressions, we
- // just need to capture a pointer to the whole LHS.
- // Before:
- // a[idx()] += rhs;
- // After:
- // let lhs_ptr = &a[idx()];
- // (*lhs_ptr) = (*lhs_ptr) + rhs;
- auto lhs_ptr = hoist_pointer_to(lhs);
- new_lhs = [&, lhs_ptr] { return b.Deref(lhs_ptr); };
- }
-
- // Replace the statement with a regular assignment statement.
- auto* value = b.create<BinaryExpression>(op, new_lhs(), rhs);
- ctx.Replace(stmt, b.Assign(new_lhs(), value));
- }
-
- private:
- /// The clone context.
- program::CloneContext& ctx;
-
- /// The AST builder.
- ast::Builder& b;
-
- /// The HoistToDeclBefore helper instance.
- HoistToDeclBefore hoist_to_decl_before;
-};
-
-ExpandCompoundAssignment::ExpandCompoundAssignment() = default;
-
-ExpandCompoundAssignment::~ExpandCompoundAssignment() = default;
-
-Transform::ApplyResult ExpandCompoundAssignment::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
- State state(ctx);
- for (auto* node : src.ASTNodes().Objects()) {
- if (auto* assign = node->As<CompoundAssignmentStatement>()) {
- state.Expand(assign, assign->lhs, ctx.Clone(assign->rhs), assign->op);
- } else if (auto* inc_dec = node->As<IncrementDecrementStatement>()) {
- // For increment/decrement statements, `i++` becomes `i = i + 1`.
- auto op = inc_dec->increment ? core::BinaryOp::kAdd : core::BinaryOp::kSubtract;
- state.Expand(inc_dec, inc_dec->lhs, ctx.dst->Expr(1_a), op);
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h
deleted file mode 100644
index 2443128..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Converts compound assignment statements to regular assignment statements,
-/// hoisting the LHS expression if necessary.
-///
-/// Before:
-/// ```
-/// a += 1;
-/// vector_array[foo()][bar()] *= 2.0;
-/// ```
-///
-/// After:
-/// ```
-/// a = a + 1;
-/// let _vec = &vector_array[foo()];
-/// let _idx = bar();
-/// (*_vec)[_idx] = (*_vec)[_idx] * 2.0;
-/// ```
-///
-/// This transform also handles increment and decrement statements in the same
-/// manner, by replacing `i++` with `i = i + 1`.
-class ExpandCompoundAssignment final : public Castable<ExpandCompoundAssignment, Transform> {
- public:
- /// Constructor
- ExpandCompoundAssignment();
- /// Destructor
- ~ExpandCompoundAssignment() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc
deleted file mode 100644
index 8ffc045..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc
+++ /dev/null
@@ -1,996 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ExpandCompoundAssignmentTest = TransformTest;
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunHasCompoundAssignment) {
- auto* src = R"(
-fn foo() {
- var v : i32;
- v += 1;
-}
-)";
-
- EXPECT_TRUE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunHasIncrementDecrement) {
- auto* src = R"(
-fn foo() {
- var v : i32;
- v++;
-}
-)";
-
- EXPECT_TRUE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Basic) {
- auto* src = R"(
-fn main() {
- var v : i32;
- v += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : i32;
- v = (v + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsPointer) {
- auto* src = R"(
-fn main() {
- var v : i32;
- let p = &v;
- *p += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : i32;
- let p = &(v);
- let tint_symbol = &(*(p));
- *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsStructMember) {
- auto* src = R"(
-struct S {
- m : f32,
-}
-
-fn main() {
- var s : S;
- s.m += 1.0;
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : f32,
-}
-
-fn main() {
- var s : S;
- s.m = (s.m + 1.0);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsArrayElement) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
- a[1] = 42;
- return 1;
-}
-
-fn main() {
- a[idx()] += 1;
-}
-)";
-
- auto* expect = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
- a[1] = 42;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(a[idx()]);
- *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_ArrayAccessor) {
- auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- v[idx()] += 1;
-}
-)";
-
- auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(v);
- let tint_symbol_1 = idx();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- v.y += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- v.y = (v.y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsArrayOfVectorComponent_MemberAccessor_ViaArrayIndex) {
- auto* src = R"(
-fn main() {
- var v : array<vec4<i32>, 3>;
- v[0].y += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : array<vec4<i32>, 3>;
- let tint_symbol = &(v[0]);
- (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaDerefPointerDot) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- (*p).y += 1;
-}
-)";
-
- // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- let tint_symbol = &(*(p));
- (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaPointerDot) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- p.y += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- p.y = (p.y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaDerefPointerIndex) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- (*p)[0] += 1;
-}
-)";
-
- // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- let tint_symbol = &(*(p));
- let tint_symbol_1 = 0;
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaPointerIndex) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- p[0] += 1;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- let tint_symbol = p;
- let tint_symbol_1 = 0;
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMatrixColumn) {
- auto* src = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx() -> i32 {
- m[0].y = 42.0;
- return 1;
-}
-
-fn main() {
- m[idx()] += 1.0;
-}
-)";
-
- auto* expect = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx() -> i32 {
- m[0].y = 42.0;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(m[idx()]);
- *(tint_symbol) = (*(tint_symbol) + 1.0);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMatrixElement) {
- auto* src = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx1() -> i32 {
- m[0].y = 42.0;
- return 1;
-}
-
-fn idx2() -> i32 {
- m[1].z = 42.0;
- return 1;
-}
-
-fn main() {
- m[idx1()][idx2()] += 1.0;
-}
-)";
-
- auto* expect = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx1() -> i32 {
- m[0].y = 42.0;
- return 1;
-}
-
-fn idx2() -> i32 {
- m[1].z = 42.0;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(m[idx1()]);
- let tint_symbol_1 = idx2();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1.0);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMultipleSideEffects) {
- auto* src = R"(
-struct S {
- a : array<vec4<f32>, 3>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : array<S>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p += 1;
- return 3;
-}
-
-fn idx2() -> i32 {
- p *= 3;
- return 2;
-}
-
-fn idx3() -> i32 {
- p -= 2;
- return 1;
-}
-
-fn main() {
- buffer[idx1()].a[idx2()][idx3()] += 1.0;
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : array<vec4<f32>, 3>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : array<S>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn idx3() -> i32 {
- p = (p - 2);
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(buffer[idx1()].a[idx2()]);
- let tint_symbol_1 = idx3();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1.0);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ForLoopInit) {
- auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- for (a[idx1()][idx2()] += 1; ; ) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- {
- let tint_symbol = &(a[idx1()]);
- let tint_symbol_1 = idx2();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
- loop {
- {
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ForLoopCont) {
- auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- for (; ; a[idx1()][idx2()] += 1) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- loop {
- {
- break;
- }
-
- continuing {
- let tint_symbol = &(a[idx1()]);
- let tint_symbol_1 = idx2();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
- }
- }
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_I32) {
- auto* src = R"(
-fn main() {
- var v : i32;
- v++;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : i32;
- v = (v + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_U32) {
- auto* src = R"(
-fn main() {
- var v : u32;
- v++;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : u32;
- v = (v + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Decrement_I32) {
- auto* src = R"(
-fn main() {
- var v : i32;
- v--;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : i32;
- v = (v - 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Decrement_U32) {
- auto* src = R"(
-fn main() {
- var v : u32;
- v--;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : u32;
- v = (v - 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsPointer) {
- auto* src = R"(
-fn main() {
- var v : i32;
- let p = &v;
- *p++;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : i32;
- let p = &(v);
- let tint_symbol = &(*(p));
- *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsStructMember) {
- auto* src = R"(
-struct S {
- m : i32,
-}
-
-fn main() {
- var s : S;
- s.m++;
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : i32,
-}
-
-fn main() {
- var s : S;
- s.m = (s.m + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsArrayElement) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
- a[1] = 42;
- return 1;
-}
-
-fn main() {
- a[idx()]++;
-}
-)";
-
- auto* expect = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
- a[1] = 42;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(a[idx()]);
- *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_ArrayAccessor) {
- auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- v[idx()]++;
-}
-)";
-
- auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let tint_symbol = &(v);
- let tint_symbol_1 = idx();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest,
- Increment_LhsVectorComponent_ArrayAccessor_ViaDerefPointerIndex) {
- auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let p = &v;
- (*p)[idx()]++;
-}
-)";
-
- auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let p = &(v);
- let tint_symbol = &(*(p));
- let tint_symbol_1 = idx();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_ArrayAccessor_ViaPointerIndex) {
- auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let p = &v;
- p[idx()]++;
-}
-)";
-
- auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
- v.y = 42;
- return 1;
-}
-
-fn main() {
- let p = &(v);
- let tint_symbol = p;
- let tint_symbol_1 = idx();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_MemberAccessor) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- v.y++;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- v.y = (v.y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest,
- Increment_LhsVectorComponent_MemberAccessor_ViaDerefPointerDot) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- (*p).y++;
-}
-)";
-
- // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- let tint_symbol = &(*(p));
- (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_MemberAccessor_ViaPointerDot) {
- auto* src = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &v;
- p.y++;
-}
-)";
-
- auto* expect = R"(
-fn main() {
- var v : vec4<i32>;
- let p = &(v);
- p.y = (p.y + 1);
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_ForLoopCont) {
- auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- for (; ; a[idx1()][idx2()]++) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
- p = (p + 1);
- return 3;
-}
-
-fn idx2() -> i32 {
- p = (p * 3);
- return 2;
-}
-
-fn main() {
- loop {
- {
- break;
- }
-
- continuing {
- let tint_symbol = &(a[idx1()]);
- let tint_symbol_1 = idx2();
- (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
- }
- }
-}
-)";
-
- auto got = Run<ExpandCompoundAssignment>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset.cc b/src/tint/lang/wgsl/ast/transform/first_index_offset.cc
deleted file mode 100644
index feaa575..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/first_index_offset.h"
-
-#include <memory>
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-
-using namespace tint::core::fluent_types; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset::BindingPoint);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset::Data);
-
-namespace tint::ast::transform {
-namespace {
-
-// Uniform buffer member names
-constexpr char kFirstVertexName[] = "first_vertex_index";
-constexpr char kFirstInstanceName[] = "first_instance_index";
-
-bool ShouldRun(const Program& program) {
- for (auto* fn : program.AST().Functions()) {
- if (fn->PipelineStage() == PipelineStage::kVertex) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-FirstIndexOffset::BindingPoint::BindingPoint() = default;
-FirstIndexOffset::BindingPoint::BindingPoint(uint32_t b, uint32_t g) : binding(b), group(g) {}
-FirstIndexOffset::BindingPoint::~BindingPoint() = default;
-
-FirstIndexOffset::Data::Data() = default;
-FirstIndexOffset::Data::Data(bool has_vtx_index, bool has_inst_index)
- : has_vertex_index(has_vtx_index), has_instance_index(has_inst_index) {}
-FirstIndexOffset::Data::Data(const Data&) = default;
-FirstIndexOffset::Data::~Data() = default;
-
-FirstIndexOffset::FirstIndexOffset() = default;
-FirstIndexOffset::~FirstIndexOffset() = default;
-
-Transform::ApplyResult FirstIndexOffset::Apply(const Program& src,
- const DataMap& inputs,
- DataMap& outputs) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- // Get the uniform buffer binding point
- uint32_t ub_binding = binding_;
- uint32_t ub_group = group_;
- if (auto* binding_point = inputs.Get<BindingPoint>()) {
- ub_binding = binding_point->binding;
- ub_group = binding_point->group;
- }
-
- // Map of builtin usages
- std::unordered_map<const sem::Variable*, const char*> builtin_vars;
- std::unordered_map<const core::type::StructMember*, const char*> builtin_members;
-
- bool has_vertex_index = false;
- bool has_instance_index = false;
-
- // Traverse the AST scanning for builtin accesses via variables (includes
- // parameters) or structure member accesses.
- for (auto* node : ctx.src->ASTNodes().Objects()) {
- if (auto* var = node->As<Variable>()) {
- for (auto* attr : var->attributes) {
- if (auto* builtin_attr = attr->As<BuiltinAttribute>()) {
- core::BuiltinValue builtin = builtin_attr->builtin;
- if (builtin == core::BuiltinValue::kVertexIndex) {
- auto* sem_var = ctx.src->Sem().Get(var);
- builtin_vars.emplace(sem_var, kFirstVertexName);
- has_vertex_index = true;
- }
- if (builtin == core::BuiltinValue::kInstanceIndex) {
- auto* sem_var = ctx.src->Sem().Get(var);
- builtin_vars.emplace(sem_var, kFirstInstanceName);
- has_instance_index = true;
- }
- }
- }
- }
- if (auto* member = node->As<StructMember>()) {
- for (auto* attr : member->attributes) {
- if (auto* builtin_attr = attr->As<BuiltinAttribute>()) {
- core::BuiltinValue builtin = builtin_attr->builtin;
- if (builtin == core::BuiltinValue::kVertexIndex) {
- auto* sem_mem = ctx.src->Sem().Get(member);
- builtin_members.emplace(sem_mem, kFirstVertexName);
- has_vertex_index = true;
- }
- if (builtin == core::BuiltinValue::kInstanceIndex) {
- auto* sem_mem = ctx.src->Sem().Get(member);
- builtin_members.emplace(sem_mem, kFirstInstanceName);
- has_instance_index = true;
- }
- }
- }
- }
- }
-
- if (has_vertex_index || has_instance_index) {
- // Add uniform buffer members and calculate byte offsets
- tint::Vector<const StructMember*, 8> members;
- members.Push(b.Member(kFirstVertexName, b.ty.u32()));
- members.Push(b.Member(kFirstInstanceName, b.ty.u32()));
- auto* struct_ = b.Structure(b.Sym(), std::move(members));
-
- // Create a global to hold the uniform buffer
- Symbol buffer_name = b.Sym();
- b.GlobalVar(buffer_name, b.ty.Of(struct_), core::AddressSpace::kUniform,
- tint::Vector{
- b.Binding(AInt(ub_binding)),
- b.Group(AInt(ub_group)),
- });
-
- // Fix up all references to the builtins with the offsets
- ctx.ReplaceAll([=, &ctx](const Expression* expr) -> const Expression* {
- if (auto* sem = ctx.src->Sem().GetVal(expr)) {
- if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) {
- auto it = builtin_vars.find(user->Variable());
- if (it != builtin_vars.end()) {
- return ctx.dst->Add(ctx.CloneWithoutTransform(expr),
- ctx.dst->MemberAccessor(buffer_name, it->second));
- }
- }
- if (auto* access = sem->As<sem::StructMemberAccess>()) {
- auto it = builtin_members.find(access->Member());
- if (it != builtin_members.end()) {
- return ctx.dst->Add(ctx.CloneWithoutTransform(expr),
- ctx.dst->MemberAccessor(buffer_name, it->second));
- }
- }
- }
- // Not interested in this expression. Just clone.
- return nullptr;
- });
- }
-
- outputs.Add<Data>(has_vertex_index, has_instance_index);
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset.h b/src/tint/lang/wgsl/ast/transform/first_index_offset.h
deleted file mode 100644
index 63598b9..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
-
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Adds firstVertex/Instance (injected via root constants) to
-/// vertex/instance index builtins.
-///
-/// This transform assumes that Name transform has been run before.
-///
-/// Unlike other APIs, D3D always starts vertex and instance numbering at 0,
-/// regardless of the firstVertex/Instance value specified. This transformer
-/// adds the value of firstVertex/Instance to each builtin. This action is
-/// performed by adding a new constant equal to original builtin +
-/// firstVertex/Instance to each function that references one of these builtins.
-///
-/// Note that D3D does not have any semantics for firstVertex/Instance.
-/// Therefore, these values must by passed to the shader.
-///
-/// Before:
-/// ```
-/// @builtin(vertex_index) var<in> vert_idx : u32;
-/// fn func() -> u32 {
-/// return vert_idx;
-/// }
-/// ```
-///
-/// After:
-/// ```
-/// struct TintFirstIndexOffsetData {
-/// tint_first_vertex_index : u32;
-/// tint_first_instance_index : u32;
-/// };
-/// @builtin(vertex_index) var<in> tint_first_index_offset_vert_idx : u32;
-/// @binding(N) @group(M) var<uniform> tint_first_index_data :
-/// TintFirstIndexOffsetData;
-/// fn func() -> u32 {
-/// const vert_idx = (tint_first_index_offset_vert_idx +
-/// tint_first_index_data.tint_first_vertex_index);
-/// return vert_idx;
-/// }
-/// ```
-///
-class FirstIndexOffset final : public Castable<FirstIndexOffset, Transform> {
- public:
- /// BindingPoint is consumed by the FirstIndexOffset transform.
- /// BindingPoint specifies the binding point of the first index uniform
- /// buffer.
- struct BindingPoint final : public Castable<BindingPoint, Data> {
- /// Constructor
- BindingPoint();
-
- /// Constructor
- /// @param b the binding index
- /// @param g the binding group
- BindingPoint(uint32_t b, uint32_t g);
-
- /// Destructor
- ~BindingPoint() override;
-
- /// `@binding()` for the first vertex / first instance uniform buffer
- uint32_t binding = 0;
- /// `@group()` for the first vertex / first instance uniform buffer
- uint32_t group = 0;
-
- /// Reflection for this struct
- TINT_REFLECT(BindingPoint, binding, group);
- };
-
- /// Data is outputted by the FirstIndexOffset transform.
- /// Data holds information about shader usage and constant buffer offsets.
- struct Data final : public Castable<Data, transform::Data> {
- /// Constructor
- Data();
-
- /// Constructor
- /// @param has_vtx_index True if the shader uses vertex_index
- /// @param has_inst_index True if the shader uses instance_index
- Data(bool has_vtx_index, bool has_inst_index);
-
- /// Copy constructor
- Data(const Data&);
-
- /// Destructor
- ~Data() override;
-
- /// True if the shader uses vertex_index
- bool has_vertex_index = false;
- /// True if the shader uses instance_index
- bool has_instance_index = false;
-
- /// Reflection for this struct
- TINT_REFLECT(Data, has_vertex_index, has_instance_index);
- };
-
- /// Constructor
- FirstIndexOffset();
- /// Destructor
- ~FirstIndexOffset() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- uint32_t binding_ = 0;
- uint32_t group_ = 0;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc b/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc
deleted file mode 100644
index ee0fd20..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc
+++ /dev/null
@@ -1,645 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/first_index_offset.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using FirstIndexOffsetTest = TransformTest;
-
-TEST_F(FirstIndexOffsetTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, ShouldRunFragmentStage) {
- auto* src = R"(
-@fragment
-fn entry() {
- return;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, ShouldRunVertexStage) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- EXPECT_TRUE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(0, 0);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- EXPECT_EQ(data, nullptr);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicVertexShader) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
- auto* expect = src;
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(0, 0);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, false);
- EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex) {
- auto* src = R"(
-fn test(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- test(vert_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- test((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- test(vert_idx);
- return vec4<f32>();
-}
-
-fn test(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- test((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-
-fn test(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) {
- auto* src = R"(
-fn test(inst_idx : u32) -> u32 {
- return inst_idx;
-}
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- test(inst_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(7) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(inst_idx : u32) -> u32 {
- return inst_idx;
-}
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- test((inst_idx + tint_symbol_1.first_instance_index));
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 7);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, false);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- test(inst_idx);
- return vec4<f32>();
-}
-
-fn test(inst_idx : u32) -> u32 {
- return inst_idx;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(7) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- test((inst_idx + tint_symbol_1.first_instance_index));
- return vec4<f32>();
-}
-
-fn test(inst_idx : u32) -> u32 {
- return inst_idx;
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 7);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, false);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) {
- auto* src = R"(
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
- return instance_idx + vert_idx;
-}
-
-struct Inputs {
- @builtin(instance_index) instance_idx : u32,
- @builtin(vertex_index) vert_idx : u32,
-};
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
- test(inputs.instance_idx, inputs.vert_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
- return (instance_idx + vert_idx);
-}
-
-struct Inputs {
- @builtin(instance_index)
- instance_idx : u32,
- @builtin(vertex_index)
- vert_idx : u32,
-}
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
- test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
- test(inputs.instance_idx, inputs.vert_idx);
- return vec4<f32>();
-}
-
-struct Inputs {
- @builtin(instance_index) instance_idx : u32,
- @builtin(vertex_index) vert_idx : u32,
-};
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
- return instance_idx + vert_idx;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
- test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-
-struct Inputs {
- @builtin(instance_index)
- instance_idx : u32,
- @builtin(vertex_index)
- vert_idx : u32,
-}
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
- return (instance_idx + vert_idx);
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, NestedCalls) {
- auto* src = R"(
-fn func1(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-
-fn func2(vert_idx : u32) -> u32 {
- return func1(vert_idx);
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func2(vert_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn func1(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-
-fn func2(vert_idx : u32) -> u32 {
- return func1(vert_idx);
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func2((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, NestedCalls_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func2(vert_idx);
- return vec4<f32>();
-}
-
-fn func2(vert_idx : u32) -> u32 {
- return func1(vert_idx);
-}
-
-fn func1(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func2((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-
-fn func2(vert_idx : u32) -> u32 {
- return func1(vert_idx);
-}
-
-fn func1(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, MultipleEntryPoints) {
- auto* src = R"(
-fn func(i : u32) -> u32 {
- return i;
-}
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func(vert_idx);
- return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(vert_idx + inst_idx);
- return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(inst_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn func(i : u32) -> u32 {
- return i;
-}
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)));
- return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func((inst_idx + tint_symbol_1.first_instance_index));
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, MultipleEntryPoints_OutOfOrder) {
- auto* src = R"(
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func(vert_idx);
- return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(vert_idx + inst_idx);
- return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(inst_idx);
- return vec4<f32>();
-}
-
-fn func(i : u32) -> u32 {
- return i;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- first_vertex_index : u32,
- first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
- func((vert_idx + tint_symbol_1.first_vertex_index));
- return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)));
- return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
- func((inst_idx + tint_symbol_1.first_instance_index));
- return vec4<f32>();
-}
-
-fn func(i : u32) -> u32 {
- return i;
-}
-)";
-
- DataMap config;
- config.Add<FirstIndexOffset::BindingPoint>(1, 2);
- auto got = Run<FirstIndexOffset>(src, std::move(config));
-
- EXPECT_EQ(expect, str(got));
-
- auto* data = got.data.Get<FirstIndexOffset::Data>();
-
- ASSERT_NE(data, nullptr);
- EXPECT_EQ(data->has_vertex_index, true);
- EXPECT_EQ(data->has_instance_index, true);
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants.cc b/src/tint/lang/wgsl/ast/transform/fold_constants.cc
deleted file mode 100644
index 7eee69c..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/fold_constants.h"
-
-#include <utility>
-
-#include "src/tint/lang/core/constant/splat.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/abstract_float.h"
-#include "src/tint/lang/core/type/abstract_int.h"
-#include "src/tint/lang/core/type/bool.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FoldConstants);
-
-namespace tint::ast::transform {
-namespace {
-
-struct State {
- enum class Splat {
- kAllowed,
- kDisallowed,
- };
-
- const ast::Expression* Constant(const core::constant::Value* c) {
- auto composite = [&](Splat splat) -> const ast::Expression* {
- auto ty = FoldConstants::CreateASTTypeFor(ctx, c->Type());
- if (c->AllZero()) {
- return b.Call(ty);
- }
- if (splat == Splat::kAllowed && c->Is<core::constant::Splat>()) {
- return b.Call(ty, Constant(c->Index(0)));
- }
-
- Vector<const ast::Expression*, 8> els;
- for (size_t i = 0, n = c->NumElements(); i < n; i++) {
- els.Push(Constant(c->Index(i)));
- }
- return b.Call(ty, std::move(els));
- };
-
- return tint::Switch(
- c->Type(), //
- [&](const core::type::AbstractFloat*) { return b.Expr(c->ValueAs<AFloat>()); },
- [&](const core::type::AbstractInt*) { return b.Expr(c->ValueAs<AInt>()); },
- [&](const core::type::I32*) { return b.Expr(c->ValueAs<i32>()); },
- [&](const core::type::U32*) { return b.Expr(c->ValueAs<u32>()); },
- [&](const core::type::F32*) { return b.Expr(c->ValueAs<f32>()); },
- [&](const core::type::F16*) { return b.Expr(c->ValueAs<f16>()); },
- [&](const core::type::Bool*) { return b.Expr(c->ValueAs<bool>()); },
- [&](const core::type::Array*) { return composite(Splat::kDisallowed); },
- [&](const core::type::Vector*) { return composite(Splat::kAllowed); },
- [&](const core::type::Matrix*) { return composite(Splat::kDisallowed); },
- [&](const core::type::Struct*) { return composite(Splat::kDisallowed); },
- TINT_ICE_ON_NO_MATCH);
- }
-
- Transform::ApplyResult Run() {
- ctx.ReplaceAll([&](const Expression* expr) -> const Expression* {
- auto& sem = ctx.src->Sem();
- auto* ve = sem.Get<sem::ValueExpression>(expr);
-
- // No value expression SEM node found
- if (!ve) {
- return nullptr;
- }
-
- auto* cv = ve->ConstantValue();
-
- // No constant value for this expression
- if (!cv) {
- return nullptr;
- }
-
- if (cv->Type()->IsAbstract() && !cv->Type()->IsFloatScalar() &&
- !cv->Type()->IsSignedIntegerScalar() && !cv->Type()->IsUnsignedIntegerScalar()) {
- return nullptr;
- }
-
- return Constant(cv);
- });
-
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- const Program& src;
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-};
-
-} // namespace
-
-FoldConstants::FoldConstants() = default;
-
-FoldConstants::~FoldConstants() = default;
-
-Transform::ApplyResult FoldConstants::Apply(const Program& src, const DataMap&, DataMap&) const {
- State s{src, {}};
- return s.Run();
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants.h b/src/tint/lang/wgsl/ast/transform/fold_constants.h
deleted file mode 100644
index c9057cd..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that replaces constant expressions with the constant values.
-///
-/// # Example
-/// ```
-/// const a = false && true;
-/// const b = sin(1);
-/// ```
-///
-/// ```
-/// const a = false;
-/// const b = 0.841470;
-/// ```
-class FoldConstants final : public Castable<FoldConstants, Transform> {
- public:
- /// Constructor
- FoldConstants();
-
- /// Destructor
- ~FoldConstants() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outpus) const override;
-
- private:
- const ast::Expression* Constant(const core::constant::Value* c);
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc b/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc
deleted file mode 100644
index 081f90f..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc
+++ /dev/null
@@ -1,440 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/fold_constants.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using FoldConstantsTest = TransformTest;
-
-TEST_F(FoldConstantsTest, NoFolding) {
- auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Expression) {
- auto* src = R"(
-fn foo() -> i32 {
- return (1 + (2 * 4)) - (3 * 5);
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- return -6i;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Abstract) {
- auto* src = R"(
-fn foo() {
- const v = vec4(1, 2, 3, vec3(0)[1 + 1 - 0]);
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- const v = vec4(1, 2, 3, 0);
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, IndexExpression) {
- auto* src = R"(
-struct S {
- a : array<i32>,
-}
-
-@group(0) @binding(0) var<storage> s : S;
-
-fn foo() -> i32 {
- return s.a[(3 * 5) - (1 + (2 * 4))];
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : array<i32>,
-}
-
-@group(0i) @binding(0i) var<storage> s : S;
-
-fn foo() -> i32 {
- return s.a[6i];
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, IndexAccessorToConst) {
- auto* src = R"(
-const a : array<i32, 4> = array(0, 1, 2, 3);
-
-fn foo() -> i32 {
- return a[3];
-}
-)";
-
- auto* expect = R"(
-const a : array<i32, 4i> = array<i32, 4u>(0i, 1i, 2i, 3i);
-
-fn foo() -> i32 {
- return 3i;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, SplatParam) {
- auto* src = R"(
-fn foo() -> i32 {
- let v = vec3(2 + 5 - 4);
- return v.x;
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- let v = vec3<i32>(3i);
- return v.x;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, CallParam) {
- auto* src = R"(
-fn foo() -> i32 {
- let v = vec3(2 + 5 - 4, 5 * 9, -2 + -3);
- return v.x;
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- let v = vec3<i32>(3i, 45i, -5i);
- return v.x;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnAssign) {
- auto* src = R"(
-fn foo() -> i32 {
- var v = vec4(0);
- v[1 * 2 + (3 - 2)] = 1;
-
- return v[1];
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- var v = vec4<i32>();
- v[3i] = 1i;
- return v[1i];
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnCompoundAssign) {
- auto* src = R"(
-fn foo() -> i32 {
- var v = vec4(0);
- v[1 * 2 + (3 - 2)] += 1;
-
- return v[1];
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- var v = vec4<i32>();
- v[3i] += 1i;
- return v[1i];
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnIncrement) {
- auto* src = R"(
-fn foo() -> i32 {
- var v = vec4(0);
- v[1 * 2 + (3 - 2)]++;
-
- return v[1];
-}
-)";
-
- auto* expect = R"(
-fn foo() -> i32 {
- var v = vec4<i32>();
- v[3i]++;
- return v[1i];
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Attribute) {
- auto* src = R"(
-struct A {
- @location(1 + 2)
- v : i32,
-}
-
-@group(0) @binding(0) var<storage> a : A;
-
-fn foo() -> i32 {
- return a.v;
-}
-)";
-
- auto* expect = R"(
-struct A {
- @location(3i)
- v : i32,
-}
-
-@group(0i) @binding(0i) var<storage> a : A;
-
-fn foo() -> i32 {
- return a.v;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, ShortCircut) {
- auto* src = R"(
-fn foo() -> bool {
- return false && (1 + 2) == 3;
-}
-)";
-
- auto* expect = R"(
-fn foo() -> bool {
- return false;
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, BreakIf) {
- auto* src = R"(
-fn foo() {
- loop {
-
- continuing {
- break if 4 > 3;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- loop {
-
- continuing {
- break if true;
- }
- }
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Switch) {
- auto* src = R"(
-fn foo() {
- switch(1 + 2 + (3 * 5)) {
- case 1 + 2, 3 + 4: {
- break;
- }
- default: {
- break;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- switch(18i) {
- case 3i, 7i: {
- break;
- }
- default: {
- break;
- }
- }
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, If) {
- auto* src = R"(
-fn foo() {
- if 4 - 2 > 3 {
- return;
- }
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- if (false) {
- return;
- }
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, For) {
- auto* src = R"(
-fn foo() {
- for(var i = 2 + (3 * 4); i < 9 + (5 * 10); i = i + (2 * 3)) {
- }
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- for(var i = 14i; (i < 59i); i = (i + 6i)) {
- }
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, While) {
- auto* src = R"(
-fn foo() {
- while(2 > 4) {
- }
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- while(false) {
- }
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, ConstAssert) {
- auto* src = R"(
-const_assert 2 < (3 + 5);
-)";
-
- auto* expect = R"(
-const_assert true;
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, InternalStruct) {
- auto* src = R"(
-fn foo() {
- let a = modf(1.5);
-}
-)";
-
- auto* expect = R"(
-fn foo() {
- let a = __modf_result_f32(0.5f, 1.0f);
-}
-)";
-
- auto got = Run<FoldConstants>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc b/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc
deleted file mode 100644
index 4f0d9c7..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/utils/diagnostic/diagnostic.h"
-#include "src/tint/utils/ice/ice.h"
-#include "src/tint/utils/rtti/switch.h"
-
-namespace tint::ast::transform::utils {
-
-InsertionPoint GetInsertionPoint(program::CloneContext& ctx, const Statement* stmt) {
- auto& sem = ctx.src->Sem();
- using RetType = std::pair<const sem::BlockStatement*, const Statement*>;
-
- if (auto* sem_stmt = sem.Get(stmt)) {
- auto* parent = sem_stmt->Parent();
- return Switch(
- parent,
- [&](const sem::BlockStatement* block) -> RetType {
- // Common case, can insert in the current block above/below the input
- // statement.
- return {block, stmt};
- },
- [&](const sem::ForLoopStatement* fl) -> RetType {
- // `stmt` is either the for loop initializer or the continuing
- // statement of a for-loop.
- if (fl->Declaration()->initializer == stmt) {
- // For loop init, can insert above the for loop itself.
- return {fl->Block(), fl->Declaration()};
- }
-
- // Cannot insert before or after continuing statement of a for-loop
- return {};
- }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- return {};
-}
-
-} // namespace tint::ast::transform::utils
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point.h b/src/tint/lang/wgsl/ast/transform/get_insertion_point.h
deleted file mode 100644
index 2c1658e..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/builder.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-
-// Forward declarations
-namespace tint::program {
-class CloneContext;
-}
-
-namespace tint::ast::transform::utils {
-
-/// InsertionPoint is a pair of the block (`first`) within which, and the
-/// statement (`second`) before or after which to insert.
-using InsertionPoint = std::pair<const sem::BlockStatement*, const Statement*>;
-
-/// For the input statement, returns the block and statement within that
-/// block to insert before/after. If `stmt` is a for-loop continue statement,
-/// the function returns {nullptr, nullptr} as we cannot insert before/after it.
-/// @param ctx the clone context
-/// @param stmt the statement to insert before or after
-/// @return the insertion point
-InsertionPoint GetInsertionPoint(program::CloneContext& ctx, const Statement* stmt);
-
-} // namespace tint::ast::transform::utils
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc b/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc
deleted file mode 100644
index 4c20059..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/utils/ice/ice.h"
-
-using namespace tint::core::number_suffixes; // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-using GetInsertionPointTest = ::testing::Test;
-
-TEST_F(GetInsertionPointTest, Block) {
- // fn f() {
- // var a = 1i;
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* var = b.Decl(b.Var("a", expr));
- auto* block = b.Block(var);
- b.Func("f", tint::Empty, b.ty.void_(), Vector{block});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- // Can insert in block containing the variable, above or below the input statement.
- auto ip = utils::GetInsertionPoint(ctx, var);
- ASSERT_EQ(ip.first->Declaration(), block);
- ASSERT_EQ(ip.second, var);
-}
-
-TEST_F(GetInsertionPointTest, ForLoopInit) {
- // fn f() {
- // for(var a = 1i; true; ) {
- // }
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* var = b.Decl(b.Var("a", expr));
- auto* fl = b.For(var, b.Expr(true), nullptr, b.Block());
- auto* func_block = b.Block(fl);
- b.Func("f", tint::Empty, b.ty.void_(), Vector{func_block});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- // Can insert in block containing for-loop above the for-loop itself.
- auto ip = utils::GetInsertionPoint(ctx, var);
- ASSERT_EQ(ip.first->Declaration(), func_block);
- ASSERT_EQ(ip.second, fl);
-}
-
-TEST_F(GetInsertionPointTest, ForLoopCont_Invalid) {
- // fn f() {
- // for(; true; var a = 1i) {
- // }
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* var = b.Decl(b.Var("a", expr));
- auto* s = b.For({}, b.Expr(true), var, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- // Can't insert before/after for loop continue statement (would ned to be converted to loop).
- auto ip = utils::GetInsertionPoint(ctx, var);
- ASSERT_EQ(ip.first, nullptr);
- ASSERT_EQ(ip.second, nullptr);
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc
deleted file mode 100644
index 1a506ec..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/builder.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/if_statement.h"
-#include "src/tint/lang/wgsl/sem/while_statement.h"
-#include "src/tint/utils/containers/hashmap.h"
-#include "src/tint/utils/containers/transform.h"
-
-namespace tint::ast::transform {
-
-/// Private implementation of HoistToDeclBefore transform
-struct HoistToDeclBefore::State {
- /// Constructor
- /// @param ctx_in the clone context
- explicit State(program::CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst) {}
-
- /// @copydoc HoistToDeclBefore::Add()
- bool Add(const sem::ValueExpression* before_expr,
- const Expression* expr,
- VariableKind kind,
- const char* decl_name) {
- auto name = b.Symbols().New(decl_name);
-
- switch (kind) {
- case VariableKind::kLet: {
- auto* ty = ctx.src->Sem().GetVal(expr)->Type();
- TINT_ASSERT(!ty->IsAbstract());
- auto builder = [this, expr, name, ty] {
- return b.Decl(b.Let(name, Transform::CreateASTTypeFor(ctx, ty),
- ctx.CloneWithoutTransform(expr)));
- };
- if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
- return false;
- }
- break;
- }
-
- case VariableKind::kVar: {
- auto* ty = ctx.src->Sem().GetVal(expr)->Type();
- TINT_ASSERT(!ty->IsAbstract());
- auto builder = [this, expr, name, ty] {
- return b.Decl(b.Var(name, Transform::CreateASTTypeFor(ctx, ty),
- ctx.CloneWithoutTransform(expr)));
- };
- if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
- return false;
- }
- break;
- }
-
- case VariableKind::kConst: {
- auto builder = [this, expr, name] {
- return b.Decl(b.Const(name, ctx.CloneWithoutTransform(expr)));
- };
- if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
- return false;
- }
- break;
- }
- }
-
- // Replace the source expression with a reference to the hoisted declaration.
- ctx.Replace(expr, b.Expr(name));
- return true;
- }
-
- /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const Statement*)
- bool InsertBefore(const sem::Statement* before_stmt, const Statement* stmt) {
- if (stmt) {
- auto builder = [stmt] { return stmt; };
- return InsertBeforeImpl(before_stmt, std::move(builder));
- }
- return InsertBeforeImpl(before_stmt, Decompose{});
- }
-
- /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const StmtBuilder&)
- bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder) {
- return InsertBeforeImpl(before_stmt, std::move(builder));
- }
-
- /// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const Statement* with)
- bool Replace(const sem::Statement* what, const Statement* with) {
- auto builder = [with] { return with; };
- return Replace(what, std::move(builder));
- }
-
- /// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with)
- bool Replace(const sem::Statement* what, const StmtBuilder& with) {
- if (!InsertBeforeImpl(what, Decompose{})) {
- return false;
- }
- ctx.Replace(what->Declaration(), with);
- return true;
- }
-
- /// @copydoc HoistToDeclBefore::Prepare()
- bool Prepare(const sem::ValueExpression* before_expr) {
- return InsertBefore(before_expr->Stmt(), nullptr);
- }
-
- private:
- program::CloneContext& ctx;
- ast::Builder& b;
-
- /// Holds information about a for-loop that needs to be decomposed into a
- /// loop, so that declaration statements can be inserted before the
- /// condition expression or continuing statement.
- struct LoopInfo {
- Vector<StmtBuilder, 8> init_decls;
- Vector<StmtBuilder, 8> cond_decls;
- Vector<StmtBuilder, 8> cont_decls;
- };
-
- /// Info for each else-if that needs decomposing
- struct ElseIfInfo {
- /// Decls to insert before condition
- Vector<StmtBuilder, 8> cond_decls;
- };
-
- /// For-loops that need to be decomposed to loops.
- Hashmap<const sem::ForLoopStatement*, LoopInfo, 4> for_loops;
-
- /// Whiles that need to be decomposed to loops.
- Hashmap<const sem::WhileStatement*, LoopInfo, 4> while_loops;
-
- /// 'else if' statements that need to be decomposed to 'else {if}'
- Hashmap<const IfStatement*, ElseIfInfo, 4> else_ifs;
-
- template <size_t N>
- static auto Build(const Vector<StmtBuilder, N>& builders) {
- return tint::Transform(builders, [&](auto& builder) { return builder(); });
- }
-
- /// @returns a new LoopInfo reference for the given @p for_loop.
- /// @note if this is the first call to this method, then RegisterForLoopTransform() is
- /// automatically called.
- /// @warning the returned reference is invalid if this is called a second time, or the
- /// #for_loops map is mutated.
- LoopInfo& ForLoop(const sem::ForLoopStatement* for_loop) {
- if (for_loops.IsEmpty()) {
- RegisterForLoopTransform();
- }
- return for_loops.GetOrAddZero(for_loop);
- }
-
- /// @returns a new LoopInfo reference for the given @p while_loop.
- /// @note if this is the first call to this method, then RegisterWhileLoopTransform() is
- /// automatically called.
- /// @warning the returned reference is invalid if this is called a second time, or the
- /// #for_loops map is mutated.
- LoopInfo& WhileLoop(const sem::WhileStatement* while_loop) {
- if (while_loops.IsEmpty()) {
- RegisterWhileLoopTransform();
- }
- return while_loops.GetOrAddZero(while_loop);
- }
-
- /// @returns a new ElseIfInfo reference for the given @p else_if.
- /// @note if this is the first call to this method, then RegisterElseIfTransform() is
- /// automatically called.
- /// @warning the returned reference is invalid if this is called a second time, or the
- /// #else_ifs map is mutated.
- ElseIfInfo& ElseIf(const IfStatement* else_if) {
- if (else_ifs.IsEmpty()) {
- RegisterElseIfTransform();
- }
- return else_ifs.GetOrAddZero(else_if);
- }
-
- /// Registers the handler for transforming for-loops based on the content of the #for_loops map.
- void RegisterForLoopTransform() const {
- ctx.ReplaceAll([&](const ForLoopStatement* stmt) -> const Statement* {
- auto& sem = ctx.src->Sem();
-
- if (auto* fl = sem.Get(stmt)) {
- if (auto info = for_loops.Get(fl)) {
- auto* for_loop = fl->Declaration();
- // For-loop needs to be decomposed to a loop.
- // Build the loop body's statements.
- // Start with any let declarations for the conditional expression.
- auto body_stmts = Build(info->cond_decls);
- // If the for-loop has a condition, emit this next as:
- // if (!cond) { break; }
- if (auto* cond = for_loop->condition) {
- // !condition
- auto* not_cond =
- b.create<UnaryOpExpression>(core::UnaryOp::kNot, ctx.Clone(cond));
- // { break; }
- auto* break_body = b.Block(b.create<BreakStatement>());
- // if (!condition) { break; }
- body_stmts.Push(b.If(not_cond, break_body));
- }
- // Next emit the for-loop body
- body_stmts.Push(ctx.Clone(for_loop->body));
-
- // Create the continuing block if there was one.
- const BlockStatement* continuing = nullptr;
- if (auto* cont = for_loop->continuing) {
- // Continuing block starts with any let declarations used by
- // the continuing.
- auto cont_stmts = Build(info->cont_decls);
- cont_stmts.Push(ctx.Clone(cont));
- continuing = b.Block(cont_stmts);
- }
-
- auto* body = b.Block(body_stmts);
- auto* loop = b.Loop(body, continuing);
-
- // If the loop has no initializer statements, then we're done.
- // Otherwise, wrap loop with another block, prefixed with the initializer
- // statements
- if (!info->init_decls.IsEmpty() || for_loop->initializer) {
- auto stmts = Build(info->init_decls);
- if (auto* init = for_loop->initializer) {
- stmts.Push(ctx.Clone(init));
- }
- stmts.Push(loop);
- return b.Block(std::move(stmts));
- }
- return loop;
- }
- }
- return nullptr;
- });
- }
-
- /// Registers the handler for transforming while-loops based on the content of the #while_loops
- /// map.
- void RegisterWhileLoopTransform() const {
- // At least one while needs to be transformed into a loop.
- ctx.ReplaceAll([&](const WhileStatement* stmt) -> const Statement* {
- auto& sem = ctx.src->Sem();
-
- if (auto* w = sem.Get(stmt)) {
- if (auto info = while_loops.Get(w)) {
- auto* while_loop = w->Declaration();
- // While needs to be decomposed to a loop.
- // Build the loop body's statements.
- // Start with any let declarations for the conditional
- // expression.
- auto body_stmts =
- tint::Transform(info->cond_decls, [&](auto& builder) { return builder(); });
- // Emit the condition as:
- // if (!cond) { break; }
- auto* cond = while_loop->condition;
- // !condition
- auto* not_cond = b.Not(ctx.Clone(cond));
- // { break; }
- auto* break_body = b.Block(b.Break());
- // if (!condition) { break; }
- body_stmts.Push(b.If(not_cond, break_body));
-
- // Next emit the body
- body_stmts.Push(ctx.Clone(while_loop->body));
-
- const BlockStatement* continuing = nullptr;
-
- auto* body = b.Block(body_stmts);
- auto* loop = b.Loop(body, continuing);
- return loop;
- }
- }
- return nullptr;
- });
- }
-
- /// Registers the handler for transforming if-statements based on the content of the #else_ifs
- /// map.
- void RegisterElseIfTransform() const {
- // Decompose 'else-if' statements into 'else { if }' blocks.
- ctx.ReplaceAll([&](const IfStatement* stmt) -> const Statement* {
- if (auto info = else_ifs.Get(stmt)) {
- // Build the else block's body statements, starting with let decls for the
- // conditional expression.
- auto body_stmts = Build(info->cond_decls);
-
- // Move the 'else-if' into the new `else` block as a plain 'if'.
- auto* cond = ctx.Clone(stmt->condition);
- auto* body = ctx.Clone(stmt->body);
- auto* new_if = b.If(cond, body, b.Else(ctx.Clone(stmt->else_statement)));
- body_stmts.Push(new_if);
-
- // Replace the 'else-if' with the new 'else' block.
- return b.Block(body_stmts);
- }
- return nullptr;
- });
- }
-
- /// A type used to signal to InsertBeforeImpl that no insertion should take place - instead flow
- /// control statements should just be decomposed.
- struct Decompose {};
-
- template <typename BUILDER>
- bool InsertBeforeImpl(const sem::Statement* before_stmt, BUILDER&& builder) {
- (void)builder; // Avoid 'unused parameter' warning due to 'if constexpr'
-
- auto* ip = before_stmt->Declaration();
-
- auto* else_if = before_stmt->As<sem::IfStatement>();
- if (else_if && else_if->Parent()->Is<sem::IfStatement>()) {
- // Insertion point is an 'else if' condition.
- // Need to convert 'else if' to 'else { if }'.
- auto& else_if_info = ElseIf(else_if->Declaration());
-
- // Index the map to decompose this else if, even if `stmt` is nullptr.
- auto& decls = else_if_info.cond_decls;
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- decls.Push(std::forward<BUILDER>(builder));
- }
- return true;
- }
-
- if (auto* fl = before_stmt->As<sem::ForLoopStatement>()) {
- // Insertion point is a for-loop condition.
- // For-loop needs to be decomposed to a loop.
-
- // Index the map to decompose this for-loop, even if `stmt` is nullptr.
- auto& decls = ForLoop(fl).cond_decls;
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- decls.Push(std::forward<BUILDER>(builder));
- }
- return true;
- }
-
- if (auto* w = before_stmt->As<sem::WhileStatement>()) {
- // Insertion point is a while condition.
- // While needs to be decomposed to a loop.
-
- // Index the map to decompose this while, even if `stmt` is nullptr.
- auto& decls = WhileLoop(w).cond_decls;
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- decls.Push(std::forward<BUILDER>(builder));
- }
- return true;
- }
-
- auto* parent = before_stmt->Parent(); // The statement's parent
- if (auto* block = parent->As<sem::BlockStatement>()) {
- // Insert point sits in a block. Simple case.
- // Insert the stmt before the parent statement.
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- ctx.InsertBefore(block->Declaration()->statements, ip,
- std::forward<BUILDER>(builder));
- }
- return true;
- }
-
- auto* fl = parent->As<sem::ForLoopStatement>();
- if (DAWN_LIKELY(fl)) {
- // Insertion point is a for-loop initializer or continuing statement.
- // These require special care.
- if (fl->Declaration()->initializer == ip) {
- // Insertion point is a for-loop initializer.
- // For-loop needs to be decomposed to a loop.
-
- // Index the map to decompose this for-loop, even if `stmt` is nullptr.
- auto& decls = ForLoop(fl).init_decls;
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- decls.Push(std::forward<BUILDER>(builder));
- }
-
- return true;
- }
-
- if (DAWN_LIKELY(fl->Declaration()->continuing == ip)) {
- // Insertion point is a for-loop continuing statement.
- // For-loop needs to be decomposed to a loop.
-
- // Index the map to decompose this for-loop, even if `stmt` is nullptr.
- auto& decls = ForLoop(fl).cont_decls;
- if constexpr (!std::is_same_v<BUILDER, Decompose>) {
- decls.Push(std::forward<BUILDER>(builder));
- }
-
- return true;
- }
-
- TINT_ICE() << "unhandled use of expression in for-loop";
- return false;
- }
-
- TINT_ICE() << "unhandled expression parent statement type: " << parent->TypeInfo().name;
- return false;
- }
-};
-
-HoistToDeclBefore::HoistToDeclBefore(program::CloneContext& ctx)
- : state_(std::make_unique<State>(ctx)) {}
-
-HoistToDeclBefore::~HoistToDeclBefore() {}
-
-bool HoistToDeclBefore::Add(const sem::ValueExpression* before_expr,
- const Expression* expr,
- VariableKind kind,
- const char* decl_name) {
- return state_->Add(before_expr, expr, kind, decl_name);
-}
-
-bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt, const Statement* stmt) {
- return state_->InsertBefore(before_stmt, stmt);
-}
-
-bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt,
- const StmtBuilder& builder) {
- return state_->InsertBefore(before_stmt, builder);
-}
-
-bool HoistToDeclBefore::Replace(const sem::Statement* what, const Statement* with) {
- return state_->Replace(what, with);
-}
-
-bool HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with) {
- return state_->Replace(what, with);
-}
-
-bool HoistToDeclBefore::Prepare(const sem::ValueExpression* before_expr) {
- return state_->Prepare(before_expr);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h
deleted file mode 100644
index cbe90e1..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
-
-#include <functional>
-#include <memory>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-
-namespace tint::ast::transform {
-
-/// Utility class that can be used to hoist expressions before other
-/// expressions, possibly converting 'for-loop's to 'loop's and 'else-if's to
-// 'else {if}'s.
-class HoistToDeclBefore {
- public:
- /// Constructor
- /// @param ctx the clone context
- explicit HoistToDeclBefore(program::CloneContext& ctx);
-
- /// Destructor
- ~HoistToDeclBefore();
-
- /// StmtBuilder is a builder of an AST statement
- using StmtBuilder = std::function<const Statement*()>;
-
- /// VariableKind is either a var, let or const
- enum class VariableKind {
- kVar,
- kLet,
- kConst,
- };
-
- /// Hoists @p expr to a `let` or `var` with optional `decl_name`, inserting it
- /// before @p before_expr.
- /// @param before_expr expression to insert `expr` before
- /// @param expr expression to hoist
- /// @param kind variable kind to hoist to
- /// @param decl_name optional name to use for the variable/constant name
- /// @return true on success
- bool Add(const sem::ValueExpression* before_expr,
- const Expression* expr,
- VariableKind kind,
- const char* decl_name = "");
-
- /// Inserts @p stmt before @p before_stmt, possibly converting 'for-loop's to 'loop's if
- /// necessary.
- /// @warning If the container of @p before_stmt is cloned multiple times, then the resolver will
- /// ICE as the same statement cannot be shared.
- /// @param before_stmt statement to insert @p stmt before
- /// @param stmt statement to insert
- /// @return true on success
- bool InsertBefore(const sem::Statement* before_stmt, const Statement* stmt);
-
- /// Inserts the returned statement of @p builder before @p before_stmt, possibly converting
- /// 'for-loop's to 'loop's if necessary.
- /// @note If the container of @p before_stmt is cloned multiple times, then @p builder will be
- /// called for each clone.
- /// @param before_stmt the preceding statement that the statement of @p builder will be inserted
- /// before
- /// @param builder the statement builder used to create the new statement
- /// @return true on success
- bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder);
-
- /// Replaces the statement @p what with the statement @p stmt, possibly converting 'for-loop's
- /// to 'loop's if necessary.
- /// @param what the statement to replace
- /// @param with the replacement statement
- /// @return true on success
- bool Replace(const sem::Statement* what, const Statement* with);
-
- /// Replaces the statement @p what with the statement returned by @p stmt, possibly converting
- /// 'for-loop's to 'loop's if necessary.
- /// @param what the statement to replace
- /// @param with the replacement statement builder
- /// @return true on success
- bool Replace(const sem::Statement* what, const StmtBuilder& with);
-
- /// Use to signal that we plan on hoisting a decl before `before_expr`. This
- /// will convert 'for-loop's to 'loop's and 'else-if's to 'else {if}'s if
- /// needed.
- /// @param before_expr expression we would hoist a decl before
- /// @return true on success
- bool Prepare(const sem::ValueExpression* before_expr);
-
- private:
- struct State;
- std::unique_ptr<State> state_;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
deleted file mode 100644
index 36f03a9..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
+++ /dev/null
@@ -1,1156 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/if_statement.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-
-using namespace tint::core::number_suffixes; // NOLINT
-using namespace tint::core::fluent_types; // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-using HoistToDeclBeforeTest = ::testing::Test;
-
-TEST_F(HoistToDeclBeforeTest, VarInit) {
- // fn f() {
- // var a = 1;
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* var = b.Decl(b.Var("a", expr));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- let tint_symbol : i32 = 1i;
- var a = tint_symbol;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopInit) {
- // fn f() {
- // for(var a = 1i; true; ) {
- // }
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* s = b.For(b.Decl(b.Var("a", expr)), b.Expr(true), nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- {
- var tint_symbol : i32 = 1i;
- var a = tint_symbol;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopCond) {
- // fn f() {
- // const a = true;
- // for(; a; ) {
- // }
- // }
- ProgramBuilder b;
- auto* var = b.Decl(b.Const("a", b.Expr(true)));
- auto* expr = b.Expr("a");
- auto* s = b.For(nullptr, expr, nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().GetVal(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- const a = true;
- loop {
- const tint_symbol = a;
- if (!(tint_symbol)) {
- break;
- }
- {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopCont) {
- // fn f() {
- // for(; true; var a = 1i) {
- // }
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* s = b.For(nullptr, b.Expr(true), b.Decl(b.Var("a", expr)), b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- let tint_symbol : i32 = 1i;
- var a = tint_symbol;
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, WhileCond) {
- // fn f() {
- // var a : bool;
- // while(a) {
- // }
- // }
- ProgramBuilder b;
- auto* var = b.Decl(b.Var("a", b.ty.bool_()));
- auto* expr = b.Expr("a");
- auto* s = b.While(expr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().GetVal(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var a : bool;
- loop {
- var tint_symbol : bool = a;
- if (!(tint_symbol)) {
- break;
- }
- {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ElseIf) {
- // fn f() {
- // const a = true;
- // if (true) {
- // } else if (a) {
- // } else {
- // }
- // }
- ProgramBuilder b;
- auto* var = b.Decl(b.Const("a", b.Expr(true)));
- auto* expr = b.Expr("a");
- auto* s = b.If(b.Expr(true), b.Block(), //
- b.Else(b.If(expr, b.Block(), //
- b.Else(b.Block()))));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().GetVal(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- const a = true;
- if (true) {
- } else {
- const tint_symbol = a;
- if (tint_symbol) {
- } else {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Array1D) {
- // fn f() {
- // var a : array<i32, 10>;
- // var b = a[0];
- // }
- ProgramBuilder b;
- auto* var1 = b.Decl(b.Var("a", b.ty.array<i32, 10>()));
- auto* expr = b.IndexAccessor("a", 0_i);
- auto* var2 = b.Decl(b.Var("b", expr));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var1, var2});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var a : array<i32, 10u>;
- let tint_symbol : i32 = a[0i];
- var b = tint_symbol;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Array2D) {
- // fn f() {
- // var a : array<array<i32, 10>, 10>;
- // var b = a[0][0];
- // }
- ProgramBuilder b;
-
- auto* var1 = b.Decl(b.Var("a", b.ty.array(b.ty.array<i32, 10>(), 10_i)));
- auto* expr = b.IndexAccessor(b.IndexAccessor("a", 0_i), 0_i);
- auto* var2 = b.Decl(b.Var("b", expr));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var1, var2});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var a : array<array<i32, 10u>, 10i>;
- var tint_symbol : i32 = a[0i][0i];
- var b = tint_symbol;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ForLoopCond) {
- // fn f() {
- // var a : bool;
- // for(; a; ) {
- // }
- // }
- ProgramBuilder b;
- auto* var = b.Decl(b.Var("a", b.ty.bool_()));
- auto* expr = b.Expr("a");
- auto* s = b.For(nullptr, expr, nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().GetVal(expr);
- hoistToDeclBefore.Prepare(sem_expr);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var a : bool;
- loop {
- if (!(a)) {
- break;
- }
- {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ForLoopCont) {
- // fn f() {
- // for(; true; var a = 1i) {
- // }
- // }
- ProgramBuilder b;
- auto* expr = b.Expr(1_i);
- auto* s = b.For(nullptr, b.Expr(true), b.Decl(b.Var("a", expr)), b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Prepare(sem_expr);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- var a = 1i;
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ElseIf) {
- // fn f() {
- // var a : bool;
- // if (true) {
- // } else if (a) {
- // } else {
- // }
- // }
- ProgramBuilder b;
- auto* var = b.Decl(b.Var("a", b.ty.bool_()));
- auto* expr = b.Expr("a");
- auto* s = b.If(b.Expr(true), b.Block(), //
- b.Else(b.If(expr, b.Block(), //
- b.Else(b.Block()))));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().GetVal(expr);
- hoistToDeclBefore.Prepare(sem_expr);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var a : bool;
- if (true) {
- } else {
- if (a) {
- } else {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_Block) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(var);
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- foo();
- var a = 1i;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_Block_Function) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(var);
- hoistToDeclBefore.InsertBefore(before_stmt,
- [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- foo();
- var a = 1i;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopInit) {
- // fn foo() {
- // }
- // fn f() {
- // for(var a = 1i; true;) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(var);
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- {
- foo();
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopInit_Function) {
- // fn foo() {
- // }
- // fn f() {
- // for(var a = 1i; true;) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(var);
- hoistToDeclBefore.InsertBefore(before_stmt,
- [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- {
- foo();
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopCont) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // for(; true; a+=1i) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
- auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(cont->As<Statement>());
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- foo();
- a += 1i;
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopCont_Function) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // for(; true; a+=1i) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
- auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(cont->As<Statement>());
- hoistToDeclBefore.InsertBefore(before_stmt,
- [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- foo();
- a += 1i;
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf) {
- // fn foo() {
- // }
- // fn f() {
- // var a : bool;
- // if (true) {
- // } else if (a) {
- // } else {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.ty.bool_()));
- auto* elseif = b.If(b.Expr("a"), b.Block(), b.Else(b.Block()));
- auto* s = b.If(b.Expr(true), b.Block(), //
- b.Else(elseif));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(elseif);
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a : bool;
- if (true) {
- } else {
- foo();
- if (a) {
- } else {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf_Function) {
- // fn foo() {
- // }
- // fn f() {
- // var a : bool;
- // if (true) {
- // } else if (a) {
- // } else {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.ty.bool_()));
- auto* elseif = b.If(b.Expr("a"), b.Block(), b.Else(b.Block()));
- auto* s = b.If(b.Expr(true), b.Block(), //
- b.Else(elseif));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* before_stmt = ctx.src->Sem().Get(elseif);
- hoistToDeclBefore.InsertBefore(before_stmt,
- [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a : bool;
- if (true) {
- } else {
- foo();
- if (a) {
- } else {
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, AbstractArray_ToLet) {
- // fn f() {
- // var a : array<f32, 1> = array(1);
- // }
- ProgramBuilder b;
- auto* expr = b.Call(b.ty("array"), b.Expr(1_a));
- auto* var = b.Decl(b.Var("a", b.ty.array(b.ty.f32(), 1_a), expr));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- let tint_symbol : array<f32, 1u> = array(1);
- var a : array<f32, 1> = tint_symbol;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, AbstractArray_ToVar) {
- // fn f() {
- // var a : array<f32, 1> = array(1);
- // }
- ProgramBuilder b;
- auto* expr = b.Call(b.ty("array"), b.Expr(1_a));
- auto* var = b.Decl(b.Var("a", b.ty.array(b.ty.f32(), 1_a), expr));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* sem_expr = ctx.src->Sem().Get(expr);
- hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn f() {
- var tint_symbol : array<f32, 1u> = array(1);
- var a : array<f32, 1> = tint_symbol;
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_Block) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(var);
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- foo();
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_Block_Function) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(var);
- hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- foo();
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit) {
- // fn foo() {
- // }
- // fn f() {
- // for(var a = 1i; true;) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(var);
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- {
- foo();
- loop {
- if (!(true)) {
- break;
- }
- {
- }
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit_Function) {
- // fn foo() {
- // }
- // fn f() {
- // for(var a = 1i; true;) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(var);
- hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- {
- foo();
- loop {
- if (!(true)) {
- break;
- }
- {
- }
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // for(; true; a+=1i) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
- auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(cont->As<Statement>());
- auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
- hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- foo();
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont_Function) {
- // fn foo() {
- // }
- // fn f() {
- // var a = 1i;
- // for(; true; a+=1i) {
- // }
- // }
- ProgramBuilder b;
- b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
- auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
- auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
- auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
- b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
- Program original(resolver::Resolve(b));
- ProgramBuilder cloned_b;
- program::CloneContext ctx(&cloned_b, &original);
-
- HoistToDeclBefore hoistToDeclBefore(ctx);
- auto* target_stmt = ctx.src->Sem().Get(cont->As<Statement>());
- hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
- ctx.Clone();
- Program cloned(resolver::Resolve(cloned_b));
-
- auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
- var a = 1i;
- loop {
- if (!(true)) {
- break;
- }
- {
- }
-
- continuing {
- foo();
- }
- }
-}
-)";
-
- EXPECT_EQ(expect, str(cloned));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
deleted file mode 100644
index 6d40cf3..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
+++ /dev/null
@@ -1,614 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
-
-#include <string>
-#include <vector>
-
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/wgsl/ast/function.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture::NewBindingPoints);
-
-namespace tint::ast::transform {
-namespace {
-
-using namespace tint::core::fluent_types; // NOLINT
-using namespace tint::core::number_suffixes; // NOLINT
-
-bool ShouldRun(const Program& program) {
- auto ext = program.Types().Find<core::type::ExternalTexture>();
- return ext != nullptr;
-}
-
-/// This struct stores symbols for new bindings created as a result of transforming a
-/// texture_external instance.
-struct NewBindingSymbols {
- Symbol params;
- Symbol plane_0;
- Symbol plane_1;
-};
-} // namespace
-
-/// PIMPL state for the transform
-struct MultiplanarExternalTexture::State {
- /// The clone context.
- program::CloneContext& ctx;
-
- /// Alias to `*ctx.dst`
- ast::Builder& b;
-
- /// Destination binding locations for the expanded texture_external provided
- /// as input into the transform.
- const NewBindingPoints* new_binding_points;
-
- /// Symbol for the GammaTransferParams
- Symbol gamma_transfer_struct_sym;
-
- /// Symbol for the ExternalTextureParams struct
- Symbol params_struct_sym;
-
- /// Symbol for the textureLoadExternal functions
- Hashmap<const sem::CallTarget*, Symbol, 2> texture_load_external_fns;
-
- /// Symbol for the textureSampleExternal function
- Symbol texture_sample_external_sym;
-
- /// Symbol for the textureSampleExternalDEPRECATED function
- Symbol texture_sample_external_deprecated_sym;
-
- /// Symbol for the gammaCorrection function
- Symbol gamma_correction_sym;
-
- /// Storage for new bindings that have been created corresponding to an original
- /// texture_external binding.
- std::unordered_map<const sem::Variable*, NewBindingSymbols> new_binding_symbols;
-
- /// Constructor
- /// @param context the clone
- /// @param newBindingPoints the input destination binding locations for the
- /// expanded texture_external
- State(program::CloneContext& context, const NewBindingPoints* newBindingPoints)
- : ctx(context), b(*context.dst), new_binding_points(newBindingPoints) {}
-
- /// Processes the module
- void Process() {
- auto& sem = ctx.src->Sem();
-
- // For each texture_external binding, we replace it with a texture_2d<f32> binding and
- // create two additional bindings (one texture_2d<f32> to represent the secondary plane and
- // one uniform buffer for the ExternalTextureParams struct).
- for (auto* global : ctx.src->AST().GlobalVariables()) {
- auto* sem_var = sem.Get<sem::GlobalVariable>(global);
- if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) {
- continue;
- }
-
- // If the attributes are empty, then this must be a texture_external passed as a
- // function parameter. These variables are transformed elsewhere.
- if (global->attributes.IsEmpty()) {
- continue;
- }
-
- // If we find a texture_external binding, we know we must emit the ExternalTextureParams
- // struct.
- if (!params_struct_sym.IsValid()) {
- createExtTexParamsStructs();
- }
-
- // The binding points for the newly introduced bindings must have been provided to this
- // transform. We fetch the new binding points by providing the original texture_external
- // binding points into the passed map.
- BindingPoint bp = *sem_var->Attributes().binding_point;
-
- const tint::transform::multiplanar::BindingsMap::const_iterator it =
- new_binding_points->bindings_map.find(bp);
- if (it == new_binding_points->bindings_map.end()) {
- b.Diagnostics().AddError(Source{})
- << "missing new binding points for texture_external at binding {" << bp.group
- << "," << bp.binding << "}";
- continue;
- }
-
- const tint::transform::multiplanar::BindingPoints bps = it->second;
-
- // Symbols for the newly created bindings must be saved so they can be passed as
- // parameters later. These are placed in a map and keyed by the source symbol associated
- // with the texture_external binding that corresponds with the new destination bindings.
- // NewBindingSymbols new_binding_syms;
- auto& syms = new_binding_symbols[sem_var];
- syms.plane_0 = ctx.Clone(global->name->symbol);
- syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
- if (new_binding_points->allow_collisions) {
- b.GlobalVar(syms.plane_1,
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
- b.Disable(DisabledValidation::kBindingPointCollision),
- b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding)));
- } else {
- b.GlobalVar(syms.plane_1,
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
- b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding)));
- }
- syms.params = b.Symbols().New("ext_tex_params");
- if (new_binding_points->allow_collisions) {
- b.GlobalVar(syms.params, b.ty("ExternalTextureParams"),
- core::AddressSpace::kUniform,
- b.Disable(DisabledValidation::kBindingPointCollision),
- b.Group(AInt(bps.params.group)), b.Binding(AInt(bps.params.binding)));
- } else {
- b.GlobalVar(syms.params, b.ty("ExternalTextureParams"),
- core::AddressSpace::kUniform, b.Group(AInt(bps.params.group)),
- b.Binding(AInt(bps.params.binding)));
- }
-
- // Replace the original texture_external binding with a texture_2d<f32> binding.
- auto cloned_attributes = ctx.Clone(global->attributes);
-
- // Allow the originating binding to have collisions.
- if (new_binding_points->allow_collisions) {
- cloned_attributes.Push(b.Disable(DisabledValidation::kBindingPointCollision));
- }
-
- const Expression* cloned_initializer = ctx.Clone(global->initializer);
-
- auto* replacement = b.Var(
- syms.plane_0, b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
- cloned_initializer, cloned_attributes);
- ctx.Replace(global, replacement);
- }
-
- // We must update all the texture_external parameters for user declared functions.
- for (auto* fn : ctx.src->AST().Functions()) {
- for (const Variable* param : fn->params) {
- if (auto* sem_var = sem.Get(param)) {
- if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) {
- continue;
- }
- // If we find a texture_external, we must ensure the ExternalTextureParams
- // struct exists.
- if (!params_struct_sym.IsValid()) {
- createExtTexParamsStructs();
- }
- // When a texture_external is found, we insert all components the
- // texture_external into the parameter list. We must also place the new symbols
- // into the transform state so they can be used when transforming function
- // calls.
- auto& syms = new_binding_symbols[sem_var];
- syms.plane_0 = ctx.Clone(param->name->symbol);
- syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
- syms.params = b.Symbols().New("ext_tex_params");
- auto tex2d_f32 = [&] {
- return b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32());
- };
- ctx.Replace(param, b.Param(syms.plane_0, tex2d_f32()));
- ctx.InsertAfter(fn->params, param, b.Param(syms.plane_1, tex2d_f32()));
- ctx.InsertAfter(fn->params, param,
- b.Param(syms.params, b.ty(params_struct_sym)));
- }
- }
- }
-
- // Transform the external texture builtin calls into calls to the external texture
- // functions.
- ctx.ReplaceAll([&](const CallExpression* expr) -> const Expression* {
- auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>();
- auto* builtin = call->Target()->As<sem::BuiltinFn>();
-
- if (builtin && !builtin->Parameters().IsEmpty() &&
- builtin->Parameters()[0]->Type()->Is<core::type::ExternalTexture>()) {
- if (auto* var_user =
- sem.GetVal(expr->args[0])->UnwrapLoad()->As<sem::VariableUser>()) {
- auto it = new_binding_symbols.find(var_user->Variable());
- if (it == new_binding_symbols.end()) {
- // If valid new binding locations were not provided earlier, we would have
- // been unable to create these symbols. An error message was emitted
- // earlier, so just return early to avoid internal compiler errors and
- // retain a clean error message.
- return nullptr;
- }
- auto& syms = it->second;
-
- switch (builtin->Fn()) {
- case wgsl::BuiltinFn::kTextureLoad:
- return createTextureLoad(call, syms);
- case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
- return createTextureSampleBaseClampToEdge(expr, syms);
- case wgsl::BuiltinFn::kTextureDimensions:
- return createTextureDimensions(call, syms);
- default:
- break;
- }
- }
- } else if (call->Target()->Is<sem::Function>()) {
- // The call expression may be to a user-defined function that contains a
- // texture_external parameter. These need to be expanded out to multiple plane
- // textures and the texture parameters structure.
- for (auto* arg : expr->args) {
- if (auto* var_user = sem.GetVal(arg)->UnwrapLoad()->As<sem::VariableUser>()) {
- // Check if a parameter is a texture_external by trying to find
- // it in the transform state.
- auto it = new_binding_symbols.find(var_user->Variable());
- if (it != new_binding_symbols.end()) {
- auto& syms = it->second;
- // When we find a texture_external, we must unpack it into its
- // components.
- ctx.Replace(arg, b.Expr(syms.plane_0));
- ctx.InsertAfter(expr->args, arg, b.Expr(syms.plane_1));
- ctx.InsertAfter(expr->args, arg, b.Expr(syms.params));
- }
- }
- }
- }
-
- return nullptr;
- });
- }
-
- /// Creates the parameter structs associated with the transform.
- void createExtTexParamsStructs() {
- // Create GammaTransferParams struct.
- tint::Vector gamma_transfer_member_list{
- b.Member("G", b.ty.f32()), b.Member("A", b.ty.f32()), b.Member("B", b.ty.f32()),
- b.Member("C", b.ty.f32()), b.Member("D", b.ty.f32()), b.Member("E", b.ty.f32()),
- b.Member("F", b.ty.f32()), b.Member("padding", b.ty.u32())};
-
- gamma_transfer_struct_sym = b.Symbols().New("GammaTransferParams");
-
- b.Structure(gamma_transfer_struct_sym, gamma_transfer_member_list);
-
- // Create ExternalTextureParams struct.
- tint::Vector ext_tex_params_member_list{
- b.Member("numPlanes", b.ty.u32()),
- b.Member("doYuvToRgbConversionOnly", b.ty.u32()),
- b.Member("yuvToRgbConversionMatrix", b.ty.mat3x4<f32>()),
- b.Member("gammaDecodeParams", b.ty("GammaTransferParams")),
- b.Member("gammaEncodeParams", b.ty("GammaTransferParams")),
- b.Member("gamutConversionMatrix", b.ty.mat3x3<f32>()),
- b.Member("sampleTransform", b.ty.mat3x2<f32>()),
- b.Member("loadTransform", b.ty.mat3x2<f32>()),
- b.Member("samplePlane0RectMin", b.ty.vec2<f32>()),
- b.Member("samplePlane0RectMax", b.ty.vec2<f32>()),
- b.Member("samplePlane1RectMin", b.ty.vec2<f32>()),
- b.Member("samplePlane1RectMax", b.ty.vec2<f32>()),
- b.Member("apparentSize", b.ty.vec2<u32>()),
- b.Member("plane1CoordFactor", b.ty.vec2<f32>())};
-
- params_struct_sym = b.Symbols().New("ExternalTextureParams");
-
- b.Structure(params_struct_sym, ext_tex_params_member_list);
- }
-
- /// Creates the gammaCorrection function if needed and returns a call
- /// expression to it.
- void createGammaCorrectionFn() {
- gamma_correction_sym = b.Symbols().New("gammaCorrection");
-
- b.Func(gamma_correction_sym,
- tint::Vector{
- b.Param("v", b.ty.vec3<f32>()),
- b.Param("params", b.ty(gamma_transfer_struct_sym)),
- },
- b.ty.vec3<f32>(),
- tint::Vector{
- // let cond = abs(v) < vec3(params.D);
- b.Decl(b.Let("cond",
- b.LessThan(b.Call("abs", "v"),
- b.Call<vec3<f32>>(b.MemberAccessor("params", "D"))))),
- // let t = sign(v) * ((params.C * abs(v)) + params.F);
- b.Decl(b.Let(
- "t", b.Mul(b.Call("sign", "v"),
- b.Add(b.Mul(b.MemberAccessor("params", "C"), b.Call("abs", "v")),
- b.MemberAccessor("params", "F"))))),
- // let f = (sign(v) * pow(((params.A * abs(v)) + params.B),
- // vec3(params.G))) + params.E;
- b.Decl(b.Let(
- "f", b.Mul(b.Call("sign", "v"),
- b.Add(b.Call("pow",
- b.Add(b.Mul(b.MemberAccessor("params", "A"),
- b.Call("abs", "v")),
- b.MemberAccessor("params", "B")),
- b.Call<vec3<f32>>(b.MemberAccessor("params", "G"))),
- b.MemberAccessor("params", "E"))))),
- // return select(f, t, cond);
- b.Return(b.Call("select", "f", "t", "cond")),
- });
- }
-
- /// Constructs a StatementList containing all the statements making up the body of the texture
- /// builtin function.
- /// @param call_type determines which function body to generate
- /// @returns a statement list that makes of the body of the chosen function
- auto buildTextureBuiltinBody(wgsl::BuiltinFn call_type) {
- tint::Vector<const Statement*, 16> stmts;
- const BlockStatement* single_plane_block = nullptr;
- const BlockStatement* multi_plane_block = nullptr;
- switch (call_type) {
- case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
- stmts.Push(b.Decl(
- b.Let("modifiedCoords", b.Mul(b.MemberAccessor("params", "sampleTransform"),
- b.Call<vec3<f32>>("coord", 1_a)))));
-
- stmts.Push(b.Decl(b.Let(
- "plane0_clamped", b.Call("clamp", "modifiedCoords",
- b.MemberAccessor("params", "samplePlane0RectMin"),
- b.MemberAccessor("params", "samplePlane0RectMax")))));
-
- // var color: vec4<f32>;
- stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32()))));
-
- single_plane_block = b.Block(
- b.Assign("color", b.MemberAccessor(b.Call("textureSampleLevel", "plane0", "smp",
- "plane0_clamped", 0_a),
- "rgba")));
-
- multi_plane_block = b.Block(
- b.Decl(b.Let("plane1_clamped",
- b.Call("clamp", "modifiedCoords",
- b.MemberAccessor("params", "samplePlane1RectMin"),
- b.MemberAccessor("params", "samplePlane1RectMax")))),
-
- b.Assign("color",
- b.Call<vec4<f32>>(
- b.Mul(b.Call<vec4<f32>>(
- b.MemberAccessor(b.Call("textureSampleLevel", "plane0",
- "smp", "plane0_clamped", 0_a),
- "r"),
- b.MemberAccessor(b.Call("textureSampleLevel", "plane1",
- "smp", "plane1_clamped", 0_a),
- "rg"),
- 1_a),
- b.MemberAccessor("params", "yuvToRgbConversionMatrix")),
- 1_a)));
- break;
- case wgsl::BuiltinFn::kTextureLoad:
- stmts.Push(b.Decl(
- b.Let("clampedCoords", b.Call("min", b.Call<vec2<u32>>("coord"),
- b.MemberAccessor("params", "apparentSize")))));
- stmts.Push(b.Decl(b.Let(
- "plane0_clamped",
- b.Call<vec2<u32>>(b.Call(
- "round",
- b.Mul(b.MemberAccessor("params", "loadTransform"),
- b.Call<vec3<f32>>(b.Call<vec2<f32>>("clampedCoords"), 1_a)))))));
-
- // var color: vec4<f32>;
- stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32()))));
-
- single_plane_block = b.Block(b.Assign(
- "color", b.MemberAccessor(
- b.Call("textureLoad", "plane0", "plane0_clamped", 0_a), "rgba")));
-
- multi_plane_block = b.Block(
- b.Decl(b.Let(
- "plane1_clamped",
- b.Call<vec2<u32>>(b.Mul(b.Call<vec2<f32>>("plane0_clamped"),
- b.MemberAccessor("params", "plane1CoordFactor"))))),
-
- b.Assign("color",
- b.Call<vec4<f32>>(
- b.Mul(b.Call<vec4<f32>>(
- b.MemberAccessor(b.Call("textureLoad", "plane0",
- "plane0_clamped", 0_a),
- "r"),
- b.MemberAccessor(b.Call("textureLoad", "plane1",
- "plane1_clamped", 0_a),
- "rg"),
- 1_a),
- b.MemberAccessor("params", "yuvToRgbConversionMatrix")),
- 1_a)));
- break;
- default:
- TINT_ICE() << "unhandled builtin: " << call_type;
- }
-
- // if ((params.numPlanes == 1u))
- stmts.Push(b.If(b.Equal(b.MemberAccessor("params", "numPlanes"), b.Expr(1_a)),
- single_plane_block, b.Else(multi_plane_block)));
-
- // if (params.doYuvToRgbConversionOnly == 0u)
- stmts.Push(b.If(
- b.Equal(b.MemberAccessor("params", "doYuvToRgbConversionOnly"), b.Expr(0_a)),
- b.Block(
- // color = vec4<f32>(gammaConversion(color.rgb, gammaDecodeParams), color.a);
- b.Assign("color", b.Call<vec4<f32>>(
- b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"),
- b.MemberAccessor("params", "gammaDecodeParams")),
- b.MemberAccessor("color", "a"))),
- // color = vec4<f32>(params.gamutConversionMatrix * color.rgb), color.a);
- b.Assign("color", b.Call<vec4<f32>>(
- b.Mul(b.MemberAccessor("params", "gamutConversionMatrix"),
- b.MemberAccessor("color", "rgb")),
- b.MemberAccessor("color", "a"))),
- // color = vec4<f32>(gammaConversion(color.rgb, gammaEncodeParams), color.a);
- b.Assign("color", b.Call<vec4<f32>>(
- b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"),
- b.MemberAccessor("params", "gammaEncodeParams")),
- b.MemberAccessor("color", "a"))))));
-
- // return color;
- stmts.Push(b.Return("color"));
-
- return stmts;
- }
-
- /// Creates the textureSampleExternal function if needed and returns a call expression to it.
- /// @param expr the call expression being transformed
- /// @param syms the expanded symbols to be used in the new call
- /// @returns a call expression to textureSampleExternal
- const CallExpression* createTextureSampleBaseClampToEdge(const CallExpression* expr,
- NewBindingSymbols syms) {
- const Expression* plane_0_binding_param = ctx.Clone(expr->args[0]);
-
- if (DAWN_UNLIKELY(expr->args.Length() != 3)) {
- TINT_ICE() << "expected textureSampleBaseClampToEdge call with a "
- "texture_external to have 3 parameters, found "
- << expr->args.Length() << " parameters";
- }
-
- // TextureSampleExternal calls the gammaCorrection function, so ensure it
- // exists.
- if (!gamma_correction_sym.IsValid()) {
- createGammaCorrectionFn();
- }
-
- if (!texture_sample_external_sym.IsValid()) {
- texture_sample_external_sym = b.Symbols().New("textureSampleExternal");
-
- // Emit the textureSampleExternal function.
- b.Func(texture_sample_external_sym,
- tint::Vector{
- b.Param("plane0",
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
- b.Param("plane1",
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
- b.Param("smp", b.ty.sampler(core::type::SamplerKind::kSampler)),
- b.Param("coord", b.ty.vec2(b.ty.f32())),
- b.Param("params", b.ty(params_struct_sym)),
- },
- b.ty.vec4(b.ty.f32()),
- buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureSampleBaseClampToEdge));
- }
-
- return b.Call(texture_sample_external_sym, tint::Vector{
- plane_0_binding_param,
- b.Expr(syms.plane_1),
- ctx.Clone(expr->args[1]),
- ctx.Clone(expr->args[2]),
- b.Expr(syms.params),
- });
- }
-
- /// Creates the textureLoadExternal function if needed and returns a call expression to it.
- /// @param call the call expression being transformed
- /// @param syms the expanded symbols to be used in the new call
- /// @returns a call expression to textureLoadExternal
- const CallExpression* createTextureLoad(const sem::Call* call, NewBindingSymbols syms) {
- if (DAWN_UNLIKELY(call->Arguments().Length() != 2)) {
- TINT_ICE()
- << "expected textureLoad call with a texture_external to have 2 arguments, found "
- << call->Arguments().Length() << " arguments";
- }
-
- auto& args = call->Arguments();
-
- // TextureLoadExternal calls the gammaCorrection function, so ensure it exists.
- if (!gamma_correction_sym.IsValid()) {
- createGammaCorrectionFn();
- }
-
- auto texture_load_external_sym = texture_load_external_fns.GetOrAdd(call->Target(), [&] {
- auto& sig = call->Target()->Signature();
- auto* coord_ty = sig.Parameter(core::ParameterUsage::kCoords)->Type();
-
- auto name = b.Symbols().New("textureLoadExternal");
-
- // Emit the textureLoadExternal() function.
- b.Func(name,
- tint::Vector{
- b.Param("plane0",
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
- b.Param("plane1",
- b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
- b.Param("coord", CreateASTTypeFor(ctx, coord_ty)),
- b.Param("params", b.ty(params_struct_sym)),
- },
- b.ty.vec4(b.ty.f32()), //
- buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureLoad));
-
- return name;
- });
-
- auto plane_0_binding_arg = ctx.Clone(args[0]->Declaration());
-
- return b.Call(texture_load_external_sym, plane_0_binding_arg, syms.plane_1,
- ctx.Clone(args[1]->Declaration()), syms.params);
- }
-
- /// Returns the expression used to replace a textureDimensions call.
- /// @param call the call expression being transformed
- /// @param syms the expanded symbols to be used in the new call
- /// @returns a load of params.apparentSize
- const Expression* createTextureDimensions(const sem::Call* call, NewBindingSymbols syms) {
- if (DAWN_UNLIKELY(call->Arguments().Length() != 1)) {
- TINT_ICE() << "expected textureDimensions call with a texture_external to have 1 "
- "arguments, found "
- << call->Arguments().Length() << " arguments";
- }
- return b.Add(b.MemberAccessor(syms.params, "apparentSize"), b.Call<vec2<u32>>(1_a));
- }
-};
-
-MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints() = default;
-MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints(
- tint::transform::multiplanar::BindingsMap inputBindingsMap,
- bool may_collide)
- : bindings_map(std::move(inputBindingsMap)), allow_collisions(may_collide) {}
-
-MultiplanarExternalTexture::NewBindingPoints::~NewBindingPoints() = default;
-
-MultiplanarExternalTexture::MultiplanarExternalTexture() = default;
-MultiplanarExternalTexture::~MultiplanarExternalTexture() = default;
-
-// Within this transform, an instance of a texture_external binding is unpacked into two
-// texture_2d<f32> bindings representing two possible planes of a single texture and a uniform
-// buffer binding representing a struct of parameters. Calls to texture builtins that contain a
-// texture_external parameter will be transformed into a newly generated version of the function,
-// which can perform the desired operation on a single RGBA plane or on separate Y and UV planes.
-Transform::ApplyResult MultiplanarExternalTexture::Apply(const Program& src,
- const DataMap& inputs,
- DataMap&) const {
- auto* new_binding_points = inputs.Get<NewBindingPoints>();
-
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
- if (!new_binding_points) {
- b.Diagnostics().AddError(Source{})
- << "missing new binding point data for " << TypeInfo().name;
- return resolver::Resolve(b);
- }
-
- State state(ctx, new_binding_points);
-
- state.Process();
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h
deleted file mode 100644
index 9ae282b..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/builtin_fn.h"
-#include "src/tint/lang/core/ir/transform/multiplanar_options.h"
-#include "src/tint/lang/wgsl/ast/struct_member.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Within the MultiplanarExternalTexture transform, each instance of a
-/// texture_external binding is unpacked into two texture_2d<f32> bindings
-/// representing two possible planes of a texture and a uniform buffer binding
-/// representing a struct of parameters. Calls to textureLoad or
-/// textureSampleLevel that contain a texture_external parameter will be
-/// transformed into a newly generated version of the function, which can
-/// perform the desired operation on a single RGBA plane or on separate Y and UV
-/// planes, and do colorspace conversions including yuv->rgb conversion, gamma
-/// decoding, gamut conversion, and gamma encoding steps. Specifically
-// for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
-// step and skipping all other steps.
-class MultiplanarExternalTexture final : public Castable<MultiplanarExternalTexture, Transform> {
- public:
- /// NewBindingPoints is consumed by the MultiplanarExternalTexture transform.
- /// Data holds information about location of each texture_external binding and
- /// which binding slots it should expand into.
- struct NewBindingPoints final : public Castable<NewBindingPoints, Data> {
- /// Constructor
- NewBindingPoints();
-
- /// Constructor
- /// @param bm a map to the new binding slots to use.
- /// @param may_collide if true, then validation will be disabled for binding point
- /// collisions generated by this transform
- explicit NewBindingPoints(tint::transform::multiplanar::BindingsMap bm,
- bool may_collide = false);
-
- /// Destructor
- ~NewBindingPoints() override;
-
- /// A map of new binding points to use.
- tint::transform::multiplanar::BindingsMap bindings_map;
-
- /// If true, then validation will be disabled for binding point collisions generated by this
- /// transform.
- bool allow_collisions = false;
-
- /// Reflection for this struct
- TINT_REFLECT(NewBindingPoints, bindings_map, allow_collisions);
- };
-
- /// Constructor
- MultiplanarExternalTexture();
- /// Destructor
- ~MultiplanarExternalTexture() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
deleted file mode 100644
index 8214c0a..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
+++ /dev/null
@@ -1,2014 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using MultiplanarExternalTextureTest = TransformTest;
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
- EXPECT_FALSE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureAlias) {
- auto* src = R"(
-alias ET = texture_external;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
- EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureGlobal) {
- auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
- EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureParam) {
- auto* src = R"(
-fn f(ext_tex : texture_external) {}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
- EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-// Running the transform without passing in data for the new bindings should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorNoPassedData_SampleBaseClampToEdge) {
- auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
- auto* expect =
- "error: missing new binding point data for "
- "tint::ast::transform::MultiplanarExternalTexture";
-
- auto got = Run<MultiplanarExternalTexture>(src);
- EXPECT_EQ(expect, str(got));
-}
-
-// Running the transform with incorrect binding data should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorIncorrectBindingPont_SampleBaseClampToEdge) {
- auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
-
- auto* expect = R"(error: missing new binding points for texture_external at binding {0,1})";
-
- DataMap data;
- // This bindings map specifies 0,0 as the location of the texture_external,
- // which is incorrect.
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureDimensions call.
-TEST_F(MultiplanarExternalTextureTest, Dimensions) {
- auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var dim : vec2<u32>;
- dim = textureDimensions(ext_tex);
- return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var dim : vec2<u32>;
- dim = (ext_tex_params.apparentSize + vec2<u32>(1));
- return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(MultiplanarExternalTextureTest, Collisions) {
- auto* src = R"(
-@group(0) @binding(0) var myTexture: texture_external;
-
-@fragment
-fn fragmentMain() -> @location(0) vec4f {
- let result = textureLoad(myTexture, vec2u(1, 1));
- return vec4f(1);
-})";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@internal(disable_validation__binding_point_collision) @group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@internal(disable_validation__binding_point_collision) @group(0) @binding(0) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var myTexture : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn fragmentMain() -> @location(0) vec4f {
- let result = textureLoadExternal(myTexture, ext_tex_plane_1, vec2u(1, 1), ext_tex_params);
- return vec4f(1);
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 0}}}},
- /* allow collisions */ true);
- auto got = Run<MultiplanarExternalTexture>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureDimensions call.
-TEST_F(MultiplanarExternalTextureTest, Dimensions_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var dim : vec2<u32>;
- dim = textureDimensions(ext_tex);
- return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var dim : vec2<u32>;
- dim = (ext_tex_params.apparentSize + vec2<u32>(1));
- return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that the transform works with a textureSampleBaseClampToEdge call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge) {
- auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Test that the transform works with a textureSampleBaseClampToEdge call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(0) var s : sampler;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(0) var s : sampler;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad) {
- auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var val_signed = textureLoad(ext_tex, vec2<i32>(1));
- var val_unsigned = textureLoad(ext_tex, vec2<u32>(1));
- return val_signed + val_unsigned;
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn textureLoadExternal_1(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var val_signed = textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1), ext_tex_params);
- var val_unsigned = textureLoadExternal_1(ext_tex, ext_tex_plane_1, vec2<u32>(1), ext_tex_params);
- return (val_signed + val_unsigned);
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var val_signed = textureLoad(ext_tex, vec2<i32>(1));
- var val_unsigned = textureLoad(ext_tex, vec2<u32>(1));
- return val_signed + val_unsigned;
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn textureLoadExternal_1(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- var val_signed = textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1), ext_tex_params);
- var val_unsigned = textureLoadExternal_1(ext_tex, ext_tex_plane_1, vec2<u32>(1), ext_tex_params);
- return (val_signed + val_unsigned);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad) {
- auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
- let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
- let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureLoad(plane0, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
- color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with many instances of texture_external.
-TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleBaseClampToEdge) {
- auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(2) var ext_tex_1 : texture_external;
-@group(0) @binding(3) var ext_tex_2 : texture_external;
-@group(1) @binding(0) var ext_tex_3 : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) +
- textureSampleBaseClampToEdge(ext_tex_1, s, coord.xy) +
- textureSampleBaseClampToEdge(ext_tex_2, s, coord.xy) +
- textureSampleBaseClampToEdge(ext_tex_3, s, coord.xy);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(4) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(5) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(6) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(7) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-@group(0) @binding(8) var ext_tex_plane_1_2 : texture_2d<f32>;
-
-@group(0) @binding(9) var<uniform> ext_tex_params_2 : ExternalTextureParams;
-
-@group(1) @binding(1) var ext_tex_plane_1_3 : texture_2d<f32>;
-
-@group(1) @binding(2) var<uniform> ext_tex_params_3 : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(2) var ext_tex_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var ext_tex_2 : texture_2d<f32>;
-
-@group(1) @binding(0) var ext_tex_3 : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
- return (((textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureSampleExternal(ext_tex_1, ext_tex_plane_1_1, s, coord.xy, ext_tex_params_1)) + textureSampleExternal(ext_tex_2, ext_tex_plane_1_2, s, coord.xy, ext_tex_params_2)) + textureSampleExternal(ext_tex_3, ext_tex_plane_1_3, s, coord.xy, ext_tex_params_3));
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 1}, {{0, 4}, {0, 5}}},
- {{0, 2}, {{0, 6}, {0, 7}}},
- {{0, 3}, {{0, 8}, {0, 9}}},
- {{1, 0}, {{1, 1}, {1, 2}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a function parameter produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParam) {
- auto* src = R"(
-fn f(t : texture_external, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a function parameter produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParam_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-
-fn f(t : texture_external, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a parameter not in the first
-// position produces the correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsSecondParam) {
- auto* src = R"(
-fn f(s : sampler, t : texture_external) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(smp, ext_tex);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(s : sampler, t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(smp, ext_tex, ext_tex_plane_1, ext_tex_params);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that multiple texture_external params passed to a function produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamMultiple) {
- auto* src = R"(
-fn f(t : texture_external, s : sampler, t2 : texture_external) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
- _ = textureSampleBaseClampToEdge(t2, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-@group(0) @binding(2) var ext_tex2 : texture_external;
-
-@fragment
-fn main() {
- f(ext_tex, smp, ext_tex2);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
- _ = textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
- _ = textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 3}, {0, 4}}},
- {{0, 2}, {{0, 5}, {0, 6}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that multiple texture_external params passed to a function produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamMultiple_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main() {
- f(ext_tex, smp, ext_tex2);
-}
-
-fn f(t : texture_external, s : sampler, t2 : texture_external) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
- _ = textureSampleBaseClampToEdge(t2, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-@group(0) @binding(2) var ext_tex2 : texture_external;
-
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
- _ = textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
- _ = textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 3}, {0, 4}}},
- {{0, 2}, {{0, 5}, {0, 6}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed to as a parameter to multiple
-// functions produces the correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamNested) {
- auto* src = R"(
-fn nested(t : texture_external, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-fn f(t : texture_external, s : sampler) {
- nested(t, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
- nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed to as a parameter to multiple functions produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamNested_OutOfOrder) {
- auto* src = R"(
-fn nested(t : texture_external, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-fn f(t : texture_external, s : sampler) {
- nested(t, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
- nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a function using an external texture,
-// even if there's no external texture declared at module scope.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamWithoutGlobalDecl) {
- auto* src = R"(
-fn f(ext_tex : texture_external) -> vec2<u32> {
- return textureDimensions(ext_tex);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-fn f(ext_tex : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams) -> vec2<u32> {
- return (ext_tex_params.apparentSize + vec2<u32>(1));
-}
-)";
-
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform handles aliases to external textures
-TEST_F(MultiplanarExternalTextureTest, ExternalTextureAlias) {
- auto* src = R"(
-alias ET = texture_external;
-
-fn f(t : ET, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : ET;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-alias ET = texture_external;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform handles aliases to external textures
-TEST_F(MultiplanarExternalTextureTest, ExternalTextureAlias_OutOfOrder) {
- auto* src = R"(
-@fragment
-fn main() {
- f(ext_tex, smp);
-}
-
-fn f(t : ET, s : sampler) {
- _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : ET;
-@group(0) @binding(1) var smp : sampler;
-
-alias ET = texture_external;
-)";
-
- auto* expect = R"(
-struct GammaTransferParams {
- G : f32,
- A : f32,
- B : f32,
- C : f32,
- D : f32,
- E : f32,
- F : f32,
- padding : u32,
-}
-
-struct ExternalTextureParams {
- numPlanes : u32,
- doYuvToRgbConversionOnly : u32,
- yuvToRgbConversionMatrix : mat3x4<f32>,
- gammaDecodeParams : GammaTransferParams,
- gammaEncodeParams : GammaTransferParams,
- gamutConversionMatrix : mat3x3<f32>,
- sampleTransform : mat3x2<f32>,
- loadTransform : mat3x2<f32>,
- samplePlane0RectMin : vec2<f32>,
- samplePlane0RectMax : vec2<f32>,
- samplePlane1RectMin : vec2<f32>,
- samplePlane1RectMax : vec2<f32>,
- apparentSize : vec2<u32>,
- plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main() {
- f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
- let cond = (abs(v) < vec3<f32>(params.D));
- let t = (sign(v) * ((params.C * abs(v)) + params.F));
- let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
- return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
- let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
- let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
- var color : vec4<f32>;
- if ((params.numPlanes == 1)) {
- color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
- } else {
- let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
- color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
- }
- if ((params.doYuvToRgbConversionOnly == 0)) {
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
- color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
- color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
- }
- return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
- _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-alias ET = texture_external;
-)";
- DataMap data;
- data.Add<MultiplanarExternalTexture::NewBindingPoints>(
- tint::transform::multiplanar::BindingsMap{
- {{0, 0}, {{0, 2}, {0, 3}}},
- });
- auto got = Run<MultiplanarExternalTexture>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc
deleted file mode 100644
index 46176c1..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
-
-#include "src/tint/lang/core/type/struct.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/utils/containers/hashset.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::PromoteInitializersToLet);
-
-namespace tint::ast::transform {
-
-PromoteInitializersToLet::PromoteInitializersToLet() = default;
-
-PromoteInitializersToLet::~PromoteInitializersToLet() = default;
-
-Transform::ApplyResult PromoteInitializersToLet::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- // Returns true if the expression should be hoisted to a new let statement before the
- // expression's statement.
- auto should_hoist = [&](const sem::ValueExpression* expr) {
- if (!expr->Type()->IsAnyOf<core::type::Array, core::type::Struct>()) {
- // We only care about array and struct initializers
- return false;
- }
-
- if (expr->Type()->IsAbstract()) {
- // Do not hoist expressions that are not materialized, as doing so would cause
- // premature materialization.
- return false;
- }
-
- // Check whether the expression is an array or structure constructor
- {
- // Follow const-chains
- auto* root_expr = expr;
- if (expr->Stage() == core::EvaluationStage::kConstant) {
- while (auto* user = root_expr->UnwrapMaterialize()->As<sem::VariableUser>()) {
- root_expr = user->Variable()->Initializer();
- }
- }
-
- auto* ctor = root_expr->UnwrapMaterialize()->As<sem::Call>();
- if (!ctor || !ctor->Target()->Is<sem::ValueConstructor>()) {
- // Root expression is not a value constructor. Not interested in this.
- return false;
- }
- }
-
- if (auto* src_var_decl = expr->Stmt()->Declaration()->As<VariableDeclStatement>()) {
- if (src_var_decl->variable->initializer == expr->Declaration()) {
- // This statement is just a variable declaration with the initializer as the
- // initializer value. This is what we're attempting to transform to, and so
- // ignore.
- return false;
- }
- }
-
- return true;
- };
-
- // A list of expressions that should be hoisted.
- tint::Vector<const sem::ValueExpression*, 32> to_hoist;
- // A set of expressions that are constant, which _may_ need to be hoisted.
- Hashset<const Expression*, 32> const_chains;
-
- // Walk the AST nodes. This order guarantees that leaf-expressions are visited first.
- for (auto* node : src.ASTNodes().Objects()) {
- if (auto* sem = src.Sem().GetVal(node)) {
- auto* stmt = sem->Stmt();
- if (!stmt) {
- // Expression is outside of a statement. This usually means the expression is part
- // of a global (module-scope) constant declaration. These must be constexpr, and so
- // cannot contain the type of expressions that must be sanitized.
- continue;
- }
-
- if (sem->Stage() == core::EvaluationStage::kConstant ||
- sem->Stage() == core::EvaluationStage::kNotEvaluated) {
- // Expression is constant or not evaluated. We only need to hoist expressions if
- // they're the outermost constant expression in a chain. Remove the immediate child
- // nodes of the expression from const_chains, and add this expression to the
- // const_chains. As we visit leaf-expressions first, this means the content of
- // const_chains only contains the outer-most constant expressions.
- auto* expr = sem->Declaration();
- bool ok = TraverseExpressions(expr, [&](const Expression* child) {
- const_chains.Remove(child);
- return child == expr ? TraverseAction::Descend : TraverseAction::Skip;
- });
- if (!ok) {
- return resolver::Resolve(b);
- }
- if (sem->Stage() == core::EvaluationStage::kConstant) {
- const_chains.Add(expr);
- }
- } else if (should_hoist(sem)) {
- to_hoist.Push(sem);
- }
- }
- }
-
- // After walking the full AST, const_chains only contains the outer-most constant expressions.
- // Check if any of these need hoisting, and append those to to_hoist.
- for (auto& expr : const_chains) {
- if (auto* sem = src.Sem().GetVal(expr.Value()); should_hoist(sem)) {
- to_hoist.Push(sem);
- }
- }
-
- if (to_hoist.IsEmpty()) {
- // Nothing to do. Skip.
- return SkipTransform;
- }
-
- // The order of to_hoist is currently undefined. Sort by AST node id, which will make this
- // deterministic.
- to_hoist.Sort([&](auto* expr_a, auto* expr_b) {
- return expr_a->Declaration()->node_id < expr_b->Declaration()->node_id;
- });
-
- // Hoist all the expressions in to_hoist to a constant variable, declared just before the
- // statement of usage.
- HoistToDeclBefore hoist_to_decl_before(ctx);
- for (auto* expr : to_hoist) {
- if (!hoist_to_decl_before.Add(expr, expr->Declaration(),
- HoistToDeclBefore::VariableKind::kLet)) {
- return resolver::Resolve(b);
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h
deleted file mode 100644
index 4ce1988..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that hoists array and structure initializers, and identifiers resolving to a
-/// 'const' array to a 'let' variable, declared just before the statement of usage.
-/// This transform is used by backends that do not support expressions that operate on an immediate
-/// array or structure. For example, the following is not immediately expressible for HLSL:
-/// `array<i32, 2>(1, 2)[0]`
-/// @see crbug.com/tint/406
-class PromoteInitializersToLet final : public Castable<PromoteInitializersToLet, Transform> {
- public:
- /// Constructor
- PromoteInitializersToLet();
-
- /// Destructor
- ~PromoteInitializersToLet() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
deleted file mode 100644
index ded078e..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
+++ /dev/null
@@ -1,1410 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using PromoteInitializersToLetTest = TransformTest;
-
-TEST_F(PromoteInitializersToLetTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicConstArray) {
- auto* src = R"(
-fn f() {
- const f0 = 1.0;
- const f1 = 2.0;
- const f2 = 3.0;
- const f3 = 4.0;
- var i = array<f32, 4u>(f0, f1, f2, f3)[2];
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicRuntimeArray) {
- auto* src = R"(
-fn f() {
- var f0 = 1.0;
- var f1 = 2.0;
- var f2 = 3.0;
- var f3 = 4.0;
- var i = array<f32, 4u>(f0, f1, f2, f3)[2];
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var f0 = 1.0;
- var f1 = 2.0;
- var f2 = 3.0;
- var f3 = 4.0;
- let tint_symbol : array<f32, 4u> = array<f32, 4u>(f0, f1, f2, f3);
- var i = tint_symbol[2];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicConstStruct) {
- auto* src = R"(
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-};
-
-fn f() {
- var x = S(1, 2.0, vec3<f32>()).b;
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicRuntimeStruct) {
- auto* src = R"(
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-};
-
-fn f() {
- let runtime_value = 1;
- var x = S(runtime_value, 2.0, vec3<f32>()).b;
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-}
-
-fn f() {
- let runtime_value = 1;
- let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>());
- var x = tint_symbol.b;
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicStruct_OutOfOrder) {
- auto* src = R"(
-fn f() {
- let runtime_value = 1;
- var x = S(runtime_value, 2.0, vec3<f32>()).b;
-}
-
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-};
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 1;
- let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>());
- var x = tint_symbol.b;
-}
-
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray) {
- auto* src = R"(
-const f0 = 1.0;
-
-const f1 = 2.0;
-
-const C = array<f32, 2u>(f0, f1);
-
-fn f() {
- var f0 = 100.0;
- var f1 = 100.0;
- var i = C[1]; // Not hoisted, as the final const value is not an array
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray_OutOfOrder) {
- auto* src = R"(
-fn f() {
- var f0 = 100.0;
- var f1 = 100.0;
- var i = C[1];
-}
-
-const C = array<f32, 2u>(f0, f1);
-
-const f0 = 1.0;
-
-const f1 = 2.0;
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayDynamicIndex) {
- auto* src = R"(
-const TRI_VERTICES = array(
- vec4(0., 0., 0., 1.),
- vec4(0., 1., 0., 1.),
- vec4(1., 1., 0., 1.),
-);
-
-@vertex
-fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
- // note: TRI_VERTICES requires a materialize before the dynamic index.
- return TRI_VERTICES[in_vertex_index];
-}
-)";
-
- auto* expect = R"(
-const TRI_VERTICES = array(vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0));
-
-@vertex
-fn vs_main(@builtin(vertex_index) in_vertex_index : u32) -> @builtin(position) vec4<f32> {
- let tint_symbol : array<vec4<f32>, 3u> = TRI_VERTICES;
- return tint_symbol[in_vertex_index];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstBasicArray) {
- auto* src = R"(
-fn f() {
- const f0 = 1.0;
- const f1 = 2.0;
- const C = array<f32, 2u>(f0, f1);
- var i = C[1]; // Not hoisted, as the final const value is not an array
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstBasicArrayRuntimeIndex) {
- auto* src = R"(
-fn f() {
- const f0 = 1.0;
- const f1 = 2.0;
- const C = array<f32, 2u>(f0, f1);
- let runtime_value = 1;
- var i = C[runtime_value];
-}
-)";
-
- auto* expect = R"(
-fn f() {
- const f0 = 1.0;
- const f1 = 2.0;
- const C = array<f32, 2u>(f0, f1);
- let runtime_value = 1;
- let tint_symbol : array<f32, 2u> = C;
- var i = tint_symbol[runtime_value];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInit) {
- auto* src = R"(
-fn f() {
- var insert_after = 1;
- for(var i = array<f32, 4u>(0.0, 1.0, 2.0, 3.0)[insert_after]; ; ) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var insert_after = 1;
- {
- let tint_symbol : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
- var i = tint_symbol[insert_after];
- loop {
- {
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInit) {
- auto* src = R"(
-fn f() {
- const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
- let runtime_value = 1;
- var insert_after = 1;
- for(var i = arr[runtime_value]; ; ) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
- let runtime_value = 1;
- var insert_after = 1;
- {
- let tint_symbol : array<f32, 4u> = arr;
- var i = tint_symbol[runtime_value];
- loop {
- {
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopInit) {
- auto* src = R"(
-const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-fn f() {
- let runtime_value = 1;
- var insert_after = 1;
- for(var i = arr[runtime_value]; ; ) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-fn f() {
- let runtime_value = 1;
- var insert_after = 1;
- {
- let tint_symbol : array<f32, 4u> = arr;
- var i = tint_symbol[runtime_value];
- loop {
- {
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructInForLoopInit) {
- auto* src = R"(
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-};
-
-fn get_b_runtime(s : S) -> f32 {
- return s.b;
-}
-
-fn f() {
- var insert_after = 1;
- for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-}
-
-fn get_b_runtime(s : S) -> f32 {
- return s.b;
-}
-
-fn f() {
- var insert_after = 1;
- {
- let tint_symbol : S = S(1, 2.0, vec3<f32>());
- var x = get_b_runtime(tint_symbol);
- loop {
- {
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructInForLoopInit_OutOfOrder) {
- auto* src = R"(
-fn f() {
- var insert_after = 1;
- for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) {
- break;
- }
-}
-
-fn get_b_runtime(s : S) -> f32 {
- return s.b;
-}
-
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-};
-)";
-
- auto* expect = R"(
-fn f() {
- var insert_after = 1;
- {
- let tint_symbol : S = S(1, 2.0, vec3<f32>());
- var x = get_b_runtime(tint_symbol);
- loop {
- {
- break;
- }
- }
- }
-}
-
-fn get_b_runtime(s : S) -> f32 {
- return s.b;
-}
-
-struct S {
- a : i32,
- b : f32,
- c : vec3<f32>,
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCond) {
- auto* src = R"(
-fn f() {
- var f = 1.0;
- for(; f == array<f32, 1u>(f)[0]; f = f + 1.0) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var f = 1.0;
- loop {
- let tint_symbol : array<f32, 1u> = array<f32, 1u>(f);
- if (!((f == tint_symbol[0]))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- f = (f + 1.0);
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCond) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- const f = 1.0;
- const arr = array<f32, 1u>(f);
- for(var i = f; i == arr[runtime_value]; i = i + 1.0) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- const f = 1.0;
- const arr = array<f32, 1u>(f);
- {
- var i = f;
- loop {
- let tint_symbol : array<f32, 1u> = arr;
- if (!((i == tint_symbol[runtime_value]))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- i = (i + 1.0);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCond) {
- auto* src = R"(
-const f = 1.0;
-
-const arr = array<f32, 1u>(f);
-
-fn F() {
- let runtime_value = 0;
- for(var i = f; i == arr[runtime_value]; i = i + 1.0) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-const f = 1.0;
-
-const arr = array<f32, 1u>(f);
-
-fn F() {
- let runtime_value = 0;
- {
- var i = f;
- loop {
- let tint_symbol : array<f32, 1u> = arr;
- if (!((i == tint_symbol[runtime_value]))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- i = (i + 1.0);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCont) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- var f = 0.0;
- for(; f < 10.0; f = f + array<f32, 1u>(1.0)[runtime_value]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- var f = 0.0;
- loop {
- if (!((f < 10.0))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- let tint_symbol : array<f32, 1u> = array<f32, 1u>(1.0);
- f = (f + tint_symbol[runtime_value]);
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCont) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- const arr = array<f32, 1u>(1.0);
- var f = 0.0;
- for(; f < 10.0; f = f + arr[runtime_value]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- const arr = array<f32, 1u>(1.0);
- var f = 0.0;
- loop {
- if (!((f < 10.0))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- let tint_symbol : array<f32, 1u> = arr;
- f = (f + tint_symbol[runtime_value]);
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCont) {
- auto* src = R"(
-const arr = array<f32, 1u>(1.0);
-
-fn f() {
- let runtime_value = 0;
- var f = 0.0;
- for(; f < 10.0; f = f + arr[runtime_value]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-const arr = array<f32, 1u>(1.0);
-
-fn f() {
- let runtime_value = 0;
- var f = 0.0;
- loop {
- if (!((f < 10.0))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- let tint_symbol : array<f32, 1u> = arr;
- f = (f + tint_symbol[runtime_value]);
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInitCondCont) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- for(var f = array<f32, 1u>(0.0)[runtime_value];
- f < array<f32, 1u>(1.0)[runtime_value];
- f = f + array<f32, 1u>(2.0)[runtime_value]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- {
- let tint_symbol : array<f32, 1u> = array<f32, 1u>(0.0);
- var f = tint_symbol[runtime_value];
- loop {
- let tint_symbol_1 : array<f32, 1u> = array<f32, 1u>(1.0);
- if (!((f < tint_symbol_1[runtime_value]))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- let tint_symbol_2 : array<f32, 1u> = array<f32, 1u>(2.0);
- f = (f + tint_symbol_2[runtime_value]);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInitCondCont) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- const arr_a = array<f32, 1u>(0.0);
- const arr_b = array<f32, 1u>(1.0);
- const arr_c = array<f32, 1u>(2.0);
- for(var f = arr_a[runtime_value]; f < arr_b[runtime_value]; f = f + arr_c[runtime_value]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- const arr_a = array<f32, 1u>(0.0);
- const arr_b = array<f32, 1u>(1.0);
- const arr_c = array<f32, 1u>(2.0);
- {
- let tint_symbol : array<f32, 1u> = arr_a;
- var f = tint_symbol[runtime_value];
- loop {
- let tint_symbol_1 : array<f32, 1u> = arr_b;
- if (!((f < tint_symbol_1[runtime_value]))) {
- break;
- }
- {
- var marker = 1;
- }
-
- continuing {
- let tint_symbol_2 : array<f32, 1u> = arr_c;
- f = (f + tint_symbol_2[runtime_value]);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInElseIf) {
- auto* src = R"(
-fn f() {
- var f = 1.0;
- if (true) {
- var marker = 0;
- } else if (f == array<f32, 2u>(f, f)[0]) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var f = 1.0;
- if (true) {
- var marker = 0;
- } else {
- let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f);
- if ((f == tint_symbol[0])) {
- var marker = 1;
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInElseIfChain) {
- auto* src = R"(
-fn f() {
- var f = 1.0;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else if (f == array<f32, 2u>(f, f)[0]) {
- var marker = 2;
- } else if (f == array<f32, 2u>(f, f)[1]) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var f = 1.0;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else {
- let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f);
- if ((f == tint_symbol[0])) {
- var marker = 2;
- } else {
- let tint_symbol_1 : array<f32, 2u> = array<f32, 2u>(f, f);
- if ((f == tint_symbol_1[1])) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInElseIfChain) {
- auto* src = R"(
-fn f() {
- let runtime_value = 0;
- const f = 1.0;
- const arr = array<f32, 2u>(f, f);
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else if (f == arr[runtime_value]) {
- var marker = 2;
- } else if (f == arr[runtime_value + 1]) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 0;
- const f = 1.0;
- const arr = array<f32, 2u>(f, f);
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else {
- let tint_symbol : array<f32, 2u> = arr;
- if ((f == tint_symbol[runtime_value])) {
- var marker = 2;
- } else {
- let tint_symbol_1 : array<f32, 2u> = arr;
- if ((f == tint_symbol_1[(runtime_value + 1)])) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInElseIfChain) {
- auto* src = R"(
-const f = 1.0;
-
-const arr = array<f32, 2u>(f, f);
-
-fn F() {
- let runtime_value = 0;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else if (f == arr[runtime_value]) {
- var marker = 2;
- } else if (f == arr[runtime_value + 1]) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
-}
-)";
-
- auto* expect = R"(
-const f = 1.0;
-
-const arr = array<f32, 2u>(f, f);
-
-fn F() {
- let runtime_value = 0;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else {
- let tint_symbol : array<f32, 2u> = arr;
- if ((f == tint_symbol[runtime_value])) {
- var marker = 2;
- } else {
- let tint_symbol_1 : array<f32, 2u> = arr;
- if ((f == tint_symbol_1[(runtime_value + 1)])) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayConstIndex) {
- auto* src = R"(
-fn f() {
- var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[0][1];
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayRuntimeIndex) {
- auto* src = R"(
-fn f() {
- let runtime_value = 1;
- var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[runtime_value][runtime_value + 1];
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 1;
- let tint_symbol : array<array<f32, 2u>, 2u> = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0));
- var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayConstIndex) {
- auto* src = R"(
-fn f() {
- const arr_0 = array<f32, 2u>(1.0, 2.0);
- const arr_1 = array<f32, 2u>(3.0, 4.0);
- const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
- var i = arr_2[0][1];
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayRuntimeIndex) {
- auto* src = R"(
-fn f() {
- let runtime_value = 1;
- const arr_0 = array<f32, 2u>(1.0, 2.0);
- const arr_1 = array<f32, 2u>(3.0, 4.0);
- const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
- var i = arr_2[runtime_value][runtime_value + 1];
-}
-)";
-
- auto* expect = R"(
-fn f() {
- let runtime_value = 1;
- const arr_0 = array<f32, 2u>(1.0, 2.0);
- const arr_1 = array<f32, 2u>(3.0, 4.0);
- const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
- let tint_symbol : array<array<f32, 2u>, 2u> = arr_2;
- var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInArrayArray) {
- auto* src = R"(
-const arr_0 = array<f32, 2u>(1.0, 2.0);
-
-const arr_1 = array<f32, 2u>(3.0, 4.0);
-
-const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-
-fn f() {
- let runtime_value = 1;
- var i = arr_2[runtime_value][runtime_value + 1];
-}
-)";
-
- auto* expect = R"(
-const arr_0 = array<f32, 2u>(1.0, 2.0);
-
-const arr_1 = array<f32, 2u>(3.0, 4.0);
-
-const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-
-fn f() {
- let runtime_value = 1;
- let tint_symbol : array<array<f32, 2u>, 2u> = arr_2;
- var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructNested) {
- auto* src = R"(
-struct S1 {
- a : i32,
-};
-
-struct S2 {
- a : i32,
- b : S1,
- c : i32,
-};
-
-struct S3 {
- a : S2,
-};
-
-fn get_a(s : S3) -> S2 {
- return s.a;
-}
-
-fn f() {
- var x = get_a(S3(S2(1, S1(2), 3))).b.a;
-}
-)";
-
- auto* expect = R"(
-struct S1 {
- a : i32,
-}
-
-struct S2 {
- a : i32,
- b : S1,
- c : i32,
-}
-
-struct S3 {
- a : S2,
-}
-
-fn get_a(s : S3) -> S2 {
- return s.a;
-}
-
-fn f() {
- let tint_symbol : S3 = S3(S2(1, S1(2), 3));
- var x = get_a(tint_symbol).b.a;
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, Mixed) {
- auto* src = R"(
-struct S1 {
- a : i32,
-};
-
-struct S2 {
- a : array<S1, 3u>,
-};
-
-fn get_a(s : S2) -> array<S1, 3u> {
- return s.a;
-}
-
-fn f() {
- var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a;
-}
-)";
-
- auto* expect = R"(
-struct S1 {
- a : i32,
-}
-
-struct S2 {
- a : array<S1, 3u>,
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
- return s.a;
-}
-
-fn f() {
- let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3)));
- var x = get_a(tint_symbol)[1].a;
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, Mixed_OutOfOrder) {
- auto* src = R"(
-fn f() {
- var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a;
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
- return s.a;
-}
-
-struct S2 {
- a : array<S1, 3u>,
-};
-
-struct S1 {
- a : i32,
-};
-)";
-
- auto* expect = R"(
-fn f() {
- let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3)));
- var x = get_a(tint_symbol)[1].a;
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
- return s.a;
-}
-
-struct S2 {
- a : array<S1, 3u>,
-}
-
-struct S1 {
- a : i32,
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl) {
- auto* src = R"(
-alias F = f32;
-
-fn f() {
- var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
- var local_str = F(3.0);
-}
-
-const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-const module_str : F = F(2.0);
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl_OutOfOrder) {
- auto* src = R"(
-fn f() {
- var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
- var local_str = F(3.0);
-}
-
-const module_str : F = F(2.0);
-
-alias F = f32;
-
-const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ForLoopShadowing) {
- auto* src = R"(
-fn X() {
- var i = 10;
- for(var f = 0; f < 10; f = f + array<i32, 1u>(i)[0]) {
- var i = 20;
- }
-}
-
-fn Y() {
- var i = 10;
- for(var f = 0; f < array<i32, 1u>(i)[0]; f = f + 1) {
- var i = 20;
- }
-}
-
-fn Z() {
- var i = 10;
- for(var f = array<i32, 1u>(i)[0]; f < 10; f = f + 1) {
- var i = 20;
- }
-}
-)";
-
- auto* expect = R"(
-fn X() {
- var i = 10;
- {
- var f = 0;
- loop {
- if (!((f < 10))) {
- break;
- }
- {
- var i = 20;
- }
-
- continuing {
- let tint_symbol : array<i32, 1u> = array<i32, 1u>(i);
- f = (f + tint_symbol[0]);
- }
- }
- }
-}
-
-fn Y() {
- var i = 10;
- {
- var f = 0;
- loop {
- let tint_symbol_1 : array<i32, 1u> = array<i32, 1u>(i);
- if (!((f < tint_symbol_1[0]))) {
- break;
- }
- {
- var i = 20;
- }
-
- continuing {
- f = (f + 1);
- }
- }
- }
-}
-
-fn Z() {
- var i = 10;
- {
- let tint_symbol_2 : array<i32, 1u> = array<i32, 1u>(i);
- var f = tint_symbol_2[0];
- loop {
- if (!((f < 10))) {
- break;
- }
- {
- var i = 20;
- }
-
- continuing {
- f = (f + 1);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, AssignAbstractArray) {
- // Test that hoisting an array with abstract type still materializes to the correct type.
- auto* src = R"(
-fn f() {
- var arr : array<f32, 4>;
- arr = array(1, 2, 3, 4);
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var arr : array<f32, 4>;
- let tint_symbol : array<f32, 4u> = array(1, 2, 3, 4);
- arr = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteInitializersToLet>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, AssignAbstractArray_ToPhony) {
- // Test that we do not try to hoist an abstract array expression that is the RHS of a phony
- // assignment, as its type will not be materialized.
- auto* src = R"(
-fn f() {
- _ = array(1, 2, 3, 4);
-}
-)";
-
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, Bug2241) {
- auto* src = R"(
-fn f () {
- let v = false && array<i32, array(array(6))[0][0]>()[0] == 0;
-}
-)";
- EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
deleted file mode 100644
index 0bc61d2..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
+++ /dev/null
@@ -1,671 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
-
-#include <memory>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/transform/manager.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/if_statement.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/lang/wgsl/sem/while_statement.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::PromoteSideEffectsToDecl);
-
-namespace tint::ast::transform {
-namespace {
-
-// Base state class for common members
-class StateBase {
- protected:
- program::CloneContext& ctx;
- ast::Builder& b;
- const sem::Info& sem;
-
- explicit StateBase(program::CloneContext& ctx_in)
- : ctx(ctx_in), b(*ctx_in.dst), sem(ctx_in.src->Sem()) {}
-};
-
-// This first transform converts side-effecting for-loops to loops and else-ifs
-// to else {if}s so that the next transform, DecomposeSideEffects, can insert
-// hoisted expressions above their current location.
-struct SimplifySideEffectStatements : Castable<PromoteSideEffectsToDecl, Transform> {
- ApplyResult Apply(const Program& src, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-Transform::ApplyResult SimplifySideEffectStatements::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- bool made_changes = false;
-
- HoistToDeclBefore hoist_to_decl_before(ctx);
- for (auto* node : ctx.src->ASTNodes().Objects()) {
- if (auto* sem_expr = src.Sem().GetVal(node)) {
- if (!sem_expr->HasSideEffects()) {
- continue;
- }
-
- hoist_to_decl_before.Prepare(sem_expr);
- made_changes = true;
- }
- }
-
- if (!made_changes) {
- return SkipTransform;
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-// Decomposes side-effecting expressions to ensure order of evaluation. This
-// handles both breaking down logical binary expressions for short-circuit
-// evaluation, as well as hoisting expressions to ensure order of evaluation.
-struct DecomposeSideEffects : Castable<PromoteSideEffectsToDecl, Transform> {
- class CollectHoistsState;
- class DecomposeState;
- ApplyResult Apply(const Program& src, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-// CollectHoistsState traverses the AST top-down, identifying which expressions
-// need to be hoisted to ensure order of evaluation, both those that give
-// side-effects, as well as those that receive, and returns a set of these
-// expressions.
-using ToHoistSet = std::unordered_set<const Expression*>;
-class DecomposeSideEffects::CollectHoistsState : public StateBase {
- // Expressions to hoist because they either cause or receive side-effects.
- ToHoistSet to_hoist;
-
- // Used to mark expressions as not or no longer having side-effects.
- std::unordered_set<const Expression*> no_side_effects;
-
- // Returns true if `expr` has side-effects. Unlike invoking
- // sem::ValueExpression::HasSideEffects(), this function takes into account whether
- // `expr` has been hoisted, returning false in that case. Furthermore, it
- // returns the correct result on parent expression nodes by traversing the
- // expression tree, memoizing the results to ensure O(1) amortized lookup.
- bool HasSideEffects(const Expression* expr) {
- if (no_side_effects.count(expr)) {
- return false;
- }
-
- return Switch(
- expr, //
- [&](const CallExpression* e) -> bool { return sem.Get(e)->HasSideEffects(); },
- [&](const BinaryExpression* e) {
- if (HasSideEffects(e->lhs) || HasSideEffects(e->rhs)) {
- return true;
- }
- no_side_effects.insert(e);
- return false;
- },
- [&](const IndexAccessorExpression* e) {
- if (HasSideEffects(e->object) || HasSideEffects(e->index)) {
- return true;
- }
- no_side_effects.insert(e);
- return false;
- },
- [&](const MemberAccessorExpression* e) {
- if (HasSideEffects(e->object)) {
- return true;
- }
- no_side_effects.insert(e);
- return false;
- },
- [&](const UnaryOpExpression* e) {
- if (HasSideEffects(e->expr)) {
- return true;
- }
- no_side_effects.insert(e);
- return false;
- },
- [&](const IdentifierExpression* e) {
- no_side_effects.insert(e);
- return false;
- },
- [&](const LiteralExpression* e) {
- no_side_effects.insert(e);
- return false;
- },
- [&](const PhonyExpression* e) {
- no_side_effects.insert(e);
- return false;
- }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- // Adds `e` to `to_hoist` for hoisting to a let later on.
- void Hoist(const Expression* e) {
- no_side_effects.insert(e);
- to_hoist.emplace(e);
- }
-
- // Hoists any expressions in `maybe_hoist` and clears it
- template <size_t N>
- void Flush(tint::Vector<const Expression*, N>& maybe_hoist) {
- for (auto* m : maybe_hoist) {
- Hoist(m);
- }
- maybe_hoist.Clear();
- }
-
- // Recursive function that processes expressions for side-effects. It
- // traverses the expression tree child before parent, left-to-right. Each call
- // returns whether the input expression should maybe be hoisted, allowing the
- // parent node to decide whether to hoist or not. Generally:
- // * When 'true' is returned, the expression is added to the maybe_hoist list.
- // * When a side-effecting expression is met, we flush the expressions in the
- // maybe_hoist list, as they are potentially receivers of the side-effects.
- // * For index and member accessor expressions, special care is taken to not
- // over-hoist the lhs expressions, as these may be be chained to refer to a
- // single memory location.
- template <size_t N>
- bool ProcessExpression(const Expression* expr,
- tint::Vector<const Expression*, N>& maybe_hoist) {
- auto process = [&](const Expression* e) -> bool {
- return ProcessExpression(e, maybe_hoist);
- };
-
- auto default_process = [&](const Expression* e) {
- auto maybe = process(e);
- if (maybe) {
- maybe_hoist.Push(e);
- }
- if (HasSideEffects(e)) {
- Flush(maybe_hoist);
- }
- return false;
- };
-
- auto binary_process = [&](const Expression* lhs, const Expression* rhs) {
- // If neither side causes side-effects, but at least one receives them,
- // let parent node hoist. This avoids over-hoisting side-effect receivers
- // of compound binary expressions (e.g. for "((a && b) && c) && f()", we
- // don't want to hoist each of "a", "b", and "c" separately, but want to
- // hoist "((a && b) && c)".
- if (!HasSideEffects(lhs) && !HasSideEffects(rhs)) {
- auto lhs_maybe = process(lhs);
- auto rhs_maybe = process(rhs);
- if (lhs_maybe || rhs_maybe) {
- return true;
- }
- return false;
- }
-
- default_process(lhs);
- default_process(rhs);
- return false;
- };
-
- auto accessor_process = [&](const Expression* lhs, const Expression* rhs = nullptr) {
- auto maybe = process(lhs);
- // If lhs is a variable, let parent node hoist otherwise flush it right
- // away. This is to avoid over-hoisting the lhs of accessor chains (e.g.
- // for "v[a][b][c] + g()" we want to hoist all of "v[a][b][c]", not "t1 =
- // v[a]", then "t2 = t1[b]" then "t3 = t2[c]").
- if (maybe && HasSideEffects(lhs)) {
- maybe_hoist.Push(lhs);
- Flush(maybe_hoist);
- maybe = false;
- }
- if (rhs) {
- default_process(rhs);
- }
- return maybe;
- };
-
- return Switch(
- expr,
- [&](const CallExpression* e) -> bool {
- // We eagerly flush any variables in maybe_hoist for the current
- // call expression. Then we scope maybe_hoist to the processing of
- // the call args. This ensures that given: g(c, a(0), d) we hoist
- // 'c' because of 'a(0)', but not 'd' because there's no need, since
- // the call to g() will be hoisted if necessary.
- if (HasSideEffects(e)) {
- Flush(maybe_hoist);
- }
-
- TINT_SCOPED_ASSIGNMENT(maybe_hoist, {});
- for (auto* a : e->args) {
- default_process(a);
- }
-
- // Always hoist this call, even if it has no side-effects to ensure
- // left-to-right order of evaluation.
- // E.g. for "no_side_effects() + side_effects()", we want to hoist
- // no_side_effects() first.
- return true;
- },
- [&](const IdentifierExpression* e) {
- if (auto* sem_e = sem.GetVal(e)) {
- if (auto* var_user = sem_e->UnwrapLoad()->As<sem::VariableUser>()) {
- // Don't hoist constants.
- if (var_user->ConstantValue()) {
- return false;
- }
- // Don't hoist read-only variables as they cannot receive side-effects.
- if (var_user->Variable()->Access() == core::Access::kRead) {
- return false;
- }
- // Don't hoist textures / samplers as they can't be placed into a let, nor
- // can they have side effects.
- if (var_user->Variable()->Type()->IsHandle()) {
- return false;
- }
- return true;
- }
- }
- return false;
- },
- [&](const BinaryExpression* e) {
- if (e->IsLogical() && HasSideEffects(e)) {
- // Don't hoist children of logical binary expressions with
- // side-effects. These will be handled by DecomposeState.
- process(e->lhs);
- process(e->rhs);
- return false;
- }
- return binary_process(e->lhs, e->rhs);
- },
- [&](const UnaryOpExpression* e) { //
- auto r = process(e->expr);
- // Don't hoist address-of expressions.
- // E.g. for "g(&b, a(0))", we hoist "a(0)" only.
- if (e->op == core::UnaryOp::kAddressOf) {
- return false;
- }
- return r;
- },
- [&](const IndexAccessorExpression* e) { return accessor_process(e->object, e->index); },
- [&](const MemberAccessorExpression* e) { return accessor_process(e->object); },
- [&](const LiteralExpression*) {
- // Leaf
- return false;
- },
- [&](const PhonyExpression*) {
- // Leaf
- return false;
- }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- // Starts the recursive processing of a statement's expression(s) to hoist side-effects to lets.
- void ProcessExpression(const Expression* expr) {
- if (!expr) {
- return;
- }
-
- tint::Vector<const Expression*, 8> maybe_hoist;
- ProcessExpression(expr, maybe_hoist);
- }
-
- public:
- explicit CollectHoistsState(program::CloneContext& ctx_in) : StateBase(ctx_in) {}
-
- ToHoistSet Run() {
- // Traverse all statements, recursively processing their expression tree(s)
- // to hoist side-effects to lets.
- for (auto* node : ctx.src->ASTNodes().Objects()) {
- auto* stmt = node->As<Statement>();
- if (!stmt) {
- continue;
- }
-
- Switch(
- stmt, //
- [&](const AssignmentStatement* s) {
- tint::Vector<const Expression*, 8> maybe_hoist;
- ProcessExpression(s->lhs, maybe_hoist);
- ProcessExpression(s->rhs, maybe_hoist);
- },
- [&](const CallStatement* s) { //
- ProcessExpression(s->expr);
- },
- [&](const ForLoopStatement* s) { ProcessExpression(s->condition); },
- [&](const WhileStatement* s) { ProcessExpression(s->condition); },
- [&](const IfStatement* s) { //
- ProcessExpression(s->condition);
- },
- [&](const ReturnStatement* s) { //
- ProcessExpression(s->value);
- },
- [&](const SwitchStatement* s) { ProcessExpression(s->condition); },
- [&](const VariableDeclStatement* s) {
- ProcessExpression(s->variable->initializer);
- });
- }
-
- return std::move(to_hoist);
- }
-};
-
-// DecomposeState performs the actual transforming of the AST to ensure order of
-// evaluation, using the set of expressions to hoist collected by
-// CollectHoistsState.
-class DecomposeSideEffects::DecomposeState : public StateBase {
- ToHoistSet to_hoist;
-
- // Returns true if `binary_expr` should be decomposed for short-circuit eval.
- bool IsLogicalWithSideEffects(const BinaryExpression* binary_expr) {
- return binary_expr->IsLogical() && (sem.GetVal(binary_expr->lhs)->HasSideEffects() ||
- sem.GetVal(binary_expr->rhs)->HasSideEffects());
- }
-
- // Recursive function used to decompose an expression for short-circuit eval.
- template <size_t N>
- const Expression* Decompose(const Expression* expr,
- tint::Vector<const Statement*, N>* curr_stmts) {
- // Helper to avoid passing in same args.
- auto decompose = [&](auto& e) { return Decompose(e, curr_stmts); };
-
- // Clones `expr`, possibly hoisting it to a let.
- auto clone_maybe_hoisted = [&](const Expression* e) -> const Expression* {
- if (to_hoist.count(e)) {
- auto name = b.Symbols().New();
- auto* ty = sem.GetVal(e)->Type();
- auto* v = b.Let(name, Transform::CreateASTTypeFor(ctx, ty), ctx.Clone(e));
- auto* decl = b.Decl(v);
- curr_stmts->Push(decl);
- return b.Expr(name);
- }
- return ctx.Clone(e);
- };
-
- return Switch(
- expr,
- [&](const BinaryExpression* bin_expr) -> const Expression* {
- if (!IsLogicalWithSideEffects(bin_expr)) {
- // No short-circuit, emit usual binary expr
- ctx.Replace(bin_expr->lhs, decompose(bin_expr->lhs));
- ctx.Replace(bin_expr->rhs, decompose(bin_expr->rhs));
- return clone_maybe_hoisted(bin_expr);
- }
-
- // Decompose into ifs to implement short-circuiting
- // For example, 'let r = a && b' becomes:
- //
- // var temp = a;
- // if (temp) {
- // temp = b;
- // }
- // let r = temp;
- //
- // and similarly, 'let r = a || b' becomes:
- //
- // var temp = a;
- // if (!temp) {
- // temp = b;
- // }
- // let r = temp;
- //
- // Further, compound logical binary expressions are also handled
- // recursively, for example, 'let r = (a && (b && c))' becomes:
- //
- // var temp = a;
- // if (temp) {
- // var temp2 = b;
- // if (temp2) {
- // temp2 = c;
- // }
- // temp = temp2;
- // }
- // let r = temp;
-
- auto name = b.Sym();
- curr_stmts->Push(b.Decl(b.Var(name, decompose(bin_expr->lhs))));
-
- const Expression* if_cond = nullptr;
- if (bin_expr->IsLogicalOr()) {
- if_cond = b.Not(name);
- } else {
- if_cond = b.Expr(name);
- }
-
- const BlockStatement* if_body = nullptr;
- {
- tint::Vector<const Statement*, N> stmts;
- TINT_SCOPED_ASSIGNMENT(curr_stmts, &stmts);
- auto* new_rhs = decompose(bin_expr->rhs);
- curr_stmts->Push(b.Assign(name, new_rhs));
- if_body = b.Block(std::move(*curr_stmts));
- }
-
- curr_stmts->Push(b.If(if_cond, if_body));
-
- return b.Expr(name);
- },
- [&](const IndexAccessorExpression* idx) {
- ctx.Replace(idx->object, decompose(idx->object));
- ctx.Replace(idx->index, decompose(idx->index));
- return clone_maybe_hoisted(idx);
- },
- [&](const CallExpression* call) {
- for (auto* a : call->args) {
- ctx.Replace(a, decompose(a));
- }
- return clone_maybe_hoisted(call);
- },
- [&](const MemberAccessorExpression* member) {
- ctx.Replace(member->object, decompose(member->object));
- return clone_maybe_hoisted(member);
- },
- [&](const UnaryOpExpression* unary) {
- ctx.Replace(unary->expr, decompose(unary->expr));
- return clone_maybe_hoisted(unary);
- },
- [&](const LiteralExpression* lit) {
- return clone_maybe_hoisted(lit); // Leaf expression, just clone as is
- },
- [&](const IdentifierExpression* id) {
- return clone_maybe_hoisted(id); // Leaf expression, just clone as is
- },
- [&](const PhonyExpression* phony) {
- return clone_maybe_hoisted(phony); // Leaf expression, just clone as is
- }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- // Inserts statements in `stmts` before `stmt`
- template <size_t N>
- void InsertBefore(tint::Vector<const Statement*, N>& stmts, const Statement* stmt) {
- if (!stmts.IsEmpty()) {
- auto ip = utils::GetInsertionPoint(ctx, stmt);
- for (auto* s : stmts) {
- ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, s);
- }
- }
- }
-
- // Decomposes expressions of `stmt`, returning a replacement statement or
- // nullptr if not replacing it.
- const Statement* DecomposeStatement(const Statement* stmt) {
- return Switch(
- stmt,
- [&](const AssignmentStatement* s) -> const Statement* {
- if (!sem.GetVal(s->lhs)->HasSideEffects() &&
- !sem.GetVal(s->rhs)->HasSideEffects()) {
- return nullptr;
- }
- // lhs before rhs
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->lhs, Decompose(s->lhs, &stmts));
- ctx.Replace(s->rhs, Decompose(s->rhs, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const CallStatement* s) -> const Statement* {
- if (!sem.Get(s->expr)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->expr, Decompose(s->expr, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const ForLoopStatement* s) -> const Statement* {
- if (!s->condition || !sem.GetVal(s->condition)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->condition, Decompose(s->condition, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const WhileStatement* s) -> const Statement* {
- if (!sem.GetVal(s->condition)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->condition, Decompose(s->condition, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const IfStatement* s) -> const Statement* {
- if (!sem.GetVal(s->condition)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->condition, Decompose(s->condition, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const ReturnStatement* s) -> const Statement* {
- if (!s->value || !sem.GetVal(s->value)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->value, Decompose(s->value, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const SwitchStatement* s) -> const Statement* {
- if (!sem.Get(s->condition)) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(s->condition, Decompose(s->condition, &stmts));
- InsertBefore(stmts, s);
- return ctx.CloneWithoutTransform(s);
- },
- [&](const VariableDeclStatement* s) -> const Statement* {
- auto* var = s->variable;
- if (!var->initializer || !sem.GetVal(var->initializer)->HasSideEffects()) {
- return nullptr;
- }
- tint::Vector<const Statement*, 8> stmts;
- ctx.Replace(var->initializer, Decompose(var->initializer, &stmts));
- InsertBefore(stmts, s);
- return b.Decl(ctx.CloneWithoutTransform(var));
- },
- [](Default) -> const Statement* {
- // Other statement types don't have expressions
- return nullptr;
- });
- }
-
- public:
- explicit DecomposeState(program::CloneContext& ctx_in, ToHoistSet to_hoist_in)
- : StateBase(ctx_in), to_hoist(std::move(to_hoist_in)) {}
-
- void Run() {
- // We replace all BlockStatements as this allows us to iterate over the
- // block statements and ctx.InsertBefore hoisted declarations on them.
- ctx.ReplaceAll([&](const BlockStatement* block) -> const Statement* {
- for (auto* stmt : block->statements) {
- if (auto* new_stmt = DecomposeStatement(stmt)) {
- ctx.Replace(stmt, new_stmt);
- }
-
- // Handle for loops, as they are the only other AST node that
- // contains statements outside of BlockStatements.
- if (auto* fl = stmt->As<ForLoopStatement>()) {
- if (auto* new_stmt = DecomposeStatement(fl->initializer)) {
- ctx.Replace(fl->initializer, new_stmt);
- }
- if (auto* new_stmt = DecomposeStatement(fl->continuing)) {
- ctx.Replace(fl->continuing, new_stmt);
- }
- }
- }
- return nullptr;
- });
- }
-};
-
-Transform::ApplyResult DecomposeSideEffects::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- // First collect side-effecting expressions to hoist
- CollectHoistsState collect_hoists_state{ctx};
- auto to_hoist = collect_hoists_state.Run();
-
- // Now decompose these expressions
- DecomposeState decompose_state{ctx, std::move(to_hoist)};
- decompose_state.Run();
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace
-
-PromoteSideEffectsToDecl::PromoteSideEffectsToDecl() = default;
-PromoteSideEffectsToDecl::~PromoteSideEffectsToDecl() = default;
-
-Transform::ApplyResult PromoteSideEffectsToDecl::Apply(const Program& src,
- const DataMap& inputs,
- DataMap& outputs) const {
- Manager manager;
- manager.Add<SimplifySideEffectStatements>();
- manager.Add<DecomposeSideEffects>();
- return manager.Run(src, inputs, outputs);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h
deleted file mode 100644
index c488745..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that hoists expressions with side-effects to variable
-/// declarations before the statement of usage with the goal of ensuring
-/// left-to-right order of evaluation, while respecting short-circuit
-/// evaluation.
-/// As `@diagnostic` statements can be decomposed, this transform requires that the
-/// DisableUniformityAnalysis transform is run first.
-class PromoteSideEffectsToDecl final : public Castable<PromoteSideEffectsToDecl, Transform> {
- public:
- /// Constructor
- PromoteSideEffectsToDecl();
-
- /// Destructor
- ~PromoteSideEffectsToDecl() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
deleted file mode 100644
index e89dad0..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
+++ /dev/null
@@ -1,4264 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using PromoteSideEffectsToDeclTest = TransformTest;
-
-TEST_F(PromoteSideEffectsToDeclTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Unary_Arith_SE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let r = -(a(0));
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_BothSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn b() -> i32 {
- return 1;
-}
-
-fn f() {
- let r = a() + b();
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn b() -> i32 {
- return 1;
-}
-
-fn f() {
- let tint_symbol : i32 = a();
- let tint_symbol_1 : i32 = b();
- let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_LeftSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = a() + b;
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a();
- let r = (tint_symbol + b);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_RightSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = b + a();
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = a();
- let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_LeftmostSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- let r = a() + b + c + d;
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- let tint_symbol : i32 = a();
- let r = (((tint_symbol + b) + c) + d);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_RightmostSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- let r = b + c + d + a();
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- let tint_symbol : i32 = ((b + c) + d);
- let tint_symbol_1 : i32 = a();
- let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_MiddleSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- var e = 1;
- let r = b + c + a() + d + e;
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- var d = 1;
- var e = 1;
- let tint_symbol : i32 = (b + c);
- let tint_symbol_1 : i32 = a();
- let r = (((tint_symbol + tint_symbol_1) + d) + e);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_ThreeSE) {
- auto* src = R"(
-fn a(v : i32) -> i32 {
- return v;
-}
-
-fn f() {
- let r = a(0) + a(1) + a(2);
-}
-)";
-
- auto* expect = R"(
-fn a(v : i32) -> i32 {
- return v;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- let r = ((tint_symbol + tint_symbol_1) + tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_NoRecvSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let r = ((((1 + a(0)) - 2) + 3) - 4);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let r = ((((1 + tint_symbol) - 2) + 3) - 4);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_RecvSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = a(0) + 1 + b + a(1) + 2;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = b;
- let tint_symbol_2 : i32 = a(1);
- let r = ((((tint_symbol + 1) + tint_symbol_1) + tint_symbol_2) + 2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_ConstAndSEAndVar) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- var c = 1;
- let r = 1 + a(0) + b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- var c = 1;
- let tint_symbol : i32 = a(0);
- let r = ((1 + tint_symbol) + b);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_VarAndSEAndConst) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- let r = b + a(0) + 1;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = a(0);
- let r = ((tint_symbol + tint_symbol_1) + 1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_SEAndVarAndConstAndVar) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- var c = 1;
- let r = a(0) + b + 1 + c;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn main() {
- var b = 1;
- var c = 1;
- let tint_symbol : i32 = a(0);
- let r = (((tint_symbol + b) + 1) + c);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_WithSE) {
- auto* src = R"(
-struct SB {
- a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
- var b = 0;
- let r = atomicAdd(&sb.a, 123) + b;
-}
-)";
-
- auto* expect = R"(
-struct SB {
- a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
- var b = 0;
- let tint_symbol : i32 = atomicAdd(&(sb.a), 123);
- let r = (tint_symbol + b);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_NoSEAndVar) {
- auto* src = R"(
-struct SB {
- a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
- var b = 0;
- let r = (atomicLoad(&(sb.a)) + b);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_NoSEAndSE) {
- auto* src = R"(
-struct SB {
- a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 0;
- let r = (atomicLoad(&(sb.a)) + a(0));
-}
-)";
-
- auto* expect = R"(
-struct SB {
- a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 0;
- let tint_symbol : i32 = atomicLoad(&(sb.a));
- let tint_symbol_1 : i32 = a(0);
- let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Vector_RightSE) {
- auto* src = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b : vec3<i32>;
- var c : i32;
- let r = b[c] + a();
-}
-)";
-
- auto* expect = R"(
-fn a() -> i32 {
- return 1;
-}
-
-fn f() {
- var b : vec3<i32>;
- var c : i32;
- let tint_symbol : i32 = c;
- let tint_symbol_1 : i32 = b[tint_symbol];
- let tint_symbol_2 : i32 = a();
- let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InCall) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn g(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let r = g(a(0)) - g(b + a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn g(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = g(tint_symbol);
- let tint_symbol_2 : i32 = b;
- let tint_symbol_3 : i32 = a(1);
- let tint_symbol_4 : i32 = g((tint_symbol_2 + tint_symbol_3));
- let r = (tint_symbol_1 - tint_symbol_4);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InTypeInit) {
- auto* src = R"(
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = i32(a(0)) + i32(a(1) + b) - i32(a(2) - a(3));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = i32(tint_symbol);
- let tint_symbol_2 : i32 = a(1);
- let tint_symbol_3 : i32 = i32((tint_symbol_2 + b));
- let tint_symbol_4 : i32 = a(2);
- let tint_symbol_5 : i32 = a(3);
- let tint_symbol_6 : i32 = i32((tint_symbol_4 - tint_symbol_5));
- let r = ((tint_symbol_1 + tint_symbol_3) - tint_symbol_6);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InTypeConversion) {
- auto* src = R"(
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1u;
- let r = u32(a(0)) + u32(a(1)) - b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1u;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : u32 = u32(tint_symbol);
- let tint_symbol_2 : i32 = a(1);
- let tint_symbol_3 : u32 = u32(tint_symbol_2);
- let r = ((tint_symbol_1 + tint_symbol_3) - b);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InIntrinsic) {
- auto* src = R"(
-
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let r = abs(a(0)) + abs(a(1) + b) - abs(a(2) + a(3));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = abs(tint_symbol);
- let tint_symbol_2 : i32 = a(1);
- let tint_symbol_3 : i32 = abs((tint_symbol_2 + b));
- let tint_symbol_4 : i32 = a(2);
- let tint_symbol_5 : i32 = a(3);
- let tint_symbol_6 : i32 = abs((tint_symbol_4 + tint_symbol_5));
- let r = ((tint_symbol_1 + tint_symbol_3) - tint_symbol_6);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InMemberAccessor) {
- auto* src = R"(
-
-struct S {
- v : i32,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- var b = 1;
- let r = a(0).v + b + a(1).v;
-}
-)";
-
- auto* expect = R"(
-struct S {
- v : i32,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : S = a(0);
- let tint_symbol_1 : i32 = b;
- let tint_symbol_2 : S = a(1);
- let r = ((tint_symbol.v + tint_symbol_1) + tint_symbol_2.v);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InUnary) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let r = -a(0) + -(b + a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = -(a(0));
- let tint_symbol_1 : i32 = b;
- let tint_symbol_2 : i32 = a(1);
- let r = (tint_symbol + -((tint_symbol_1 + tint_symbol_2)));
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InBitcast) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let r = bitcast<u32>(a(0) + a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let r = bitcast<u32>((tint_symbol + tint_symbol_1));
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopInit) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- for(var r = a(0) + b; ; ) {
- var marker = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- {
- let tint_symbol : i32 = a(0);
- var r = (tint_symbol + b);
- loop {
- {
- var marker = 0;
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopCond) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- for(; a(0) + b > 0;) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- loop {
- let tint_symbol : i32 = a(0);
- if (!(((tint_symbol + b) > 0))) {
- break;
- }
- {
- var marker = 0;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopCont) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- var r = 0;
- for(; ; r = a(0) + b) {
- var marker = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- var r = 0;
- loop {
- {
- var marker = 0;
- break;
- }
-
- continuing {
- let tint_symbol : i32 = a(0);
- r = (tint_symbol + b);
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopInitCondCont) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- var c = 2;
- var d = 3;
- var r = 0;
- for(var r = a(0) + b; a(1) + c > 0; r = a(2) + d) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- var c = 2;
- var d = 3;
- var r = 0;
- {
- let tint_symbol : i32 = a(0);
- var r = (tint_symbol + b);
- loop {
- let tint_symbol_1 : i32 = a(1);
- if (!(((tint_symbol_1 + c) > 0))) {
- break;
- }
- {
- var marker = 0;
- }
-
- continuing {
- let tint_symbol_2 : i32 = a(2);
- r = (tint_symbol_2 + d);
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InWhileCond) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- while(a(0) + b > 0) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- loop {
- let tint_symbol : i32 = a(0);
- if (!(((tint_symbol + b) > 0))) {
- break;
- }
- {
- var marker = 0;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InElseIf) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- if (true) {
- var marker = 0;
- } else if (a(0) + b > 0) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- if (true) {
- var marker = 0;
- } else {
- let tint_symbol : i32 = a(0);
- if (((tint_symbol + b) > 0)) {
- var marker = 1;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InElseIfChain) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else if (a(0) + b > 0) {
- var marker = 2;
- } else if (a(1) + a(2) > 0) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else {
- let tint_symbol : i32 = a(0);
- if (((tint_symbol + b) > 0)) {
- var marker = 2;
- } else {
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- if (((tint_symbol_1 + tint_symbol_2) > 0)) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InReturn) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() -> i32 {
- var b = 1;
- return b + a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() -> i32 {
- var b = 1;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = a(0);
- return (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InSwitch) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- switch (b + a(0)) {
- default: {
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = a(0);
- switch((tint_symbol + tint_symbol_1)) {
- default: {
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_LeftSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = a(0) && b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_RightSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = b && a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = b;
- if (tint_symbol) {
- tint_symbol = a(0);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_BothSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- let r = a(0) && a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = a(1);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_LeftmostSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- let r = a(0) && b && c && d;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var tint_symbol_2 = a(0);
- if (tint_symbol_2) {
- tint_symbol_2 = b;
- }
- var tint_symbol_1 = tint_symbol_2;
- if (tint_symbol_1) {
- tint_symbol_1 = c;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = d;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_RightmostSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- let r = b && c && d && a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var tint_symbol = ((b && c) && d);
- if (tint_symbol) {
- tint_symbol = a(0);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MiddleSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- let r = b && c && a(0) && c && d;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var tint_symbol_2 = (b && c);
- if (tint_symbol_2) {
- tint_symbol_2 = a(0);
- }
- var tint_symbol_1 = tint_symbol_2;
- if (tint_symbol_1) {
- tint_symbol_1 = c;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = d;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_NoRecvSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- let r = true && a(0) && false && a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var tint_symbol_2 = true;
- if (tint_symbol_2) {
- tint_symbol_2 = a(0);
- }
- var tint_symbol_1 = tint_symbol_2;
- if (tint_symbol_1) {
- tint_symbol_1 = false;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = a(1);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_RecvSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = b && true && a(0) && false && a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol_2 = (b && true);
- if (tint_symbol_2) {
- tint_symbol_2 = a(0);
- }
- var tint_symbol_1 = tint_symbol_2;
- if (tint_symbol_1) {
- tint_symbol_1 = false;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = a(1);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_ConstAndSEAndVar) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- var c = true;
- let r = true && a(0) && b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- var c = true;
- var tint_symbol_1 = true;
- if (tint_symbol_1) {
- tint_symbol_1 = a(0);
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = b;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_VarAndSEAndConst) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- let r = b && a(0) && true;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- var tint_symbol_1 = b;
- if (tint_symbol_1) {
- tint_symbol_1 = a(0);
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = true;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_SEAndVarAndConstAndVar) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- var c = true;
- let r = a(0) && b && true && c;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn main() {
- var b = true;
- var c = true;
- var tint_symbol_2 = a(0);
- if (tint_symbol_2) {
- tint_symbol_2 = b;
- }
- var tint_symbol_1 = tint_symbol_2;
- if (tint_symbol_1) {
- tint_symbol_1 = true;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- tint_symbol = c;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MixedSE) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- let r = (b && a(0)) || (c && a(1) && c && d) || a(2);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var tint_symbol_2 = b;
- if (tint_symbol_2) {
- tint_symbol_2 = a(0);
- }
- var tint_symbol_1 = tint_symbol_2;
- if (!(tint_symbol_1)) {
- var tint_symbol_5 = c;
- if (tint_symbol_5) {
- tint_symbol_5 = a(1);
- }
- var tint_symbol_4 = tint_symbol_5;
- if (tint_symbol_4) {
- tint_symbol_4 = c;
- }
- var tint_symbol_3 = tint_symbol_4;
- if (tint_symbol_3) {
- tint_symbol_3 = d;
- }
- tint_symbol_1 = tint_symbol_3;
- }
- var tint_symbol = tint_symbol_1;
- if (!(tint_symbol)) {
- tint_symbol = a(2);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_NestedAnds) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- let r = a(0) && (a(1) && (a(2) && (a(3) && (a(4) && a(5)))));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var tint_symbol = a(0);
- if (tint_symbol) {
- var tint_symbol_1 = a(1);
- if (tint_symbol_1) {
- var tint_symbol_2 = a(2);
- if (tint_symbol_2) {
- var tint_symbol_3 = a(3);
- if (tint_symbol_3) {
- var tint_symbol_4 = a(4);
- if (tint_symbol_4) {
- tint_symbol_4 = a(5);
- }
- tint_symbol_3 = tint_symbol_4;
- }
- tint_symbol_2 = tint_symbol_3;
- }
- tint_symbol_1 = tint_symbol_2;
- }
- tint_symbol = tint_symbol_1;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_NestedOrs) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- let r = a(0) || (a(1) || (a(2) || (a(3) || (a(4) || a(5)))));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var tint_symbol = a(0);
- if (!(tint_symbol)) {
- var tint_symbol_1 = a(1);
- if (!(tint_symbol_1)) {
- var tint_symbol_2 = a(2);
- if (!(tint_symbol_2)) {
- var tint_symbol_3 = a(3);
- if (!(tint_symbol_3)) {
- var tint_symbol_4 = a(4);
- if (!(tint_symbol_4)) {
- tint_symbol_4 = a(5);
- }
- tint_symbol_3 = tint_symbol_4;
- }
- tint_symbol_2 = tint_symbol_3;
- }
- tint_symbol_1 = tint_symbol_2;
- }
- tint_symbol = tint_symbol_1;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MultipleStatements) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r1 = b && a(0);
- let r2 = a(1) || b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = b;
- if (tint_symbol) {
- tint_symbol = a(0);
- }
- let r1 = tint_symbol;
- var tint_symbol_1 = a(1);
- if (!(tint_symbol_1)) {
- tint_symbol_1 = b;
- }
- let r2 = tint_symbol_1;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InCall) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn g(v : bool) -> bool {
- return v;
-}
-
-fn f() {
- var b = true;
- g(b && a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn g(v : bool) -> bool {
- return v;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = b;
- if (tint_symbol) {
- tint_symbol = a(1);
- }
- g(tint_symbol);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InTypeInit) {
- auto* src = R"(
-
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = (bool(a(0)) && bool(a(1) && b)) || bool(a(2) && a(3));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let tint_symbol_2 : bool = a(0);
- var tint_symbol_1 = bool(tint_symbol_2);
- if (tint_symbol_1) {
- var tint_symbol_3 = a(1);
- if (tint_symbol_3) {
- tint_symbol_3 = b;
- }
- tint_symbol_1 = bool(tint_symbol_3);
- }
- var tint_symbol = tint_symbol_1;
- if (!(tint_symbol)) {
- var tint_symbol_4 = a(2);
- if (tint_symbol_4) {
- tint_symbol_4 = a(3);
- }
- tint_symbol = bool(tint_symbol_4);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InTypeConversion) {
- auto* src = R"(
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = true;
- let r = (bool(a(0)) && bool(a(1))) || b;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = true;
- let tint_symbol_2 : i32 = a(0);
- var tint_symbol_1 = bool(tint_symbol_2);
- if (tint_symbol_1) {
- let tint_symbol_3 : i32 = a(1);
- tint_symbol_1 = bool(tint_symbol_3);
- }
- var tint_symbol = tint_symbol_1;
- if (!(tint_symbol)) {
- tint_symbol = b;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-// Make sure we process logical binary expressions of non-logical binary
-// expressions.
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_OfNonLogical) {
- auto* src = R"(
-
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = bool(a(0) == b) && bool(a(1) == b);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol_1 : i32 = a(0);
- var tint_symbol = bool((tint_symbol_1 == b));
- if (tint_symbol) {
- let tint_symbol_2 : i32 = a(1);
- tint_symbol = bool((tint_symbol_2 == b));
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InIntrinsic) {
- auto* src = R"(
-
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = (all(a(0)) && all(a(1) && b)) || all(a(2) && a(3));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let tint_symbol_2 : bool = a(0);
- var tint_symbol_1 = all(tint_symbol_2);
- if (tint_symbol_1) {
- var tint_symbol_3 = a(1);
- if (tint_symbol_3) {
- tint_symbol_3 = b;
- }
- tint_symbol_1 = all(tint_symbol_3);
- }
- var tint_symbol = tint_symbol_1;
- if (!(tint_symbol)) {
- var tint_symbol_4 = a(2);
- if (tint_symbol_4) {
- tint_symbol_4 = a(3);
- }
- tint_symbol = all(tint_symbol_4);
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InMemberAccessor) {
- auto* src = R"(
-
-struct S {
- v : bool,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- var b = true;
- let r = a(0).v && b && a(1).v;
-}
-)";
-
- auto* expect = R"(
-struct S {
- v : bool,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- var b = true;
- let tint_symbol_2 : S = a(0);
- var tint_symbol_1 = tint_symbol_2.v;
- if (tint_symbol_1) {
- tint_symbol_1 = b;
- }
- var tint_symbol = tint_symbol_1;
- if (tint_symbol) {
- let tint_symbol_3 : S = a(1);
- tint_symbol = tint_symbol_3.v;
- }
- let r = tint_symbol;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InUnary) {
- auto* src = R"(
-
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = !(b || a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = b;
- if (!(tint_symbol)) {
- tint_symbol = a(1);
- }
- let r = !(tint_symbol);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InBitcast) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- let r = bitcast<u32>(i32(a(0) && a(1)));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = a(1);
- }
- let tint_symbol_1 : i32 = i32(tint_symbol);
- let r = bitcast<u32>(tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopInit) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- for(var r = a(0) && b; ; ) {
- var marker = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- var r = tint_symbol;
- loop {
- {
- var marker = 0;
- break;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopCond) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- for(; a(0) && b;) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- loop {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- if (!(tint_symbol)) {
- break;
- }
- {
- var marker = 0;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopCont) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var r = true;
- for(; ; r = a(0) && b) {
- var marker = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var r = true;
- loop {
- {
- var marker = 0;
- break;
- }
-
- continuing {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- r = tint_symbol;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopInitCondCont) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var r = true;
- for(var r = a(0) && b; a(1) && c; r = a(2) && d) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- var c = true;
- var d = true;
- var r = true;
- {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- var r = tint_symbol;
- loop {
- var tint_symbol_1 = a(1);
- if (tint_symbol_1) {
- tint_symbol_1 = c;
- }
- if (!(tint_symbol_1)) {
- break;
- }
- {
- var marker = 0;
- }
-
- continuing {
- var tint_symbol_2 = a(2);
- if (tint_symbol_2) {
- tint_symbol_2 = d;
- }
- r = tint_symbol_2;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InWhileCond) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- while(a(0) && b) {
- var marker = 0;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- loop {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- if (!(tint_symbol)) {
- break;
- }
- {
- var marker = 0;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InElseIf) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- if (true) {
- var marker = 0;
- } else if (a(0) && b) {
- var marker = 1;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- if (true) {
- var marker = 0;
- } else {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- if (tint_symbol) {
- var marker = 1;
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InElseIfChain) {
- auto* src = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else if (a(0) && b) {
- var marker = 2;
- } else if (a(1) && a(2)) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> bool {
- return true;
-}
-
-fn f() {
- var b = true;
- if (true) {
- var marker = 0;
- } else if (true) {
- var marker = 1;
- } else {
- var tint_symbol = a(0);
- if (tint_symbol) {
- tint_symbol = b;
- }
- if (tint_symbol) {
- var marker = 2;
- } else {
- var tint_symbol_1 = a(1);
- if (tint_symbol_1) {
- tint_symbol_1 = a(2);
- }
- if (tint_symbol_1) {
- var marker = 3;
- } else if (true) {
- var marker = 4;
- } else {
- var marker = 5;
- }
- }
- }
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_NoSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- var c = 1;
- let r = g(b, c, 3);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_OneSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let r = g(a(0), b, 3);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let r = g(tint_symbol, b, 3);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_AllSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let r = g(a(0), a(1), a(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- let r = g(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_MiddleNotSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-
-fn f() {
- var b = 1;
- let r = g(a(0), b, a(1));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 1;
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = b;
- let tint_symbol_2 : i32 = a(1);
- let r = g(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_InBinary) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(i : i32, j : i32, k : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 0;
- var c = 0;
- var d = 0;
- let r = b + g(c, a(0), d) + a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn g(i : i32, j : i32, k : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = 0;
- var c = 0;
- var d = 0;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = c;
- let tint_symbol_2 : i32 = a(0);
- let tint_symbol_3 : i32 = g(tint_symbol_1, tint_symbol_2, d);
- let tint_symbol_4 : i32 = a(1);
- let r = ((tint_symbol + tint_symbol_3) + tint_symbol_4);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_LeftSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- var c = 1;
- var r = b[a(0)][c];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- var c = 1;
- let tint_symbol : i32 = a(0);
- var r = b[tint_symbol][c];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_RightSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- var c = 1;
- let tint_symbol = c;
- let tint_symbol_1 = a(0);
- var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- var c = 1;
- let tint_symbol = c;
- let tint_symbol_1 = a(0);
- var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_BothSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- var r = b[a(0)][a(1)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_LeftSE_ViaPointerIndex) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &b;
- var c = 1;
- var r = p[a(0)][c];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &(b);
- var c = 1;
- let tint_symbol : i32 = a(0);
- var r = p[tint_symbol][c];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_RightSE_ViaPointerIndex) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &b;
- var c = 1;
- let tint_symbol = c;
- let tint_symbol_1 = a(0);
- var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &(b);
- var c = 1;
- let tint_symbol = c;
- let tint_symbol_1 = a(0);
- var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_BothSE_ViaPointerIndex) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &b;
- var r = p[a(0)][a(1)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let p = &(b);
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToPhony) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- _ = a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- _ = a(0);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray1D) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<i32, 10>();
- b[a(0)] = a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<i32, 10>();
- let tint_symbol : i32 = a(0);
- b[tint_symbol] = a(1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray2D) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- b[a(0)][a(1)] = a(2);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<i32, 10>, 10>();
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- b[tint_symbol][tint_symbol_1] = a(2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray3D) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<array<i32, 10>, 10>, 10>();
- b[a(0)][a(1)][a(2)] = a(3);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<array<array<i32, 10>, 10>, 10>();
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- b[tint_symbol][tint_symbol_1][tint_symbol_2] = a(3);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray_FromArray) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<i32, 3>();
- var d = array<array<i32, 3>, 3>();
- var a_1 = 0;
- b[a(2)] = d[a(0)][a_1];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = array<i32, 3>();
- var d = array<array<i32, 3>, 3>();
- var a_1 = 0;
- let tint_symbol : i32 = a(2);
- let tint_symbol_1 : i32 = a(0);
- b[tint_symbol] = d[tint_symbol_1][a_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_BothSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- b[a(0)] = a(1);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- let tint_symbol : i32 = a(0);
- b[tint_symbol] = a(1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_LeftSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- var c = 0;
- b[a(0)] = c;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- var c = 0;
- let tint_symbol : i32 = a(0);
- b[tint_symbol] = c;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_RightSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- var c = 0;
- b[c] = a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var b = vec3<i32>();
- var c = 0;
- let tint_symbol : i32 = c;
- b[tint_symbol] = a(0);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Struct) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-struct S {
- x : i32,
- y : i32,
- z : i32,
-}
-
-fn f() {
- var r = S(a(0), a(1), a(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-struct S {
- x : i32,
- y : i32,
- z : i32,
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- var r = S(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Array1D) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var r = array<i32, 3>(a(0), a(1), a(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : i32 = a(2);
- var r = array<i32, 3>(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Array2D) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- var r = array<array<i32, 2>, 2>(array<i32, 2>(a(0), a(1)), array<i32, 2>(a(2), a(3)));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 1;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let tint_symbol_1 : i32 = a(1);
- let tint_symbol_2 : array<i32, 2u> = array<i32, 2>(tint_symbol, tint_symbol_1);
- let tint_symbol_3 : i32 = a(2);
- let tint_symbol_4 : i32 = a(3);
- let tint_symbol_5 : array<i32, 2u> = array<i32, 2>(tint_symbol_3, tint_symbol_4);
- var r = array<array<i32, 2>, 2>(tint_symbol_2, tint_symbol_5);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Vec) {
- auto* src = R"(
-fn a(i : i32) -> vec3<i32> {
- return vec3<i32>();
-}
-
-fn f() {
- var r = a(0).x + a(1).y;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> vec3<i32> {
- return vec3<i32>();
-}
-
-fn f() {
- let tint_symbol : vec3<i32> = a(0);
- let tint_symbol_1 : vec3<i32> = a(1);
- var r = (tint_symbol.x + tint_symbol_1.y);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Struct) {
- auto* src = R"(
-struct S {
- x : i32,
- y : i32,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- var r = a(0).x + a(1).y;
-}
-)";
-
- auto* expect = R"(
-struct S {
- x : i32,
- y : i32,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn f() {
- let tint_symbol : S = a(0);
- let tint_symbol_1 : S = a(1);
- var r = (tint_symbol.x + tint_symbol_1.y);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Struct_Mixed) {
- auto* src = R"(
-struct S {
- x : i32,
- y : i32,
- arr : array<i32, 10>,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn b(i : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- var i = 0;
- var j = 0;
- var k = 0;
- var l = 0;
- var m = 0;
- var r = a(0).x + i + a(1).y + j + a(2).arr[k + b(3) + l] + m;
-}
-)";
-
- auto* expect = R"(
-struct S {
- x : i32,
- y : i32,
- arr : array<i32, 10>,
-}
-
-fn a(i : i32) -> S {
- return S();
-}
-
-fn b(i : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- var i = 0;
- var j = 0;
- var k = 0;
- var l = 0;
- var m = 0;
- let tint_symbol : S = a(0);
- let tint_symbol_1 : i32 = i;
- let tint_symbol_2 : S = a(1);
- let tint_symbol_3 : i32 = j;
- let tint_symbol_4 : S = a(2);
- let tint_symbol_5 : i32 = k;
- let tint_symbol_6 : i32 = b(3);
- var r = (((((tint_symbol.x + tint_symbol_1) + tint_symbol_2.y) + tint_symbol_3) + tint_symbol_4.arr[((tint_symbol_5 + tint_symbol_6) + l)]) + m);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Plus_SE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let r = v[0] + a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let tint_symbol : i32 = v[0];
- let tint_symbol_1 : i32 = a(0);
- let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Of_SE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let r = v[a(0)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let tint_symbol : i32 = a(0);
- let r = v[tint_symbol];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_LeftSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- let r = v[a(0)][0];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- let tint_symbol : i32 = a(0);
- let r = v[tint_symbol][0];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_RightSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- let r = v[0][a(0)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- let tint_symbol : i32 = a(0);
- let r = v[0][tint_symbol];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_SEAndVar) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- var b : i32;
- let r = v[a(0)][b];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- var b : i32;
- let tint_symbol : i32 = a(0);
- let r = v[tint_symbol][b];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_VarAndSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- var b : i32;
- let r = v[b][a(0)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<array<i32, 10>, 10>();
- var b : i32;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = a(0);
- let r = v[tint_symbol][tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessorOfVar_Plus_SE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var b = 0;
- let r = v[b] + a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var b = 0;
- let tint_symbol : i32 = b;
- let tint_symbol_1 : i32 = v[tint_symbol];
- let tint_symbol_2 : i32 = a(0);
- let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Plus_IndexAccessorOfSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let r = v[0] + v[a(0)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- let tint_symbol : i32 = v[0];
- let tint_symbol_1 : i32 = a(0);
- let r = (tint_symbol + v[tint_symbol_1]);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, AssignTo_IndexAccessorOfIndexAccessorOfSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- v[w[a(0)]] = 1;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- let tint_symbol : i32 = a(0);
- v[w[tint_symbol]] = 1;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, AssignTo_IndexAccessorOfIndexAccessorOfLiteralPlusSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- v[w[0] + a(0)] = 1;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- let tint_symbol : i32 = w[0];
- let tint_symbol_1 : i32 = a(0);
- v[(tint_symbol + tint_symbol_1)] = 1;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest,
- AssignTo_IndexAccessorOfIndexAccessorOfLiteralPlusIndexAccessorOfSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- v[w[0] + w[a(0)]] = 1;
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn f() {
- var v = array<i32, 10>();
- var w = array<i32, 10>();
- let tint_symbol : i32 = w[0];
- let tint_symbol_1 : i32 = a(0);
- v[(tint_symbol + w[tint_symbol_1])] = 1;
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessorOfLhsSERhsSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn b() -> array<i32, 10> {
- return array<i32, 10>();
-}
-
-fn f() {
- let r = b()[a(0)];
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn b() -> array<i32, 10> {
- return array<i32, 10>();
-}
-
-fn f() {
- let tint_symbol : array<i32, 10u> = b();
- let tint_symbol_1 : i32 = a(0);
- let r = tint_symbol[tint_symbol_1];
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryIndexAccessorOfLhsSERhsSE) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn b() -> array<i32, 10> {
- return array<i32, 10>();
-}
-
-fn f() {
- let r = b()[0] + a(0);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return i;
-}
-
-fn b() -> array<i32, 10> {
- return array<i32, 10>();
-}
-
-fn f() {
- let tint_symbol : array<i32, 10u> = b();
- let tint_symbol_1 : i32 = a(0);
- let r = (tint_symbol[0] + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryMemberAccessorPlusSE) {
- // bclayton@'s example:
- // https://dawn-review.googlesource.com/c/tint/+/78620/6..8/src/transform/promote_side_effects_to_decl.cc#b490
- auto* src = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
- (*p).x = 42;
- return 0;
-}
-
-fn f() {
- var v = vec4<i32>();
- let l = v.x + modify_vec(&v);
- // l should be 0, not 42
-}
-)";
-
- auto* expect = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
- (*(p)).x = 42;
- return 0;
-}
-
-fn f() {
- var v = vec4<i32>();
- let tint_symbol : i32 = v.x;
- let tint_symbol_1 : i32 = modify_vec(&(v));
- let l = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryMemberAccessorPlusSE_ViaPointerDot) {
- // bclayton@'s example:
- // https://dawn-review.googlesource.com/c/tint/+/78620/6..8/src/transform/promote_side_effects_to_decl.cc#b490
- auto* src = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
- (*p).x = 42;
- return 0;
-}
-
-fn f() {
- var v = vec4<i32>();
- let p = &v;
- let l = p.x + modify_vec(&v);
- // l should be 0, not 42
-}
-)";
-
- auto* expect = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
- (*(p)).x = 42;
- return 0;
-}
-
-fn f() {
- var v = vec4<i32>();
- let p = &(v);
- let tint_symbol : i32 = p.x;
- let tint_symbol_1 : i32 = modify_vec(&(v));
- let l = (tint_symbol + tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_ReadOnlyArgAndSE) {
- // Make sure that read-only args don't get hoisted (tex and samp)
- auto* src = R"(
-@group(1) @binding(1) var tex: texture_2d_array<u32>;
-@group(1) @binding(2) var samp: sampler;
-@group(1) @binding(2) var arr: binding_array<texture_2d<f32>, 3>;
-
-fn get_uv() -> vec2<f32> {
- return vec2<f32>(1.0, 2.0);
-}
-
-fn f() {
- let r = textureGather(1, tex, samp, get_uv(), 1);
- let p = textureGather(1, arr[0], samp, get_uv());
-}
-)";
-
- auto* expect = R"(
-@group(1) @binding(1) var tex : texture_2d_array<u32>;
-
-@group(1) @binding(2) var samp : sampler;
-
-@group(1) @binding(2) var arr : binding_array<texture_2d<f32>, 3>;
-
-fn get_uv() -> vec2<f32> {
- return vec2<f32>(1.0, 2.0);
-}
-
-fn f() {
- let tint_symbol : vec2<f32> = get_uv();
- let r = textureGather(1, tex, samp, tint_symbol, 1);
- let tint_symbol_1 : vec2<f32> = get_uv();
- let p = textureGather(1, arr[0], samp, tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_PtrArgAndSE) {
- // Make sure that read-only args don't get hoisted (tex and samp)
- auto* src = R"(
-
-var<private> b : i32 = 0;
-
-fn a(i : i32) -> i32 {
- b = 42;
- return 0;
-}
-
-fn g(i : ptr<private, i32>, j : i32) -> i32 {
- return *i;
-}
-
-fn f() {
- // a(0) should be hoisted, but not &b
- let r = g(&b, a(0));
- // r should be 42
-}
-)";
-
- auto* expect = R"(
-var<private> b : i32 = 0;
-
-fn a(i : i32) -> i32 {
- b = 42;
- return 0;
-}
-
-fn g(i : ptr<private, i32>, j : i32) -> i32 {
- return *(i);
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- let r = g(&(b), tint_symbol);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInit_VarPlusI32InitPlusVar) {
- auto* src = R"(
-fn f() {
- var b = 0;
- var c = 0;
- var d = 0;
- let r = ((b + i32(c)) + d);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_ArithPlusLogical) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- let r = a(0) + g(b(1) && b(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- var tint_symbol_1 = b(1);
- if (tint_symbol_1) {
- tint_symbol_1 = b(2);
- }
- let tint_symbol_2 : i32 = g(tint_symbol_1);
- let r = (tint_symbol + tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_LogicalPlusArith) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- let r = g(b(0) && b(1)) + a(2);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- var tint_symbol = b(0);
- if (tint_symbol) {
- tint_symbol = b(1);
- }
- let tint_symbol_1 : i32 = g(tint_symbol);
- let tint_symbol_2 : i32 = a(2);
- let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_ArithAndLogicalArgs) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : i32, j : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- let r = g(a(0), b(1) && b(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : i32, j : bool) -> i32 {
- return 0;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- var tint_symbol_1 = b(1);
- if (tint_symbol_1) {
- tint_symbol_1 = b(2);
- }
- let r = g(tint_symbol, tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_LogicalAndArithArgs) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool, j : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- let r = g(b(0) && b(1), a(2));
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn g(i : bool, j : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- var tint_symbol = b(0);
- if (tint_symbol) {
- tint_symbol = b(1);
- }
- let tint_symbol_1 : i32 = a(2);
- let r = g(tint_symbol, tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_Complex) {
- auto* src = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn c(i : bool) -> i32 {
- return 0;
-}
-
-fn g(i : bool, j : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- let r = a(0) + g(b(1) && b(a(2) + a(3)), a(4)) + a(5);
-}
-)";
-
- auto* expect = R"(
-fn a(i : i32) -> i32 {
- return 0;
-}
-
-fn b(i : i32) -> bool {
- return true;
-}
-
-fn c(i : bool) -> i32 {
- return 0;
-}
-
-fn g(i : bool, j : i32) -> i32 {
- return 0;
-}
-
-fn f() {
- let tint_symbol : i32 = a(0);
- var tint_symbol_1 = b(1);
- if (tint_symbol_1) {
- let tint_symbol_2 : i32 = a(2);
- let tint_symbol_3 : i32 = a(3);
- tint_symbol_1 = b((tint_symbol_2 + tint_symbol_3));
- }
- let tint_symbol_4 : i32 = a(4);
- let tint_symbol_5 : i32 = g(tint_symbol_1, tint_symbol_4);
- let tint_symbol_6 : i32 = a(5);
- let r = ((tint_symbol + tint_symbol_5) + tint_symbol_6);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, HandleParameter) {
- auto* src = R"(
-@group(0) @binding(0) var T : texture_2d<f32>;
-@group(0) @binding(1) var S : sampler;
-@group(0) @binding(2) var A : binding_array<texture_2d<f32>, 3>;
-
-var<private> P : vec2<f32>;
-fn side_effects() -> vec2<f32> {
- P += vec2(1.0);
- return P;
-}
-
-fn f(t : texture_2d<f32>, s : sampler, a: binding_array<texture_2d<f32>, 3>) {
- let p = textureSample(t, s, side_effects());
- let r = textureSample(a[0], s, side_effects());
-}
-
-fn m() {
- f(T, S, A);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var T : texture_2d<f32>;
-
-@group(0) @binding(1) var S : sampler;
-
-@group(0) @binding(2) var A : binding_array<texture_2d<f32>, 3>;
-
-var<private> P : vec2<f32>;
-
-fn side_effects() -> vec2<f32> {
- P += vec2(1.0);
- return P;
-}
-
-fn f(t : texture_2d<f32>, s : sampler, a : binding_array<texture_2d<f32>, 3>) {
- let tint_symbol : vec2<f32> = side_effects();
- let p = textureSample(t, s, tint_symbol);
- let tint_symbol_1 : vec2<f32> = side_effects();
- let r = textureSample(a[0], s, tint_symbol_1);
-}
-
-fn m() {
- f(T, S, A);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BuiltinReturnType) {
- auto* src = R"(
-fn X(a:vec2f, b:vec2f) {
-}
-
-fn Y() -> vec2f { return vec2f(); }
-
-fn f() {
- var v: vec2f;
- X(vec2(), v); // okay
- X(vec2(), Y()); // errors
-}
-)";
-
- auto* expect = R"(
-fn X(a : vec2f, b : vec2f) {
-}
-
-fn Y() -> vec2f {
- return vec2f();
-}
-
-fn f() {
- var v : vec2f;
- X(vec2(), v);
- let tint_symbol : vec2<f32> = vec2();
- let tint_symbol_1 : vec2<f32> = Y();
- X(tint_symbol, tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Bug1963) {
- auto* src = R"(
-fn X(a:vec2f, b:vec2f) {
-}
-
-fn Y() -> vec2f { return vec2f(); }
-
-fn f() {
- var v: vec2f;
- X(vec2(), v); // okay
- X(vec2(), Y()); // errors
-}
-)";
-
- auto* expect = R"(
-fn X(a : vec2f, b : vec2f) {
-}
-
-fn Y() -> vec2f {
- return vec2f();
-}
-
-fn f() {
- var v : vec2f;
- X(vec2(), v);
- let tint_symbol : vec2<f32> = vec2();
- let tint_symbol_1 : vec2<f32> = Y();
- X(tint_symbol, tint_symbol_1);
-}
-)";
-
- auto got = Run<PromoteSideEffectsToDecl>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc
deleted file mode 100644
index 1ee9333..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/continue_statement.h"
-#include "src/tint/lang/wgsl/ast/switch_statement.h"
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/loop_statement.h"
-#include "src/tint/lang/wgsl/sem/switch_statement.h"
-#include "src/tint/utils/containers/hashmap.h"
-#include "src/tint/utils/containers/hashset.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::RemoveContinueInSwitch);
-
-namespace tint::ast::transform {
-
-/// PIMPL state for the transform
-struct RemoveContinueInSwitch::State {
- /// Constructor
- /// @param program the source program
- explicit State(const Program& program) : src(program) {}
-
- /// Runs the transform
- /// @returns the new program or SkipTransform if the transform is not required
- ApplyResult Run() {
- // First collect all switch statements within loops that contain a continue statement.
- for (auto* node : src.ASTNodes().Objects()) {
- auto* cont = node->As<ast::ContinueStatement>();
- if (!cont) {
- continue;
- }
-
- // If first parent is not a switch within a loop, skip
- auto* switch_stmt = GetParentSwitchInLoop(sem, cont);
- if (!switch_stmt) {
- continue;
- }
-
- auto& info = switch_infos.GetOrAdd(switch_stmt, [&] {
- switch_stmts.Push(switch_stmt);
- auto* block = sem.Get(switch_stmt)->FindFirstParent<sem::LoopBlockStatement>();
- return SwitchInfo{/* loop_block */ block, /* continues */ Empty};
- });
- info.continues.Push(cont);
- }
-
- if (switch_stmts.IsEmpty()) {
- return SkipTransform;
- }
-
- // For each switch statement:
- // 1. Declare a 'tint_continue' var just before the parent loop, and reset it to false at
- // the top of the loop body.
- // 2. Replace 'continue' with 'tint_continue = true; break;'
- // 3. Insert 'if (tint_continue) { break; }' after switch, and all parent switches, except
- // for the parent-most, for which we insert 'if (tint_continue) { continue; }'
- for (auto* switch_stmt : switch_stmts) {
- const auto& info = switch_infos.Get(switch_stmt);
-
- auto var_name = loop_to_var.GetOrAdd(info->loop_block, [&] {
- // Create and insert 'var tint_continue : bool;' before loop
- auto var = b.Symbols().New("tint_continue");
- auto* decl = b.Decl(b.Var(var, b.ty.bool_()));
- auto ip = ast::transform::utils::GetInsertionPoint(
- ctx, info->loop_block->Parent()->Declaration());
- ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, decl);
-
- // Insert 'tint_continue = false' at top of loop body
- auto assign_false = b.Assign(var, false);
- ctx.InsertFront(info->loop_block->Declaration()->statements, assign_false);
-
- return var;
- });
-
- for (auto& c : info->continues) {
- // Replace 'continue;' with 'tint_continue = true; break;'
- ctx.Replace(c, b.Assign(b.Expr(var_name), true));
- ctx.InsertAfter(sem.Get(c)->Block()->Declaration()->statements, c, b.Break());
- }
-
- // Insert 'if (tint_continue) { break; }' after switch, and all parent switches,
- // except for the parent-most, for which we insert 'if (tint_continue) { continue; }'
- auto* curr_switch = switch_stmt;
- while (curr_switch) {
- auto* curr_switch_sem = sem.Get(curr_switch);
- auto* parent = curr_switch_sem->Parent()->Declaration();
- auto* next_switch = GetParentSwitchInLoop(sem, parent);
-
- if (switch_handles_continue.Add(curr_switch)) {
- const ast::IfStatement* if_stmt = nullptr;
- if (next_switch) {
- if_stmt = b.If(b.Expr(var_name), b.Block(b.Break()));
- } else {
- if_stmt = b.If(b.Expr(var_name), b.Block(b.Continue()));
- }
- ctx.InsertAfter(curr_switch_sem->Block()->Declaration()->statements,
- curr_switch, if_stmt);
- }
-
- curr_switch = next_switch;
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- private:
- /// The source program
- const Program& src;
- /// The target program builder
- ProgramBuilder b;
- /// The clone context
- program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
- /// Alias to src.sem
- const sem::Info& sem = src.Sem();
-
- // Vector of switch statements within a loop that contains at least one continue statement.
- Vector<const ast::SwitchStatement*, 4> switch_stmts;
-
- // Info for each switch statement within a loop that contains at least one continue statement.
- struct SwitchInfo {
- // Loop block containing this switch
- const sem::LoopBlockStatement* loop_block;
- // Continue statements within this switch
- Vector<const ast::ContinueStatement*, 4> continues;
- };
-
- // Map of switch statements to per-switch info for switch statements within a loop that contains
- // at least one continue statement.
- Hashmap<const ast::SwitchStatement*, SwitchInfo, 4> switch_infos;
-
- // Map of loop block statement to the single 'tint_continue' variable used to replace 'continue'
- // control flow.
- Hashmap<const sem::LoopBlockStatement*, Symbol, 4> loop_to_var;
-
- // Set used to avoid duplicating 'if (tint_continue) { break/continue; }' after each switch
- // within a loop.
- Hashset<const ast::SwitchStatement*, 4> switch_handles_continue;
-
- // If `stmt` is within a switch statement within a loop, returns a pointer to
- // that switch statement.
- static const ast::SwitchStatement* GetParentSwitchInLoop(const sem::Info& sem,
- const ast::Statement* stmt) {
- // Find whether first parent is a switch or a loop
- auto* sem_stmt = sem.Get(stmt);
- auto* sem_parent =
- sem_stmt->FindFirstParent<sem::SwitchStatement, sem::LoopBlockStatement>();
-
- if (!sem_parent) {
- return nullptr;
- }
- return sem_parent->Declaration()->As<ast::SwitchStatement>();
- }
-};
-
-RemoveContinueInSwitch::RemoveContinueInSwitch() = default;
-RemoveContinueInSwitch::~RemoveContinueInSwitch() = default;
-
-ast::transform::Transform::ApplyResult RemoveContinueInSwitch::Apply(
- const Program& src,
- const ast::transform::DataMap&,
- ast::transform::DataMap&) const {
- State state(src);
- return state.Run();
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h
deleted file mode 100644
index 6c62826..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// This transform replaces continue statements in switch cases with setting a
-/// bool variable, and checking if the variable is set after the switch to
-/// continue. It is necessary to work around various issues including:
-/// * FXC "error X3708: continue cannot be used in a switch". See crbug.com/tint/1080.
-/// * MSL and GLSL invalid code-gen. See crbug.com/tint/2039.
-class RemoveContinueInSwitch final
- : public Castable<RemoveContinueInSwitch, ast::transform::Transform> {
- public:
- /// Constructor
- RemoveContinueInSwitch();
-
- /// Destructor
- ~RemoveContinueInSwitch() override;
-
- /// @copydoc ast::transform::Transform::Apply
- ApplyResult Apply(const Program& program,
- const ast::transform::DataMap& inputs,
- ast::transform::DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
deleted file mode 100644
index 6f36aab..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
+++ /dev/null
@@ -1,770 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using RemoveContinueInSwitchTest = ast::transform::TransformTest;
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRun_True) {
- auto* src = R"(
-fn f() {
- var i = 0;
- loop {
- switch(i) {
- case 0: {
- continue;
- }
- default: {
- break;
- }
- }
- break;
- }
-}
-)";
-
- EXPECT_TRUE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunEmptyModule_False) {
- auto* src = "";
-
- EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunContinueNotInSwitch_False) {
- auto* src = R"(
-fn f() {
- var i = 0;
- loop {
- switch(i) {
- case 0: {
- break;
- }
- default: {
- break;
- }
- }
-
- if (true) {
- continue;
- }
- break;
- }
-}
-)";
-
- EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunContinueInLoopInSwitch_False) {
- auto* src = R"(
-fn f() {
- var i = 0;
- switch(i) {
- case 0: {
- loop {
- if (true) {
- continue;
- }
- break;
- }
- break;
- }
- default: {
- break;
- }
- }
-}
-)";
-
- EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, EmptyModule) {
- auto* src = "";
- auto* expect = src;
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, SingleContinue) {
- auto* src = R"(
-fn f() {
- var i = 0;
- loop {
- let marker1 = 0;
- switch(i) {
- case 0: {
- continue;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
- break;
-
- continuing {
- let marker3 = 0;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i = 0;
- var tint_continue : bool;
- loop {
- tint_continue = false;
- let marker1 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- break;
-
- continuing {
- let marker3 = 0;
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, MultipleContinues) {
- auto* src = R"(
-fn f() {
- var i = 0;
- loop {
- let marker1 = 0;
- switch(i) {
- case 0: {
- continue;
- }
- case 1: {
- continue;
- }
- case 2: {
- continue;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
- break;
-
- continuing {
- let marker3 = 0;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i = 0;
- var tint_continue : bool;
- loop {
- tint_continue = false;
- let marker1 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- }
- case 1: {
- tint_continue = true;
- break;
- }
- case 2: {
- tint_continue = true;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- break;
-
- continuing {
- let marker3 = 0;
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, MultipleSwitch) {
- auto* src = R"(
-fn f() {
- var i = 0;
- loop {
- let marker1 = 0;
- switch(i) {
- case 0: {
- continue;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
-
- let marker3 = 0;
- switch(i) {
- case 0: {
- continue;
- }
- default: {
- break;
- }
- }
- let marker4 = 0;
-
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i = 0;
- var tint_continue : bool;
- loop {
- tint_continue = false;
- let marker1 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- let marker3 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker4 = 0;
- break;
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchSwitch) {
- auto* src = R"(
-fn f() {
- var j = 0;
- for (var i = 0; i < 2; i += 2) {
- switch(i) {
- case 0: {
- switch(j) {
- case 0: {
- continue;
- }
- default: {
- }
- }
- }
- default: {
- }
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var j = 0;
- var tint_continue : bool;
- for(var i = 0; (i < 2); i += 2) {
- tint_continue = false;
- switch(i) {
- case 0: {
- switch(j) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue) {
- break;
- }
- }
- default: {
- }
- }
- if (tint_continue) {
- continue;
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopLoopSwitch) {
- auto* src = R"(
-fn f() {
- for (var i = 0; i < 2; i += 2) {
- for (var j = 0; j < 2; j += 2) {
- switch(i) {
- case 0: {
- continue;
- }
- default: {
- }
- }
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- for(var i = 0; (i < 2); i += 2) {
- var tint_continue : bool;
- for(var j = 0; (j < 2); j += 2) {
- tint_continue = false;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue) {
- continue;
- }
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchLoopSwitch) {
- auto* src = R"(
-fn f() {
- for (var i = 0; i < 2; i += 2) {
- switch(i) {
- case 0: {
- for (var j = 0; j < 2; j += 2) {
- switch(j) {
- case 0: {
- continue; // j loop
- }
- default: {
- }
- }
- }
- continue; // i loop
- }
- default: {
- }
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var tint_continue_1 : bool;
- for(var i = 0; (i < 2); i += 2) {
- tint_continue_1 = false;
- switch(i) {
- case 0: {
- var tint_continue : bool;
- for(var j = 0; (j < 2); j += 2) {
- tint_continue = false;
- switch(j) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue) {
- continue;
- }
- }
- tint_continue_1 = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue_1) {
- continue;
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchLoopSwitchSwitch) {
- auto* src = R"(
-fn f() {
- var k = 0;
- for (var i = 0; i < 2; i += 2) {
- switch(i) {
- case 0: {
- for (var j = 0; j < 2; j += 2) {
- switch(j) {
- case 0: {
- continue; // j loop
- }
- case 1: {
- switch (k) {
- case 0: {
- continue; // j loop
- }
- default: {
- }
- }
- }
- default: {
- }
- }
- }
- continue; // i loop
- }
- default: {
- }
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var k = 0;
- var tint_continue_1 : bool;
- for(var i = 0; (i < 2); i += 2) {
- tint_continue_1 = false;
- switch(i) {
- case 0: {
- var tint_continue : bool;
- for(var j = 0; (j < 2); j += 2) {
- tint_continue = false;
- switch(j) {
- case 0: {
- tint_continue = true;
- break;
- }
- case 1: {
- switch(k) {
- case 0: {
- tint_continue = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue) {
- break;
- }
- }
- default: {
- }
- }
- if (tint_continue) {
- continue;
- }
- }
- tint_continue_1 = true;
- break;
- }
- default: {
- }
- }
- if (tint_continue_1) {
- continue;
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ExtraScopes) {
- auto* src = R"(
-fn f() {
- var i = 0;
- var a = true;
- var b = true;
- var c = true;
- var d = true;
- loop {
- if (a) {
- if (b) {
- let marker1 = 0;
- switch(i) {
- case 0: {
- if (c) {
- if (d) {
- continue;
- }
- }
- break;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
- break;
- }
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i = 0;
- var a = true;
- var b = true;
- var c = true;
- var d = true;
- var tint_continue : bool;
- loop {
- tint_continue = false;
- if (a) {
- if (b) {
- let marker1 = 0;
- switch(i) {
- case 0: {
- if (c) {
- if (d) {
- tint_continue = true;
- break;
- }
- }
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- break;
- }
- }
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ForLoop) {
- auto* src = R"(
-fn f() {
- for (var i = 0; i < 4; i = i + 1) {
- let marker1 = 0;
- switch(i) {
- case 0: {
- continue;
- break;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var tint_continue : bool;
- for(var i = 0; (i < 4); i = (i + 1)) {
- tint_continue = false;
- let marker1 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- break;
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, While) {
- auto* src = R"(
-fn f() {
- var i = 0;
- while (i < 4) {
- let marker1 = 0;
- switch(i) {
- case 0: {
- continue;
- break;
- }
- default: {
- break;
- }
- }
- let marker2 = 0;
- break;
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i = 0;
- var tint_continue : bool;
- while((i < 4)) {
- tint_continue = false;
- let marker1 = 0;
- switch(i) {
- case 0: {
- tint_continue = true;
- break;
- break;
- }
- default: {
- break;
- }
- }
- if (tint_continue) {
- continue;
- }
- let marker2 = 0;
- break;
- }
-}
-)";
-
- ast::transform::DataMap data;
- auto got = Run<RemoveContinueInSwitch>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
deleted file mode 100644
index 2994e09..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
-
-#include <memory>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/evaluation_stage.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::RemovePhonies);
-
-namespace tint::ast::transform {
-namespace {
-
-using SinkSignature = std::vector<const core::type::Type*>;
-
-} // namespace
-
-RemovePhonies::RemovePhonies() = default;
-
-RemovePhonies::~RemovePhonies() = default;
-
-Transform::ApplyResult RemovePhonies::Apply(const Program& src, const DataMap&, DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- auto& sem = src.Sem();
-
- Hashmap<SinkSignature, Symbol, 8> sinks;
-
- bool made_changes = false;
- for (auto* node : src.ASTNodes().Objects()) {
- Switch(
- node,
- [&](const AssignmentStatement* stmt) {
- if (stmt->lhs->Is<PhonyExpression>()) {
- made_changes = true;
-
- std::vector<const Expression*> side_effects;
- if (!TraverseExpressions(stmt->rhs, [&](const CallExpression* expr) {
- // CallExpression may map to a function or builtin call
- // (both may have side-effects), or a value constructor or value
- // conversion (both do not have side effects).
- auto* call = sem.Get<sem::Call>(expr);
- if (!call) {
- // Semantic node must be a Materialize, in which case the
- // expression was creation-time (compile time), so could not
- // have side effects. Just skip.
- return TraverseAction::Skip;
- }
- if (call->Target()->IsAnyOf<sem::Function, sem::BuiltinFn>() &&
- call->HasSideEffects() &&
- call->Stage() != core::EvaluationStage::kNotEvaluated) {
- side_effects.push_back(expr);
- return TraverseAction::Skip;
- }
- return TraverseAction::Descend;
- })) {
- return;
- }
-
- if (side_effects.empty()) {
- // Phony assignment with no side effects.
- // Just remove it.
- RemoveStatement(ctx, stmt);
- return;
- }
-
- if (side_effects.size() == 1) {
- if (auto* call_expr = side_effects[0]->As<CallExpression>()) {
- // Phony assignment with single call side effect.
- auto* call = sem.Get(call_expr)->Unwrap()->As<sem::Call>();
- if (call->Target()->MustUse()) {
- // Replace phony assignment assignment to uniquely named let.
- ctx.Replace<Statement>(stmt, [&, call_expr] { //
- auto name = b.Symbols().New("tint_phony");
- auto* rhs = ctx.Clone(call_expr);
- return b.Decl(b.Let(name, rhs));
- });
- } else {
- // Replace phony assignment with call statement.
- ctx.Replace(stmt, [&, call_expr] { //
- return b.CallStmt(ctx.Clone(call_expr));
- });
- }
- return;
- }
- }
-
- // Phony assignment with multiple side effects.
- // Generate a call to a placeholder function with the side
- // effects as arguments.
- ctx.Replace(stmt, [&, side_effects] {
- SinkSignature sig;
- for (auto* arg : side_effects) {
- sig.push_back(sem.GetVal(arg)->Type()->UnwrapRef());
- }
- auto sink = sinks.GetOrAdd(sig, [&] {
- auto name = b.Symbols().New("phony_sink");
- tint::Vector<const Parameter*, 8> params;
- for (auto* ty : sig) {
- auto ast_ty = CreateASTTypeFor(ctx, ty);
- params.Push(b.Param("p" + std::to_string(params.Length()), ast_ty));
- }
- b.Func(name, params, b.ty.void_(), {});
- return name;
- });
- tint::Vector<const Expression*, 8> args;
- for (auto* arg : side_effects) {
- args.Push(ctx.Clone(arg));
- }
- return b.CallStmt(b.Call(sink, args));
- });
- }
- },
- [&](const CallStatement* stmt) {
- // Remove call statements to const value-returning functions.
- // TODO(crbug.com/tint/1637): Remove if `stmt->expr` has no side-effects.
- auto* sem_expr = sem.Get(stmt->expr);
- if ((sem_expr->ConstantValue() != nullptr) && !sem_expr->HasSideEffects()) {
- made_changes = true;
- ctx.Remove(sem.Get(stmt)->Block()->Declaration()->statements, stmt);
- }
- });
- }
-
- if (!made_changes) {
- return SkipTransform;
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies.h b/src/tint/lang/wgsl/ast/transform/remove_phonies.h
deleted file mode 100644
index 665dc7f..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// RemovePhonies is a Transform that removes all phony-assignment statements,
-/// while preserving function call expressions in the RHS of the assignment that
-/// may have side-effects. It also removes calls to builtins that return a constant value.
-/// @note RemovePhonies must be run after the PromoteSideEffectsToDecl transform, otherwise `f` in
-/// `_ = cond && f()` may get hoisted to a call statement without the short-circuiting conditional.
-class RemovePhonies final : public Castable<RemovePhonies, Transform> {
- public:
- /// Constructor
- RemovePhonies();
-
- /// Destructor
- ~RemovePhonies() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc
deleted file mode 100644
index dc1dbcd..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc
+++ /dev/null
@@ -1,525 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using RemovePhoniesTest = TransformTest;
-
-TEST_F(RemovePhoniesTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<RemovePhonies>(src));
-}
-
-TEST_F(RemovePhoniesTest, ShouldRunHasPhony) {
- auto* src = R"(
-fn f() {
- _ = 1;
-}
-)";
-
- EXPECT_TRUE(ShouldRun<RemovePhonies>(src));
-}
-
-TEST_F(RemovePhoniesTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, NoSideEffects) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn f() {
- var v : i32;
- _ = &v;
- _ = 1;
- _ = 1 + 2;
- _ = t;
- _ = u32(3.0);
- _ = f32(i32(4u));
- _ = vec2<f32>(5.0);
- _ = vec3<i32>(6, 7, 8);
- _ = mat2x2<f32>(9.0, 10.0, 11.0, 12.0);
- _ = atan2(1.0, 2.0);
- _ = clamp(1.0, 2.0, 3.0);
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn f() {
- var v : i32;
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffects) {
- auto* src = R"(
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn f() {
- _ = neg(1);
- _ = add(2, 3);
- _ = add(neg(4), neg(5));
- _ = u32(neg(6));
- _ = f32(add(7, 8));
- _ = vec2<f32>(f32(neg(9)));
- _ = vec3<i32>(1, neg(10), 3);
- _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-)";
-
- auto* expect = R"(
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn f() {
- neg(1);
- add(2, 3);
- add(neg(4), neg(5));
- neg(6);
- add(7, 8);
- neg(9);
- neg(10);
- add(11, 12);
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffectsMustUse) {
- auto* src = R"(
-@must_use
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn f() {
- let tint_phony = 42;
- _ = neg(1);
- _ = 100 + add(2, 3) + 200;
- _ = add(neg(4), neg(5)) + 6;
- _ = u32(neg(6));
- _ = f32(add(7, 8));
- _ = vec2<f32>(f32(neg(9)));
- _ = vec3<i32>(1, neg(10), 3);
- _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-)";
-
- auto* expect = R"(
-@must_use
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn f() {
- let tint_phony = 42;
- let tint_phony_1 = neg(1);
- let tint_phony_2 = add(2, 3);
- let tint_phony_3 = add(neg(4), neg(5));
- let tint_phony_4 = neg(6);
- let tint_phony_5 = add(7, 8);
- let tint_phony_6 = neg(9);
- let tint_phony_7 = neg(10);
- let tint_phony_8 = add(11, 12);
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffects_OutOfOrder) {
- auto* src = R"(
-fn f() {
- _ = neg(1);
- _ = add(2, 3);
- _ = add(neg(4), neg(5));
- _ = u32(neg(6));
- _ = f32(add(7, 8));
- _ = vec2<f32>(f32(neg(9)));
- _ = vec3<i32>(1, neg(10), 3);
- _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-)";
-
- auto* expect = R"(
-fn f() {
- neg(1);
- add(2, 3);
- add(neg(4), neg(5));
- neg(6);
- add(7, 8);
- neg(9);
- neg(10);
- add(11, 12);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, MultipleSideEffects) {
- auto* src = R"(
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
- return (a ^ b);
-}
-
-fn f() {
- _ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
- _ = add(9, neg(10)) + neg(11);
- _ = xor(12u, 13u) + xor(14u, 15u);
- _ = neg(16) / neg(17) + add(18, 19);
-}
-)";
-
- auto* expect = R"(
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
- return (a ^ b);
-}
-
-fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
-}
-
-fn phony_sink_1(p0 : i32, p1 : i32) {
-}
-
-fn phony_sink_2(p0 : u32, p1 : u32) {
-}
-
-fn f() {
- phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
- phony_sink_1(add(9, neg(10)), neg(11));
- phony_sink_2(xor(12u, 13u), xor(14u, 15u));
- phony_sink(neg(16), neg(17), add(18, 19));
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, MultipleSideEffects_OutOfOrder) {
- auto* src = R"(
-fn f() {
- _ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
- _ = add(9, neg(10)) + neg(11);
- _ = xor(12u, 13u) + xor(14u, 15u);
- _ = neg(16) / neg(17) + add(18, 19);
-}
-
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
- return (a ^ b);
-}
-)";
-
- auto* expect = R"(
-fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
-}
-
-fn phony_sink_1(p0 : i32, p1 : i32) {
-}
-
-fn phony_sink_2(p0 : u32, p1 : u32) {
-}
-
-fn f() {
- phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
- phony_sink_1(add(9, neg(10)), neg(11));
- phony_sink_2(xor(12u, 13u), xor(14u, 15u));
- phony_sink(neg(16), neg(17), add(18, 19));
-}
-
-fn neg(a : i32) -> i32 {
- return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
- return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
- return (a ^ b);
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ForLoop) {
- auto* src = R"(
-struct S {
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn x() -> i32 {
- return 0;
-}
-
-fn y() -> i32 {
- return 0;
-}
-
-fn z() -> i32 {
- return 0;
-}
-
-fn f() {
- for (_ = &s.arr; ;_ = &s.arr) {
- break;
- }
- for (_ = x(); ;_ = y() + z()) {
- break;
- }
-}
-)";
-
- auto* expect = R"(
-struct S {
- arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn x() -> i32 {
- return 0;
-}
-
-fn y() -> i32 {
- return 0;
-}
-
-fn z() -> i32 {
- return 0;
-}
-
-fn phony_sink(p0 : i32, p1 : i32) {
-}
-
-fn f() {
- for(; ; ) {
- break;
- }
- for(x(); ; phony_sink(y(), z())) {
- break;
- }
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ForLoop_OutOfOrder) {
- auto* src = R"(
-fn f() {
- for (_ = &s.arr; ;_ = &s.arr) {
- break;
- }
- for (_ = x(); ;_ = y() + z()) {
- break;
- }
-}
-
-fn x() -> i32 {
- return 0;
-}
-
-fn y() -> i32 {
- return 0;
-}
-
-fn z() -> i32 {
- return 0;
-}
-
-struct S {
- arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-)";
-
- auto* expect = R"(
-fn phony_sink(p0 : i32, p1 : i32) {
-}
-
-fn f() {
- for(; ; ) {
- break;
- }
- for(x(); ; phony_sink(y(), z())) {
- break;
- }
-}
-
-fn x() -> i32 {
- return 0;
-}
-
-fn y() -> i32 {
- return 0;
-}
-
-fn z() -> i32 {
- return 0;
-}
-
-struct S {
- arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ConstShortCircuit) {
- auto* src = R"(
-fn a(v : i32) -> i32 {
- return v;
-}
-
-fn b() {
- _ = false && (a(4294967295) < a(a(4294967295)));
-}
-)";
-
- auto* expect = R"(
-fn a(v : i32) -> i32 {
- return v;
-}
-
-fn b() {
-}
-)";
-
- auto got = Run<RemovePhonies>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/renamer.cc b/src/tint/lang/wgsl/ast/transform/renamer.cc
deleted file mode 100644
index a329488..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer.cc
+++ /dev/null
@@ -1,1421 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/renamer.h"
-
-#include <memory>
-#include <utility>
-
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/builtin_enum_expression.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/type_expression.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/unicode.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Renamer);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Renamer::Config);
-
-namespace tint::ast::transform {
-
-namespace {
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 358> kReservedKeywordsGLSL = {
- "abs",
- "acos",
- "acosh",
- "active",
- "all",
- "any",
- "asin",
- "asinh",
- "asm",
- "atan",
- "atanh",
- "atomicAdd",
- "atomicAnd",
- "atomicCompSwap",
- "atomicCounter",
- "atomicCounterDecrement",
- "atomicCounterIncrement",
- "atomicExchange",
- "atomicMax",
- "atomicMin",
- "atomicOr",
- "atomicXor",
- "atomic_uint",
- "attribute",
- "barrier",
- "bitCount",
- "bitfieldExtract",
- "bitfieldInsert",
- "bitfieldReverse",
- "bool",
- "break",
- "buffer",
- "bvec2",
- "bvec3",
- "bvec4",
- "case",
- "cast",
- "ceil",
- "centroid",
- "clamp",
- "class",
- "coherent",
- "common",
- "const",
- "continue",
- "cos",
- "cosh",
- "cross",
- "dFdx",
- "dFdy",
- "default",
- "degrees",
- "determinant",
- "discard",
- "distance",
- "dmat2",
- "dmat2x2",
- "dmat2x3",
- "dmat2x4",
- "dmat3",
- "dmat3x2",
- "dmat3x3",
- "dmat3x4",
- "dmat4",
- "dmat4x2",
- "dmat4x3",
- "dmat4x4",
- "do",
- "dot",
- "double",
- "dvec2",
- "dvec3",
- "dvec4",
- "else",
- "enum",
- "equal",
- "exp",
- "exp2",
- "extern",
- "external",
- "faceforward",
- "false",
- "filter",
- "findLSB",
- "findMSB",
- "fixed",
- "flat",
- "float",
- "floatBitsToInt",
- "floatBitsToUint",
- "floor",
- "for",
- "fract",
- "frexp",
- "fvec2",
- "fvec3",
- "fvec4",
- "fwidth",
- "gl_BaseInstance",
- "gl_BaseVertex",
- "gl_ClipDistance",
- "gl_DepthRangeParameters",
- "gl_DrawID",
- "gl_FragCoord",
- "gl_FragDepth",
- "gl_FrontFacing",
- "gl_GlobalInvocationID",
- "gl_InstanceID",
- "gl_LocalInvocationID",
- "gl_LocalInvocationIndex",
- "gl_NumSamples",
- "gl_NumWorkGroups",
- "gl_PerVertex",
- "gl_PointCoord",
- "gl_PointSize",
- "gl_Position",
- "gl_PrimitiveID",
- "gl_SampleID",
- "gl_SampleMask",
- "gl_SampleMaskIn",
- "gl_SamplePosition",
- "gl_VertexID",
- "gl_WorkGroupID",
- "gl_WorkGroupSize",
- "goto",
- "greaterThan",
- "greaterThanEqual",
- "groupMemoryBarrier",
- "half",
- "highp",
- "hvec2",
- "hvec3",
- "hvec4",
- "if",
- "iimage1D",
- "iimage1DArray",
- "iimage2D",
- "iimage2DArray",
- "iimage2DMS",
- "iimage2DMSArray",
- "iimage2DRect",
- "iimage3D",
- "iimageBuffer",
- "iimageCube",
- "iimageCubeArray",
- "image1D",
- "image1DArray",
- "image2D",
- "image2DArray",
- "image2DMS",
- "image2DMSArray",
- "image2DRect",
- "image3D",
- "imageBuffer",
- "imageCube",
- "imageCubeArray",
- "imageLoad",
- "imageSize",
- "imageStore",
- "imulExtended",
- "in",
- "inline",
- "inout",
- "input",
- "int",
- "intBitsToFloat",
- "interface",
- "invariant",
- "inverse",
- "inversesqrt",
- "isampler1D",
- "isampler1DArray",
- "isampler2D",
- "isampler2DArray",
- "isampler2DMS",
- "isampler2DMSArray",
- "isampler2DRect",
- "isampler3D",
- "isamplerBuffer",
- "isamplerCube",
- "isamplerCubeArray",
- "isinf",
- "isnan",
- "ivec2",
- "ivec3",
- "ivec4",
- "layout",
- "ldexp",
- "length",
- "lessThan",
- "lessThanEqual",
- "log",
- "log2",
- "long",
- "lowp",
- "main",
- "mat2",
- "mat2x2",
- "mat2x3",
- "mat2x4",
- "mat3",
- "mat3x2",
- "mat3x3",
- "mat3x4",
- "mat4",
- "mat4x2",
- "mat4x3",
- "mat4x4",
- "matrixCompMult",
- "max",
- "mediump",
- "memoryBarrier",
- "memoryBarrierAtomicCounter",
- "memoryBarrierBuffer",
- "memoryBarrierImage",
- "memoryBarrierShared",
- "min",
- "mix",
- "mod",
- "modf",
- "namespace",
- "noinline",
- "non_coherent",
- "noncoherent",
- "noperspective",
- "normalize",
- "not",
- "notEqual",
- "out",
- "outerProduct",
- "output",
- "packHalf2x16",
- "packSnorm2x16",
- "packSnorm4x8",
- "packUnorm2x16",
- "packUnorm4x8",
- "partition",
- "patch",
- "pow",
- "precise",
- "precision",
- "public",
- "radians",
- "readonly",
- "reflect",
- "refract",
- "resource",
- "restrict",
- "return",
- "round",
- "roundEven",
- "sample",
- "sampler1D",
- "sampler1DArray",
- "sampler1DArrayShadow",
- "sampler1DShadow",
- "sampler2D",
- "sampler2DArray",
- "sampler2DArrayShadow",
- "sampler2DMS",
- "sampler2DMSArray",
- "sampler2DRect",
- "sampler2DRectShadow",
- "sampler2DShadow",
- "sampler3D",
- "sampler3DRect",
- "samplerBuffer",
- "samplerCube",
- "samplerCubeArray",
- "samplerCubeArrayShadow",
- "samplerCubeShadow",
- "shared",
- "short",
- "sign",
- "sin",
- "sinh",
- "sizeof",
- "smooth",
- "smoothstep",
- "sqrt",
- "static",
- "step",
- "struct",
- "subroutine",
- "superp",
- "switch",
- "tan",
- "tanh",
- "template",
- "texelFetch",
- "texelFetchOffset",
- "texture",
- "textureGather",
- "textureGatherOffset",
- "textureGrad",
- "textureGradOffset",
- "textureLod",
- "textureLodOffset",
- "textureOffset",
- "textureProj",
- "textureProjGrad",
- "textureProjGradOffset",
- "textureProjLod",
- "textureProjLodOffset",
- "textureProjOffset",
- "textureSize",
- "this",
- "transpose",
- "true",
- "trunc",
- "typedef",
- "uaddCarry",
- "uimage1D",
- "uimage1DArray",
- "uimage2D",
- "uimage2DArray",
- "uimage2DMS",
- "uimage2DMSArray",
- "uimage2DRect",
- "uimage3D",
- "uimageBuffer",
- "uimageCube",
- "uimageCubeArray",
- "uint",
- "uintBitsToFloat",
- "umulExtended",
- "uniform",
- "union",
- "unpackHalf2x16",
- "unpackSnorm2x16",
- "unpackSnorm4x8",
- "unpackUnorm2x16",
- "unpackUnorm4x8",
- "unsigned",
- "usampler1D",
- "usampler1DArray",
- "usampler2D",
- "usampler2DArray",
- "usampler2DMS",
- "usampler2DMSArray",
- "usampler2DRect",
- "usampler3D",
- "usamplerBuffer",
- "usamplerCube",
- "usamplerCubeArray",
- "using",
- "usubBorrow",
- "uvec2",
- "uvec3",
- "uvec4",
- "varying",
- "vec2",
- "vec3",
- "vec4",
- "void",
- "volatile",
- "while",
- "writeonly",
-};
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 570> kReservedKeywordsHLSL = {
- "AddressU",
- "AddressV",
- "AddressW",
- "AllMemoryBarrier",
- "AllMemoryBarrierWithGroupSync",
- "AppendStructuredBuffer",
- "BINORMAL",
- "BLENDINDICES",
- "BLENDWEIGHT",
- "BlendState",
- "BorderColor",
- "Buffer",
- "ByteAddressBuffer",
- "COLOR",
- "CheckAccessFullyMapped",
- "ComparisonFunc",
- "CompileShader",
- "ComputeShader",
- "ConsumeStructuredBuffer",
- "D3DCOLORtoUBYTE4",
- "DEPTH",
- "DepthStencilState",
- "DepthStencilView",
- "DeviceMemoryBarrier",
- "DeviceMemroyBarrierWithGroupSync",
- "DomainShader",
- "EvaluateAttributeAtCentroid",
- "EvaluateAttributeAtSample",
- "EvaluateAttributeSnapped",
- "FOG",
- "Filter",
- "GeometryShader",
- "GetRenderTargetSampleCount",
- "GetRenderTargetSamplePosition",
- "GroupMemoryBarrier",
- "GroupMemroyBarrierWithGroupSync",
- "Hullshader",
- "InputPatch",
- "InterlockedAdd",
- "InterlockedAnd",
- "InterlockedCompareExchange",
- "InterlockedCompareStore",
- "InterlockedExchange",
- "InterlockedMax",
- "InterlockedMin",
- "InterlockedOr",
- "InterlockedXor",
- "LineStream",
- "MaxAnisotropy",
- "MaxLOD",
- "MinLOD",
- "MipLODBias",
- "NORMAL",
- "NULL",
- "Normal",
- "OutputPatch",
- "POSITION",
- "POSITIONT",
- "PSIZE",
- "PixelShader",
- "PointStream",
- "Process2DQuadTessFactorsAvg",
- "Process2DQuadTessFactorsMax",
- "Process2DQuadTessFactorsMin",
- "ProcessIsolineTessFactors",
- "ProcessQuadTessFactorsAvg",
- "ProcessQuadTessFactorsMax",
- "ProcessQuadTessFactorsMin",
- "ProcessTriTessFactorsAvg",
- "ProcessTriTessFactorsMax",
- "ProcessTriTessFactorsMin",
- "RWBuffer",
- "RWByteAddressBuffer",
- "RWStructuredBuffer",
- "RWTexture1D",
- "RWTexture1DArray",
- "RWTexture2D",
- "RWTexture2DArray",
- "RWTexture3D",
- "RasterizerState",
- "RenderTargetView",
- "SV_ClipDistance",
- "SV_Coverage",
- "SV_CullDistance",
- "SV_Depth",
- "SV_DepthGreaterEqual",
- "SV_DepthLessEqual",
- "SV_DispatchThreadID",
- "SV_DomainLocation",
- "SV_GSInstanceID",
- "SV_GroupID",
- "SV_GroupIndex",
- "SV_GroupThreadID",
- "SV_InnerCoverage",
- "SV_InsideTessFactor",
- "SV_InstanceID",
- "SV_IsFrontFace",
- "SV_OutputControlPointID",
- "SV_Position",
- "SV_PrimitiveID",
- "SV_RenderTargetArrayIndex",
- "SV_SampleIndex",
- "SV_StencilRef",
- "SV_Target",
- "SV_TessFactor",
- "SV_VertexArrayIndex",
- "SV_VertexID",
- "Sampler",
- "Sampler1D",
- "Sampler2D",
- "Sampler3D",
- "SamplerCUBE",
- "SamplerComparisonState",
- "SamplerState",
- "StructuredBuffer",
- "TANGENT",
- "TESSFACTOR",
- "TEXCOORD",
- "Texcoord",
- "Texture",
- "Texture1D",
- "Texture1DArray",
- "Texture2D",
- "Texture2DArray",
- "Texture2DMS",
- "Texture2DMSArray",
- "Texture3D",
- "TextureCube",
- "TextureCubeArray",
- "TriangleStream",
- "VFACE",
- "VPOS",
- "VertexShader",
- "abort",
- "allow_uav_condition",
- "asdouble",
- "asfloat",
- "asint",
- "asm",
- "asm_fragment",
- "asuint",
- "auto",
- "bool",
- "bool1",
- "bool1x1",
- "bool1x2",
- "bool1x3",
- "bool1x4",
- "bool2",
- "bool2x1",
- "bool2x2",
- "bool2x3",
- "bool2x4",
- "bool3",
- "bool3x1",
- "bool3x2",
- "bool3x3",
- "bool3x4",
- "bool4",
- "bool4x1",
- "bool4x2",
- "bool4x3",
- "bool4x4",
- "branch",
- "break",
- "call",
- "case",
- "catch",
- "cbuffer",
- "centroid",
- "char",
- "class",
- "clip",
- "column_major",
- "compile",
- "compile_fragment",
- "const",
- "const_cast",
- "continue",
- "countbits",
- "ddx",
- "ddx_coarse",
- "ddx_fine",
- "ddy",
- "ddy_coarse",
- "ddy_fine",
- "default",
- "degrees",
- "delete",
- "discard",
- "do",
- "double",
- "double1",
- "double1x1",
- "double1x2",
- "double1x3",
- "double1x4",
- "double2",
- "double2x1",
- "double2x2",
- "double2x3",
- "double2x4",
- "double3",
- "double3x1",
- "double3x2",
- "double3x3",
- "double3x4",
- "double4",
- "double4x1",
- "double4x2",
- "double4x3",
- "double4x4",
- "dst",
- "dword",
- "dword1",
- "dword1x1",
- "dword1x2",
- "dword1x3",
- "dword1x4",
- "dword2",
- "dword2x1",
- "dword2x2",
- "dword2x3",
- "dword2x4",
- "dword3",
- "dword3x1",
- "dword3x2",
- "dword3x3",
- "dword3x4",
- "dword4",
- "dword4x1",
- "dword4x2",
- "dword4x3",
- "dword4x4",
- "dynamic_cast",
- "else",
- "enum",
- "errorf",
- "explicit",
- "export",
- "extern",
- "f16to32",
- "f32tof16",
- "false",
- "fastopt",
- "firstbithigh",
- "firstbitlow",
- "flatten",
- "float",
- "float1",
- "float1x1",
- "float1x2",
- "float1x3",
- "float1x4",
- "float2",
- "float2x1",
- "float2x2",
- "float2x3",
- "float2x4",
- "float3",
- "float3x1",
- "float3x2",
- "float3x3",
- "float3x4",
- "float4",
- "float4x1",
- "float4x2",
- "float4x3",
- "float4x4",
- "fmod",
- "for",
- "forcecase",
- "frac",
- "friend",
- "fxgroup",
- "goto",
- "groupshared",
- "half",
- "half1",
- "half1x1",
- "half1x2",
- "half1x3",
- "half1x4",
- "half2",
- "half2x1",
- "half2x2",
- "half2x3",
- "half2x4",
- "half3",
- "half3x1",
- "half3x2",
- "half3x3",
- "half3x4",
- "half4",
- "half4x1",
- "half4x2",
- "half4x3",
- "half4x4",
- "if",
- "in",
- "inline",
- "inout",
- "int",
- "int1",
- "int1x1",
- "int1x2",
- "int1x3",
- "int1x4",
- "int2",
- "int2x1",
- "int2x2",
- "int2x3",
- "int2x4",
- "int3",
- "int3x1",
- "int3x2",
- "int3x3",
- "int3x4",
- "int4",
- "int4x1",
- "int4x2",
- "int4x3",
- "int4x4",
- "interface",
- "isfinite",
- "isinf",
- "isnan",
- "lerp",
- "line",
- "lineadj",
- "linear",
- "lit",
- "log10",
- "long",
- "loop",
- "mad",
- "matrix",
- "min10float",
- "min10float1",
- "min10float1x1",
- "min10float1x2",
- "min10float1x3",
- "min10float1x4",
- "min10float2",
- "min10float2x1",
- "min10float2x2",
- "min10float2x3",
- "min10float2x4",
- "min10float3",
- "min10float3x1",
- "min10float3x2",
- "min10float3x3",
- "min10float3x4",
- "min10float4",
- "min10float4x1",
- "min10float4x2",
- "min10float4x3",
- "min10float4x4",
- "min12int",
- "min12int1",
- "min12int1x1",
- "min12int1x2",
- "min12int1x3",
- "min12int1x4",
- "min12int2",
- "min12int2x1",
- "min12int2x2",
- "min12int2x3",
- "min12int2x4",
- "min12int3",
- "min12int3x1",
- "min12int3x2",
- "min12int3x3",
- "min12int3x4",
- "min12int4",
- "min12int4x1",
- "min12int4x2",
- "min12int4x3",
- "min12int4x4",
- "min16float",
- "min16float1",
- "min16float1x1",
- "min16float1x2",
- "min16float1x3",
- "min16float1x4",
- "min16float2",
- "min16float2x1",
- "min16float2x2",
- "min16float2x3",
- "min16float2x4",
- "min16float3",
- "min16float3x1",
- "min16float3x2",
- "min16float3x3",
- "min16float3x4",
- "min16float4",
- "min16float4x1",
- "min16float4x2",
- "min16float4x3",
- "min16float4x4",
- "min16int",
- "min16int1",
- "min16int1x1",
- "min16int1x2",
- "min16int1x3",
- "min16int1x4",
- "min16int2",
- "min16int2x1",
- "min16int2x2",
- "min16int2x3",
- "min16int2x4",
- "min16int3",
- "min16int3x1",
- "min16int3x2",
- "min16int3x3",
- "min16int3x4",
- "min16int4",
- "min16int4x1",
- "min16int4x2",
- "min16int4x3",
- "min16int4x4",
- "min16uint",
- "min16uint1",
- "min16uint1x1",
- "min16uint1x2",
- "min16uint1x3",
- "min16uint1x4",
- "min16uint2",
- "min16uint2x1",
- "min16uint2x2",
- "min16uint2x3",
- "min16uint2x4",
- "min16uint3",
- "min16uint3x1",
- "min16uint3x2",
- "min16uint3x3",
- "min16uint3x4",
- "min16uint4",
- "min16uint4x1",
- "min16uint4x2",
- "min16uint4x3",
- "min16uint4x4",
- "msad4",
- "mul",
- "mutable",
- "namespace",
- "new",
- "nointerpolation",
- "noise",
- "noperspective",
- "numthreads",
- "operator",
- "out",
- "packoffset",
- "pass",
- "pixelfragment",
- "pixelshader",
- "point",
- "precise",
- "printf",
- "private",
- "protected",
- "public",
- "radians",
- "rcp",
- "refract",
- "register",
- "reinterpret_cast",
- "return",
- "row_major",
- "rsqrt",
- "sample",
- "sampler",
- "sampler1D",
- "sampler2D",
- "sampler3D",
- "samplerCUBE",
- "sampler_state",
- "saturate",
- "shared",
- "short",
- "signed",
- "sincos",
- "sizeof",
- "snorm",
- "stateblock",
- "stateblock_state",
- "static",
- "static_cast",
- "string",
- "struct",
- "switch",
- "tbuffer",
- "technique",
- "technique10",
- "technique11",
- "template",
- "tex1D",
- "tex1Dbias",
- "tex1Dgrad",
- "tex1Dlod",
- "tex1Dproj",
- "tex2D",
- "tex2Dbias",
- "tex2Dgrad",
- "tex2Dlod",
- "tex2Dproj",
- "tex3D",
- "tex3Dbias",
- "tex3Dgrad",
- "tex3Dlod",
- "tex3Dproj",
- "texCUBE",
- "texCUBEbias",
- "texCUBEgrad",
- "texCUBElod",
- "texCUBEproj",
- "texture",
- "texture1D",
- "texture1DArray",
- "texture2D",
- "texture2DArray",
- "texture2DMS",
- "texture2DMSArray",
- "texture3D",
- "textureCube",
- "textureCubeArray",
- "this",
- "throw",
- "transpose",
- "triangle",
- "triangleadj",
- "true",
- "try",
- "typedef",
- "typename",
- "uint",
- "uint1",
- "uint1x1",
- "uint1x2",
- "uint1x3",
- "uint1x4",
- "uint2",
- "uint2x1",
- "uint2x2",
- "uint2x3",
- "uint2x4",
- "uint3",
- "uint3x1",
- "uint3x2",
- "uint3x3",
- "uint3x4",
- "uint4",
- "uint4x1",
- "uint4x2",
- "uint4x3",
- "uint4x4",
- "uniform",
- "union",
- "unorm",
- "unroll",
- "unsigned",
- "using",
- "vector",
- "vertexfragment",
- "vertexshader",
- "virtual",
- "void",
- "volatile",
- "while",
-};
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 270> kReservedKeywordsMSL = {
- "HUGE_VALF",
- "HUGE_VALH",
- "INFINITY",
- "MAXFLOAT",
- "MAXHALF",
- "M_1_PI_F",
- "M_1_PI_H",
- "M_2_PI_F",
- "M_2_PI_H",
- "M_2_SQRTPI_F",
- "M_2_SQRTPI_H",
- "M_E_F",
- "M_E_H",
- "M_LN10_F",
- "M_LN10_H",
- "M_LN2_F",
- "M_LN2_H",
- "M_LOG10E_F",
- "M_LOG10E_H",
- "M_LOG2E_F",
- "M_LOG2E_H",
- "M_PI_2_F",
- "M_PI_2_H",
- "M_PI_4_F",
- "M_PI_4_H",
- "M_PI_F",
- "M_PI_H",
- "M_SQRT1_2_F",
- "M_SQRT1_2_H",
- "M_SQRT2_F",
- "M_SQRT2_H",
- "NAN",
- "access",
- "alignas",
- "alignof",
- "and",
- "and_eq",
- "array",
- "array_ref",
- "as_type",
- "asm",
- "atomic",
- "atomic_bool",
- "atomic_int",
- "atomic_uint",
- "auto",
- "bitand",
- "bitor",
- "bool",
- "bool2",
- "bool3",
- "bool4",
- "break",
- "buffer",
- "case",
- "catch",
- "char",
- "char16_t",
- "char2",
- "char3",
- "char32_t",
- "char4",
- "class",
- "compl",
- "const",
- "const_cast",
- "const_reference",
- "constant",
- "constexpr",
- "continue",
- "decltype",
- "default",
- "delete",
- "depth2d",
- "depth2d_array",
- "depth2d_ms",
- "depth2d_ms_array",
- "depthcube",
- "depthcube_array",
- "device",
- "discard_fragment",
- "do",
- "double",
- "dynamic_cast",
- "else",
- "enum",
- "explicit",
- "extern",
- "false",
- "final",
- "float",
- "float2",
- "float2x2",
- "float2x3",
- "float2x4",
- "float3",
- "float3x2",
- "float3x3",
- "float3x4",
- "float4",
- "float4x2",
- "float4x3",
- "float4x4",
- "for",
- "fragment",
- "friend",
- "goto",
- "half",
- "half2",
- "half2x2",
- "half2x3",
- "half2x4",
- "half3",
- "half3x2",
- "half3x3",
- "half3x4",
- "half4",
- "half4x2",
- "half4x3",
- "half4x4",
- "if",
- "imageblock",
- "infinity",
- "inline",
- "int",
- "int16_t",
- "int2",
- "int3",
- "int32_t",
- "int4",
- "int64_t",
- "int8_t",
- "kernel",
- "long",
- "long2",
- "long3",
- "long4",
- "main",
- "matrix",
- "metal",
- "mutable",
- "namespace",
- "new",
- "noexcept",
- "not",
- "not_eq",
- "nullptr",
- "operator",
- "or",
- "or_eq",
- "override",
- "packed_bool2",
- "packed_bool3",
- "packed_bool4",
- "packed_char2",
- "packed_char3",
- "packed_char4",
- "packed_float2",
- "packed_float3",
- "packed_float4",
- "packed_half2",
- "packed_half3",
- "packed_half4",
- "packed_int2",
- "packed_int3",
- "packed_int4",
- "packed_short2",
- "packed_short3",
- "packed_short4",
- "packed_uchar2",
- "packed_uchar3",
- "packed_uchar4",
- "packed_uint2",
- "packed_uint3",
- "packed_uint4",
- "packed_ushort2",
- "packed_ushort3",
- "packed_ushort4",
- "patch_control_point",
- "private",
- "protected",
- "ptrdiff_t",
- "public",
- "r16snorm",
- "r16unorm",
- "r8unorm",
- "reference",
- "register",
- "reinterpret_cast",
- "return",
- "rg11b10f",
- "rg16snorm",
- "rg16unorm",
- "rg8snorm",
- "rg8unorm",
- "rgb10a2",
- "rgb9e5",
- "rgba16snorm",
- "rgba16unorm",
- "rgba8snorm",
- "rgba8unorm",
- "sampler",
- "short",
- "short2",
- "short3",
- "short4",
- "signed",
- "size_t",
- "sizeof",
- "srgba8unorm",
- "static",
- "static_assert",
- "static_cast",
- "struct",
- "switch",
- "template",
- "texture",
- "texture1d",
- "texture1d_array",
- "texture2d",
- "texture2d_array",
- "texture2d_ms",
- "texture2d_ms_array",
- "texture3d",
- "texture_buffer",
- "texturecube",
- "texturecube_array",
- "this",
- "thread",
- "thread_local",
- "threadgroup",
- "threadgroup_imageblock",
- "throw",
- "true",
- "try",
- "typedef",
- "typeid",
- "typename",
- "uchar",
- "uchar2",
- "uchar3",
- "uchar4",
- "uint",
- "uint16_t",
- "uint2",
- "uint3",
- "uint32_t",
- "uint4",
- "uint64_t",
- "uint8_t",
- "ulong2",
- "ulong3",
- "ulong4",
- "uniform",
- "union",
- "unsigned",
- "ushort",
- "ushort2",
- "ushort3",
- "ushort4",
- "using",
- "vec",
- "vertex",
- "virtual",
- "void",
- "volatile",
- "wchar_t",
- "while",
- "xor",
- "xor_eq",
-};
-
-} // namespace
-
-Renamer::Config::Config() = default;
-
-Renamer::Config::Config(Target t) : target(t) {}
-
-Renamer::Config::Config(Target t, Remappings&& remappings)
- : target(t), requested_names(std::move(remappings)) {}
-
-Renamer::Config::Config(const Config&) = default;
-
-Renamer::Config::~Config() = default;
-
-Renamer::Renamer() = default;
-Renamer::~Renamer() = default;
-
-Transform::ApplyResult Renamer::Apply(const Program& src, const DataMap& inputs, DataMap&) const {
- Hashset<Symbol, 16> global_decls;
- for (auto* decl : src.AST().TypeDecls()) {
- global_decls.Add(decl->name->symbol);
- }
-
- // Identifiers that need to keep their symbols preserved.
- Hashset<const Identifier*, 16> preserved_identifiers;
-
- for (auto* node : src.ASTNodes().Objects()) {
- auto preserve_if_builtin_type = [&](const Identifier* ident) {
- if (!global_decls.Contains(ident->symbol)) {
- preserved_identifiers.Add(ident);
- }
- };
-
- Switch(
- node,
- [&](const MemberAccessorExpression* accessor) {
- auto* sem = src.Sem().Get(accessor)->Unwrap();
- if (sem->Is<sem::Swizzle>()) {
- preserved_identifiers.Add(accessor->member);
- } else if (auto* str_expr = src.Sem().GetVal(accessor->object)) {
- if (auto* ty = str_expr->Type()->UnwrapPtrOrRef()->As<core::type::Struct>()) {
- if (!ty->Is<sem::Struct>()) { // Builtin structure
- preserved_identifiers.Add(accessor->member);
- }
- }
- }
- },
- [&](const DiagnosticAttribute* diagnostic) {
- if (auto* category = diagnostic->control.rule_name->category) {
- preserved_identifiers.Add(category);
- }
- preserved_identifiers.Add(diagnostic->control.rule_name->name);
- },
- [&](const DiagnosticDirective* diagnostic) {
- if (auto* category = diagnostic->control.rule_name->category) {
- preserved_identifiers.Add(category);
- }
- preserved_identifiers.Add(diagnostic->control.rule_name->name);
- },
- [&](const IdentifierExpression* expr) {
- Switch(
- src.Sem().Get(expr), //
- [&](const sem::BuiltinEnumExpressionBase*) {
- preserved_identifiers.Add(expr->identifier);
- },
- [&](const sem::TypeExpression*) {
- preserve_if_builtin_type(expr->identifier);
- });
- },
- [&](const CallExpression* call) {
- Switch(
- src.Sem().Get(call)->UnwrapMaterialize()->As<sem::Call>()->Target(),
- [&](const sem::BuiltinFn*) {
- preserved_identifiers.Add(call->target->identifier);
- },
- [&](const sem::ValueConversion*) {
- preserve_if_builtin_type(call->target->identifier);
- },
- [&](const sem::ValueConstructor*) {
- preserve_if_builtin_type(call->target->identifier);
- });
- });
- }
-
- Target target = Target::kAll;
- const Remappings* requested_names = nullptr;
-
- if (auto* cfg = inputs.Get<Config>()) {
- target = cfg->target;
- requested_names = &(cfg->requested_names);
- }
-
- // Returns true if the symbol should be renamed based on the input configuration settings.
- auto should_rename = [&](Symbol symbol) {
- if (target == Target::kAll) {
- return true;
- }
- if (requested_names->count(symbol.Name())) {
- return true;
- }
- auto name = symbol.Name();
- if (!tint::utf8::IsASCII(name)) {
- // name is non-ascii. All of the backend keywords are ascii, so rename.
- return true;
- }
- switch (target) {
- case Target::kGlslKeywords:
- return std::binary_search(kReservedKeywordsGLSL.cbegin(),
- kReservedKeywordsGLSL.cend(), name) ||
- name.compare(0, 3, "gl_") == 0 || name.find("__") != std::string::npos;
- case Target::kHlslKeywords:
- return std::binary_search(kReservedKeywordsHLSL.cbegin(),
- kReservedKeywordsHLSL.cend(), name);
- case Target::kMslKeywords:
- return std::binary_search(kReservedKeywordsMSL.cbegin(),
- kReservedKeywordsMSL.cend(), name);
- default:
- break;
- }
- return true;
- };
-
- Hashmap<Symbol, Symbol, 32> remappings;
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ false};
-
- ctx.ReplaceAll([&](const Identifier* ident) -> const Identifier* {
- const auto symbol = ident->symbol;
- if (preserved_identifiers.Contains(ident) || !should_rename(symbol)) {
- return nullptr; // Preserve symbol
- }
-
- // Create a replacement for this symbol, if we haven't already.
- auto replacement = remappings.GetOrAdd(symbol, [&] {
- if (requested_names) {
- auto iter = requested_names->find(symbol.Name());
- if (iter != requested_names->end()) {
- // Use the explicitly given name for renaming this symbol
- // if the extra is given in the config.
- return b.Symbols().New(iter->second);
- }
- }
- return b.Symbols().New();
- });
-
- // Reconstruct the identifier
- if (auto* tmpl_ident = ident->As<TemplatedIdentifier>()) {
- auto args = ctx.Clone(tmpl_ident->arguments);
- return ctx.dst->create<TemplatedIdentifier>(ctx.Clone(ident->source), replacement,
- std::move(args), tint::Empty);
- }
- return ctx.dst->create<Identifier>(ctx.Clone(ident->source), replacement);
- });
-
- ctx.Clone();
-
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/renamer.h b/src/tint/lang/wgsl/ast/transform/renamer.h
deleted file mode 100644
index 8c1ff26..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_RENAMER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_RENAMER_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Renamer is a Transform that renames all the symbols in a program.
-class Renamer final : public Castable<Renamer, Transform> {
- public:
- /// Remappings is a map of old symbol name to new symbol name
- using Remappings = std::unordered_map<std::string, std::string>;
-
- /// Target is an enumerator of rename targets that can be used
- enum class Target {
- /// Rename every symbol.
- kAll,
- /// Only rename symbols that are reserved keywords in GLSL.
- kGlslKeywords,
- /// Only rename symbols that are reserved keywords in HLSL.
- kHlslKeywords,
- /// Only rename symbols that are reserved keywords in MSL.
- kMslKeywords,
- };
-
- /// Optional configuration options for the transform.
- /// If omitted, then the renamer will use Target::kAll.
- struct Config final : public Castable<Config, transform::Data> {
- /// Constructor
- Config();
-
- /// Constructor
- /// @param tgt the targets to rename
- /// renamed
- explicit Config(Target tgt);
-
- /// Constructor
- /// @param tgt the targets to rename
- /// @param remappings requested old to new name map
- Config(Target tgt, Remappings&& remappings);
-
- /// Copy constructor
- Config(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// The targets to rename
- Target target = Target::kAll;
-
- /// Requested renaming rules
- Remappings requested_names = {};
-
- /// Reflection for this class
- TINT_REFLECT(Config, target, requested_names);
- };
-
- /// Constructor using a the configuration provided in the input Data
- Renamer();
-
- /// Destructor
- ~Renamer() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-namespace tint {
-
-/// Reflection for Target
-TINT_REFLECT_ENUM_RANGE(tint::ast::transform::Renamer::Target, kAll, kMslKeywords);
-
-} // namespace tint
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_RENAMER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/renamer_test.cc b/src/tint/lang/wgsl/ast/transform/renamer_test.cc
deleted file mode 100644
index f7e9a7e..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer_test.cc
+++ /dev/null
@@ -1,2145 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/renamer.h"
-
-#include <memory>
-#include <unordered_set>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/core/builtin_type.h"
-#include "src/tint/lang/core/texel_format.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-constexpr const char kUnicodeIdentifier[] = // "𝖎𝖉𝖊𝖓𝖙𝖎𝖋𝖎𝖊𝖗123"
- "\xf0\x9d\x96\x8e\xf0\x9d\x96\x89\xf0\x9d\x96\x8a\xf0\x9d\x96\x93"
- "\xf0\x9d\x96\x99\xf0\x9d\x96\x8e\xf0\x9d\x96\x8b\xf0\x9d\x96\x8e"
- "\xf0\x9d\x96\x8a\xf0\x9d\x96\x97\x31\x32\x33";
-
-using ::testing::ContainerEq;
-
-using RenamerTest = TransformTest;
-
-TEST_F(RenamerTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, BasicModuleVertexIndex) {
- auto* src = R"(
-fn test(vert_idx : u32) -> u32 {
- return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32
- ) -> @builtin(position) vec4<f32> {
- _ = test(vert_idx);
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-fn tint_symbol(tint_symbol_1 : u32) -> u32 {
- return tint_symbol_1;
-}
-
-@vertex
-fn tint_symbol_2(@builtin(vertex_index) tint_symbol_1 : u32) -> @builtin(position) vec4<f32> {
- _ = tint_symbol(tint_symbol_1);
- return vec4<f32>();
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, RequestedNamesWithRenameAll) {
- auto* src = R"(
-struct ShaderIO {
- @location(1) var1: f32,
- @location(3) @interpolate(flat) var3: u32,
- @builtin(position) pos: vec4f,
-}
-
-@vertex fn main(@builtin(vertex_index) vert_idx : u32)
- -> ShaderIO {
- var pos = array(
- vec2f(-1.0, 3.0),
- vec2f(-1.0, -3.0),
- vec2f(3.0, 0.0));
-
- var shaderIO: ShaderIO;
- shaderIO.var1 = 0.0;
- shaderIO.var3 = 1u;
- shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
-
- return shaderIO;
-}
-)";
-
- auto* expect = R"(
-struct tint_symbol {
- @location(1)
- user_var1 : f32,
- @location(3) @interpolate(flat)
- user_var3 : u32,
- @builtin(position)
- tint_symbol_1 : vec4f,
-}
-
-@vertex
-fn tint_symbol_2(@builtin(vertex_index) tint_symbol_3 : u32) -> tint_symbol {
- var tint_symbol_1 = array(vec2f(-(1.0), 3.0), vec2f(-(1.0), -(3.0)), vec2f(3.0, 0.0));
- var tint_symbol_4 : tint_symbol;
- tint_symbol_4.user_var1 = 0.0;
- tint_symbol_4.user_var3 = 1u;
- tint_symbol_4.tint_symbol_1 = vec4f(tint_symbol_1[tint_symbol_3], 0.0, 1.0);
- return tint_symbol_4;
-}
-)";
-
- DataMap inputs;
- inputs.Add<Renamer::Config>(Renamer::Target::kAll,
- /* remappings */
- Renamer::Remappings{
- {"var1", "user_var1"},
- {"var3", "user_var3"},
- });
- auto got = Run<Renamer>(src, inputs);
-
- EXPECT_EQ(expect, str(got));
-}
-
-using RenamerTestRequestedNamesWithoutRenameAll = TransformTestWithParam<Renamer::Target>;
-
-TEST_P(RenamerTestRequestedNamesWithoutRenameAll, RequestedNames) {
- auto* src = R"(
-struct ShaderIO {
- @location(1) var1: f32,
- @location(3) @interpolate(flat) var3: u32,
- @builtin(position) pos: vec4f,
-}
-
-@vertex fn entry_point(@builtin(vertex_index) vert_idx : u32)
- -> ShaderIO {
- var pos = array(
- vec2f(-1.0, 3.0),
- vec2f(-1.0, -3.0),
- vec2f(3.0, 0.0));
-
- var shaderIO: ShaderIO;
- shaderIO.var1 = 0.0;
- shaderIO.var3 = 1u;
- shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
-
- return shaderIO;
-}
-)";
-
- auto* expect = R"(
-struct ShaderIO {
- @location(1)
- user_var1 : f32,
- @location(3) @interpolate(flat)
- user_var3 : u32,
- @builtin(position)
- pos : vec4f,
-}
-
-@vertex
-fn entry_point(@builtin(vertex_index) vert_idx : u32) -> ShaderIO {
- var pos = array(vec2f(-(1.0), 3.0), vec2f(-(1.0), -(3.0)), vec2f(3.0, 0.0));
- var shaderIO : ShaderIO;
- shaderIO.user_var1 = 0.0;
- shaderIO.user_var3 = 1u;
- shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
- return shaderIO;
-}
-)";
-
- DataMap inputs;
- inputs.Add<Renamer::Config>(GetParam(),
- /* remappings */
- Renamer::Remappings{
- {"var1", "user_var1"},
- {"var3", "user_var3"},
- });
- auto got = Run<Renamer>(src, inputs);
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestRequestedNamesWithoutRenameAll,
- RenamerTestRequestedNamesWithoutRenameAll,
- testing::Values(Renamer::Target::kGlslKeywords,
- Renamer::Target::kHlslKeywords,
- Renamer::Target::kMslKeywords));
-
-TEST_F(RenamerTest, PreserveSwizzles) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- var v : vec4<f32>;
- var rgba : f32;
- var xyzw : f32;
- var z : f32;
- return v.zyxw + v.rgab * v.z;
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
- var tint_symbol_1 : vec4<f32>;
- var tint_symbol_2 : f32;
- var tint_symbol_3 : f32;
- var tint_symbol_4 : f32;
- return (tint_symbol_1.zyxw + (tint_symbol_1.rgab * tint_symbol_1.z));
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveSwizzles_ThroughMaterialize) {
- auto* src = R"(
-const v = vec4();
-const x = v.x * 2u;
-)";
-
- auto* expect = R"(
-const tint_symbol = vec4();
-
-const tint_symbol_1 = (tint_symbol.x * 2u);
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltins) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- var blah : vec4<f32>;
- return abs(blah);
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
- var tint_symbol_1 : vec4<f32>;
- return abs(tint_symbol_1);
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltinTypes) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn entry() {
- var a = modf(1.0).whole;
- var b = modf(1.0).fract;
- var c = frexp(1.0).fract;
- var d = frexp(1.0).exp;
-}
-)";
-
- auto* expect = R"(
-@compute @workgroup_size(1)
-fn tint_symbol() {
- var tint_symbol_1 = modf(1.0).whole;
- var tint_symbol_2 = modf(1.0).fract;
- var tint_symbol_3 = frexp(1.0).fract;
- var tint_symbol_4 = frexp(1.0).exp;
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltinTypes_ViaPointerDot) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn entry() {
- var m = modf(1.0);
- let p1 = &m;
- var f = frexp(1.0);
- let p2 = &f;
-
- var a = p1.whole;
- var b = p1.fract;
- var c = p2.fract;
- var d = p2.exp;
-}
-)";
-
- auto* expect = R"(
-@compute @workgroup_size(1)
-fn tint_symbol() {
- var tint_symbol_1 = modf(1.0);
- let tint_symbol_2 = &(tint_symbol_1);
- var tint_symbol_3 = frexp(1.0);
- let tint_symbol_4 = &(tint_symbol_3);
- var tint_symbol_5 = tint_symbol_2.whole;
- var tint_symbol_6 = tint_symbol_2.fract;
- var tint_symbol_7 = tint_symbol_4.fract;
- var tint_symbol_8 = tint_symbol_4.exp;
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveCoreDiagnosticRuleName) {
- auto* src = R"(
-diagnostic(off, chromium.unreachable_code);
-
-@diagnostic(off, derivative_uniformity)
-@fragment
-fn entry(@location(0) value : f32) -> @location(0) f32 {
- if (value > 0) {
- return dpdx(value);
- return 0.0;
- }
- return 1.0;
-}
-)";
-
- auto* expect = R"(
-diagnostic(off, chromium.unreachable_code);
-
-@diagnostic(off, derivative_uniformity) @fragment
-fn tint_symbol(@location(0) tint_symbol_1 : f32) -> @location(0) f32 {
- if ((tint_symbol_1 > 0)) {
- return dpdx(tint_symbol_1);
- return 0.0;
- }
- return 1.0;
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, AttemptSymbolCollision) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- var tint_symbol : vec4<f32>;
- var tint_symbol_2 : vec4<f32>;
- var tint_symbol_4 : vec4<f32>;
- return tint_symbol + tint_symbol_2 + tint_symbol_4;
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
- var tint_symbol_1 : vec4<f32>;
- var tint_symbol_2 : vec4<f32>;
- var tint_symbol_3 : vec4<f32>;
- return ((tint_symbol_1 + tint_symbol_2) + tint_symbol_3);
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveTexelFormatAndAccess) {
- auto src = R"(
-@group(0) @binding(0) var texture : texture_storage_2d<rgba8unorm, write>;
-
-fn f() {
- var dims = textureDimensions(texture);
-}
-)";
-
- auto expect = R"(
-@group(0) @binding(0) var tint_symbol : texture_storage_2d<rgba8unorm, write>;
-
-fn tint_symbol_1() {
- var tint_symbol_2 = textureDimensions(tint_symbol);
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveAddressSpace) {
- auto src = R"(
-var<private> p : i32;
-
-fn f() {
- var v = p;
-}
-)";
-
- auto expect = R"(
-var<private> tint_symbol : i32;
-
-fn tint_symbol_1() {
- var tint_symbol_2 = tint_symbol;
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-using RenamerTestGlsl = TransformTestWithParam<std::string>;
-using RenamerTestHlsl = TransformTestWithParam<std::string>;
-using RenamerTestMsl = TransformTestWithParam<std::string>;
-
-TEST_P(RenamerTestGlsl, Keywords) {
- auto keyword = GetParam();
-
- auto src = R"(
-@fragment
-fn frag_main() {
- var )" + keyword +
- R"( : i32;
-}
-)";
-
- auto* expect = R"(
-@fragment
-fn frag_main() {
- var tint_symbol : i32;
-}
-)";
-
- DataMap inputs;
- inputs.Add<Renamer::Config>(Renamer::Target::kGlslKeywords);
- auto got = Run<Renamer>(src, inputs);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerTestHlsl, Keywords) {
- auto keyword = GetParam();
-
- auto src = R"(
-@fragment
-fn frag_main() {
- var )" + keyword +
- R"( : i32;
-}
-)";
-
- auto* expect = R"(
-@fragment
-fn frag_main() {
- var tint_symbol : i32;
-}
-)";
-
- DataMap inputs;
- inputs.Add<Renamer::Config>(Renamer::Target::kHlslKeywords);
- auto got = Run<Renamer>(src, inputs);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerTestMsl, Keywords) {
- auto keyword = GetParam();
-
- auto src = R"(
-@fragment
-fn frag_main() {
- var )" + keyword +
- R"( : i32;
-}
-)";
-
- auto* expect = R"(
-@fragment
-fn frag_main() {
- var tint_symbol : i32;
-}
-)";
-
- DataMap inputs;
- inputs.Add<Renamer::Config>(Renamer::Target::kMslKeywords);
- auto got = Run<Renamer>(src, inputs);
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestGlsl,
- RenamerTestGlsl,
- testing::Values( // "active", // Also reserved in WGSL
- // "asm", // WGSL keyword
- "atomic_uint",
- // "attribute", // Also reserved in WGSL
- // "bool", // WGSL keyword
- // "break", // WGSL keyword
- "buffer",
- "bvec2",
- "bvec3",
- "bvec4",
- // "case", // WGSL keyword
- // "cast", // Also reserved in WGSL
- "centroid",
- // "class", // Also reserved in WGSL
- // "coherent", // Also reserved in WGSL
- // "common", // Also reserved in WGSL
- // "const", // WGSL keyword
- // "continue", // WGSL keyword
- // "default", // WGSL keyword
- // "discard", // WGSL keyword
- "dmat2",
- "dmat2x2",
- "dmat2x3",
- "dmat2x4",
- "dmat3",
- "dmat3x2",
- "dmat3x3",
- "dmat3x4",
- "dmat4",
- "dmat4x2",
- "dmat4x3",
- "dmat4x4",
- // "do", // WGSL keyword
- "double",
- "dvec2",
- "dvec3",
- "dvec4",
- // "else" // WGSL keyword
- // "enum", // WGSL keyword
- // "extern", // Also reserved in WGSL
- // "external", // Also reserved in WGSL
- // "false", // WGSL keyword
- // "filter", // Also reserved in WGSL
- "fixed",
- "flat",
- "float",
- // "for", // WGSL keyword
- "fvec2",
- "fvec3",
- "fvec4",
- "gl_BaseInstance",
- "gl_BaseVertex",
- "gl_ClipDistance",
- "gl_DepthRangeParameters",
- "gl_DrawID",
- "gl_FragCoord",
- "gl_FragDepth",
- "gl_FrontFacing",
- "gl_GlobalInvocationID",
- "gl_InstanceID",
- "gl_LocalInvocationID",
- "gl_LocalInvocationIndex",
- "gl_NumSamples",
- "gl_NumWorkGroups",
- "gl_PerVertex",
- "gl_PointCoord",
- "gl_PointSize",
- "gl_Position",
- "gl_PrimitiveID",
- "gl_SampleID",
- "gl_SampleMask",
- "gl_SampleMaskIn",
- "gl_SamplePosition",
- "gl_VertexID",
- "gl_WorkGroupID",
- "gl_WorkGroupSize",
- // "goto", // Also reserved in WGSL
- "half",
- // "highp", // Also reserved in WGSL
- "hvec2",
- "hvec3",
- "hvec4",
- // "if", // WGSL keyword
- "iimage1D",
- "iimage1DArray",
- "iimage2D",
- "iimage2DArray",
- "iimage2DMS",
- "iimage2DMSArray",
- "iimage2DRect",
- "iimage3D",
- "iimageBuffer",
- "iimageCube",
- "iimageCubeArray",
- "image1D",
- "image1DArray",
- "image2D",
- "image2DArray",
- "image2DMS",
- "image2DMSArray",
- "image2DRect",
- "image3D",
- "imageBuffer",
- "imageCube",
- "imageCubeArray",
- "in",
- // "inline", // Also reserved in WGSL
- // "inout", // Also reserved in WGSL
- "input",
- "int",
- // "interface", // Also reserved in WGSL
- // "invariant", // Also reserved in WGSL
- "isampler1D",
- "isampler1DArray",
- "isampler2D",
- "isampler2DArray",
- "isampler2DMS",
- "isampler2DMSArray",
- "isampler2DRect",
- "isampler3D",
- "isamplerBuffer",
- "isamplerCube",
- "isamplerCubeArray",
- "ivec2",
- "ivec3",
- "ivec4",
- // "layout", // Also reserved in WGSL
- "long",
- // "lowp", // Also reserved in WGSL
- // "mat2x2", // WGSL keyword
- // "mat2x3", // WGSL keyword
- // "mat2x4", // WGSL keyword
- // "mat2",
- "mat3",
- // "mat3x2", // WGSL keyword
- // "mat3x3", // WGSL keyword
- // "mat3x4", // WGSL keyword
- "mat4",
- // "mat4x2", // WGSL keyword
- // "mat4x3", // WGSL keyword
- // "mat4x4", // WGSL keyword
- // "mediump", // Also reserved in WGSL
- // "namespace", // Also reserved in WGSL
- // "noinline", // Also reserved in WGSL
- // "noncoherent", // Reserved in WGSL
- // "noperspective", // Also reserved in WGSL
- // "non_coherent", // Reserved in WGSL
- "out",
- "output",
- // "partition", // Also reserved in WGSL
- // "patch", // Also reserved in WGSL
- // "precise", // Also reserved in WGSL
- // "precision", // Also reserved in WGSL
- // "public", // Also reserved in WGSL
- // "readonly", // Also reserved in WGSL
- // "resource", // Also reserved in WGSL
- // "restrict", // Also reserved in WGSL
- // "return", // WGSL keyword
- "sample",
- "sampler1D",
- "sampler1DArray",
- "sampler1DArrayShadow",
- "sampler1DShadow",
- "sampler2D",
- "sampler2DArray",
- "sampler2DArrayShadow",
- "sampler2DMS",
- "sampler2DMSArray",
- "sampler2DRect",
- "sampler2DRectShadow",
- "sampler2DShadow",
- "sampler3D",
- "sampler3DRect",
- "samplerBuffer",
- "samplerCube",
- "samplerCubeArray",
- "samplerCubeArrayShadow",
- "samplerCubeShadow",
- // "shared" // Also reserved in WGSL,
- "short",
- // "sizeof", // Also reserved in WGSL
- // "smooth", // Also reserved in WGSL
- // "static", // Also reserved in WGSL
- // "struct", // WGSL keyword
- // "subroutine", // Also reserved in WGSL
- "superp",
- // "switch", // WGSL keyword
- // "template", // Also reserved in WGSL
- // "this", // Also reserved in WGSL
- // "true", // WGSL keyword
- // "typedef", // WGSL keyword
- "uimage1D",
- "uimage1DArray",
- "uimage2D",
- "uimage2DArray",
- "uimage2DMS",
- "uimage2DMSArray",
- "uimage2DRect",
- "uimage3D",
- "uimageBuffer",
- "uimageCube",
- "uimageCubeArray",
- "uint",
- // "uniform", // WGSL keyword
- // "union", // Also reserved in WGSL
- "unsigned",
- "usampler1D",
- "usampler1DArray",
- "usampler2D",
- "usampler2DArray",
- "usampler2DMS",
- "usampler2DMSArray",
- "usampler2DRect",
- "usampler3D",
- "usamplerBuffer",
- "usamplerCube",
- "usamplerCubeArray",
- // "using", // WGSL keyword
- "uvec2",
- "uvec3",
- "uvec4",
- // "varying", // Also reserved in WGSL
- // "vec2", // WGSL keyword
- // "vec3", // WGSL keyword
- // "vec4", // WGSL keyword
- // "void", // WGSL keyword
- // "volatile", // Also reserved in WGSL
- // "while", // WGSL keyword
- // "writeonly", // Also reserved in WGSL
- kUnicodeIdentifier));
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestHlsl,
- RenamerTestHlsl,
- testing::Values("AddressU",
- "AddressV",
- "AddressW",
- "AllMemoryBarrier",
- "AllMemoryBarrierWithGroupSync",
- "AppendStructuredBuffer",
- "BINORMAL",
- "BLENDINDICES",
- "BLENDWEIGHT",
- "BlendState",
- "BorderColor",
- "Buffer",
- "ByteAddressBuffer",
- "COLOR",
- "CheckAccessFullyMapped",
- "ComparisonFunc",
- // "CompileShader", // Also reserved in WGSL
- // "ComputeShader", // Also reserved in WGSL
- "ConsumeStructuredBuffer",
- "D3DCOLORtoUBYTE4",
- "DEPTH",
- "DepthStencilState",
- "DepthStencilView",
- "DeviceMemoryBarrier",
- "DeviceMemroyBarrierWithGroupSync",
- // "DomainShader", // Also reserved in WGSL
- "EvaluateAttributeAtCentroid",
- "EvaluateAttributeAtSample",
- "EvaluateAttributeSnapped",
- "FOG",
- "Filter",
- // "GeometryShader", // Also reserved in WGSL
- "GetRenderTargetSampleCount",
- "GetRenderTargetSamplePosition",
- "GroupMemoryBarrier",
- "GroupMemroyBarrierWithGroupSync",
- // "Hullshader", // Also reserved in WGSL
- "InputPatch",
- "InterlockedAdd",
- "InterlockedAnd",
- "InterlockedCompareExchange",
- "InterlockedCompareStore",
- "InterlockedExchange",
- "InterlockedMax",
- "InterlockedMin",
- "InterlockedOr",
- "InterlockedXor",
- "LineStream",
- "MaxAnisotropy",
- "MaxLOD",
- "MinLOD",
- "MipLODBias",
- "NORMAL",
- // "NULL", // Also reserved in WGSL
- "Normal",
- "OutputPatch",
- "POSITION",
- "POSITIONT",
- "PSIZE",
- "PixelShader",
- "PointStream",
- "Process2DQuadTessFactorsAvg",
- "Process2DQuadTessFactorsMax",
- "Process2DQuadTessFactorsMin",
- "ProcessIsolineTessFactors",
- "ProcessQuadTessFactorsAvg",
- "ProcessQuadTessFactorsMax",
- "ProcessQuadTessFactorsMin",
- "ProcessTriTessFactorsAvg",
- "ProcessTriTessFactorsMax",
- "ProcessTriTessFactorsMin",
- "RWBuffer",
- "RWByteAddressBuffer",
- "RWStructuredBuffer",
- "RWTexture1D",
- "RWTexture1DArray",
- "RWTexture2D",
- "RWTexture2DArray",
- "RWTexture3D",
- "RasterizerState",
- "RenderTargetView",
- "SV_ClipDistance",
- "SV_Coverage",
- "SV_CullDistance",
- "SV_Depth",
- "SV_DepthGreaterEqual",
- "SV_DepthLessEqual",
- "SV_DispatchThreadID",
- "SV_DomainLocation",
- "SV_GSInstanceID",
- "SV_GroupID",
- "SV_GroupIndex",
- "SV_GroupThreadID",
- "SV_InnerCoverage",
- "SV_InsideTessFactor",
- "SV_InstanceID",
- "SV_IsFrontFace",
- "SV_OutputControlPointID",
- "SV_Position",
- "SV_PrimitiveID",
- "SV_RenderTargetArrayIndex",
- "SV_SampleIndex",
- "SV_StencilRef",
- "SV_Target",
- "SV_TessFactor",
- "SV_VertexArrayIndex",
- "SV_VertexID",
- "Sampler",
- "Sampler1D",
- "Sampler2D",
- "Sampler3D",
- "SamplerCUBE",
- "SamplerComparisonState",
- "SamplerState",
- "StructuredBuffer",
- "TANGENT",
- "TESSFACTOR",
- "TEXCOORD",
- "Texcoord",
- "Texture",
- "Texture1D",
- "Texture1DArray",
- "Texture2D",
- "Texture2DArray",
- "Texture2DMS",
- "Texture2DMSArray",
- "Texture3D",
- "TextureCube",
- "TextureCubeArray",
- "TriangleStream",
- "VFACE",
- "VPOS",
- "VertexShader",
- "abort",
- // "abs", // WGSL builtin
- // "acos", // WGSL builtin
- // "all", // WGSL builtin
- "allow_uav_condition",
- // "any", // WGSL builtin
- "asdouble",
- "asfloat",
- // "asin", // WGSL builtin
- "asint",
- // "asm", // WGSL keyword
- // "asm_fragment", // Also reserved in WGSL
- "asuint",
- // "atan", // WGSL builtin
- // "atan2", // WGSL builtin
- // "auto", // Also reserved in WGSL
- // "bool", // WGSL keyword
- "bool1",
- "bool1x1",
- "bool1x2",
- "bool1x3",
- "bool1x4",
- "bool2",
- "bool2x1",
- "bool2x2",
- "bool2x3",
- "bool2x4",
- "bool3",
- "bool3x1",
- "bool3x2",
- "bool3x3",
- "bool3x4",
- "bool4",
- "bool4x1",
- "bool4x2",
- "bool4x3",
- "bool4x4",
- "branch",
- // "break", // WGSL keyword
- // "call", // WGSL builtin
- // "case", // WGSL keyword
- // "catch", // Also reserved in WGSL
- "cbuffer",
- // "ceil", // WGSL builtin
- "centroid",
- "char",
- // "clamp", // WGSL builtin
- // "class", // Also reserved in WGSL
- "clip",
- // "column_major", // Also reserved in WGSL
- // "compile", // Also reserved in WGSL
- // "compile_fragment", // Also reserved in WGSL
- // "const", // WGSL keyword
- // "const_cast", // Also reserved in WGSL
- // "continue", // WGSL keyword
- // "cos", // WGSL builtin
- // "cosh", // WGSL builtin
- "countbits",
- // "cross", // WGSL builtin
- "ddx",
- "ddx_coarse",
- "ddx_fine",
- "ddy",
- "ddy_coarse",
- "ddy_fine",
- // "default", // WGSL keyword
- "degrees",
- // "delete", // Also reserved in WGSL
- // "determinant", // WGSL builtin
- // "discard", // WGSL keyword
- // "distance", // WGSL builtin
- // "do", // WGSL keyword
- // "dot", // WGSL builtin
- "double",
- "double1",
- "double1x1",
- "double1x2",
- "double1x3",
- "double1x4",
- "double2",
- "double2x1",
- "double2x2",
- "double2x3",
- "double2x4",
- "double3",
- "double3x1",
- "double3x2",
- "double3x3",
- "double3x4",
- "double4",
- "double4x1",
- "double4x2",
- "double4x3",
- "double4x4",
- "dst",
- "dword",
- "dword1",
- "dword1x1",
- "dword1x2",
- "dword1x3",
- "dword1x4",
- "dword2",
- "dword2x1",
- "dword2x2",
- "dword2x3",
- "dword2x4",
- "dword3",
- "dword3x1",
- "dword3x2",
- "dword3x3",
- "dword3x4",
- "dword4",
- "dword4x1",
- "dword4x2",
- "dword4x3",
- "dword4x4",
- // "dynamic_cast", // Also reserved in WGSL
- // "else", // WGSL keyword
- // "enum", // WGSL keyword
- "errorf",
- // "exp", // WGSL builtin
- // "exp2", // WGSL builtin
- // "explicit", // Also reserved in WGSL
- // "export", // Also reserved in WGSL
- // "extern", // Also reserved in WGSL
- "f16to32",
- "f32tof16",
- // "faceforward", // WGSL builtin
- // "false", // WGSL keyword
- "fastopt",
- "firstbithigh",
- "firstbitlow",
- "flatten",
- "float",
- "float1",
- "float1x1",
- "float1x2",
- "float1x3",
- "float1x4",
- "float2",
- "float2x1",
- "float2x2",
- "float2x3",
- "float2x4",
- "float3",
- "float3x1",
- "float3x2",
- "float3x3",
- "float3x4",
- "float4",
- "float4x1",
- "float4x2",
- "float4x3",
- "float4x4",
- // "floor", // WGSL builtin
- // "fma", // WGSL builtin
- "fmod",
- // "for", // WGSL keyword
- "forcecase",
- "frac",
- // "frexp", // WGSL builtin
- // "friend", // Also reserved in WGSL
- // "fwidth", // WGSL builtin
- // "fxgroup", // Also reserved in WGSL
- // "goto", // Also reserved in WGSL
- // "groupshared", // Also reserved in WGSL
- "half",
- "half1",
- "half1x1",
- "half1x2",
- "half1x3",
- "half1x4",
- "half2",
- "half2x1",
- "half2x2",
- "half2x3",
- "half2x4",
- "half3",
- "half3x1",
- "half3x2",
- "half3x3",
- "half3x4",
- "half4",
- "half4x1",
- "half4x2",
- "half4x3",
- "half4x4",
- // "if", // WGSL keyword
- // "in", // WGSL keyword
- // "inline", // Also reserved in WGSL
- // "inout", // Also reserved in WGSL
- "int",
- "int1",
- "int1x1",
- "int1x2",
- "int1x3",
- "int1x4",
- "int2",
- "int2x1",
- "int2x2",
- "int2x3",
- "int2x4",
- "int3",
- "int3x1",
- "int3x2",
- "int3x3",
- "int3x4",
- "int4",
- "int4x1",
- "int4x2",
- "int4x3",
- "int4x4",
- // "interface", // Also reserved in WGSL
- "isfinite",
- "isinf",
- "isnan",
- // "ldexp", // WGSL builtin
- // "length", // WGSL builtin
- "lerp",
- // "line", // Also reserved in WGSL
- // "lineadj", // Also reserved in WGSL
- "linear",
- "lit",
- // "log", // WGSL builtin
- "log10",
- // "log2", // WGSL builtin
- "long",
- // "loop", // WGSL keyword
- "mad",
- "matrix",
- // "max", // WGSL builtin
- // "min", // WGSL builtin
- "min10float",
- "min10float1",
- "min10float1x1",
- "min10float1x2",
- "min10float1x3",
- "min10float1x4",
- "min10float2",
- "min10float2x1",
- "min10float2x2",
- "min10float2x3",
- "min10float2x4",
- "min10float3",
- "min10float3x1",
- "min10float3x2",
- "min10float3x3",
- "min10float3x4",
- "min10float4",
- "min10float4x1",
- "min10float4x2",
- "min10float4x3",
- "min10float4x4",
- "min12int",
- "min12int1",
- "min12int1x1",
- "min12int1x2",
- "min12int1x3",
- "min12int1x4",
- "min12int2",
- "min12int2x1",
- "min12int2x2",
- "min12int2x3",
- "min12int2x4",
- "min12int3",
- "min12int3x1",
- "min12int3x2",
- "min12int3x3",
- "min12int3x4",
- "min12int4",
- "min12int4x1",
- "min12int4x2",
- "min12int4x3",
- "min12int4x4",
- "min16float",
- "min16float1",
- "min16float1x1",
- "min16float1x2",
- "min16float1x3",
- "min16float1x4",
- "min16float2",
- "min16float2x1",
- "min16float2x2",
- "min16float2x3",
- "min16float2x4",
- "min16float3",
- "min16float3x1",
- "min16float3x2",
- "min16float3x3",
- "min16float3x4",
- "min16float4",
- "min16float4x1",
- "min16float4x2",
- "min16float4x3",
- "min16float4x4",
- "min16int",
- "min16int1",
- "min16int1x1",
- "min16int1x2",
- "min16int1x3",
- "min16int1x4",
- "min16int2",
- "min16int2x1",
- "min16int2x2",
- "min16int2x3",
- "min16int2x4",
- "min16int3",
- "min16int3x1",
- "min16int3x2",
- "min16int3x3",
- "min16int3x4",
- "min16int4",
- "min16int4x1",
- "min16int4x2",
- "min16int4x3",
- "min16int4x4",
- "min16uint",
- "min16uint1",
- "min16uint1x1",
- "min16uint1x2",
- "min16uint1x3",
- "min16uint1x4",
- "min16uint2",
- "min16uint2x1",
- "min16uint2x2",
- "min16uint2x3",
- "min16uint2x4",
- "min16uint3",
- "min16uint3x1",
- "min16uint3x2",
- "min16uint3x3",
- "min16uint3x4",
- "min16uint4",
- "min16uint4x1",
- "min16uint4x2",
- "min16uint4x3",
- "min16uint4x4",
- // "modf", // WGSL builtin
- "msad4",
- "mul",
- // "mutable", // Also reserved in WGSL
- // "namespace", // Also reserved in WGSL
- // "new", // Also reserved in WGSL
- // "nointerpolation", // Also reserved in WGSL
- "noise",
- // "noperspective", // Also reserved in WGSL
- // "normalize", // WGSL builtin
- "numthreads",
- // "operator", // Also reserved in WGSL
- // "out", // WGSL keyword
- // "packoffset", // Also reserved in WGSL
- // "pass", // Also reserved in WGSL
- // "pixelfragment", // Also reserved in WGSL
- "pixelshader",
- // "point", // Also reserved in WGSL
- // "pow", // WGSL builtin
- // "precise", // Also reserved in WGSL
- "printf",
- // "private", // WGSL keyword
- // "protected", // Also reserved in WGSL
- // "public", // Also reserved in WGSL
- "radians",
- "rcp",
- // "reflect", // WGSL builtin
- "refract",
- // "register", // Also reserved in WGSL
- // "reinterpret_cast", // Also reserved in WGSL
- // "return", // WGSL keyword
- // "reversebits", // WGSL builtin
- // "round", // WGSL builtin
- "row_major",
- "rsqrt",
- "sample",
- "sampler1D",
- "sampler2D",
- "sampler3D",
- "samplerCUBE",
- "sampler_state",
- "saturate",
- // "shared", // Also reserved in WGSL
- "short",
- // "sign", // WGSL builtin
- // "signed", // Also reserved in WGSL
- // "sin", // WGSL builtin
- "sincos",
- // "sinh", // WGSL builtin
- // "sizeof", // Also reserved in WGSL
- // "smoothstep", // WGSL builtin
- // "snorm", // Also reserved in WGSL
- // "sqrt", // WGSL builtin
- "stateblock",
- "stateblock_state",
- // "static", // Also reserved in WGSL
- // "static_cast", // Also reserved in WGSL
- // "step", // WGSL builtin
- "string",
- // "struct", // WGSL keyword
- // "switch", // WGSL keyword
- // "tan", // WGSL builtin
- // "tanh", // WGSL builtin
- "tbuffer",
- "technique",
- "technique10",
- "technique11",
- // "template", // Also reserved in WGSL
- "tex1D",
- "tex1Dbias",
- "tex1Dgrad",
- "tex1Dlod",
- "tex1Dproj",
- "tex2D",
- "tex2Dbias",
- "tex2Dgrad",
- "tex2Dlod",
- "tex2Dproj",
- "tex3D",
- "tex3Dbias",
- "tex3Dgrad",
- "tex3Dlod",
- "tex3Dproj",
- "texCUBE",
- "texCUBEbias",
- "texCUBEgrad",
- "texCUBElod",
- "texCUBEproj",
- "texture",
- "texture1D",
- "texture1DArray",
- "texture2D",
- "texture2DArray",
- "texture2DMS",
- "texture2DMSArray",
- "texture3D",
- "textureCube",
- "textureCubeArray",
- // "this", // Also reserved in WGSL
- // "throw", // Also reserved in WGSL
- "transpose",
- "triangle",
- "triangleadj",
- // "true", // WGSL keyword
- // "trunc", // WGSL builtin
- // "try", // Also reserved in WGSL
- // "typedef", // WGSL keyword
- // "typename", // Also reserved in WGSL
- "uint",
- "uint1",
- "uint1x1",
- "uint1x2",
- "uint1x3",
- "uint1x4",
- "uint2",
- "uint2x1",
- "uint2x2",
- "uint2x3",
- "uint2x4",
- "uint3",
- "uint3x1",
- "uint3x2",
- "uint3x3",
- "uint3x4",
- "uint4",
- "uint4x1",
- "uint4x2",
- "uint4x3",
- "uint4x4",
- // "uniform", // WGSL keyword
- // "union", // Also reserved in WGSL
- // "unorm", // Also reserved in WGSL
- "unroll",
- "unsigned",
- // "using", // WGSL reserved keyword
- "vector",
- "vertexfragment",
- "vertexshader",
- // "virtual", // Also reserved in WGSL
- // "void", // WGSL keyword
- // "volatile", // Also reserved in WGSL
- // "while" // WGSL reserved keyword
- kUnicodeIdentifier));
-
-INSTANTIATE_TEST_SUITE_P(
- RenamerTestMsl,
- RenamerTestMsl,
- testing::Values(
- // c++14 spec
- // "alignas", // Also reserved in WGSL
- // "alignof", // Also reserved in WGSL
- "and",
- "and_eq",
- // "asm", // Also reserved in WGSL
- // "auto", // Also reserved in WGSL
- "bitand",
- "bitor",
- // "bool", // Also used in WGSL
- // "break", // Also used in WGSL
- // "case", // Also used in WGSL
- // "catch", // Also reserved in WGSL
- "char",
- "char16_t",
- "char32_t",
- // "class", // Also reserved in WGSL
- "compl",
- // "const", // Also used in WGSL
- // "const_cast", // Also reserved in WGSL
- // "constexpr", // Also reserved in WGSL
- // "continue", // Also used in WGSL
- // "decltype", // Also reserved in WGSL
- // "default", // Also used in WGSL
- // "delete", // Also reserved in WGSL
- // "do", // Also used in WGSL
- "double",
- // "dynamic_cast", // Also reserved in WGSL
- // "else", // Also used in WGSL
- // "enum", // Also used in WGSL
- // "explicit", // Also reserved in WGSL
- // "extern", // Also reserved in WGSL
- // "false", // Also used in WGSL
- // "final", // Also reserved in WGSL
- "float",
- // "for", // Also used in WGSL
- // "friend", // Also reserved in WGSL
- // "goto", // Also reserved in WGSL
- // "if", // Also used in WGSL
- // "inline", // Also reserved in WGSL
- "int",
- "long",
- // "mutable", // Also reserved in WGSL
- // "namespace", // Also reserved in WGSL
- // "new", // Also reserved in WGSL
- // "noexcept", // Also reserved in WGSL
- "not",
- "not_eq",
- // "nullptr", // Also reserved in WGSL
- // "operator", // Also reserved in WGSL
- "or",
- "or_eq",
- // "override", // Also used in WGSL
- // "private", // Also used in WGSL
- // "protected", // Also reserved in WGSL
- // "public", // Also reserved in WGSL
- // "register", // Also reserved in WGSL
- // "reinterpret_cast", // Also reserved in WGSL
- // "return", // Also used in WGSL
- "short",
- // "signed", // Also reserved in WGSL
- // "sizeof", // Also reserved in WGSL
- // "static", // Also reserved in WGSL
- // "static_assert", // Also reserved in WGSL
- // "static_cast", // Also reserved in WGSL
- // "struct", // Also used in WGSL
- // "switch", // Also used in WGSL
- // "template", // Also reserved in WGSL
- // "this", // Also reserved in WGSL
- // "thread_local", // Also reserved in WGSL
- // "throw", // Also reserved in WGSL
- // "true", // Also used in WGSL
- // "try", // Also reserved in WGSL
- // "typedef", // Also used in WGSL
- // "typeid", // Also reserved in WGSL
- // "typename", // Also reserved in WGSL
- // "union", // Also reserved in WGSL
- "unsigned",
- // "using", // WGSL reserved keyword
- // "virtual", // Also reserved in WGSL
- // "void", // Also used in WGSL
- // "volatile", // Also reserved in WGSL
- "wchar_t",
- // "while", // WGSL reserved keyword
- "xor",
- "xor_eq",
-
- // Metal Spec
- "access",
- // "array", // Also used in WGSL
- "array_ref",
- "as_type",
- // "atomic", // Also used in WGSL
- "atomic_bool",
- "atomic_int",
- "atomic_uint",
- "bool2",
- "bool3",
- "bool4",
- "buffer",
- "char2",
- "char3",
- "char4",
- "const_reference",
- "constant",
- "depth2d",
- "depth2d_array",
- "depth2d_ms",
- "depth2d_ms_array",
- "depthcube",
- "depthcube_array",
- "device",
- "discard_fragment",
- "float2",
- "float2x2",
- "float2x3",
- "float2x4",
- "float3",
- "float3x2",
- "float3x3",
- "float3x4",
- "float4",
- "float4x2",
- "float4x3",
- "float4x4",
- "fragment",
- "half",
- "half2",
- "half2x2",
- "half2x3",
- "half2x4",
- "half3",
- "half3x2",
- "half3x3",
- "half3x4",
- "half4",
- "half4x2",
- "half4x3",
- "half4x4",
- "imageblock",
- "int16_t",
- "int2",
- "int3",
- "int32_t",
- "int4",
- "int64_t",
- "int8_t",
- "kernel",
- "long2",
- "long3",
- "long4",
- "main", // No functions called main
- "matrix",
- "metal", // The namespace
- "packed_bool2",
- "packed_bool3",
- "packed_bool4",
- "packed_char2",
- "packed_char3",
- "packed_char4",
- "packed_float2",
- "packed_float3",
- "packed_float4",
- "packed_half2",
- "packed_half3",
- "packed_half4",
- "packed_int2",
- "packed_int3",
- "packed_int4",
- "packed_short2",
- "packed_short3",
- "packed_short4",
- "packed_uchar2",
- "packed_uchar3",
- "packed_uchar4",
- "packed_uint2",
- "packed_uint3",
- "packed_uint4",
- "packed_ushort2",
- "packed_ushort3",
- "packed_ushort4",
- "patch_control_point",
- "ptrdiff_t",
- "r16snorm",
- "r16unorm",
- "r8unorm",
- "reference",
- "rg11b10f",
- "rg16snorm",
- "rg16unorm",
- "rg8snorm",
- "rg8unorm",
- "rgb10a2",
- "rgb9e5",
- "rgba16snorm",
- "rgba16unorm",
- "rgba8snorm",
- "rgba8unorm",
- // "sampler", // Also used in WGSL
- "short2",
- "short3",
- "short4",
- "size_t",
- "srgba8unorm",
- "texture",
- "texture1d",
- "texture1d_array",
- "texture2d",
- "texture2d_array",
- "texture2d_ms",
- "texture2d_ms_array",
- "texture3d",
- "texture_buffer",
- "texturecube",
- "texturecube_array",
- "thread",
- "threadgroup",
- "threadgroup_imageblock",
- "uchar",
- "uchar2",
- "uchar3",
- "uchar4",
- "uint",
- "uint16_t",
- "uint2",
- "uint3",
- "uint32_t",
- "uint4",
- "uint64_t",
- "uint8_t",
- "ulong2",
- "ulong3",
- "ulong4",
- // "uniform", // Also used in WGSL
- "ushort",
- "ushort2",
- "ushort3",
- "ushort4",
- // "vec", // WGSL reserved keyword
- "vertex",
-
- // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
- // Table 6.5. Constants for single-precision floating-point math
- // functions
- "MAXFLOAT",
- "HUGE_VALF",
- "INFINITY",
- "infinity",
- "NAN",
- "M_E_F",
- "M_LOG2E_F",
- "M_LOG10E_F",
- "M_LN2_F",
- "M_LN10_F",
- "M_PI_F",
- "M_PI_2_F",
- "M_PI_4_F",
- "M_1_PI_F",
- "M_2_PI_F",
- "M_2_SQRTPI_F",
- "M_SQRT2_F",
- "M_SQRT1_2_F",
- "MAXHALF",
- "HUGE_VALH",
- "M_E_H",
- "M_LOG2E_H",
- "M_LOG10E_H",
- "M_LN2_H",
- "M_LN10_H",
- "M_PI_H",
- "M_PI_2_H",
- "M_PI_4_H",
- "M_1_PI_H",
- "M_2_PI_H",
- "M_2_SQRTPI_H",
- "M_SQRT2_H",
- "M_SQRT1_2_H",
- // "while" // WGSL reserved keyword
- kUnicodeIdentifier));
-
-std::string ExpandBuiltinType(std::string_view name) {
- if (name == "array") {
- return "array<i32, 4>";
- }
- if (name == "atomic") {
- return "atomic<i32>";
- }
- if (name == "mat2x2") {
- return "mat2x2<f32>";
- }
- if (name == "mat2x2f") {
- return "mat2x2<f32>";
- }
- if (name == "mat2x2h") {
- return "mat2x2<f16>";
- }
- if (name == "mat2x3") {
- return "mat2x3<f32>";
- }
- if (name == "mat2x3f") {
- return "mat2x3<f32>";
- }
- if (name == "mat2x3h") {
- return "mat2x3<f16>";
- }
- if (name == "mat2x4") {
- return "mat2x4<f32>";
- }
- if (name == "mat2x4f") {
- return "mat2x4<f32>";
- }
- if (name == "mat2x4h") {
- return "mat2x4<f16>";
- }
- if (name == "mat3x2") {
- return "mat3x2<f32>";
- }
- if (name == "mat3x2f") {
- return "mat3x2<f32>";
- }
- if (name == "mat3x2h") {
- return "mat3x2<f16>";
- }
- if (name == "mat3x3") {
- return "mat3x3<f32>";
- }
- if (name == "mat3x3f") {
- return "mat3x3<f32>";
- }
- if (name == "mat3x3h") {
- return "mat3x3<f16>";
- }
- if (name == "mat3x4") {
- return "mat3x4<f32>";
- }
- if (name == "mat3x4f") {
- return "mat3x4<f32>";
- }
- if (name == "mat3x4h") {
- return "mat3x4<f16>";
- }
- if (name == "mat4x2") {
- return "mat4x2<f32>";
- }
- if (name == "mat4x2f") {
- return "mat4x2<f32>";
- }
- if (name == "mat4x2h") {
- return "mat4x2<f16>";
- }
- if (name == "mat4x3") {
- return "mat4x3<f32>";
- }
- if (name == "mat4x3f") {
- return "mat4x3<f32>";
- }
- if (name == "mat4x3h") {
- return "mat4x3<f16>";
- }
- if (name == "mat4x4") {
- return "mat4x4<f32>";
- }
- if (name == "mat4x4f") {
- return "mat4x4<f32>";
- }
- if (name == "mat4x4h") {
- return "mat4x4<f16>";
- }
- if (name == "ptr") {
- return "ptr<function, i32>";
- }
- if (name == "vec2") {
- return "vec2<f32>";
- }
- if (name == "vec2f") {
- return "vec2<f32>";
- }
- if (name == "vec2h") {
- return "vec2<f16>";
- }
- if (name == "vec2i") {
- return "vec2<i32>";
- }
- if (name == "vec2u") {
- return "vec2<u32>";
- }
- if (name == "vec3") {
- return "vec3<f32>";
- }
- if (name == "vec3f") {
- return "vec3<f32>";
- }
- if (name == "vec3h") {
- return "vec3<f16>";
- }
- if (name == "vec3i") {
- return "vec3<i32>";
- }
- if (name == "vec3u") {
- return "vec3<u32>";
- }
- if (name == "vec4") {
- return "vec4<f32>";
- }
- if (name == "vec4f") {
- return "vec4<f32>";
- }
- if (name == "vec4h") {
- return "vec4<f16>";
- }
- if (name == "vec4i") {
- return "vec4<i32>";
- }
- if (name == "vec4u") {
- return "vec4<u32>";
- }
- return std::string(name);
-}
-
-std::vector<std::string_view> ConstructableTypes() {
- std::vector<std::string_view> out;
- for (auto type : core::kBuiltinTypeStrings) {
- if (type != "ptr" && type != "atomic" && !tint::HasPrefix(type, "sampler") &&
- !tint::HasPrefix(type, "texture") && !tint::HasPrefix(type, "__") &&
- !tint::HasPrefix(type, "input_attachment") &&
- !tint::HasPrefix(type, "subgroup_matrix") && !tint::HasPrefix(type, "binding_array") &&
- type != "i8" && type != "u8") {
- out.push_back(type);
- }
- }
- return out;
-}
-
-using RenamerBuiltinTypeTest = TransformTestWithParam<std::string_view>;
-
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeUsage) {
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
- };
-
- auto src = expand(R"(
-enable f16;
-
-fn x(v : $type) -> $type {
- const a : $type = $type();
- let b : $type = a;
- var c : $type = b;
- return c;
-}
-
-struct y {
- a : $type,
-}
-)");
-
- auto expect = expand(R"(
-enable f16;
-
-fn tint_symbol(tint_symbol_1 : $type) -> $type {
- const tint_symbol_2 : $type = $type();
- let tint_symbol_3 : $type = tint_symbol_2;
- var tint_symbol_4 : $type = tint_symbol_3;
- return tint_symbol_4;
-}
-
-struct tint_symbol_5 {
- tint_symbol_2 : $type,
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeInitializer) {
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
- };
-
- auto src = expand(R"(
-enable f16;
-
-@fragment
-fn f() {
- var v : $type = $type();
-}
-)");
-
- auto expect = expand(R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
- var tint_symbol_1 : $type = $type();
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeConversion) {
- if (std::string_view(GetParam()) == "array") {
- return; // Cannot value convert arrays.
- }
-
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
- };
-
- auto src = expand(R"(
-enable f16;
-
-@fragment
-fn f() {
- var v : $type = $type($type());
-}
-)");
-
- auto expect = expand(R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
- var tint_symbol_1 : $type = $type($type());
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerBuiltinTypeTest, PreserveTypeExpression) {
- auto src = R"(
-enable f16;
-
-@fragment
-fn f() {
- var v : array<f32, 2> = array<f32, 2>();
-}
-)";
-
- auto expect = R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
- var tint_symbol_1 : array<f32, 2> = array<f32, 2>();
-}
-)";
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, RenameShadowedByAlias) {
- auto expand = [&](std::string_view source) {
- std::string_view ty = GetParam();
- auto out = tint::ReplaceAll(source, "$name", ty);
- out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
- out = tint::ReplaceAll(out, "$other_type", ty == "i32" ? "u32" : "i32");
- return out;
- };
-
- auto src = expand(R"(
-alias $name = $other_type;
-
-@fragment
-fn f() {
- var v : $other_type = $name();
-}
-)");
-
- auto expect = expand(R"(
-alias tint_symbol = $other_type;
-
-@fragment
-fn tint_symbol_1() {
- var tint_symbol_2 : $other_type = tint_symbol();
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, RenameShadowedByStruct) {
- auto expand = [&](std::string_view source) {
- std::string_view ty = GetParam();
- auto out = tint::ReplaceAll(source, "$name", ty);
- out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
- out = tint::ReplaceAll(out, "$other_type", ty == "i32" ? "u32" : "i32");
- return out;
- };
-
- auto src = expand(R"(
-struct $name {
- i : $other_type,
-}
-
-@fragment
-fn f() {
- var a = $name();
- var b = a.i;
-}
-)");
-
- auto expect = expand(R"(
-struct tint_symbol {
- tint_symbol_1 : $other_type,
-}
-
-@fragment
-fn tint_symbol_2() {
- var tint_symbol_3 = tint_symbol();
- var tint_symbol_4 = tint_symbol_3.tint_symbol_1;
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerBuiltinTypeTest,
- RenamerBuiltinTypeTest,
- testing::ValuesIn(ConstructableTypes()));
-
-/// @return WGSL builtin identifier keywords
-std::vector<std::string_view> Identifiers() {
- std::vector<std::string_view> out;
- for (auto ident : core::kBuiltinTypeStrings) {
- if (!tint::HasPrefix(ident, "__")) {
- out.push_back(ident);
- }
- }
- for (auto ident : core::kAddressSpaceStrings) {
- if (!tint::HasPrefix(ident, "_")) {
- out.push_back(ident);
- }
- }
- for (auto ident : core::kTexelFormatStrings) {
- out.push_back(ident);
- }
- for (auto ident : core::kAccessStrings) {
- out.push_back(ident);
- }
- return out;
-}
-
-using RenamerBuiltinIdentifierTest = TransformTestWithParam<std::string_view>;
-
-TEST_P(RenamerBuiltinIdentifierTest, GlobalConstName) {
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$name", GetParam());
- };
-
- auto src = expand(R"(
-const $name = 42;
-
-fn f() {
- const v = $name;
-}
-)");
-
- auto expect = expand(R"(
-const tint_symbol = 42;
-
-fn tint_symbol_1() {
- const tint_symbol_2 = tint_symbol;
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, LocalVarName) {
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$name", GetParam());
- };
-
- auto src = expand(R"(
-fn f() {
- var $name = 42;
-}
-)");
-
- auto expect = expand(R"(
-fn tint_symbol() {
- var tint_symbol_1 = 42;
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, FunctionName) {
- auto expand = [&](std::string_view source) {
- return tint::ReplaceAll(source, "$name", GetParam());
- };
-
- auto src = expand(R"(
-fn $name() {
-}
-
-fn f() {
- $name();
-}
-)");
-
- auto expect = expand(R"(
-fn tint_symbol() {
-}
-
-fn tint_symbol_1() {
- tint_symbol();
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, StructName) {
- auto expand = [&](std::string_view source) {
- std::string_view name = GetParam();
- auto out = tint::ReplaceAll(source, "$name", name);
- return tint::ReplaceAll(out, "$other_type", name == "i32" ? "u32" : "i32");
- };
-
- auto src = expand(R"(
-struct $name {
- i : $other_type,
-}
-
-fn f() {
- var x = $name();
-}
-)");
-
- auto expect = expand(R"(
-struct tint_symbol {
- tint_symbol_1 : $other_type,
-}
-
-fn tint_symbol_2() {
- var tint_symbol_3 = tint_symbol();
-}
-)");
-
- auto got = Run<Renamer>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerBuiltinIdentifierTest,
- RenamerBuiltinIdentifierTest,
- testing::ValuesIn(Identifiers()));
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/robustness.cc b/src/tint/lang/wgsl/ast/transform/robustness.cc
deleted file mode 100644
index 09c4d5a..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness.cc
+++ /dev/null
@@ -1,743 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/robustness.h"
-
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#include "src/tint/lang/core/type/memory_view.h"
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/load.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Robustness);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Robustness::Config);
-
-namespace tint::ast::transform {
-
-using namespace tint::core::fluent_types; // NOLINT
-using namespace tint::core::number_suffixes; // NOLINT
-
-/// PIMPL state for the transform
-struct Robustness::State {
- /// Constructor
- /// @param p the source program
- /// @param c the transform config
- State(const Program& p, Config&& c) : src(p), cfg(std::move(c)) {}
-
- /// Runs the transform
- /// @returns the new program or SkipTransform if the transform is not required
- ApplyResult Run() {
- if (HasAction(Action::kPredicate)) {
- AddPredicateParameters();
- }
-
- // Walk all the AST nodes in the module, starting with the leaf nodes.
- // The most deeply nested expressions will come first.
- for (auto* node : ctx.src->ASTNodes().Objects()) {
- Switch(
- node, //
- [&](const IndexAccessorExpression* e) {
- // obj[idx]
- // Array, matrix and vector indexing may require robustness transformation.
- auto* expr = sem.Get(e)->Unwrap()->As<sem::IndexAccessorExpression>();
- if (IsIgnoredResourceBinding(expr->Object()->RootIdentifier())) {
- return;
- }
- if (cfg.disable_runtime_sized_array_index_clamping &&
- IsIndexAccessingRuntimeSizedArray(expr)) {
- // Ensure the index is always u32 as using a negative index is an undefined
- // behavior in SPIRV.
- auto* idx = CastToU32(expr->Index());
- ctx.Replace(expr->Declaration()->index, idx);
- return;
- }
- switch (ActionFor(expr)) {
- case Action::kPredicate:
- PredicateIndexAccessor(expr);
- break;
- case Action::kClamp:
- ClampIndexAccessor(expr);
- break;
- case Action::kIgnore:
- break;
- }
- },
- [&](const IdentifierExpression* e) {
- // Identifiers may resolve to pointer lets, which may be predicated.
- // Inspect.
- if (auto* user = sem.Get<sem::VariableUser>(e)) {
- auto* v = user->Variable();
- if (v->Type()->Is<core::type::Pointer>()) {
- // Propagate predicate from pointer
- if (auto pred = predicates.Get(v->Declaration()->initializer)) {
- predicates.Add(e, *pred);
- }
- }
- }
- },
- [&](const AccessorExpression* e) {
- // obj.member
- // Propagate the predication from the object to this expression.
- if (auto pred = predicates.Get(e->object)) {
- predicates.Add(e, *pred);
- }
- },
- [&](const UnaryOpExpression* e) {
- // Includes address-of, or indirection
- // Propagate the predication from the inner expression to this expression.
- if (auto pred = predicates.Get(e->expr)) {
- predicates.Add(e, *pred);
- }
- },
- [&](const AssignmentStatement* s) {
- if (auto pred = predicates.Get(s->lhs)) {
- // Assignment target is predicated
- // Replace statement with condition on the predicate
- ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
- }
- },
- [&](const CompoundAssignmentStatement* s) {
- if (auto pred = predicates.Get(s->lhs)) {
- // Assignment expression is predicated
- // Replace statement with condition on the predicate
- ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
- }
- },
- [&](const IncrementDecrementStatement* s) {
- if (auto pred = predicates.Get(s->lhs)) {
- // Assignment expression is predicated
- // Replace statement with condition on the predicate
- ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
- }
- },
- [&](const CallExpression* e) {
- if (auto* call = sem.Get<sem::Call>(e)) {
- Switch(
- call->Target(), //
- [&](const sem::BuiltinFn* builtin) {
- // Calls to builtins may require robustness transformation.
- // Inspect.
- if (builtin->IsTexture()) {
- switch (cfg.texture_action) {
- case Action::kPredicate:
- PredicateTextureBuiltin(call, builtin);
- break;
- case Action::kClamp:
- ClampTextureBuiltin(call, builtin);
- break;
- case Action::kIgnore:
- break;
- }
- } else {
- MaybePredicateNonTextureBuiltin(call, builtin);
- }
- },
- [&](const sem::Function* fn) {
- // Calls to user function may require passing additional predicate
- // arguments.
- InsertPredicateArguments(call, fn);
- });
- }
- });
-
- // Check whether the node is an expression that:
- // * Has a predicate
- // * Is of a non-pointer or non-reference type
- // If the above is true, then we need to predicate evaluation of this expression by
- // replacing `expr` with `predicated_expr` and injecting the following above the
- // expression's statement:
- //
- // var predicated_expr : expr_ty;
- // if (predicate) {
- // predicated_expr = expr;
- // }
- //
- if (auto* expr = node->As<Expression>()) {
- if (auto pred = predicates.Get(expr)) {
- // Expression is predicated
- auto* sem_expr = sem.GetVal(expr);
- if (!sem_expr->Type()->Is<core::type::MemoryView>()) {
- auto pred_load = b.Symbols().New("predicated_expr");
- auto ty = CreateASTTypeFor(ctx, sem_expr->Type());
- hoist.InsertBefore(sem_expr->Stmt(), b.Decl(b.Var(pred_load, ty)));
- hoist.InsertBefore(
- sem_expr->Stmt(),
- b.If(*pred, b.Block(b.Assign(pred_load, ctx.Clone(expr)))));
- ctx.Replace(expr, b.Expr(pred_load));
-
- // The predication has been consumed for this expression.
- // Don't predicate expressions that use this expression.
- predicates.Remove(expr);
- }
- }
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
- }
-
- private:
- /// The source program
- const Program& src;
- /// The transform's config
- Config cfg;
- /// The target program builder
- ProgramBuilder b{};
- /// The clone context
- program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
- /// Helper for hoisting declarations
- HoistToDeclBefore hoist{ctx};
- /// Alias to the source program's semantic info
- const sem::Info& sem = ctx.src->Sem();
- /// Map of expression to predicate condition
- Hashmap<const Expression*, Symbol, 32> predicates{};
-
- /// @return the `u32` typed expression that represents the maximum indexable value for the index
- /// accessor @p expr, or nullptr if there is no robustness limit for this expression.
- const Expression* DynamicLimitFor(const sem::IndexAccessorExpression* expr) {
- auto* obj_type = expr->Object()->Type();
- return Switch(
- obj_type->UnwrapPtrOrRef(), //
- [&](const core::type::Vector* vec) -> const Expression* {
- if (expr->Index()->ConstantValue() || expr->Index()->Is<sem::Swizzle>()) {
- // Index and size is constant.
- // Validation will have rejected any OOB accesses.
- return nullptr;
- }
- return b.Expr(u32(vec->Width() - 1u));
- },
- [&](const core::type::Matrix* mat) -> const Expression* {
- if (expr->Index()->ConstantValue()) {
- // Index and size is constant.
- // Validation will have rejected any OOB accesses.
- return nullptr;
- }
- return b.Expr(u32(mat->Columns() - 1u));
- },
- [&](const core::type::Array* arr) -> const Expression* {
- if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
- // Size is unknown until runtime.
- // Must clamp, even if the index is constant.
-
- auto* arr_ptr = b.AddressOf(ctx.Clone(expr->Object()->Declaration()));
- return b.Sub(b.Call(wgsl::BuiltinFn::kArrayLength, arr_ptr), 1_u);
- }
- if (auto count = arr->ConstantCount()) {
- if (expr->Index()->ConstantValue()) {
- // Index and size is constant.
- // Validation will have rejected any OOB accesses.
- return nullptr;
- }
- return b.Expr(u32(count.value() - 1u));
- }
- // Note: Don't be tempted to use the array override variable as an expression here,
- // the name might be shadowed!
- b.Diagnostics().AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
- return nullptr;
- }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- /// Transform the program to insert additional predicate parameters to all user functions that
- /// have a pointer parameter type in an address space that has predicate action.
- void AddPredicateParameters() {
- for (auto* fn : src.AST().Functions()) {
- for (auto* param : fn->params) {
- auto* sem_param = sem.Get(param);
- if (auto* ptr = sem_param->Type()->As<core::type::Pointer>()) {
- if (ActionFor(ptr->AddressSpace()) == Action::kPredicate) {
- auto name = b.Symbols().New(param->name->symbol.Name() + "_predicate");
- ctx.InsertAfter(fn->params, param, b.Param(name, b.ty.bool_()));
-
- // Associate the pointer parameter expressions with the predicate.
- for (auto* user : sem_param->Users()) {
- predicates.Add(user->Declaration(), name);
- }
- }
- }
- }
- }
- }
-
- /// Transforms call expressions to user functions, inserting additional predicate arguments
- /// after all pointer parameters with a type in an address space that has predicate action.
- void InsertPredicateArguments(const sem::Call* call, const sem::Function* fn) {
- auto* expr = call->Declaration();
- for (size_t i = 0; i < fn->Parameters().Length(); i++) {
- auto* param = fn->Parameters()[i];
- if (auto* ptr = param->Type()->As<core::type::Pointer>()) {
- if (ActionFor(ptr->AddressSpace()) == Action::kPredicate) {
- auto* arg = expr->args[i];
- if (auto predicate = predicates.Get(arg)) {
- ctx.InsertAfter(expr->args, arg, b.Expr(*predicate));
- } else {
- ctx.InsertAfter(expr->args, arg, b.Expr(true));
- }
- }
- }
- }
- }
-
- /// Applies predication to the index on an array, vector or matrix.
- /// @param expr the index accessor expression.
- void PredicateIndexAccessor(const sem::IndexAccessorExpression* expr) {
- auto* obj = expr->Object()->Declaration();
- auto* idx = expr->Index()->Declaration();
- auto* max = DynamicLimitFor(expr);
- if (!max) {
- // robustness is not required
- // Just propagate predicate from object
- if (auto pred = predicates.Get(obj)) {
- predicates.Add(expr->Declaration(), *pred);
- }
- return;
- }
-
- auto* stmt = expr->Stmt();
- auto& obj_pred = predicates.GetOrAddZero(obj);
-
- auto idx_let = b.Symbols().New("index");
- auto pred = b.Symbols().New("predicate");
-
- hoist.InsertBefore(stmt, b.Decl(b.Let(idx_let, ctx.Clone(idx))));
- ctx.Replace(idx, b.Expr(idx_let));
-
- auto* cond = b.LessThanEqual(b.Call<u32>(b.Expr(idx_let)), max);
- if (obj_pred.IsValid()) {
- cond = b.And(b.Expr(obj_pred), cond);
- }
- hoist.InsertBefore(stmt, b.Decl(b.Let(pred, cond)));
-
- predicates.Add(expr->Declaration(), pred);
- }
-
- /// Applies bounds clamping to the index on an array, vector or matrix.
- /// @param expr the index accessor expression.
- void ClampIndexAccessor(const sem::IndexAccessorExpression* expr) {
- auto* max = DynamicLimitFor(expr);
- if (!max) {
- return; // robustness is not required
- }
-
- auto* expr_sem = expr->Unwrap()->As<sem::IndexAccessorExpression>();
- auto idx = CastToU32(expr_sem->Index());
- auto* clamped_idx = b.Call(wgsl::BuiltinFn::kMin, idx, max);
- ctx.Replace(expr->Declaration()->index, clamped_idx);
- }
-
- /// Applies predication to the non-texture builtin call, if required.
- void MaybePredicateNonTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
- // Gather the predications for the builtin arguments
- const Expression* predicate = nullptr;
- for (auto* arg : call->Declaration()->args) {
- if (auto pred = predicates.Get(arg)) {
- predicate = And(predicate, b.Expr(*pred));
- }
- }
-
- if (predicate) {
- if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupUniformLoad) {
- // https://www.w3.org/TR/WGSL/#workgroupUniformLoad-builtin:
- // "Executes a control barrier synchronization function that affects memory and
- // atomic operations in the workgroup address space."
- // Because the call acts like a control barrier, we need to make sure that we still
- // trigger a workgroup barrier if the predicate fails.
- PredicateCall(call, predicate,
- b.Block(b.CallStmt(b.Call(wgsl::BuiltinFn::kWorkgroupBarrier))));
- } else {
- PredicateCall(call, predicate);
- }
- }
- }
-
- /// Applies predication to texture builtins, based on whether the coordinates, array index and
- /// level arguments are all in bounds.
- void PredicateTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
- if (!TextureBuiltinNeedsRobustness(builtin->Fn())) {
- return;
- }
-
- auto* expr = call->Declaration();
- auto* stmt = call->Stmt();
-
- // Indices of the mandatory texture and coords parameters, and the optional
- // array and level parameters.
- auto& signature = builtin->Signature();
- auto texture_arg_idx = signature.IndexOf(core::ParameterUsage::kTexture);
- auto coords_arg_idx = signature.IndexOf(core::ParameterUsage::kCoords);
- auto array_arg_idx = signature.IndexOf(core::ParameterUsage::kArrayIndex);
- auto level_arg_idx = signature.IndexOf(core::ParameterUsage::kLevel);
-
- auto* texture_arg = expr->args[static_cast<size_t>(texture_arg_idx)];
-
- // Build the builtin predicate from the arguments
- const Expression* predicate = nullptr;
-
- Symbol level_idx, num_levels;
- if (level_arg_idx >= 0) {
- auto* param = builtin->Parameters()[static_cast<size_t>(level_arg_idx)];
- if (param->Type()->IsIntegerScalar()) {
- // let level_idx = u32(level-arg);
- level_idx = b.Symbols().New("level_idx");
- auto* arg = expr->args[static_cast<size_t>(level_arg_idx)];
- hoist.InsertBefore(stmt,
- b.Decl(b.Let(level_idx, CastToUnsigned(ctx.Clone(arg), 1u))));
-
- // let num_levels = textureNumLevels(texture-arg);
- num_levels = b.Symbols().New("num_levels");
- hoist.InsertBefore(
- stmt, b.Decl(b.Let(num_levels, b.Call(wgsl::BuiltinFn::kTextureNumLevels,
- ctx.Clone(texture_arg)))));
-
- // predicate: level_idx < num_levels
- predicate = And(predicate, b.LessThan(level_idx, num_levels));
-
- // Replace the level argument with `level_idx`
- ctx.Replace(arg, b.Expr(level_idx));
- }
- }
-
- Symbol coords;
- if (coords_arg_idx >= 0) {
- auto* param = builtin->Parameters()[static_cast<size_t>(coords_arg_idx)];
- if (param->Type()->IsIntegerScalarOrVector()) {
- // let coords = u32(coords-arg)
- coords = b.Symbols().New("coords");
- auto* arg = expr->args[static_cast<size_t>(coords_arg_idx)];
- hoist.InsertBefore(stmt,
- b.Decl(b.Let(coords, CastToUnsigned(b.Expr(ctx.Clone(arg)),
- WidthOf(param->Type())))));
-
- // predicate: all(coords < textureDimensions(texture))
- auto* dimensions =
- level_idx.IsValid()
- ? b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg),
- b.Call(wgsl::BuiltinFn::kMin, b.Expr(level_idx),
- b.Sub(num_levels, 1_a)))
- : b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg));
- predicate =
- And(predicate, b.Call(wgsl::BuiltinFn::kAll, b.LessThan(coords, dimensions)));
-
- // Replace the level argument with `coord`
- ctx.Replace(arg, b.Expr(coords));
- }
- }
-
- if (array_arg_idx >= 0) {
- // let array_idx = u32(array-arg)
- auto* arg = expr->args[static_cast<size_t>(array_arg_idx)];
- auto* num_layers = b.Call(wgsl::BuiltinFn::kTextureNumLayers, ctx.Clone(texture_arg));
- auto array_idx = b.Symbols().New("array_idx");
- hoist.InsertBefore(stmt, b.Decl(b.Let(array_idx, CastToUnsigned(ctx.Clone(arg), 1u))));
-
- // predicate: array_idx < textureNumLayers(texture)
- predicate = And(predicate, b.LessThan(array_idx, num_layers));
-
- // Replace the array index argument with `array_idx`
- ctx.Replace(arg, b.Expr(array_idx));
- }
-
- if (predicate) {
- PredicateCall(call, predicate);
- }
- }
-
- /// Applies bounds clamping to the coordinates, array index and level arguments of the texture
- /// builtin.
- void ClampTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
- if (!TextureBuiltinNeedsRobustness(builtin->Fn())) {
- return;
- }
-
- auto* expr = call->Declaration();
- auto* stmt = call->Stmt();
-
- // Indices of the mandatory texture and coords parameters, and the optional
- // array and level parameters.
- auto& signature = builtin->Signature();
- auto texture_arg_idx = signature.IndexOf(core::ParameterUsage::kTexture);
- auto coords_arg_idx = signature.IndexOf(core::ParameterUsage::kCoords);
- auto array_arg_idx = signature.IndexOf(core::ParameterUsage::kArrayIndex);
- auto level_arg_idx = signature.IndexOf(core::ParameterUsage::kLevel);
-
- auto* texture_arg = expr->args[static_cast<size_t>(texture_arg_idx)];
-
- // If the level is provided, then we need to clamp this. As the level is used by
- // textureDimensions() and the texture[Load|Store]() calls, we need to clamp both usages.
- Symbol level_idx;
- if (level_arg_idx >= 0) {
- const auto* param = builtin->Parameters()[static_cast<size_t>(level_arg_idx)];
- if (param->Type()->IsIntegerScalar()) {
- const auto* arg = expr->args[static_cast<size_t>(level_arg_idx)];
- level_idx = b.Symbols().New("level_idx");
- const auto* num_levels =
- b.Call(wgsl::BuiltinFn::kTextureNumLevels, ctx.Clone(texture_arg));
- const auto* max = b.Sub(num_levels, 1_a);
- hoist.InsertBefore(
- stmt, b.Decl(b.Let(level_idx, b.Call(wgsl::BuiltinFn::kMin,
- b.Call<u32>(ctx.Clone(arg)), max))));
- ctx.Replace(arg, b.Expr(level_idx));
- }
- }
-
- // Clamp the coordinates argument
- if (coords_arg_idx >= 0) {
- const auto* param = builtin->Parameters()[static_cast<size_t>(coords_arg_idx)];
- if (param->Type()->IsIntegerScalarOrVector()) {
- auto* arg = expr->args[static_cast<size_t>(coords_arg_idx)];
- const auto width = WidthOf(param->Type());
- const auto* dimensions =
- level_idx.IsValid()
- ? b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg),
- level_idx)
- : b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg));
-
- // dimensions is u32 or vecN<u32>
- const auto* unsigned_max = b.Sub(dimensions, ScalarOrVec(b.Expr(1_a), width));
- if (param->Type()->IsSignedIntegerScalarOrVector()) {
- const auto* zero = ScalarOrVec(b.Expr(0_a), width);
- const auto* signed_max = CastToSigned(unsigned_max, width);
- ctx.Replace(arg,
- b.Call(wgsl::BuiltinFn::kClamp, ctx.Clone(arg), zero, signed_max));
- } else {
- ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kMin, ctx.Clone(arg), unsigned_max));
- }
- }
- }
-
- // Clamp the array_index argument, if provided
- if (array_arg_idx >= 0) {
- auto* param = builtin->Parameters()[static_cast<size_t>(array_arg_idx)];
- auto* arg = expr->args[static_cast<size_t>(array_arg_idx)];
- auto* num_layers = b.Call(wgsl::BuiltinFn::kTextureNumLayers, ctx.Clone(texture_arg));
-
- const auto* unsigned_max = b.Sub(num_layers, 1_a);
- if (param->Type()->IsSignedIntegerScalar()) {
- const auto* signed_max = CastToSigned(unsigned_max, 1u);
- ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kClamp, ctx.Clone(arg), 0_a, signed_max));
- } else {
- ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kMin, ctx.Clone(arg), unsigned_max));
- }
- }
- }
-
- /// @param type builtin type
- /// @returns true if the given builtin is a texture function that requires predication or
- /// clamping of arguments.
- bool TextureBuiltinNeedsRobustness(wgsl::BuiltinFn type) {
- return type == wgsl::BuiltinFn::kTextureLoad || type == wgsl::BuiltinFn::kTextureDimensions;
- }
-
- /// @returns a bitwise and of the two expressions, or the other expression if one is null.
- const Expression* And(const Expression* lhs, const Expression* rhs) {
- if (lhs && rhs) {
- return b.And(lhs, rhs);
- }
- if (lhs) {
- return lhs;
- }
- return rhs;
- }
-
- /// Transforms a call statement or expression so that the expression is predicated by @p
- /// predicate.
- /// @param else_stmt - the statement to execute for the predication failure
- void PredicateCall(const sem::Call* call,
- const Expression* predicate,
- const BlockStatement* else_stmt = nullptr) {
- auto* expr = call->Declaration();
- auto* stmt = call->Stmt();
- auto* call_stmt = stmt->Declaration()->As<CallStatement>();
- if (call_stmt && call_stmt->expr == expr) {
- // Wrap the statement in an if-statement with the predicate condition.
- hoist.Replace(stmt, b.If(predicate, b.Block(ctx.Clone(stmt->Declaration())),
- ProgramBuilder::ElseStmt(else_stmt)));
- } else {
- // Emit the following before the expression's statement:
- // var predicated_value : return-type;
- // if (predicate) {
- // predicated_value = call(...);
- // }
- auto value = b.Symbols().New("predicated_value");
- hoist.InsertBefore(stmt, b.Decl(b.Var(value, CreateASTTypeFor(ctx, call->Type()))));
- hoist.InsertBefore(stmt, b.If(predicate, b.Block(b.Assign(value, ctx.Clone(expr))),
- ProgramBuilder::ElseStmt(else_stmt)));
-
- // Replace the call expression with `predicated_value`
- ctx.Replace(expr, b.Expr(value));
- }
- }
-
- /// @returns true if @p action is enabled for any address space
- bool HasAction(Action action) const {
- return action == cfg.function_action || //
- action == cfg.texture_action || //
- action == cfg.private_action || //
- action == cfg.immediate_action || //
- action == cfg.storage_action || //
- action == cfg.uniform_action || //
- action == cfg.workgroup_action;
- }
-
- /// @returns the robustness action to perform for an OOB access with the expression @p expr
- Action ActionFor(const sem::ValueExpression* expr) {
- return Switch(
- expr->Type(), //
- [&](const core::type::Reference* t) { return ActionFor(t->AddressSpace()); },
- [&](Default) { return cfg.value_action; });
- }
-
- /// @returns the robustness action to perform for an OOB access in the address space @p
- /// address_space
- Action ActionFor(core::AddressSpace address_space) {
- switch (address_space) {
- case core::AddressSpace::kFunction:
- return cfg.function_action;
- case core::AddressSpace::kHandle:
- return cfg.texture_action;
- case core::AddressSpace::kPrivate:
- return cfg.private_action;
- case core::AddressSpace::kImmediate:
- return cfg.immediate_action;
- case core::AddressSpace::kStorage:
- return cfg.storage_action;
- case core::AddressSpace::kUniform:
- return cfg.uniform_action;
- case core::AddressSpace::kWorkgroup:
- return cfg.workgroup_action;
- default:
- break;
- }
- TINT_UNREACHABLE() << "unhandled address space" << address_space;
- }
-
- /// @returns the vector width of @p ty, or 1 if @p ty is not a vector
- static uint32_t WidthOf(const core::type::Type* ty) {
- if (auto* vec = ty->As<core::type::Vector>()) {
- return vec->Width();
- }
- return 1u;
- }
-
- /// @returns a scalar or vector type with the element type @p scalar and width @p width
- Type ScalarOrVecTy(Type scalar, uint32_t width) const {
- if (width > 1) {
- return b.ty.vec(scalar, width);
- }
- return scalar;
- }
-
- /// @returns a vector constructed with the scalar expression @p scalar if @p width > 1,
- /// otherwise returns @p scalar.
- const Expression* ScalarOrVec(const Expression* scalar, uint32_t width) {
- if (width > 1) {
- return b.Call(b.ty.vec<Infer>(width), scalar);
- }
- return scalar;
- }
-
- /// @returns @p val cast to a `vecN<i32>`, where `N` is @p width, or cast to i32 if @p width
- /// is 1.
- const CallExpression* CastToSigned(const Expression* val, uint32_t width) {
- return b.Call(ScalarOrVecTy(b.ty.i32(), width), val);
- }
-
- /// @returns @p val cast to a `vecN<u32>`, where `N` is @p width, or cast to u32 if @p width
- /// is 1.
- const CallExpression* CastToUnsigned(const Expression* val, uint32_t width) {
- return b.Call(ScalarOrVecTy(b.ty.u32(), width), val);
- }
-
- /// @returns true if the variable represents a resource binding that should be ignored in the
- /// robustness check.
- /// TODO(tint:1890): make this function work with unrestricted pointer paramters. Note that this
- /// depends on transform::DirectVariableAccess to have been run first.
- bool IsIgnoredResourceBinding(const sem::Variable* variable) const {
- auto* globalVariable = tint::As<sem::GlobalVariable>(variable);
- if (globalVariable == nullptr) {
- return false;
- }
- auto binding_point = globalVariable->Attributes().binding_point;
- if (!binding_point.has_value()) {
- return false;
- }
- return cfg.bindings_ignored.find(*binding_point) != cfg.bindings_ignored.cend();
- }
-
- /// @returns true if expr is an IndexAccessorExpression whose object is a runtime-sized array.
- bool IsIndexAccessingRuntimeSizedArray(const sem::IndexAccessorExpression* expr) {
- auto* array_type = expr->Object()->Type()->UnwrapPtrOrRef()->As<core::type::Array>();
- return array_type != nullptr && array_type->Count()->Is<core::type::RuntimeArrayCount>();
- }
-
- /// @returns a clone of expr->Declaration() if it is an unsigned integer scalar, or
- /// expr->Declaration() cast to u32.
- const ast::Expression* CastToU32(const sem::ValueExpression* expr) {
- auto* idx = ctx.Clone(expr->Declaration());
- if (expr->Type()->IsUnsignedIntegerScalar()) {
- return idx;
- }
- return b.Call<u32>(idx); // u32(idx)
- }
-};
-
-Robustness::Config::Config() = default;
-Robustness::Config::Config(const Config&) = default;
-Robustness::Config::~Config() = default;
-Robustness::Config& Robustness::Config::operator=(const Config&) = default;
-
-Robustness::Robustness() = default;
-Robustness::~Robustness() = default;
-
-Transform::ApplyResult Robustness::Apply(const Program& src,
- const DataMap& inputs,
- DataMap&) const {
- Config cfg;
- if (auto* cfg_data = inputs.Get<Config>()) {
- cfg = *cfg_data;
- }
-
- return State{src, std::move(cfg)}.Run();
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/robustness.h b/src/tint/lang/wgsl/ast/transform/robustness.h
deleted file mode 100644
index 174e508..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
-
-#include <unordered_set>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// This transform is responsible for ensuring that all out of bounds accesses are prevented,
-/// either by conditioning the access (predication) or through clamping of the index to keep the
-/// access in bounds.
-/// @note Robustness must come after:
-/// * PromoteSideEffectsToDecl as Robustness requires side-effecting expressions to be hoisted
-/// to their own statements.
-/// Robustness must come before:
-/// * BuiltinPolyfill as 'clamp' and binary operators may need to be polyfilled.
-/// * CanonicalizeEntryPointIO as the transform does not support the 'in' and 'out' address
-/// spaces.
-class Robustness final : public Castable<Robustness, Transform> {
- public:
- /// Robustness action for out-of-bounds indexing.
- enum class Action {
- /// Do nothing to prevent the out-of-bounds action.
- kIgnore,
- /// Clamp the index to be within bounds.
- kClamp,
- /// Do not execute the read or write if the index is out-of-bounds.
- kPredicate,
-
- /// The default action
- kDefault = kClamp,
- };
-
- /// Configuration options for the transform
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- Config();
-
- /// Copy constructor
- Config(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// Assignment operator
- /// @returns this Config
- Config& operator=(const Config&);
-
- /// Robustness action for values
- Action value_action = Action::kDefault;
-
- /// Robustness action for non-sampling texture operations
- Action texture_action = Action::kDefault;
-
- /// Robustness action for variables in the 'function' address space
- Action function_action = Action::kDefault;
- /// Robustness action for variables in the 'private' address space
- Action private_action = Action::kDefault;
- /// Robustness action for variables in the 'immediate' address space
- Action immediate_action = Action::kDefault;
- /// Robustness action for variables in the 'storage' address space
- Action storage_action = Action::kDefault;
- /// Robustness action for variables in the 'uniform' address space
- Action uniform_action = Action::kDefault;
- /// Robustness action for variables in the 'workgroup' address space
- Action workgroup_action = Action::kDefault;
-
- /// Bindings that should always be applied Actions::kIgnore on
- std::unordered_set<tint::BindingPoint> bindings_ignored;
-
- /// If we should disable index clamping on runtime-sized arrays in robustness transform
- bool disable_runtime_sized_array_index_clamping = false;
- };
-
- /// Constructor
- Robustness();
- /// Destructor
- ~Robustness() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/robustness_test.cc b/src/tint/lang/wgsl/ast/transform/robustness_test.cc
deleted file mode 100644
index 51da92f..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness_test.cc
+++ /dev/null
@@ -1,5346 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/robustness.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-
-static std::ostream& operator<<(std::ostream& out, Robustness::Action action) {
- switch (action) {
- case Robustness::Action::kIgnore:
- return out << "ignore";
- case Robustness::Action::kClamp:
- return out << "clamp";
- case Robustness::Action::kPredicate:
- return out << "predicate";
- }
- return out << "unknown";
-}
-
-namespace {
-
-DataMap Config(Robustness::Action action, bool disable_runtime_sized_array_index_clamping = false) {
- Robustness::Config cfg;
- cfg.value_action = action;
- cfg.texture_action = action;
- cfg.function_action = action;
- cfg.private_action = action;
- cfg.immediate_action = action;
- cfg.storage_action = action;
- cfg.uniform_action = action;
- cfg.workgroup_action = action;
- cfg.disable_runtime_sized_array_index_clamping = disable_runtime_sized_array_index_clamping;
- DataMap data;
- data.Add<Robustness::Config>(cfg);
- return data;
-}
-
-const char* Expect(Robustness::Action action,
- const char* expect_ignore,
- const char* expect_clamp,
- const char* expect_predicate) {
- switch (action) {
- case Robustness::Action::kIgnore:
- return expect_ignore;
- case Robustness::Action::kClamp:
- return expect_clamp;
- case Robustness::Action::kPredicate:
- return expect_predicate;
- }
- return "<invalid action>";
-}
-
-using RobustnessTest = TransformTestWithParam<Robustness::Action>;
-
-////////////////////////////////////////////////////////////////////////////////
-// Constant sized array
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLiteral) {
- auto* src = R"(
-fn f() {
- var b : f32 = array<f32, 3>()[1i];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithConst) {
- auto* src = R"(
-const c : u32 = 1u;
-
-fn f() {
- let b : f32 = array<f32, 3>()[c];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLet) {
- auto* src = R"(
-fn f() {
- let l : u32 = 1u;
- let b : f32 = array<f32, 3>()[l];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let l : u32 = 1u;
- let b : f32 = array<f32, 3>()[min(l, 2u)];
-}
-)",
- /* predicate */ R"(
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = array<f32, 3>()[index];
- }
- let b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeArrayIndex) {
- auto* src = R"(
-var<private> i : u32;
-
-fn f() {
- let a = array<f32, 3>();
- let b = array<i32, 5>();
- var c : f32 = a[b[i]];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> i : u32;
-
-fn f() {
- let a = array<f32, 3>();
- let b = array<i32, 5>();
- var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> i : u32;
-
-fn f() {
- let a = array<f32, 3>();
- let b = array<i32, 5>();
- let index = i;
- let predicate = (u32(index) <= 4u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = b[index];
- }
- let index_1 = predicated_expr;
- let predicate_1 = (u32(index_1) <= 2u);
- var predicated_expr_1 : f32;
- if (predicate_1) {
- predicated_expr_1 = a[index_1];
- }
- var c : f32 = predicated_expr_1;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeExpression) {
- auto* src = R"(
-var<private> c : i32;
-
-fn f() {
- var b : f32 = array<f32, 3>()[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> c : i32;
-
-fn f() {
- var b : f32 = array<f32, 3>()[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = array<f32, 3>()[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysVal_IndexWithRuntimeExpressions) {
- auto* src = R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let a = array<array<array<f32, 1>, 2>, 3>();
- var r = a[x][y][z];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let a = array<array<array<f32, 1>, 2>, 3>();
- var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
-}
-)",
- /* predicate */ R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let a = array<array<array<f32, 1>, 2>, 3>();
- let index = x;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : array<array<f32, 1u>, 2u>;
- if (predicate) {
- predicated_expr = a[index];
- }
- let index_1 = y;
- let predicate_1 = (u32(index_1) <= 1u);
- var predicated_expr_1 : array<f32, 1u>;
- if (predicate_1) {
- predicated_expr_1 = predicated_expr[index_1];
- }
- let index_2 = z;
- let predicate_2 = (u32(index_2) <= 0u);
- var predicated_expr_2 : f32;
- if (predicate_2) {
- predicated_expr_2 = predicated_expr_1[index_2];
- }
- var r = predicated_expr_2;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithOverride) {
- auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
- let a = array<f32, 4>();
- var b : f32 = a[idx];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- let a = array<f32, 4>();
- var b : f32 = a[min(u32(idx), 3u)];
-}
-)",
- /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- let a = array<f32, 4>();
- let index = idx;
- let predicate = (u32(index) <= 3u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLiteral) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- var b : f32 = a[1i];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithConst) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-const c : u32 = 1u;
-
-fn f() {
- let b : f32 = a[c];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let b : f32 = a[l];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let b : f32 = a[min(l, 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- let b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndex) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- var c : f32 = a[b[i]];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let index = i;
- let predicate = (u32(index) <= 4u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = b[index];
- }
- let index_1 = predicated_expr;
- let predicate_1 = (u32(index_1) <= 2u);
- var predicated_expr_1 : f32;
- if (predicate_1) {
- predicated_expr_1 = a[index_1];
- }
- var c : f32 = predicated_expr_1;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndexViaPointerIndex) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let p1 = &(a);
- let p2 = &(b);
- var c : f32 = p1[p2[i]];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let p1 = &(a);
- let p2 = &(b);
- var c : f32 = p1[min(u32(p2[min(i, 4u)]), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let p1 = &(a);
- let p2 = &(b);
- let index = i;
- let predicate = (u32(index) <= 4u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = p2[index];
- }
- let index_1 = predicated_expr;
- let predicate_1 = (u32(index_1) <= 2u);
- var predicated_expr_1 : f32;
- if (predicate_1) {
- predicated_expr_1 = p1[index_1];
- }
- var c : f32 = predicated_expr_1;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeExpression) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysRef_IndexWithRuntimeExpressions) {
- auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- var r = a[x][y][z];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let index = x;
- let predicate = (u32(index) <= 2u);
- let index_1 = y;
- let predicate_1 = (predicate & (u32(index_1) <= 1u));
- let index_2 = z;
- let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
- var predicated_expr : f32;
- if (predicate_2) {
- predicated_expr = a[index][index_1][index_2];
- }
- var r = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithOverride) {
- auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : array<f32, 4>;
- var b : f32 = a[idx];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : array<f32, 4>;
- var b : f32 = a[min(u32(idx), 3u)];
-}
-)",
- /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : array<f32, 4>;
- let index = idx;
- let predicate = (u32(index) <= 3u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let p = &(a[l]);
- let f : f32 = *(p);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let p = &(a[min(l, 2u)]);
- let f : f32 = *(p);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- let p = &(a[index]);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = *(p);
- }
- let f : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let p0 = &((*(pb))[i]);
- let p1 = &(a[*(p0)]);
- var x : f32 = *(p1);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let p0 = &((*(pb))[min(i, 4u)]);
- let p1 = &(a[min(u32(*(p0)), 2u)]);
- var x : f32 = *(p1);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let index = i;
- let predicate = (u32(index) <= 4u);
- let p0 = &((*(pb))[index]);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = *(p0);
- }
- let index_1 = predicated_expr;
- let predicate_1 = (u32(index_1) <= 2u);
- let p1 = &(a[index_1]);
- var predicated_expr_1 : f32;
- if (predicate_1) {
- predicated_expr_1 = *(p1);
- }
- var x : f32 = predicated_expr_1;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
- auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[x]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[z]);
- var r = *(p3);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[min(u32(x), 2u)]);
- let p2 = &((*(p1))[min(u32(y), 1u)]);
- let p3 = &((*(p2))[min(u32(z), 0u)]);
- var r = *(p3);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let index = x;
- let predicate = (u32(index) <= 2u);
- let p1 = &((*(p0))[index]);
- let index_1 = y;
- let predicate_1 = (predicate & (u32(index_1) <= 1u));
- let p2 = &((*(p1))[index_1]);
- let index_2 = z;
- let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
- let p3 = &((*(p2))[index_2]);
- var predicated_expr : f32;
- if (predicate_2) {
- predicated_expr = *(p3);
- }
- var r = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArrays_MixedAccess) {
- auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[x]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[z]);
- var r = *(p3);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[min(u32(x), 2u)]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[min(u32(z), 0u)]);
- var r = *(p3);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let index = x;
- let predicate = (u32(index) <= 2u);
- let p1 = &((*(p0))[index]);
- let p2 = &((*(p1))[y]);
- let index_1 = z;
- let predicate_1 = (predicate & (u32(index_1) <= 0u));
- let p3 = &((*(p2))[index_1]);
- var predicated_expr : f32;
- if (predicate_1) {
- predicated_expr = *(p3);
- }
- var r = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArray_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[l] = 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[min(l, 2u)] = 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- if (predicate) {
- a[index] = 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let p = &(a[l]);
- *(p) = 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let p = &(a[min(l, 2u)]);
- *(p) = 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- let p = &(a[index]);
- if (predicate) {
- *(p) = 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let p0 = &((*(pb))[i]);
- let p1 = &(a[*(p0)]);
- *(p1) = 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let p0 = &((*(pb))[min(i, 4u)]);
- let p1 = &(a[min(u32(*(p0)), 2u)]);
- *(p1) = 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
- let pa = &(a);
- let pb = &(b);
- let index = i;
- let predicate = (u32(index) <= 4u);
- let p0 = &((*(pb))[index]);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = *(p0);
- }
- let index_1 = predicated_expr;
- let predicate_1 = (u32(index_1) <= 2u);
- let p1 = &(a[index_1]);
- if (predicate_1) {
- *(p1) = 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
- auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[x]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[z]);
- *(p3) = 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[min(u32(x), 2u)]);
- let p2 = &((*(p1))[min(u32(y), 1u)]);
- let p3 = &((*(p2))[min(u32(z), 0u)]);
- *(p3) = 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
- let p0 = &(a);
- let index = x;
- let predicate = (u32(index) <= 2u);
- let p1 = &((*(p0))[index]);
- let index_1 = y;
- let predicate_1 = (predicate & (u32(index_1) <= 1u));
- let p2 = &((*(p1))[index_1]);
- let index_2 = z;
- let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
- let p3 = &((*(p2))[index_2]);
- if (predicate_2) {
- *(p3) = 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_NestedConstantSizedArrays_MixedAccess) {
- auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[x]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[z]);
- *(p3) = 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let p1 = &((*(p0))[min(u32(x), 2u)]);
- let p2 = &((*(p1))[y]);
- let p3 = &((*(p2))[min(u32(z), 0u)]);
- *(p3) = 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
- let p0 = &(a);
- let index = x;
- let predicate = (u32(index) <= 2u);
- let p1 = &((*(p0))[index]);
- let p2 = &((*(p1))[y]);
- let index_1 = z;
- let predicate_1 = (predicate & (u32(index_1) <= 0u));
- let p3 = &((*(p2))[index_1]);
- if (predicate_1) {
- *(p3) = 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, CompoundAssign_ConstantSizedArray_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[l] += 42.0f;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[min(l, 2u)] += 42.0f;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- if (predicate) {
- a[index] += 42.0f;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Increment_ConstantSizedArray_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[l]++;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- a[min(l, 2u)]++;
-}
-)",
- /* predicate */ R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
- let l : u32 = 1u;
- let index = l;
- let predicate = (u32(index) <= 2u);
- if (predicate) {
- a[index]++;
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Runtime sized array
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_RuntimeArray_IndexWithLiteral) {
- auto* src = R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
- var d : f32 = s.b[25];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
- var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))];
-}
-)",
- /* predicate */ R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
- let index = 25;
- let predicate = (u32(index) <= (arrayLength(&(s.b)) - 1u));
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = s.b[index];
- }
- var d : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Vector
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithLiteral) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-fn f() {
- var b : f32 = a[1i];
-}
-)";
-
- auto* expect = src;
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithConst) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-fn f() {
- const i = 1;
- var b : f32 = a[i];
-}
-)";
-
- auto* expect = src;
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithLet) {
- auto* src = R"(
-fn f() {
- let i = 99;
- let v = vec4<f32>()[i];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let i = 99;
- let v = vec4<f32>()[min(u32(i), 3u)];
-}
-)",
- /* predicate */ R"(
-fn f() {
- let i = 99;
- let index = i;
- let predicate = (u32(index) <= 3u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = vec4<f32>()[index];
- }
- let v = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression_ViaPointerIndex) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- var b : f32 = p[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- var b : f32 = p[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = p[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithGlobalVar) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a.xy[c];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a.xy[min(u32(c), 1u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let index = c;
- let predicate = (u32(index) <= 1u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a.xy[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a.xy[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 1u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a.xy[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression_ViaPointerDot) {
- auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- var b : f32 = p.xy[((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- var b : f32 = p.xy[min(u32(((c + 2) - 3)), 1u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let p = &(a);
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 1u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = p.xy[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithOverride) {
- auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : vec3<f32>;
- var b : f32 = a[idx];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : vec3<f32>;
- var b : f32 = a[min(u32(idx), 2u)];
-}
-)",
- /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : vec3<f32>;
- let index = idx;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Matrix
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexingWithLiterals) {
- auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-fn f() {
- var b : f32 = a[2i][1i];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithRuntimeExpressionThenLiteral) {
- auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[((c + 2) - 3)][1];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
-}
-)",
- /* predicate */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index][1];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenRuntimeExpression) {
- auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[1][((c + 2) - 3)];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
-}
-)",
- /* predicate */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
- let index = ((c + 2) - 3);
- let predicate = (u32(index) <= 1u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[1][index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithOverrideThenLiteral) {
- auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- var b : f32 = a[idx][1];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- var b : f32 = a[min(u32(idx), 2u)][1];
-}
-)",
- /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- let index = idx;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index][1];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLetThenSwizzle) {
- auto* src = R"(
-fn f() {
- let i = 1;
- var m = mat3x2<f32>();
- var v = m[i].yx;
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let i = 1;
- var m = mat3x2<f32>();
- var v = m[min(u32(i), 2u)].yx;
-}
-)",
- /* predicate */ R"(
-fn f() {
- let i = 1;
- var m = mat3x2<f32>();
- let index = i;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : vec2<f32>;
- if (predicate) {
- predicated_expr = m[index];
- }
- var v = predicated_expr.yx;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenOverride) {
- auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- var b : f32 = a[1][idx];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- var b : f32 = a[1][min(u32(idx), 1u)];
-}
-)",
- /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
- var a : mat3x2<f32>;
- let index = idx;
- let predicate = (u32(index) <= 1u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[1][index];
- }
- var b : f32 = predicated_expr;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_Matrix_IndexWithLet) {
- auto* src = R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- m[c] = vec4f(1);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- m[min(u32(c), 2u)] = vec4f(1);
-}
-)",
- /* predicate */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- let index = c;
- let predicate = (u32(index) <= 2u);
- if (predicate) {
- m[index] = vec4f(1);
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, CompoundAssign_Matrix_IndexWithLet) {
- auto* src = R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- m[c] += vec4f(1);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- m[min(u32(c), 2u)] += vec4f(1);
-}
-)",
- /* predicate */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
- let c = 1;
- let index = c;
- let predicate = (u32(index) <= 2u);
- if (predicate) {
- m[index] += vec4f(1);
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Texture builtin calls.
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, TextureDimensions) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions() {
- let l = textureDimensions(t);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureDimensions_Level) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
- let l = textureDimensions(t, level);
-}
-
-fn dimensions_unsigned(level : u32) {
- let l = textureDimensions(t, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- dimensions_signed(i32(non_uniform.x));
- dimensions_unsigned(u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureDimensions(t, level_idx);
-}
-
-fn dimensions_unsigned(level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureDimensions(t, level_idx_1);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- dimensions_signed(i32(non_uniform.x));
- dimensions_unsigned(u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- var predicated_value : vec2<u32>;
- if ((level_idx < num_levels)) {
- predicated_value = textureDimensions(t, level_idx);
- }
- let l = predicated_value;
-}
-
-fn dimensions_unsigned(level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- var predicated_value_1 : vec2<u32>;
- if ((level_idx_1 < num_levels_1)) {
- predicated_value_1 = textureDimensions(t, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- dimensions_signed(i32(non_uniform.x));
- dimensions_unsigned(u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGather) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn gather(coords : vec2f) {
- let l = textureGather(0, t, s, coords);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGather_Array) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn gather_signed(coords : vec2f, array : i32) {
- let l = textureGather(1, t, s, coords, array);
-}
-
-fn gather_unsigned(coords : vec2f, array : u32) {
- let l = textureGather(1, t, s, coords, array);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- gather_signed(non_uniform.xy, i32(non_uniform.x));
- gather_unsigned(non_uniform.xy, u32(non_uniform.x));
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGatherCompare) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn gather(coords : vec2f, depth_ref : f32) {
- let l = textureGatherCompare(t, s, coords, depth_ref);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGatherCompare_Array) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn gather_signed(coords : vec2f, array : i32, depth_ref : f32) {
- let l = textureGatherCompare(t, s, coords, array, depth_ref);
-}
-
-fn gather_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
- let l = textureGatherCompare(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- gather_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
- gather_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_1D) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : u32, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : i32, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : u32, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : i32, level : u32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx_2) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = u32(coords);
- var predicated_value : vec4<f32>;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : u32, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = u32(coords);
- var predicated_value_1 : vec4<f32>;
- if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
- predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : i32, level : u32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = u32(coords);
- var predicated_value_2 : vec4<f32>;
- if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
- predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_2D) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- var predicated_value : vec4<f32>;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = vec2<u32>(coords);
- var predicated_value_1 : vec4<f32>;
- if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
- predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = vec2<u32>(coords);
- var predicated_value_2 : vec4<f32>;
- if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
- predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_2DArray) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- let array_idx = u32(array);
- var predicated_value : vec4<f32>;
- if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
- predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = vec2<u32>(coords);
- let array_idx_1 = u32(array);
- var predicated_value_1 : vec4<f32>;
- if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
- predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = vec2<u32>(coords);
- let array_idx_2 = u32(array);
- var predicated_value_2 : vec4<f32>;
- if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
- predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_3D) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
- load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
- load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec3(0), vec3<i32>((textureDimensions(t, level_idx) - vec3(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec3(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec3(1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
- load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
- load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec3<u32>(coords);
- var predicated_value : vec4<f32>;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = vec3<u32>(coords);
- var predicated_value_1 : vec4<f32>;
- if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
- predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = vec3<u32>(coords);
- var predicated_value_2 : vec4<f32>;
- if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
- predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
- load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
- load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Multisampled2D) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
- let l = textureLoad(t, coords, sample);
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
- let l = textureLoad(t, coords, sample);
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
- let l = textureLoad(t, coords, sample);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
- let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))), sample);
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
- let coords_1 = vec2<u32>(coords);
- var predicated_value : vec4<f32>;
- if (all((coords_1 < textureDimensions(t)))) {
- predicated_value = textureLoad(t, coords_1, sample);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
- let coords_2 = vec2<u32>(coords);
- var predicated_value_1 : vec4<f32>;
- if (all((coords_2 < textureDimensions(t)))) {
- predicated_value_1 = textureLoad(t, coords_2, sample);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
- let coords_3 = vec2<u32>(coords);
- var predicated_value_2 : vec4<f32>;
- if (all((coords_3 < textureDimensions(t)))) {
- predicated_value_2 = textureLoad(t, coords_3, sample);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Depth2D) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
- let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx_2) - vec2(1)))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- var predicated_value : f32;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = vec2<u32>(coords);
- var predicated_value_1 : f32;
- if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
- predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = vec2<u32>(coords);
- var predicated_value_2 : f32;
- if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
- predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
- load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Depth2DArray) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let l = textureLoad(t, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
- let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- let array_idx = u32(array);
- var predicated_value : f32;
- if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
- predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
- let level_idx_1 = u32(level);
- let num_levels_1 = textureNumLevels(t);
- let coords_2 = vec2<u32>(coords);
- let array_idx_1 = u32(array);
- var predicated_value_1 : f32;
- if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
- predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
- }
- let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
- let level_idx_2 = u32(level);
- let num_levels_2 = textureNumLevels(t);
- let coords_3 = vec2<u32>(coords);
- let array_idx_2 = u32(array);
- var predicated_value_2 : f32;
- if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
- predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
- }
- let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
- load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
- load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_External) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
- let l = textureLoad(t, coords);
-}
-
-fn load_unsigned(coords : vec2u) {
- let l = textureLoad(t, coords);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy));
- load_unsigned(vec2u(non_uniform.xy));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
- let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))));
-}
-
-fn load_unsigned(coords : vec2u) {
- let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))));
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy));
- load_unsigned(vec2u(non_uniform.xy));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
- let coords_1 = vec2<u32>(coords);
- var predicated_value : vec4<f32>;
- if (all((coords_1 < textureDimensions(t)))) {
- predicated_value = textureLoad(t, coords_1);
- }
- let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u) {
- let coords_2 = vec2<u32>(coords);
- var predicated_value_1 : vec4<f32>;
- if (all((coords_2 < textureDimensions(t)))) {
- predicated_value_1 = textureLoad(t, coords_2);
- }
- let l = predicated_value_1;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- load_signed(vec2i(non_uniform.xy));
- load_unsigned(vec2u(non_uniform.xy));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumLayers) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn num_layers(coords : vec2f, depth_ref : f32) {
- let l = textureNumLayers(t);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumLevels) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn num_levels(coords : vec2f, depth_ref : f32) {
- let l = textureNumLevels(t);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumSamples) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_multisampled_2d;
-
-fn num_levels(coords : vec2f, depth_ref : f32) {
- let l = textureNumSamples(t);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample(coords : vec2f) {
- let l = textureSample(t, s, coords);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample(coords : vec2f) {
- const offset = vec2i(1);
- let l = textureSample(t, s, coords, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_signed(coords : vec2f, array : i32) {
- let l = textureSample(t, s, coords, array);
-}
-
-fn sample_unsigned(coords : vec2f, array : u32) {
- let l = textureSample(t, s, coords, array);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_signed(non_uniform.xy, i32(non_uniform.x));
- sample_unsigned(non_uniform.xy, u32(non_uniform.x));
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias(coords : vec2f, bias : f32) {
- let l = textureSampleBias(t, s, coords, bias);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias(coords : vec2f, bias : f32) {
- const offset = vec2i(1);
- let l = textureSampleBias(t, s, coords, bias, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias_signed(coords : vec2f, array : i32, bias : f32) {
- let l = textureSampleBias(t, s, coords, array, bias);
-}
-
-fn sample_bias_unsigned(coords : vec2f, array : u32, bias : f32) {
- let l = textureSampleBias(t, s, coords, array, bias);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_bias_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
- sample_bias_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare(coords : vec2f, depth_ref : f32) {
- let l = textureSampleCompare(t, s, coords, depth_ref);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare(coords : vec2f, depth_ref : f32) {
- const offset = vec2i(1);
- let l = textureSampleCompare(t, s, coords, depth_ref, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_signed(coords : vec2f, array : i32, depth_ref : f32) {
- let l = textureSampleCompare(t, s, coords, array, depth_ref);
-}
-
-fn sample_compare_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
- let l = textureSampleCompare(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_compare_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
- sample_compare_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level(coords : vec2f, depth_ref : f32) {
- let l = textureSampleCompareLevel(t, s, coords, depth_ref);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level(coords : vec2f, depth_ref : f32) {
- const offset = vec2i(1);
- let l = textureSampleCompareLevel(t, s, coords, depth_ref, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level_signed(coords : vec2f, array : i32, depth_ref : f32) {
- let l = textureSampleCompareLevel(t, s, coords, array, depth_ref);
-}
-
-fn sample_compare_level_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
- let l = textureSampleCompareLevel(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_compare_level_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
- sample_compare_level_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, ddx : vec2f, ddy : vec2f) {
- let l = textureSampleGrad(t, s, coords, ddx, ddy);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, ddx : vec2f, ddy : vec2f) {
- const offset = vec2i(1);
- let l = textureSampleGrad(t, s, coords, ddx, ddy, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_grad_signed(coords : vec2f, array : i32, ddx : vec2f, ddy : vec2f) {
- let l = textureSampleGrad(t, s, coords, array, ddx, ddy);
-}
-
-fn sample_grad_unsigned(coords : vec2f, array : u32, ddx : vec2f, ddy : vec2f) {
- let l = textureSampleGrad(t, s, coords, array, ddx, ddy);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_grad_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.xy, non_uniform.xy);
- sample_grad_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.xy, non_uniform.xy);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, level : f32) {
- let l = textureSampleLevel(t, s, coords, level);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel_Offset) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, level : f32) {
- const offset = vec2i(1);
- let l = textureSampleLevel(t, s, coords, level, offset);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel_ArrayIndex) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level_signed(coords : vec2f, array : i32, level : f32) {
- let l = textureSampleLevel(t, s, coords, array, level);
-}
-
-fn sample_compare_level_unsigned(coords : vec2f, array : u32, level : f32) {
- let l = textureSampleLevel(t, s, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
- sample_compare_level_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
- sample_compare_level_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBaseClampToEdge) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_base_clamp_to_edge(coords : vec2f) {
- let l = textureSampleBaseClampToEdge(t, s, coords);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Other
-////////////////////////////////////////////////////////////////////////////////
-TEST_P(RobustnessTest, ShadowedVariable) {
- auto* src = R"(
-fn f() {
- var a : array<f32, 3>;
- var i : u32;
- {
- var a : array<f32, 5>;
- var b : f32 = a[i];
- }
- var c : f32 = a[i];
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- var a : array<f32, 3>;
- var i : u32;
- {
- var a : array<f32, 5>;
- var b : f32 = a[min(i, 4u)];
- }
- var c : f32 = a[min(i, 2u)];
-}
-)",
- /* predicate */ R"(
-fn f() {
- var a : array<f32, 3>;
- var i : u32;
- {
- var a : array<f32, 5>;
- let index = i;
- let predicate = (u32(index) <= 4u);
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var b : f32 = predicated_expr;
- }
- let index_1 = i;
- let predicate_1 = (u32(index_1) <= 2u);
- var predicated_expr_1 : f32;
- if (predicate_1) {
- predicated_expr_1 = a[index_1];
- }
- var c : f32 = predicated_expr_1;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
- EXPECT_EQ(expect, str(got));
-}
-
-// Check that existing use of min() and arrayLength() do not get renamed.
-TEST_P(RobustnessTest, DontRenameSymbols) {
- auto* src = R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
- let b : f32 = s.b[c];
- let x : i32 = min(1, 2);
- let y : u32 = arrayLength(&(s.b));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
- let b : f32 = s.b[min(c, (arrayLength(&(s.b)) - 1u))];
- let x : i32 = min(1, 2);
- let y : u32 = arrayLength(&(s.b));
-}
-)",
- /* predicate */ R"(
-struct S {
- a : f32,
- b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
- let index = c;
- let predicate = (u32(index) <= (arrayLength(&(s.b)) - 1u));
- var predicated_expr : f32;
- if (predicate) {
- predicated_expr = s.b[index];
- }
- let b : f32 = predicated_expr;
- let x : i32 = min(1, 2);
- let y : u32 = arrayLength(&(s.b));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, WorkgroupOverrideCount) {
- auto* src = R"(
-override N = 123;
-
-var<workgroup> w : array<f32, N>;
-
-fn f() {
- var b : f32 = w[1i];
-}
-)";
-
- auto* expect =
- Expect(GetParam(),
- /* ignore */ src,
- /* clamp */
- R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)",
- /* predicate */
- R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// atomic predication
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, AtomicLoad) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- let r = atomicLoad(&(a[i]));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- let r = atomicLoad(&(a[min(u32(i), 3u)]));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- var predicated_value : i32;
- if (predicate) {
- predicated_value = atomicLoad(&(a[index]));
- }
- let r = predicated_value;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicAnd) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- atomicAnd(&(a[i]), 10);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- atomicAnd(&(a[min(u32(i), 3u)]), 10);
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- if (predicate) {
- atomicAnd(&(a[index]), 10);
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// workgroupUniformLoad
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, WorkgroupUniformLoad) {
- auto* src = R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
- let i = 1;
- let p = &(a[i]);
- let v = workgroupUniformLoad(p);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
- let i = 1;
- let p = &(a[min(u32(i), 31u)]);
- let v = workgroupUniformLoad(p);
-}
-)",
- /* predicate */ R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
- let i = 1;
- let index = i;
- let predicate = (u32(index) <= 31u);
- let p = &(a[index]);
- var predicated_value : u32;
- if (predicate) {
- predicated_value = workgroupUniformLoad(p);
- } else {
- workgroupBarrier();
- }
- let v = predicated_value;
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Usage in loops
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopInit) {
- auto* src = R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- for(var i = a[v]; (i < 3); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- for(var i = a[min(u32(v), 2u)]; (i < 3); i++) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- {
- let index = v;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = a[index];
- }
- var i = predicated_expr;
- loop {
- if (!((i < 3))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopCond) {
- auto* src = R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- for(var i = 0; (i < a[v]); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- for(var i = 0; (i < a[min(u32(v), 2u)]); i++) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-fn f() {
- let a = array<i32, 3>();
- var v = 1;
- {
- var i = 0;
- loop {
- let index = v;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = a[index];
- }
- if (!((i < predicated_expr))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopCont) {
- auto* src = R"(
-fn f() {
- let a = array<i32, 3>();
- for(var i = 0; (i < 5); i = a[i]) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn f() {
- let a = array<i32, 3>();
- for(var i = 0; (i < 5); i = a[min(u32(i), 2u)]) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-fn f() {
- let a = array<i32, 3>();
- {
- var i = 0;
- loop {
- if (!((i < 5))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- let index = i;
- let predicate = (u32(index) <= 2u);
- var predicated_expr : i32;
- if (predicate) {
- predicated_expr = a[index];
- }
- i = predicated_expr;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopInit) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- for(var i = textureLoad(t, coords, level).x; (i < 3); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- var i = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x;
- loop {
- if (!((i < 3))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- var predicated_value : vec4<i32>;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- var i = predicated_value.x;
- loop {
- if (!((i < 3))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopCond) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- for(var i = 0; (i < textureLoad(t, coords, level).x); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- {
- var i = 0;
- loop {
- let level_idx = min(u32(level), (textureNumLevels(t) - 1));
- if (!((i < textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var coords = vec2(1);
- var level = 1;
- {
- var i = 0;
- loop {
- let level_idx = u32(level);
- let num_levels = textureNumLevels(t);
- let coords_1 = vec2<u32>(coords);
- var predicated_value : vec4<i32>;
- if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords_1, level_idx);
- }
- if (!((i < predicated_value.x))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopCont) {
- auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var level = 1;
- for(var i = 0; (i < 5); i = textureLoad(t, vec2(i), i).x) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var level = 1;
- {
- var i = 0;
- loop {
- if (!((i < 5))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- let level_idx = min(u32(i), (textureNumLevels(t) - 1));
- i = textureLoad(t, clamp(vec2(i), vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x;
- }
- }
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
- var level = 1;
- {
- var i = 0;
- loop {
- if (!((i < 5))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- let level_idx = u32(i);
- let num_levels = textureNumLevels(t);
- let coords = vec2<u32>(vec2(i));
- var predicated_value : vec4<i32>;
- if (((level_idx < num_levels) & all((coords < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
- predicated_value = textureLoad(t, coords, level_idx);
- }
- i = predicated_value.x;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopInit) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- var i = 0;
- for(atomicXor(&(a[i]), 4); (i < 3); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- var i = 0;
- for(atomicXor(&(a[min(u32(i), 3u)]), 4); (i < 3); i++) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- var i = 0;
- {
- let index = i;
- let predicate = (u32(index) <= 3u);
- if (predicate) {
- atomicXor(&(a[index]), 4);
- }
- loop {
- if (!((i < 3))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopCond) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- for(var i = 0; (i < atomicXor(&(a[i]), 4)); i++) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- for(var i = 0; (i < atomicXor(&(a[min(u32(i), 3u)]), 4)); i++) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- {
- var i = 0;
- loop {
- let index = i;
- let predicate = (u32(index) <= 3u);
- var predicated_value : i32;
- if (predicate) {
- predicated_value = atomicXor(&(a[index]), 4);
- }
- if (!((i < predicated_value))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- i++;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopCont) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- for(var i = 0; (i < 5); i = atomicXor(&(a[i]), 4)) {
- var in_loop = 42;
- }
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- for(var i = 0; (i < 5); i = atomicXor(&(a[min(u32(i), 3u)]), 4)) {
- var in_loop = 42;
- }
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
- {
- var i = 0;
- loop {
- if (!((i < 5))) {
- break;
- }
- {
- var in_loop = 42;
- }
-
- continuing {
- let index = i;
- let predicate = (u32(index) <= 3u);
- var predicated_value : i32;
- if (predicate) {
- predicated_value = atomicXor(&(a[index]), 4);
- }
- i = predicated_value;
- }
- }
- }
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// User pointer parameters
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithConstant) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithConstant) {
- auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithConstant) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- y(vec4(1), &(a[1]), vec4(2));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
- var predicated_expr : vec4<i32>;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(vec4(1), &(a[1]), true, vec4(2));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithConstant) {
- auto* src = R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- y(vec4(1), &(a[1]), vec4(2));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
- var predicated_expr : vec4<i32>;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(vec4(1), &(a[1]), true, vec4(2));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithConstant) {
- auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithLet) {
- auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithLet) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(vec4(1), &(a[i]), vec4(2));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(vec4(1), &(a[min(u32(i), 3u)]), vec4(2));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
- var predicated_expr : vec4<i32>;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(vec4(1), &(a[index]), predicate, vec4(2));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithLet) {
- auto* src = R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(vec4(1), &(a[i]), vec4(2));
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
- return x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(vec4(1), &(a[min(u32(i), 3u)]), vec4(2));
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
- var predicated_expr : vec4<i32>;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(vec4(1), &(a[index]), predicate, vec4(2));
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithLet) {
- auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
- return x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
- var predicated_expr : i32;
- if (p_predicate) {
- predicated_expr = *(p);
- }
- return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) -> i32 {
- return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithConstant) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithConstant) {
- auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithConstant) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithConstant) {
- auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- y(1, &(a[1]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ src,
- /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- y(1, &(a[1]), true, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithLet) {
- auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithLet) {
- auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithLet) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithLet) {
- auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- y(1, &(a[i]), 2);
-}
-)";
-
- auto* expect = Expect(GetParam(),
- /* ignore */ src,
- /* clamp */ R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
- *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
- x(pre, p, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
- /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
- if (p_predicate) {
- *(p) = (pre + post);
- }
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) {
- x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
- var a : array<i32, 4>;
- let i = 0;
- let index = i;
- let predicate = (u32(index) <= 3u);
- y(1, &(a[index]), predicate, 2);
-}
-)");
-
- auto got = Run<Robustness>(src, Config(GetParam()));
-
- EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// disable_runtime_sized_array_index_clamping == true
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_i32) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- let i = 25i;
- var d : f32 = s[i];
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- let i = 25i;
- var d : f32 = s[u32(i)];
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_u32) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- let i = 25u;
- var d : f32 = s[i];
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_abstract_int) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- var d : f32 = s[25];
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- var d : f32 = s[u32(25)];
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_abstract_int_ViaPointerIndex) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- let p = &(s);
- var d : f32 = p[25];
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
- let p = &(s);
- var d : f32 = p[u32(25)];
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_i32) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- let i = 25i;
- s[i] = 0.5f;
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- let i = 25i;
- s[u32(i)] = 0.5f;
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_u32) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- let i = 25u;
- s[i] = 0.5f;
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_abstract_int) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- s[25] = 0.5f;
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- s[u32(25)] = 0.5f;
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_abstract_int_ViaPointerIndex) {
- auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- let p = &(s);
- p[25] = 0.5f;
-}
-)";
-
- auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
- let p = &(s);
- p[u32(25)] = 0.5f;
-}
-)";
-
- auto got = Run<Robustness>(src, Config(GetParam(), true));
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(,
- RobustnessTest,
- testing::Values(Robustness::Action::kIgnore,
- Robustness::Action::kClamp,
- Robustness::Action::kPredicate));
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point.cc b/src/tint/lang/wgsl/ast/transform/single_entry_point.cc
deleted file mode 100644
index a9673f7..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/single_entry_point.h"
-
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/array.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SingleEntryPoint);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SingleEntryPoint::Config);
-
-namespace tint::ast::transform {
-
-SingleEntryPoint::SingleEntryPoint() = default;
-
-SingleEntryPoint::~SingleEntryPoint() = default;
-
-Transform::ApplyResult SingleEntryPoint::Apply(const Program& src,
- const DataMap& inputs,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- auto* cfg = inputs.Get<Config>();
- if (cfg == nullptr) {
- b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
- return resolver::Resolve(b);
- }
-
- // Find the target entry point.
- const Function* entry_point = nullptr;
- for (auto* f : src.AST().Functions()) {
- if (!f->IsEntryPoint()) {
- continue;
- }
- if (f->name->symbol.Name() == cfg->entry_point_name) {
- entry_point = f;
- break;
- }
- }
- if (entry_point == nullptr) {
- b.Diagnostics().AddError(Source{})
- << "entry point '" << cfg->entry_point_name << "' not found";
- return resolver::Resolve(b);
- }
-
- auto& sem = src.Sem();
- auto& referenced_vars = sem.Get(entry_point)->TransitivelyReferencedGlobals();
-
- // Clone any module-scope variables, types, and functions that are statically referenced by the
- // target entry point.
- for (auto* decl : src.AST().GlobalDeclarations()) {
- Switch(
- decl, //
- [&](const TypeDecl* ty) {
- // Strip aliases that reference unused override declarations.
- if (auto* arr = sem.Get(ty)->As<sem::Array>()) {
- for (auto* o : arr->TransitivelyReferencedOverrides()) {
- if (!referenced_vars.Contains(o)) {
- return;
- }
- }
- }
-
- // TODO(jrprice): Strip other unused types.
- b.AST().AddTypeDecl(ctx.Clone(ty));
- },
- [&](const Override* override) {
- if (referenced_vars.Contains(sem.Get(override))) {
- if (!HasAttribute<IdAttribute>(override->attributes)) {
- // If the override doesn't already have an @id() attribute, add one
- // so that its allocated ID so that it won't be affected by other
- // stripped away overrides
- auto* global = sem.Get(override);
- const auto* id = b.Id(global->Attributes().override_id.value());
- ctx.InsertFront(override->attributes, id);
- }
- b.AST().AddGlobalVariable(ctx.Clone(override));
- }
- },
- [&](const Var* var) {
- if (referenced_vars.Contains(sem.Get<sem::GlobalVariable>(var))) {
- b.AST().AddGlobalVariable(ctx.Clone(var));
- }
- },
- [&](const Const* c) {
- // Always keep 'const' declarations, as these can be used by attributes and array
- // sizes, which are not tracked as transitively used by functions. They also don't
- // typically get emitted by the backend unless they're actually used.
- b.AST().AddGlobalVariable(ctx.Clone(c));
- },
- [&](const Function* func) {
- if (sem.Get(func)->HasCallGraphEntryPoint(entry_point->name->symbol)) {
- b.AST().AddFunction(ctx.Clone(func));
- }
- },
- [&](const Enable* ext) { b.AST().AddEnable(ctx.Clone(ext)); },
- [&](const Requires*) {
- // Drop requires directives as they are optional, and it's non-trivial to determine
- // which features are needed for which entry points.
- },
- [&](const ConstAssert* ca) { b.AST().AddConstAssert(ctx.Clone(ca)); },
- [&](const DiagnosticDirective* d) { b.AST().AddDiagnosticDirective(ctx.Clone(d)); }, //
- TINT_ICE_ON_NO_MATCH);
- }
-
- return resolver::Resolve(b);
-}
-
-SingleEntryPoint::Config::Config(std::string entry_point) : entry_point_name(entry_point) {}
-
-SingleEntryPoint::Config::Config(const Config&) = default;
-SingleEntryPoint::Config::~Config() = default;
-SingleEntryPoint::Config& SingleEntryPoint::Config::operator=(const Config&) = default;
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point.h b/src/tint/lang/wgsl/ast/transform/single_entry_point.h
deleted file mode 100644
index 39d1943..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Strip all but one entry point a module.
-///
-/// All module-scope variables, types, and functions that are not used by the
-/// target entry point will also be removed.
-class SingleEntryPoint final : public Castable<SingleEntryPoint, Transform> {
- public:
- /// Configuration options for the transform
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- /// @param entry_point the name of the entry point to keep
- explicit Config(std::string entry_point = "");
-
- /// Copy constructor
- Config(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// Assignment operator
- /// @returns this Config
- Config& operator=(const Config&);
-
- /// The name of the entry point to keep.
- std::string entry_point_name;
- };
-
- /// Constructor
- SingleEntryPoint();
-
- /// Destructor
- ~SingleEntryPoint() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc b/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc
deleted file mode 100644
index 4d4ede3..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc
+++ /dev/null
@@ -1,718 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/single_entry_point.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using SingleEntryPointTest = TransformTest;
-
-TEST_F(SingleEntryPointTest, Error_MissingTransformData) {
- auto* src = "";
-
- auto* expect = "error: missing transform data for tint::ast::transform::SingleEntryPoint";
-
- auto got = Run<SingleEntryPoint>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_NoEntryPoints) {
- auto* src = "";
-
- auto* expect = "error: entry point 'main' not found";
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>("main");
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_InvalidEntryPoint) {
- auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = "error: entry point '_' not found";
-
- SingleEntryPoint::Config cfg("_");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_NotAnEntryPoint) {
- auto* src = R"(
-fn foo() {}
-
-@fragment
-fn main() {}
-)";
-
- auto* expect = "error: entry point 'foo' not found";
-
- SingleEntryPoint::Config cfg("foo");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, SingleEntryPoint) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(src, str(got));
-}
-
-TEST_F(SingleEntryPointTest, MultipleEntryPoints) {
- auto* src = R"(
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-}
-)";
-
- auto* expect = R"(
-@compute @workgroup_size(1)
-fn comp_main1() {
-}
-)";
-
- SingleEntryPoint::Config cfg("comp_main1");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalVariables) {
- auto* src = R"(
-var<private> a : f32;
-
-var<private> b : f32;
-
-var<private> c : f32;
-
-var<private> d : f32;
-
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
- a = 0.0;
- return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
- b = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- c = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- d = 0.0;
-}
-)";
-
- auto* expect = R"(
-var<private> c : f32;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- c = 0.0;
-}
-)";
-
- SingleEntryPoint::Config cfg("comp_main1");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalConstants) {
- auto* src = R"(
-const a : f32 = 1.0;
-
-const b : f32 = 1.0;
-
-const c : f32 = 1.0;
-
-const d : f32 = 1.0;
-
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
- let local_a : f32 = a;
- return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
- let local_b : f32 = b;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- let local_c : f32 = c;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- let local_d : f32 = d;
-}
-)";
-
- auto* expect = R"(
-const a : f32 = 1.0;
-
-const b : f32 = 1.0;
-
-const c : f32 = 1.0;
-
-const d : f32 = 1.0;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- let local_c : f32 = c;
-}
-)";
-
- SingleEntryPoint::Config cfg("comp_main1");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, WorkgroupSizeConstPreserved) {
- auto* src = R"(
-const size : i32 = 1;
-
-@compute @workgroup_size(size)
-fn main() {
-}
-)";
-
- auto* expect = src;
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants) {
- auto* src = R"(
-@id(1001) override c1 : u32 = 1u;
- override c2 : u32 = 1u;
-@id(0) override c3 : u32 = 1u;
-@id(9999) override c4 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- let local_d = c1;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- let local_d = c2;
-}
-
-@compute @workgroup_size(1)
-fn comp_main3() {
- let local_d = c3;
-}
-
-@compute @workgroup_size(1)
-fn comp_main4() {
- let local_d = c4;
-}
-
-@compute @workgroup_size(1)
-fn comp_main5() {
- let local_d = 1u;
-}
-)";
-
- {
- SingleEntryPoint::Config cfg("comp_main1");
- auto* expect = R"(
-@id(1001) override c1 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- let local_d = c1;
-}
-)";
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
- }
-
- {
- SingleEntryPoint::Config cfg("comp_main2");
- // The decorator is replaced with the one with explicit id
- // And should not be affected by other constants stripped away
- auto* expect = R"(
-@id(1) override c2 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- let local_d = c2;
-}
-)";
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
- }
-
- {
- SingleEntryPoint::Config cfg("comp_main3");
- auto* expect = R"(
-@id(0) override c3 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main3() {
- let local_d = c3;
-}
-)";
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
- }
-
- {
- SingleEntryPoint::Config cfg("comp_main4");
- auto* expect = R"(
-@id(9999) override c4 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main4() {
- let local_d = c4;
-}
-)";
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
- }
-
- {
- SingleEntryPoint::Config cfg("comp_main5");
- auto* expect = R"(
-@compute @workgroup_size(1)
-fn comp_main5() {
- let local_d = 1u;
-}
-)";
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
- }
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants_TransitiveUses) {
- // Make sure we do not strip away transitive uses of overridable constants.
- auto* src = R"(
-@id(0) override c0 : u32;
-
-@id(1) override c1 : u32 = (2 * c0);
-
-@id(2) override c2 : u32;
-
-@id(3) override c3 : u32 = (2 * c2);
-
-@id(4) override c4 : u32;
-
-@id(5) override c5 : u32 = (2 * c4);
-
-alias arr_ty = array<i32, (2 * c5)>;
-
-var<workgroup> arr : arr_ty;
-
-@compute @workgroup_size(1, 1, (2 * c3))
-fn main() {
- let local_d = c1;
- arr[0] = 42;
-}
-)";
-
- auto* expect = src;
-
- SingleEntryPoint::Config cfg("main");
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants_UnusedAliasForOverrideSizedArray) {
- // Make sure we strip away aliases that reference unused overridable constants.
- auto* src = R"(
-@id(0) override c0 : u32;
-
-// This is all unused by the target entry point.
-@id(1) override c1 : u32;
-alias arr_ty = array<i32, c1>;
-var<workgroup> arr : arr_ty;
-
-@compute @workgroup_size(64)
-fn unused() {
- arr[0] = 42;
-}
-
-@compute @workgroup_size(64)
-fn main() {
- let local_d = c0;
-}
-)";
-
- auto* expect = R"(
-@id(0) override c0 : u32;
-
-@compute @workgroup_size(64)
-fn main() {
- let local_d = c0;
-}
-)";
-
- SingleEntryPoint::Config cfg("main");
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, CalledFunctions) {
- auto* src = R"(
-fn inner1() {
-}
-
-fn inner2() {
-}
-
-fn inner_shared() {
-}
-
-fn outer1() {
- inner1();
- inner_shared();
-}
-
-fn outer2() {
- inner2();
- inner_shared();
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- outer1();
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- outer2();
-}
-)";
-
- auto* expect = R"(
-fn inner1() {
-}
-
-fn inner_shared() {
-}
-
-fn outer1() {
- inner1();
- inner_shared();
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- outer1();
-}
-)";
-
- SingleEntryPoint::Config cfg("comp_main1");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalsReferencedByCalledFunctions) {
- auto* src = R"(
-var<private> inner1_var : f32;
-
-var<private> inner2_var : f32;
-
-var<private> inner_shared_var : f32;
-
-var<private> outer1_var : f32;
-
-var<private> outer2_var : f32;
-
-fn inner1() {
- inner1_var = 0.0;
-}
-
-fn inner2() {
- inner2_var = 0.0;
-}
-
-fn inner_shared() {
- inner_shared_var = 0.0;
-}
-
-fn outer1() {
- inner1();
- inner_shared();
- outer1_var = 0.0;
-}
-
-fn outer2() {
- inner2();
- inner_shared();
- outer2_var = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- outer1();
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
- outer2();
-}
-)";
-
- auto* expect = R"(
-var<private> inner1_var : f32;
-
-var<private> inner_shared_var : f32;
-
-var<private> outer1_var : f32;
-
-fn inner1() {
- inner1_var = 0.0;
-}
-
-fn inner_shared() {
- inner_shared_var = 0.0;
-}
-
-fn outer1() {
- inner1();
- inner_shared();
- outer1_var = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
- outer1();
-}
-)";
-
- SingleEntryPoint::Config cfg("comp_main1");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalConstUsedAsArraySize) {
- // See crbug.com/tint/1598
- auto* src = R"(
-const MY_SIZE = 5u;
-
-alias Arr = array<i32, MY_SIZE>;
-
-@fragment
-fn main() {
-}
-)";
-
- auto* expect = src;
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Directives) {
- // Make sure that directives are preserved.
- auto* src = R"(
-enable f16;
-diagnostic(off, derivative_uniformity);
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(src, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Requires) {
- // Make sure that requires are handled (and dropped).
- auto* src = R"(
-requires readonly_and_readwrite_storage_textures;
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, ConstAssert_ModuleScope) {
- // module scope const_assert is preserved
- auto* src = R"(
-const C = 42;
-
-const_assert (C == 42);
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
- auto* expect = src;
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, ConstAssert_FnScope) {
- // function scope const_assert is preserved
- auto* src = R"(
-const C = 42;
-
-@compute @workgroup_size(1)
-fn main() {
- const_assert (C == 42);
-}
-)";
-
- auto* expect = src;
-
- SingleEntryPoint::Config cfg("main");
-
- DataMap data;
- data.Add<SingleEntryPoint::Config>(cfg);
- auto got = Run<SingleEntryPoint>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override.cc b/src/tint/lang/wgsl/ast/transform/substitute_override.cc
deleted file mode 100644
index 477a7fb..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/substitute_override.h"
-
-#include <functional>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_fn.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SubstituteOverride);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SubstituteOverride::Config);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* node : program.AST().GlobalVariables()) {
- if (node->Is<Override>()) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-SubstituteOverride::SubstituteOverride() = default;
-
-SubstituteOverride::~SubstituteOverride() = default;
-
-Transform::ApplyResult SubstituteOverride::Apply(const Program& src,
- const DataMap& config,
- DataMap&) const {
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- const auto* data = config.Get<Config>();
- if (!data) {
- b.Diagnostics().AddError(Source{}) << "Missing override substitution data";
- return resolver::Resolve(b);
- }
-
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ctx.ReplaceAll([&](const Override* w) -> const Const* {
- auto* sem = ctx.src->Sem().Get(w);
-
- auto source = ctx.Clone(w->source);
- auto sym = ctx.Clone(w->name->symbol);
- Type ty = w->type ? ctx.Clone(w->type) : Type{};
-
- // No replacement provided, just clone the override node as a const.
- auto iter = data->map.find(sem->Attributes().override_id.value());
- if (iter == data->map.end()) {
- if (!w->initializer) {
- b.Diagnostics().AddError(Source{})
- << "Initializer not provided for override, and override not overridden.";
- return nullptr;
- }
- return b.Const(source, sym, ty, ctx.Clone(w->initializer));
- }
-
- auto value = iter->second;
- auto* ctor = Switch(
- sem->Type(),
- [&](const core::type::Bool*) { return b.Expr(!std::equal_to<double>()(value, 0.0)); },
- [&](const core::type::I32*) { return b.Expr(i32(value)); },
- [&](const core::type::U32*) { return b.Expr(u32(value)); },
- [&](const core::type::F32*) { return b.Expr(f32(value)); },
- [&](const core::type::F16*) { return b.Expr(f16(value)); });
-
- if (!ctor) {
- b.Diagnostics().AddError(Source{}) << "Failed to create override-expression";
- return nullptr;
- }
-
- return b.Const(source, sym, ty, ctor);
- });
-
- // Ensure that objects that are indexed with an override-expression are materialized.
- // If the object is not materialized, and the 'override' variable is turned to a 'const', the
- // resulting type of the index may change. See: crbug.com/tint/1697.
- ctx.ReplaceAll([&](const IndexAccessorExpression* expr) -> const IndexAccessorExpression* {
- if (auto* sem = src.Sem().Get(expr)) {
- if (auto* access = sem->UnwrapMaterialize()->As<sem::IndexAccessorExpression>()) {
- if (access->Object()->UnwrapMaterialize()->Type()->IsAbstract() &&
- access->Index()->Stage() == core::EvaluationStage::kOverride) {
- auto* obj = b.Call(wgsl::str(wgsl::BuiltinFn::kTintMaterialize),
- ctx.Clone(expr->object));
- return b.IndexAccessor(obj, ctx.Clone(expr->index));
- }
- }
- }
- return nullptr;
- });
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-SubstituteOverride::Config::Config() = default;
-
-SubstituteOverride::Config::Config(const Config&) = default;
-
-SubstituteOverride::Config::~Config() = default;
-
-SubstituteOverride::Config& SubstituteOverride::Config::operator=(const Config&) = default;
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override.h b/src/tint/lang/wgsl/ast/transform/substitute_override.h
deleted file mode 100644
index a49bdfb..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/api/common/override_id.h"
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// A transform that replaces overrides with the constant values provided.
-///
-/// # Example
-/// ```
-/// override width: f32;
-/// @id(1) override height: i32 = 4;
-/// override depth = 1i;
-/// ```
-///
-/// When transformed with `width` -> 1, `1` -> 22, `depth` -> 42
-///
-/// ```
-/// const width: f32 = 1f;
-/// const height: i32 = 22i;
-/// const depth = 42i;
-/// ```
-///
-/// @see crbug.com/tint/1582
-class SubstituteOverride final : public Castable<SubstituteOverride, Transform> {
- public:
- /// Configuration options for the transform
- struct Config final : public Castable<Config, Data> {
- /// Constructor
- Config();
-
- /// Copy constructor
- Config(const Config&);
-
- /// Destructor
- ~Config() override;
-
- /// Assignment operator
- /// @returns this Config
- Config& operator=(const Config&);
-
- /// The map of override identifier to the override value.
- /// The value is always a double coming into the transform and will be
- /// converted to the correct type through and initializer.
- std::unordered_map<OverrideId, double> map;
-
- /// Reflect the fields of this class so that it can be used by tint::ForeachField()
- TINT_REFLECT(Config, map);
- };
-
- /// Constructor
- SubstituteOverride();
-
- /// Destructor
- ~SubstituteOverride() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc b/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc
deleted file mode 100644
index c0204aa..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/substitute_override.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-TEST(TintCheckAllFieldsReflected, WgslAstTransformSubstituteOverrideTest) {
- TINT_ASSERT_ALL_FIELDS_REFLECTED(SubstituteOverride::Config);
-}
-
-using SubstituteOverrideTest = TransformTest;
-
-TEST_F(SubstituteOverrideTest, Error_NoData) {
- auto* src = R"(
-override width: i32;
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = "error: Missing override substitution data";
-
- DataMap data;
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Error_NoOverrideValue) {
- auto* src = R"(
-override width: i32;
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = "error: Initializer not provided for override, and override not overridden.";
-
- SubstituteOverride::Config cfg;
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
-
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Module_NoOverrides) {
- auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- SubstituteOverride::Config cfg;
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, OverrideNotInFile) {
- auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- SubstituteOverride::Config cfg;
- cfg.map.insert({OverrideId{99}, 42.0});
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, ImplicitId) {
- auto* src = R"(
-enable f16;
-
-override i_width: i32;
-override i_height = 1i;
-
-override f_width: f32;
-override f_height = 1.f;
-
-override h_width: f16;
-override h_height = 1.h;
-
-override b_width: bool;
-override b_height = true;
-
-override o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-enable f16;
-
-const i_width : i32 = 42i;
-
-const i_height = 11i;
-
-const f_width : f32 = 22.299999237060546875f;
-
-const f_height = 12.3999996185302734375f;
-
-const h_width : f16 = 9.3984375h;
-
-const h_height = 3.3984375h;
-
-const b_width : bool = true;
-
-const b_height = false;
-
-const o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- SubstituteOverride::Config cfg;
- cfg.map.insert({OverrideId{0}, 42.0});
- cfg.map.insert({OverrideId{1}, 11.0});
- cfg.map.insert({OverrideId{2}, 22.3});
- cfg.map.insert({OverrideId{3}, 12.4});
- cfg.map.insert({OverrideId{4}, 9.4});
- cfg.map.insert({OverrideId{5}, 3.4});
- cfg.map.insert({OverrideId{6}, 1.0});
- cfg.map.insert({OverrideId{7}, 0.0});
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, ExplicitId) {
- auto* src = R"(
-enable f16;
-
-@id(0) override i_width: i32;
-@id(10) override i_height = 1i;
-
-@id(1) override f_width: f32;
-@id(9) override f_height = 1.f;
-
-@id(2) override h_width: f16;
-@id(8) override h_height = 1.h;
-
-@id(3) override b_width: bool;
-@id(7) override b_height = true;
-
-@id(5) override o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-enable f16;
-
-const i_width : i32 = 42i;
-
-const i_height = 11i;
-
-const f_width : f32 = 22.299999237060546875f;
-
-const f_height = 12.3999996185302734375f;
-
-const h_width : f16 = 9.3984375h;
-
-const h_height = 3.3984375h;
-
-const b_width : bool = true;
-
-const b_height = false;
-
-const o_width = 13i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- SubstituteOverride::Config cfg;
- cfg.map.insert({OverrideId{0}, 42.0});
- cfg.map.insert({OverrideId{10}, 11.0});
- cfg.map.insert({OverrideId{1}, 22.3});
- cfg.map.insert({OverrideId{9}, 12.4});
- cfg.map.insert({OverrideId{2}, 9.4});
- cfg.map.insert({OverrideId{8}, 3.4});
- cfg.map.insert({OverrideId{3}, 1.0});
- cfg.map.insert({OverrideId{7}, 0.0});
- cfg.map.insert({OverrideId{5}, 13});
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Identifier_Expression) {
- auto* src = R"(
-override i_height = ~2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- auto* expect = R"(
-const i_height = 11i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- SubstituteOverride::Config cfg;
- cfg.map.insert({OverrideId{0}, 11.0});
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, IndexMaterialization) {
- auto* src = R"(
-override O = 0; // Try switching to 'const'
-
-fn f() {
- const smaller_than_any_f32 = 1e-50;
- const large_float = 1e27;
- // When O is an override, the outer index value is not constant, so the
- // value is not calculated at shader-creation time, and does not error.
- //
- // When O is a const, and 'smaller_than_any_f32' *is not* materialized, the
- // outer index value will evaluate to 10000, resulting in an out-of-bounds
- // error.
- //
- // When O is a const, and 'smaller_than_any_f32' *is* materialized, the
- // materialization of 'smaller_than_any_f32' to f32 will evaluate to zero,
- // and so the outer index value will be zero, and we get no error.
- _ = vec2(0)[i32(vec2(smaller_than_any_f32)[O]*large_float*large_float)];
-}
-)";
-
- auto* expect = R"(
-const O = 0i;
-
-fn f() {
- const smaller_than_any_f32 = 1e-50;
- const large_float = 1000000000000000013287555072.0;
- _ = __tint_materialize(vec2(0))[i32(((__tint_materialize(vec2(smaller_than_any_f32))[O] * large_float) * large_float))];
-}
-)";
-
- SubstituteOverride::Config cfg;
- cfg.map.insert({OverrideId{0}, 0.0});
-
- DataMap data;
- data.Add<SubstituteOverride::Config>(cfg);
- auto got = Run<SubstituteOverride>(src, data);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
deleted file mode 100644
index 18e4ff7..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/abstract_numeric.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-#include "src/tint/utils/containers/map.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::VectorizeScalarMatrixInitializers);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* node : program.ASTNodes().Objects()) {
- if (auto* call = program.Sem().Get<sem::Call>(node)) {
- if (call->Target()->Is<sem::ValueConstructor>() &&
- call->Type()->Is<core::type::Matrix>()) {
- auto& args = call->Arguments();
- if (!args.IsEmpty() && args[0]->Type()->UnwrapRef()->Is<core::type::Scalar>()) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-} // namespace
-
-VectorizeScalarMatrixInitializers::VectorizeScalarMatrixInitializers() = default;
-
-VectorizeScalarMatrixInitializers::~VectorizeScalarMatrixInitializers() = default;
-
-Transform::ApplyResult VectorizeScalarMatrixInitializers::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- std::unordered_map<const core::type::Matrix*, Symbol> scalar_inits;
-
- ctx.ReplaceAll([&](const CallExpression* expr) -> const CallExpression* {
- auto* call = src.Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
- auto* ty_init = call->Target()->As<sem::ValueConstructor>();
- if (!ty_init) {
- return nullptr;
- }
- auto* mat_type = call->Type()->As<core::type::Matrix>();
- if (!mat_type) {
- return nullptr;
- }
-
- auto& args = call->Arguments();
- if (args.IsEmpty()) {
- return nullptr;
- }
-
- // If the argument type is a matrix, then this is an identity / conversion initializer.
- // If the argument type is a vector, then we're already column vectors.
- // If the argument type is abstract, then we're const-expression and there's no need to
- // adjust this, as it'll be constant folded by the backend.
- if (args[0]
- ->Type()
- ->UnwrapRef()
- ->IsAnyOf<core::type::Matrix, core::type::Vector, core::type::AbstractNumeric>()) {
- return nullptr;
- }
-
- // Constructs a matrix using vector columns, with the elements constructed using the
- // 'element(uint32_t c, uint32_t r)' callback.
- auto build_mat = [&](auto&& element) {
- tint::Vector<const Expression*, 4> columns;
- for (uint32_t c = 0; c < mat_type->Columns(); c++) {
- tint::Vector<const Expression*, 4> row_values;
- for (uint32_t r = 0; r < mat_type->Rows(); r++) {
- row_values.Push(element(c, r));
- }
-
- // Construct the column vector.
- columns.Push(b.vec(CreateASTTypeFor(ctx, mat_type->Type()), mat_type->Rows(),
- std::move(row_values)));
- }
- return b.Call(CreateASTTypeFor(ctx, mat_type), columns);
- };
-
- if (args.Length() == 1) {
- // Generate a helper function for constructing the matrix.
- // This is done to ensure that the single argument value is only evaluated once, and
- // with the correct expression evaluation order.
- auto fn = tint::GetOrAdd(scalar_inits, mat_type, [&] {
- auto name = b.Symbols().New("build_mat" + std::to_string(mat_type->Columns()) +
- "x" + std::to_string(mat_type->Rows()));
- b.Func(name,
- tint::Vector{
- // Single scalar parameter
- b.Param("value", CreateASTTypeFor(ctx, mat_type->Type())),
- },
- CreateASTTypeFor(ctx, mat_type),
- tint::Vector{
- b.Return(build_mat([&](uint32_t, uint32_t) { //
- return b.Expr("value");
- })),
- });
- return name;
- });
- return b.Call(fn, ctx.Clone(args[0]->Declaration()));
- }
-
- if (DAWN_LIKELY(args.Length() == mat_type->Columns() * mat_type->Rows())) {
- return build_mat([&](uint32_t c, uint32_t r) {
- return ctx.Clone(args[c * mat_type->Rows() + r]->Declaration());
- });
- }
-
- TINT_ICE() << "matrix initializer has unexpected number of arguments";
- });
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
deleted file mode 100644
index a7dcf75..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that converts scalar matrix initializers to the vector form.
-class VectorizeScalarMatrixInitializers final
- : public Castable<VectorizeScalarMatrixInitializers, Transform> {
- public:
- /// Constructor
- VectorizeScalarMatrixInitializers();
-
- /// Destructor
- ~VectorizeScalarMatrixInitializers() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
deleted file mode 100644
index fd69553..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using VectorizeScalarMatrixInitializersTest = TransformTestWithParam<std::pair<uint32_t, uint32_t>>;
-
-TEST_F(VectorizeScalarMatrixInitializersTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalars) {
- uint32_t cols = GetParam().first;
- uint32_t rows = GetParam().second;
- std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
- std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
- std::string scalar_values;
- std::string vector_values;
- for (uint32_t c = 0; c < cols; c++) {
- if (c > 0) {
- vector_values += ", ";
- scalar_values += ", ";
- }
- vector_values += vec_type + "(";
- for (uint32_t r = 0; r < rows; r++) {
- if (r > 0) {
- scalar_values += ", ";
- vector_values += ", ";
- }
- auto value = std::to_string(c * rows + r) + ".0";
- scalar_values += value;
- vector_values += value;
- }
- vector_values += ")";
- }
-
- std::string tmpl = R"(
-@fragment
-fn main() {
- let m = ${matrix}(${values});
-}
-)";
- tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
- auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
- auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
- EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
- auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalarsViaReference) {
- uint32_t cols = GetParam().first;
- uint32_t rows = GetParam().second;
- std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
- std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
- std::string scalar_values;
- std::string vector_values;
- for (uint32_t c = 0; c < cols; c++) {
- if (c > 0) {
- vector_values += ", ";
- scalar_values += ", ";
- }
- vector_values += vec_type + "(";
- for (uint32_t r = 0; r < rows; r++) {
- if (r > 0) {
- scalar_values += ", ";
- vector_values += ", ";
- }
- auto value = "v[" + std::to_string((c * rows + r) % 4) + "]";
- scalar_values += value;
- vector_values += value;
- }
- vector_values += ")";
- }
-
- std::string tmpl = R"(
-@fragment
-fn main() {
- let v = vec4<f32>(1.0, 2.0, 3.0, 8.0);
- let m = ${matrix}(${values});
-}
-)";
- tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
- auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
- auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
- EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
- auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalarsViaPointerIndex) {
- uint32_t cols = GetParam().first;
- uint32_t rows = GetParam().second;
- std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
- std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
- std::string scalar_values;
- std::string vector_values;
- for (uint32_t c = 0; c < cols; c++) {
- if (c > 0) {
- vector_values += ", ";
- scalar_values += ", ";
- }
- vector_values += vec_type + "(";
- for (uint32_t r = 0; r < rows; r++) {
- if (r > 0) {
- scalar_values += ", ";
- vector_values += ", ";
- }
- auto value = "p[" + std::to_string((c * rows + r) % 4) + "]";
- scalar_values += value;
- vector_values += value;
- }
- vector_values += ")";
- }
-
- std::string tmpl = R"(
-@fragment
-fn main() {
- var v = vec4<f32>(1.0, 2.0, 3.0, 8.0);
- let p = &(v);
- let m = ${matrix}(${values});
-}
-)";
- tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
- auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
- auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
- EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
- auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, NonScalarInitializers) {
- uint32_t cols = GetParam().first;
- uint32_t rows = GetParam().second;
- std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
- std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
- std::string columns;
- for (uint32_t c = 0; c < cols; c++) {
- if (c > 0) {
- columns += ", ";
- }
- columns += vec_type + "()";
- }
-
- std::string tmpl = R"(
-@fragment
-fn main() {
- let m = ${matrix}(${columns});
-}
-)";
- tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
- auto src = tint::ReplaceAll(tmpl, "${columns}", columns);
- auto expect = src;
-
- EXPECT_FALSE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
- auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(VectorizeScalarMatrixInitializersTest,
- VectorizeScalarMatrixInitializersTest,
- testing::Values(std::make_pair(2, 2),
- std::make_pair(2, 3),
- std::make_pair(2, 4),
- std::make_pair(3, 2),
- std::make_pair(3, 3),
- std::make_pair(3, 4),
- std::make_pair(4, 2),
- std::make_pair(4, 3),
- std::make_pair(4, 4)));
-
-} // namespace
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
deleted file mode 100644
index 0f08afa..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
-
-#include <algorithm>
-#include <map>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/atomic.h"
-#include "src/tint/lang/wgsl/ast/workgroup_attribute.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/containers/unique_vector.h"
-
-using namespace tint::core::fluent_types; // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ZeroInitWorkgroupMemory);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
- for (auto* global : program.AST().GlobalVariables()) {
- if (auto* var = global->As<Var>()) {
- auto* v = program.Sem().Get(var);
- if (v->AddressSpace() == core::AddressSpace::kWorkgroup) {
- return true;
- }
- }
- }
- return false;
-}
-
-} // namespace
-
-using StatementList = tint::Vector<const Statement*, 8>;
-
-/// PIMPL state for the transform
-struct ZeroInitWorkgroupMemory::State {
- /// The clone context
- program::CloneContext& ctx;
-
- /// An alias to *ctx.dst
- ast::Builder& b = *ctx.dst;
-
- /// The semantic info for the source program.
- const sem::Info& sem = ctx.src->Sem();
-
- /// The constant size of the workgroup. If 0, then #workgroup_size_expr should
- /// be used instead.
- uint32_t workgroup_size_const = 0;
- /// The size of the workgroup as an expression generator. Use if
- /// #workgroup_size_const is 0.
- std::function<const ast::Expression*()> workgroup_size_expr;
-
- /// ArrayIndex represents a function on the local invocation index, of
- /// the form: `array_index = (local_invocation_index % modulo) / division`
- struct ArrayIndex {
- /// The RHS of the modulus part of the expression
- uint32_t modulo = 1;
- /// The RHS of the division part of the expression
- uint32_t division = 1;
-
- /// @returns the hash code of the ArrayIndex
- tint::HashCode HashCode() const { return Hash(modulo, division); }
-
- /// Equality operator
- /// @param i the ArrayIndex to compare to this ArrayIndex
- /// @returns true if `i` and this ArrayIndex are equal
- bool operator==(const ArrayIndex& i) const {
- return modulo == i.modulo && division == i.division;
- }
- };
-
- /// A list of unique ArrayIndex
- using ArrayIndices = UniqueVector<ArrayIndex, 4>;
-
- /// Expression holds information about an expression that is being built for a
- /// statement will zero workgroup values.
- struct Expression {
- /// The AST expression node
- const ast::Expression* expr = nullptr;
- /// The number of iterations required to zero the value
- uint32_t num_iterations = 0;
- /// All array indices used by this expression
- ArrayIndices array_indices;
-
- /// @returns true if the expr is not null (null usually indicates a failure)
- explicit operator bool() const { return expr != nullptr; }
- };
-
- /// Statement holds information about a statement that will zero workgroup
- /// values.
- struct Statement {
- /// The AST statement node
- const ast::Statement* stmt;
- /// The number of iterations required to zero the value
- uint32_t num_iterations;
- /// All array indices used by this statement
- ArrayIndices array_indices;
- };
-
- /// All statements that zero workgroup memory
- std::vector<Statement> statements;
-
- /// A map of ArrayIndex to the name reserved for the `let` declaration of that
- /// index.
- Hashmap<ArrayIndex, Symbol, 4> array_index_names;
-
- /// Constructor
- /// @param c the program::CloneContext used for the transform
- explicit State(program::CloneContext& c) : ctx(c) {}
-
- /// Run inserts the workgroup memory zero-initialization logic at the top of
- /// the given function
- /// @param fn a compute shader entry point function
- void Run(const Function* fn) {
- CalculateWorkgroupSize(GetAttribute<WorkgroupAttribute>(fn->attributes));
-
- // Generate the workgroup zeroing function
- auto zeroing_fn_name = BuildZeroingFn(fn);
- if (!zeroing_fn_name) {
- return; // Nothing to do.
- }
-
- // Get or create the local invocation index parameter on the entry point
- auto local_invocation_index = GetOrCreateLocalInvocationIndex(fn);
-
- // Prefix the entry point body with a call to the workgroup zeroing function
- ctx.InsertFront(fn->body->statements,
- b.CallStmt(b.Call(zeroing_fn_name, local_invocation_index)));
- }
-
- /// Builds a function that zeros all the variables in the workgroup address space transitively
- /// used by @p fn. The built function takes a single `local_invocation_id : u32` parameter
- /// @param fn the entry point function.
- /// @return the name of the workgroup memory zeroing function.
- Symbol BuildZeroingFn(const Function* fn) {
- // Generate a list of statements to zero initialize each of the workgroup storage variables
- // used by `fn`. This will populate #statements.
- auto* func = sem.Get(fn);
- for (auto* var : func->TransitivelyReferencedGlobals()) {
- if (var->AddressSpace() == core::AddressSpace::kWorkgroup) {
- auto get_expr = [&](uint32_t num_values) {
- auto var_name = ctx.Clone(var->Declaration()->name->symbol);
- return Expression{b.Expr(var_name), num_values, ArrayIndices{}};
- };
- if (!BuildZeroingStatements(var->Type()->UnwrapRef(), get_expr)) {
- return Symbol{};
- }
- }
- }
-
- if (statements.empty()) {
- return Symbol{}; // No workgroup variables to initialize.
- }
-
- // Take the zeroing statements and bin them by the number of iterations
- // required to zero the workgroup data. We then emit these in blocks,
- // possibly wrapped in if-statements or for-loops.
- std::unordered_map<uint32_t, std::vector<Statement>> stmts_by_num_iterations;
- std::vector<uint32_t> num_sorted_iterations;
- for (auto& s : statements) {
- auto& stmts = stmts_by_num_iterations[s.num_iterations];
- if (stmts.empty()) {
- num_sorted_iterations.emplace_back(s.num_iterations);
- }
- stmts.emplace_back(s);
- }
- std::sort(num_sorted_iterations.begin(), num_sorted_iterations.end());
-
- auto local_idx = b.Symbols().New("local_idx");
-
- // Loop over the statements, grouped by num_iterations.
- Vector<const ast::Statement*, 8> init_body;
- for (auto num_iterations : num_sorted_iterations) {
- auto& stmts = stmts_by_num_iterations[num_iterations];
-
- // Gather all the array indices used by all the statements in the block.
- ArrayIndices array_indices;
- for (auto& s : stmts) {
- for (auto& idx : s.array_indices) {
- array_indices.Add(idx);
- }
- }
-
- // Determine the block type used to emit these statements.
-
- // TODO(crbug.com/tint/2143): Always emit an if statement around zero init, even when
- // workgroup size matches num_iteration, to work around bugs in certain drivers.
- constexpr bool kWorkaroundUnconditionalZeroInitDriverBug = true;
-
- if (workgroup_size_const == 0 || num_iterations > workgroup_size_const) {
- // Either the workgroup size is dynamic, or smaller than num_iterations.
- // In either case, we need to generate a for loop to ensure we
- // initialize all the array elements.
- //
- // for (var idx : u32 = local_index;
- // idx < num_iterations;
- // idx += workgroup_size) {
- // ...
- // }
- auto idx = b.Symbols().New("idx");
- auto* init = b.Decl(b.Var(idx, b.ty.u32(), b.Expr(local_idx)));
- auto* cond = b.LessThan(idx, u32(num_iterations));
- auto* cont = b.Assign(
- idx, b.Add(idx, workgroup_size_const ? b.Expr(u32(workgroup_size_const))
- : workgroup_size_expr()));
-
- auto block =
- DeclareArrayIndices(num_iterations, array_indices, [&] { return b.Expr(idx); });
- for (auto& s : stmts) {
- block.Push(s.stmt);
- }
- auto* for_loop = b.For(init, cond, cont, b.Block(block));
- init_body.Push(for_loop);
- } else if (num_iterations < workgroup_size_const ||
- kWorkaroundUnconditionalZeroInitDriverBug) {
- // Workgroup size is a known constant, but is greater than
- // num_iterations. Emit an if statement:
- //
- // if (local_index < num_iterations) {
- // ...
- // }
- auto* cond = b.LessThan(local_idx, u32(num_iterations));
- auto block = DeclareArrayIndices(num_iterations, array_indices,
- [&] { return b.Expr(local_idx); });
- for (auto& s : stmts) {
- block.Push(s.stmt);
- }
- auto* if_stmt = b.If(cond, b.Block(block));
- init_body.Push(if_stmt);
- } else {
- // Workgroup size exactly equals num_iterations.
- // No need for any conditionals. Just emit a basic block:
- //
- // {
- // ...
- // }
- auto block = DeclareArrayIndices(num_iterations, array_indices,
- [&] { return b.Expr(local_idx); });
- for (auto& s : stmts) {
- block.Push(s.stmt);
- }
- init_body.Push(b.Block(std::move(block)));
- }
- }
-
- // Append a single workgroup barrier after the zero initialization.
- init_body.Push(b.CallStmt(b.Call("workgroupBarrier")));
-
- // Generate the zero-init function.
- auto name = b.Symbols().New("tint_zero_workgroup_memory");
- b.Func(name, Vector{b.Param(local_idx, b.ty.u32())}, b.ty.void_(),
- b.Block(std::move(init_body)));
- return name;
- }
-
- /// Looks for an existing `local_invocation_index` parameter on the entry point function @p fn,
- /// or adds a new parameter to the function if it doesn't exist.
- /// @param fn the entry point function.
- /// @return an expression to the `local_invocation_index` parameter.
- const ast::Expression* GetOrCreateLocalInvocationIndex(const Function* fn) {
- // Scan the entry point for an existing local_invocation_index builtin parameter
- std::function<const ast::Expression*()> local_index;
- for (auto* param : fn->params) {
- if (auto* builtin_attr = GetAttribute<BuiltinAttribute>(param->attributes)) {
- if (builtin_attr->builtin == core::BuiltinValue::kLocalInvocationIndex) {
- return b.Expr(ctx.Clone(param->name->symbol));
- }
- }
-
- if (auto* str = sem.Get(param)->Type()->As<core::type::Struct>()) {
- for (auto* member : str->Members()) {
- if (member->Attributes().builtin == core::BuiltinValue::kLocalInvocationIndex) {
- auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
- auto member_name = ctx.Clone(member->Name());
- return b.MemberAccessor(param_expr, member_name);
- }
- }
- }
- }
-
- // No existing local index parameter. Append one to the entry point.
- auto param_name = b.Symbols().New("local_invocation_index");
- auto* local_invocation_index = b.Builtin(core::BuiltinValue::kLocalInvocationIndex);
- auto* param = b.Param(param_name, b.ty.u32(), tint::Vector{local_invocation_index});
- ctx.InsertBack(fn->params, param);
- return b.Expr(param->name->symbol);
- }
-
- /// BuildZeroingExpr is a function that builds a sub-expression used to zero
- /// workgroup values. `num_values` is the number of elements that the
- /// expression will be used to zero. Returns the expression.
- using BuildZeroingExpr = std::function<Expression(uint32_t num_values)>;
-
- /// BuildZeroingStatements() generates the statements required to zero
- /// initialize the workgroup storage expression of type `ty`.
- /// @param ty the expression type
- /// @param get_expr a function that builds the AST nodes for the expression.
- /// @returns true on success, false on failure
- [[nodiscard]] bool BuildZeroingStatements(const core::type::Type* ty,
- const BuildZeroingExpr& get_expr) {
- if (CanTriviallyZero(ty)) {
- auto var = get_expr(1u);
- if (!var) {
- return false;
- }
- auto* zero_init = b.Call(CreateASTTypeFor(ctx, ty));
- statements.emplace_back(
- Statement{b.Assign(var.expr, zero_init), var.num_iterations, var.array_indices});
- return true;
- }
-
- if (auto* atomic = ty->As<core::type::Atomic>()) {
- auto* zero_init = b.Call(CreateASTTypeFor(ctx, atomic->Type()));
- auto expr = get_expr(1u);
- if (!expr) {
- return false;
- }
- auto* store = b.Call("atomicStore", b.AddressOf(expr.expr), zero_init);
- statements.emplace_back(
- Statement{b.CallStmt(store), expr.num_iterations, expr.array_indices});
- return true;
- }
-
- if (auto* str = ty->As<core::type::Struct>()) {
- for (auto* member : str->Members()) {
- auto name = ctx.Clone(member->Name());
- auto get_member = [&](uint32_t num_values) {
- auto s = get_expr(num_values);
- if (!s) {
- return Expression{}; // error
- }
- return Expression{b.MemberAccessor(s.expr, name), s.num_iterations,
- s.array_indices};
- };
- if (!BuildZeroingStatements(member->Type(), get_member)) {
- return false;
- }
- }
- return true;
- }
-
- if (auto* arr = ty->As<core::type::Array>()) {
- auto get_el = [&](uint32_t num_values) {
- // num_values is the number of values to zero for the element type.
- // The number of iterations required to zero the array and its elements is:
- // `num_values * arr->Count()`
- // The index for this array is:
- // `(idx % modulo) / division`
- auto count = arr->ConstantCount();
- if (!count) {
- ctx.dst->Diagnostics().AddError(Source{})
- << core::type::Array::kErrExpectedConstantCount;
- return Expression{}; // error
- }
- auto modulo = num_values * count.value();
- auto division = num_values;
- auto a = get_expr(modulo);
- if (!a) {
- return Expression{}; // error
- }
- auto array_indices = a.array_indices;
- array_indices.Add(ArrayIndex{modulo, division});
- auto index = array_index_names.GetOrAdd(ArrayIndex{modulo, division},
- [&] { return b.Symbols().New("i"); });
- return Expression{b.IndexAccessor(a.expr, index), a.num_iterations, array_indices};
- };
- return BuildZeroingStatements(arr->ElemType(), get_el);
- }
-
- TINT_UNREACHABLE() << "could not zero workgroup type: " << ty->FriendlyName();
- }
-
- /// DeclareArrayIndices returns a list of statements that contain the `let`
- /// declarations for all of the ArrayIndices.
- /// @param num_iterations the number of iterations for the block
- /// @param array_indices the list of array indices to generate `let`
- /// declarations for
- /// @param iteration a function that returns the index of the current
- /// iteration.
- /// @returns the list of `let` statements that declare the array indices
- StatementList DeclareArrayIndices(uint32_t num_iterations,
- const ArrayIndices& array_indices,
- const std::function<const ast::Expression*()>& iteration) {
- StatementList stmts;
- std::map<Symbol, ArrayIndex> indices_by_name;
- for (auto index : array_indices) {
- auto name = array_index_names.Get(index);
- auto* mod = (num_iterations > index.modulo)
- ? b.create<BinaryExpression>(core::BinaryOp::kModulo, iteration(),
- b.Expr(u32(index.modulo)))
- : iteration();
- auto* div = (index.division != 1u) ? b.Div(mod, u32(index.division)) : mod;
- auto* decl = b.Decl(b.Let(*name, b.ty.u32(), div));
- stmts.Push(decl);
- }
- return stmts;
- }
-
- /// CalculateWorkgroupSize initializes the members #workgroup_size_const and
- /// #workgroup_size_expr with the linear workgroup size.
- /// @param attr the workgroup attribute applied to the entry point function
- void CalculateWorkgroupSize(const WorkgroupAttribute* attr) {
- bool is_signed = false;
- workgroup_size_const = 1u;
- workgroup_size_expr = nullptr;
- for (auto* expr : attr->Values()) {
- if (!expr) {
- continue;
- }
- if (auto* c = sem.GetVal(expr)->ConstantValue()) {
- workgroup_size_const *= c->ValueAs<AInt>();
- continue;
- }
- // Constant value could not be found. Build expression instead.
- workgroup_size_expr = [this, expr, size = workgroup_size_expr] {
- auto* e = ctx.Clone(expr);
- if (ctx.src->TypeOf(expr)->UnwrapRef()->Is<core::type::I32>()) {
- e = b.Call<u32>(e);
- }
- return size ? b.Mul(size(), e) : e;
- };
- }
- if (workgroup_size_expr) {
- if (workgroup_size_const != 1) {
- // Fold workgroup_size_const in to workgroup_size_expr
- workgroup_size_expr = [this, is_signed, const_size = workgroup_size_const,
- expr_size = workgroup_size_expr] {
- return is_signed ? b.Mul(expr_size(), i32(const_size))
- : b.Mul(expr_size(), u32(const_size));
- };
- }
- // Indicate that workgroup_size_expr should be used instead of the
- // constant.
- workgroup_size_const = 0;
- }
- }
-
- /// @returns true if a variable with store type `ty` can be efficiently zeroed
- /// by assignment of a value constructor without operands. If CanTriviallyZero() returns false,
- /// then the type needs to be initialized by decomposing the initialization into multiple
- /// sub-initializations.
- /// @param ty the type to inspect
- bool CanTriviallyZero(const core::type::Type* ty) {
- if (ty->Is<core::type::Atomic>()) {
- return false;
- }
- if (auto* str = ty->As<core::type::Struct>()) {
- for (auto* member : str->Members()) {
- if (!CanTriviallyZero(member->Type())) {
- return false;
- }
- }
- }
- if (ty->Is<core::type::Array>()) {
- return false;
- }
- // True for all other storable types
- return true;
- }
-};
-
-ZeroInitWorkgroupMemory::ZeroInitWorkgroupMemory() = default;
-
-ZeroInitWorkgroupMemory::~ZeroInitWorkgroupMemory() = default;
-
-Transform::ApplyResult ZeroInitWorkgroupMemory::Apply(const Program& src,
- const DataMap&,
- DataMap&) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
- ProgramBuilder b;
- program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
- for (auto* fn : src.AST().Functions()) {
- if (fn->PipelineStage() == PipelineStage::kCompute) {
- State{ctx}.Run(fn);
- }
- }
-
- ctx.Clone();
- return resolver::Resolve(b);
-}
-
-} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h
deleted file mode 100644
index aed3deb..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// ZeroInitWorkgroupMemory is a transform that injects code at the top of entry
-/// points to zero-initialize workgroup memory used by that entry point (and all
-/// transitive functions called by that entry point)
-class ZeroInitWorkgroupMemory final : public Castable<ZeroInitWorkgroupMemory, Transform> {
- public:
- /// Constructor
- ZeroInitWorkgroupMemory();
-
- /// Destructor
- ~ZeroInitWorkgroupMemory() override;
-
- /// @copydoc Transform::Apply
- ApplyResult Apply(const Program& program,
- const DataMap& inputs,
- DataMap& outputs) const override;
-
- private:
- struct State;
-};
-
-} // namespace tint::ast::transform
-
-#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
deleted file mode 100644
index c5b8677..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
+++ /dev/null
@@ -1,1552 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-// list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ZeroInitWorkgroupMemoryTest = TransformTest;
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunHasNoWorkgroupVars) {
- auto* src = R"(
-var<private> v : i32;
-)";
-
- EXPECT_FALSE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunHasWorkgroupVars) {
- auto* src = R"(
-var<workgroup> a : i32;
-)";
-
- EXPECT_TRUE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, EmptyModule) {
- auto* src = "";
- auto* expect = src;
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, NoWorkgroupVars) {
- auto* src = R"(
-var<private> v : i32;
-
-fn f() {
- v = 1;
-}
-)";
- auto* expect = src;
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, UnreferencedWorkgroupVars) {
- auto* src = R"(
-var<workgroup> a : i32;
-
-var<workgroup> b : i32;
-
-var<workgroup> c : i32;
-
-fn unreferenced() {
- b = c;
-}
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
- auto* expect = src;
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, UnreferencedWorkgroupVars_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-}
-
-fn unreferenced() {
- b = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : i32;
-
-var<workgroup> c : i32;
-)";
- auto* expect = src;
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = v; // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = v;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = v; // Initialization should be inserted above this statement
-}
-
-var<workgroup> v : i32;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndexInStruct) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-struct Params {
- @builtin(local_invocation_index) local_idx : u32,
-};
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
- _ = v; // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-struct Params {
- @builtin(local_invocation_index)
- local_idx : u32,
-}
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
- tint_zero_workgroup_memory(params.local_idx);
- _ = v;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndexInStruct_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f(params : Params) {
- _ = v; // Initialization should be inserted above this statement
-}
-
-struct Params {
- @builtin(local_invocation_index) local_idx : u32,
-};
-
-var<workgroup> v : i32;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
- tint_zero_workgroup_memory(params.local_idx);
- _ = v;
-}
-
-struct Params {
- @builtin(local_invocation_index)
- local_idx : u32,
-}
-
-var<workgroup> v : i32;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f() {
- _ = v; // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = v;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
- _ = v; // Initialization should be inserted above this statement
-}
-
-var<workgroup> v : i32;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size1) {
- auto* src = R"(
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- a = i32();
- b.x = i32();
- }
- for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 1u)) {
- let i : u32 = idx;
- b.y[i] = i32();
- }
- for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = idx_1;
- c[i_1].x = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
- let i_2 : u32 = (idx_2 / 8u);
- let i : u32 = (idx_2 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = a;
- _ = b;
- _ = c;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size1_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- a = i32();
- b.x = i32();
- }
- for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 1u)) {
- let i : u32 = idx;
- b.y[i] = i32();
- }
- for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = idx_1;
- c[i_1].x = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
- let i_2 : u32 = (idx_2 / 8u);
- let i : u32 = (idx_2 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = a;
- _ = b;
- _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3) {
- auto* src = R"(
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(2, 3)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- a = i32();
- b.x = i32();
- }
- for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 6u)) {
- let i : u32 = idx;
- b.y[i] = i32();
- }
- for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 6u)) {
- let i_1 : u32 = idx_1;
- c[i_1].x = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 6u)) {
- let i_2 : u32 = (idx_2 / 8u);
- let i : u32 = (idx_2 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(2, 3)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = a;
- _ = b;
- _ = c;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3_X) {
- auto* src = R"(
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : i32;
-
-@compute @workgroup_size(2, 3, X)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-)";
- auto* expect =
- R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- for(var idx : u32 = local_idx_1; (idx < 1u); idx = (idx + (u32(X) * 6u))) {
- a = i32();
- b.x = i32();
- }
- for(var idx_1 : u32 = local_idx_1; (idx_1 < 8u); idx_1 = (idx_1 + (u32(X) * 6u))) {
- let i : u32 = idx_1;
- b.y[i] = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 32u); idx_2 = (idx_2 + (u32(X) * 6u))) {
- let i_1 : u32 = idx_2;
- c[i_1].x = i32();
- }
- for(var idx_3 : u32 = local_idx_1; (idx_3 < 256u); idx_3 = (idx_3 + (u32(X) * 6u))) {
- let i_2 : u32 = (idx_3 / 8u);
- let i : u32 = (idx_3 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : i32;
-
-@compute @workgroup_size(2, 3, X)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = a;
- _ = b;
- _ = c;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_5u_X_10u) {
- auto* src = R"(
-struct S {
- x : array<array<i32, 8>, 10>,
- y : array<i32, 8>,
- z : array<array<array<i32, 8>, 10>, 20>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : u32;
-
-@compute @workgroup_size(5u, X, 10u)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-)";
- auto* expect =
- R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- for(var idx : u32 = local_idx_1; (idx < 1u); idx = (idx + (X * 50u))) {
- a = i32();
- }
- for(var idx_1 : u32 = local_idx_1; (idx_1 < 8u); idx_1 = (idx_1 + (X * 50u))) {
- let i_1 : u32 = idx_1;
- b.y[i_1] = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 80u); idx_2 = (idx_2 + (X * 50u))) {
- let i : u32 = (idx_2 / 8u);
- let i_1 : u32 = (idx_2 % 8u);
- b.x[i][i_1] = i32();
- }
- for(var idx_3 : u32 = local_idx_1; (idx_3 < 256u); idx_3 = (idx_3 + (X * 50u))) {
- let i_4 : u32 = (idx_3 / 8u);
- let i_1 : u32 = (idx_3 % 8u);
- c[i_4].y[i_1] = i32();
- }
- for(var idx_4 : u32 = local_idx_1; (idx_4 < 1600u); idx_4 = (idx_4 + (X * 50u))) {
- let i_2 : u32 = (idx_4 / 80u);
- let i : u32 = ((idx_4 % 80u) / 8u);
- let i_1 : u32 = (idx_4 % 8u);
- b.z[i_2][i][i_1] = i32();
- }
- for(var idx_5 : u32 = local_idx_1; (idx_5 < 2560u); idx_5 = (idx_5 + (X * 50u))) {
- let i_3 : u32 = (idx_5 / 80u);
- let i : u32 = ((idx_5 % 80u) / 8u);
- let i_1 : u32 = (idx_5 % 8u);
- c[i_3].x[i][i_1] = i32();
- }
- for(var idx_6 : u32 = local_idx_1; (idx_6 < 51200u); idx_6 = (idx_6 + (X * 50u))) {
- let i_5 : u32 = (idx_6 / 1600u);
- let i_2 : u32 = ((idx_6 % 1600u) / 80u);
- let i : u32 = ((idx_6 % 80u) / 8u);
- let i_1 : u32 = (idx_6 % 8u);
- c[i_5].z[i_2][i][i_1] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : array<array<i32, 8>, 10>,
- y : array<i32, 8>,
- z : array<array<array<i32, 8>, 10>, 20>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : u32;
-
-@compute @workgroup_size(5u, X, 10u)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- _ = a;
- _ = b;
- _ = c;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_InjectedLocalIndex) {
- auto* src = R"(
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- a = i32();
- b.x = i32();
- }
- for(var idx : u32 = local_idx; (idx < 8u); idx = (idx + 1u)) {
- let i : u32 = idx;
- b.y[i] = i32();
- }
- for(var idx_1 : u32 = local_idx; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = idx_1;
- c[i_1].x = i32();
- }
- for(var idx_2 : u32 = local_idx; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
- let i_2 : u32 = (idx_2 / 8u);
- let i : u32 = (idx_2 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = a;
- _ = b;
- _ = c;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
- _ = a; // Initialization should be inserted above this statement
- _ = b;
- _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- a = i32();
- b.x = i32();
- }
- for(var idx : u32 = local_idx; (idx < 8u); idx = (idx + 1u)) {
- let i : u32 = idx;
- b.y[i] = i32();
- }
- for(var idx_1 : u32 = local_idx; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = idx_1;
- c[i_1].x = i32();
- }
- for(var idx_2 : u32 = local_idx; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
- let i_2 : u32 = (idx_2 / 8u);
- let i : u32 = (idx_2 % 8u);
- c[i_2].y[i] = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = a;
- _ = b;
- _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints) {
- auto* src = R"(
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f1() {
- _ = a; // Initialization should be inserted above this statement
- _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
- _ = b; // Initialization should be inserted above this statement
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3() {
- _ = c; // Initialization should be inserted above this statement
- _ = a;
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- a = i32();
- }
- for(var idx : u32 = local_idx; (idx < 32u); idx = (idx + 1u)) {
- let i : u32 = idx;
- c[i].x = i32();
- }
- for(var idx_1 : u32 = local_idx; (idx_1 < 256u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = (idx_1 / 8u);
- let i_2 : u32 = (idx_1 % 8u);
- c[i_1].y[i_2] = i32();
- }
- workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_1(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- b.x = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 8u); idx_2 = (idx_2 + 6u)) {
- let i_3 : u32 = idx_2;
- b.y[i_3] = i32();
- }
- workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_2(local_idx_2 : u32) {
- if ((local_idx_2 < 1u)) {
- a = i32();
- }
- if ((local_idx_2 < 32u)) {
- let i_4 : u32 = local_idx_2;
- c[i_4].x = i32();
- }
- for(var idx_3 : u32 = local_idx_2; (idx_3 < 256u); idx_3 = (idx_3 + 120u)) {
- let i_5 : u32 = (idx_3 / 8u);
- let i_6 : u32 = (idx_3 % 8u);
- c[i_5].y[i_6] = i32();
- }
- workgroupBarrier();
-}
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f1(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = a;
- _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index_1 : u32) {
- tint_zero_workgroup_memory_1(local_invocation_index_1);
- _ = b;
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) {
- tint_zero_workgroup_memory_2(local_invocation_index_2);
- _ = c;
- _ = a;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f1() {
- _ = a; // Initialization should be inserted above this statement
- _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
- _ = b; // Initialization should be inserted above this statement
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3() {
- _ = c; // Initialization should be inserted above this statement
- _ = a;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-};
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- a = i32();
- }
- for(var idx : u32 = local_idx; (idx < 32u); idx = (idx + 1u)) {
- let i : u32 = idx;
- c[i].x = i32();
- }
- for(var idx_1 : u32 = local_idx; (idx_1 < 256u); idx_1 = (idx_1 + 1u)) {
- let i_1 : u32 = (idx_1 / 8u);
- let i_2 : u32 = (idx_1 % 8u);
- c[i_1].y[i_2] = i32();
- }
- workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_1(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- b.x = i32();
- }
- for(var idx_2 : u32 = local_idx_1; (idx_2 < 8u); idx_2 = (idx_2 + 6u)) {
- let i_3 : u32 = idx_2;
- b.y[i_3] = i32();
- }
- workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_2(local_idx_2 : u32) {
- if ((local_idx_2 < 1u)) {
- a = i32();
- }
- if ((local_idx_2 < 32u)) {
- let i_4 : u32 = local_idx_2;
- c[i_4].x = i32();
- }
- for(var idx_3 : u32 = local_idx_2; (idx_3 < 256u); idx_3 = (idx_3 + 120u)) {
- let i_5 : u32 = (idx_3 / 8u);
- let i_6 : u32 = (idx_3 % 8u);
- c[i_5].y[i_6] = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f1(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = a;
- _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index_1 : u32) {
- tint_zero_workgroup_memory_1(local_invocation_index_1);
- _ = b;
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) {
- tint_zero_workgroup_memory_2(local_invocation_index_2);
- _ = c;
- _ = a;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
- x : i32,
- y : array<i32, 8>,
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage) {
- auto* src = R"(
-var<workgroup> v : i32;
-
-fn use_v() {
- _ = v;
-}
-
-fn call_use_v() {
- use_v();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- call_use_v(); // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-fn use_v() {
- _ = v;
-}
-
-fn call_use_v() {
- use_v();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- call_use_v();
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- call_use_v(); // Initialization should be inserted above this statement
-}
-
-fn call_use_v() {
- use_v();
-}
-
-fn use_v() {
- _ = v;
-}
-
-var<workgroup> v : i32;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
- if ((local_idx_1 < 1u)) {
- v = i32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
- tint_zero_workgroup_memory(local_idx);
- call_use_v();
-}
-
-fn call_use_v() {
- use_v();
-}
-
-fn use_v() {
- _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics) {
- auto* src = R"(
-var<workgroup> i : atomic<i32>;
-var<workgroup> u : atomic<u32>;
-
-@compute @workgroup_size(1)
-fn f() {
- atomicLoad(&(i)); // Initialization should be inserted above this statement
- atomicLoad(&(u));
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- atomicStore(&(i), i32());
- atomicStore(&(u), u32());
- }
- workgroupBarrier();
-}
-
-var<workgroup> i : atomic<i32>;
-
-var<workgroup> u : atomic<u32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- atomicLoad(&(i));
- atomicLoad(&(u));
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
- atomicLoad(&(i)); // Initialization should be inserted above this statement
- atomicLoad(&(u));
-}
-
-var<workgroup> i : atomic<i32>;
-var<workgroup> u : atomic<u32>;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- atomicStore(&(i), i32());
- atomicStore(&(u), u32());
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- atomicLoad(&(i));
- atomicLoad(&(u));
-}
-
-var<workgroup> i : atomic<i32>;
-
-var<workgroup> u : atomic<u32>;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics) {
- auto* src = R"(
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-};
-
-var<workgroup> w : S;
-
-@compute @workgroup_size(1)
-fn f() {
- _ = w.a; // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- w.a = i32();
- atomicStore(&(w.i), i32());
- w.b = f32();
- atomicStore(&(w.u), u32());
- w.c = u32();
- }
- workgroupBarrier();
-}
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-}
-
-var<workgroup> w : S;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = w.a;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
- _ = w.a; // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : S;
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-};
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- w.a = i32();
- atomicStore(&(w.i), i32());
- w.b = f32();
- atomicStore(&(w.u), u32());
- w.c = u32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = w.a;
-}
-
-var<workgroup> w : S;
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics) {
- auto* src = R"(
-var<workgroup> w : array<atomic<u32>, 4>;
-
-@compute @workgroup_size(1)
-fn f() {
- atomicLoad(&w[0]); // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
- let i : u32 = idx;
- atomicStore(&(w[i]), u32());
- }
- workgroupBarrier();
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- atomicLoad(&(w[0]));
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
- atomicLoad(&w[0]); // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
- let i : u32 = idx;
- atomicStore(&(w[i]), u32());
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- atomicLoad(&(w[0]));
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics) {
- auto* src = R"(
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-};
-
-var<workgroup> w : array<S, 4>;
-
-@compute @workgroup_size(1)
-fn f() {
- _ = w[0].a; // Initialization should be inserted above this statement
-}
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
- let i_1 : u32 = idx;
- w[i_1].a = i32();
- atomicStore(&(w[i_1].i), i32());
- w[i_1].b = f32();
- atomicStore(&(w[i_1].u), u32());
- w[i_1].c = u32();
- }
- workgroupBarrier();
-}
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-}
-
-var<workgroup> w : array<S, 4>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = w[0].a;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics_OutOfOrder) {
- auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
- _ = w[0].a; // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : array<S, 4>;
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-};
-)";
- auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
- let i_1 : u32 = idx;
- w[i_1].a = i32();
- atomicStore(&(w[i_1].i), i32());
- w[i_1].b = f32();
- atomicStore(&(w[i_1].u), u32());
- w[i_1].c = u32();
- }
- workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
- tint_zero_workgroup_memory(local_invocation_index);
- _ = w[0].a;
-}
-
-var<workgroup> w : array<S, 4>;
-
-struct S {
- a : i32,
- i : atomic<i32>,
- b : f32,
- u : atomic<u32>,
- c : u32,
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ArrayWithOverrideCount) {
- auto* src =
- R"(override O = 123;
-alias A = array<i32, O*2>;
-
-var<workgroup> W : A;
-
-@compute @workgroup_size(1)
-fn main() {
- let p : ptr<workgroup, A> = &W;
- (*p)[0] = 42;
-}
-)";
-
- auto* expect =
- R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, AliasTypeWithParamName) {
- auto* src =
- R"(
-var<workgroup> W : mat2x2<f32>;
-
-@compute @workgroup_size(1) fn F(@builtin(local_invocation_index) mat2x2 : u32) {
- W[0]+=0;
-}
-)";
-
- auto* expect =
- R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
- if ((local_idx < 1u)) {
- W = mat2x2<f32>();
- }
- workgroupBarrier();
-}
-
-var<workgroup> W : mat2x2<f32>;
-
-@compute @workgroup_size(1)
-fn F(@builtin(local_invocation_index) mat2x2 : u32) {
- tint_zero_workgroup_memory(mat2x2);
- W[0] += 0;
-}
-)";
-
- auto got = Run<ZeroInitWorkgroupMemory>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::ast::transform