transform: Combine InlinePointerLets and Simplify
They are always used together, and can actually be simplified when combined. This transform cannot currently deal with shadowing, and will need need to depend on a new 'unshadow' transform. Fixes a long-standing bug where we'd get an ICE if we attempt to inline a pointer let declaration with side-effects in a for-loop initializer. Fixed: tint:1321 Bug: tint:819 Change-Id: I236fed688e33a4996e47310b5ece44c991b5249f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/70661 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
f83ada8b5d
commit
e524ee1813
|
@ -448,8 +448,6 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"transform/fold_trivial_single_use_lets.h",
|
||||
"transform/for_loop_to_loop.cc",
|
||||
"transform/for_loop_to_loop.h",
|
||||
"transform/inline_pointer_lets.cc",
|
||||
"transform/inline_pointer_lets.h",
|
||||
"transform/loop_to_for_loop.cc",
|
||||
"transform/loop_to_for_loop.h",
|
||||
"transform/manager.cc",
|
||||
|
@ -470,8 +468,8 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"transform/renamer.h",
|
||||
"transform/robustness.cc",
|
||||
"transform/robustness.h",
|
||||
"transform/simplify.cc",
|
||||
"transform/simplify.h",
|
||||
"transform/simplify_pointers.cc",
|
||||
"transform/simplify_pointers.h",
|
||||
"transform/single_entry_point.cc",
|
||||
"transform/single_entry_point.h",
|
||||
"transform/transform.cc",
|
||||
|
|
|
@ -312,8 +312,6 @@ set(TINT_LIB_SRCS
|
|||
transform/for_loop_to_loop.h
|
||||
transform/glsl.cc
|
||||
transform/glsl.h
|
||||
transform/inline_pointer_lets.cc
|
||||
transform/inline_pointer_lets.h
|
||||
transform/loop_to_for_loop.cc
|
||||
transform/loop_to_for_loop.h
|
||||
transform/manager.cc
|
||||
|
@ -334,8 +332,8 @@ set(TINT_LIB_SRCS
|
|||
transform/renamer.h
|
||||
transform/robustness.cc
|
||||
transform/robustness.h
|
||||
transform/simplify.cc
|
||||
transform/simplify.h
|
||||
transform/simplify_pointers.cc
|
||||
transform/simplify_pointers.h
|
||||
transform/single_entry_point.cc
|
||||
transform/single_entry_point.h
|
||||
transform/transform.cc
|
||||
|
@ -959,7 +957,6 @@ if(${TINT_BUILD_TESTS})
|
|||
transform/fold_constants_test.cc
|
||||
transform/fold_trivial_single_use_lets_test.cc
|
||||
transform/for_loop_to_loop_test.cc
|
||||
transform/inline_pointer_lets_test.cc
|
||||
transform/loop_to_for_loop_test.cc
|
||||
transform/module_scope_var_to_entry_point_param_test.cc
|
||||
transform/multiplanar_external_texture_test.cc
|
||||
|
@ -969,7 +966,7 @@ if(${TINT_BUILD_TESTS})
|
|||
transform/remove_phonies_test.cc
|
||||
transform/renamer_test.cc
|
||||
transform/robustness_test.cc
|
||||
transform/simplify_test.cc
|
||||
transform/simplify_pointers_test.cc
|
||||
transform/single_entry_point_test.cc
|
||||
transform/test_helper.h
|
||||
transform/vectorize_scalar_matrix_constructors.cc
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
#include "src/reader/spirv/parser_impl.h"
|
||||
#include "src/transform/decompose_strided_matrix.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/manager.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
|
||||
namespace tint {
|
||||
namespace reader {
|
||||
|
@ -53,8 +52,7 @@ Program Parse(const std::vector<uint32_t>& input) {
|
|||
// attribute then we need to decompose these into an array of vectors
|
||||
if (transform::DecomposeStridedMatrix::ShouldRun(&program)) {
|
||||
transform::Manager manager;
|
||||
manager.Add<transform::InlinePointerLets>();
|
||||
manager.Add<transform::Simplify>();
|
||||
manager.Add<transform::SimplifyPointers>();
|
||||
manager.Add<transform::DecomposeStridedMatrix>();
|
||||
return manager.Run(&program).program;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#include "src/program_builder.h"
|
||||
#include "src/sem/call.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::ArrayLengthFromUniform);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::ArrayLengthFromUniform::Config);
|
||||
|
@ -62,8 +61,8 @@ static void IterateArrayLengthOnStorageVar(CloneContext& ctx, F&& functor) {
|
|||
|
||||
// Get the storage buffer that contains the runtime array.
|
||||
// We assume that the argument to `arrayLength` has the form
|
||||
// `&resource.array`, which requires that `InlinePointerLets` and
|
||||
// `Simplify` have been run before this transform.
|
||||
// `&resource.array`, which requires that `SimplifyPointers` have been run
|
||||
// before this transform.
|
||||
auto* param = call_expr->args[0]->As<ast::UnaryOpExpression>();
|
||||
if (!param || param->op != ast::UnaryOp::kAddressOf) {
|
||||
TINT_ICE(Transform, ctx.dst->Diagnostics())
|
||||
|
@ -102,7 +101,7 @@ static void IterateArrayLengthOnStorageVar(CloneContext& ctx, F&& functor) {
|
|||
void ArrayLengthFromUniform::Run(CloneContext& ctx,
|
||||
const DataMap& inputs,
|
||||
DataMap& outputs) {
|
||||
if (!Requires<InlinePointerLets, Simplify>(ctx)) {
|
||||
if (!Requires<SimplifyPointers>(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -161,42 +160,43 @@ void ArrayLengthFromUniform::Run(CloneContext& ctx,
|
|||
|
||||
std::unordered_set<uint32_t> used_size_indices;
|
||||
|
||||
IterateArrayLengthOnStorageVar(ctx, [&](const ast::CallExpression* call_expr,
|
||||
const sem::VariableUser*
|
||||
storage_buffer_sem,
|
||||
const sem::GlobalVariable* var) {
|
||||
auto binding = var->BindingPoint();
|
||||
auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
|
||||
if (idx_itr == cfg->bindpoint_to_size_index.end()) {
|
||||
return;
|
||||
}
|
||||
IterateArrayLengthOnStorageVar(
|
||||
ctx, [&](const ast::CallExpression* call_expr,
|
||||
const sem::VariableUser* storage_buffer_sem,
|
||||
const sem::GlobalVariable* var) {
|
||||
auto binding = var->BindingPoint();
|
||||
auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
|
||||
if (idx_itr == cfg->bindpoint_to_size_index.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t size_index = idx_itr->second;
|
||||
used_size_indices.insert(size_index);
|
||||
uint32_t size_index = idx_itr->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 = ctx.dst->IndexAccessor(
|
||||
ctx.dst->MemberAccessor(get_ubo()->symbol, kBufferSizeMemberName),
|
||||
array_index);
|
||||
uint32_t vec_index = size_index % 4;
|
||||
auto* total_storage_buffer_size =
|
||||
ctx.dst->IndexAccessor(vec_expr, vec_index);
|
||||
// Load the total storage buffer size from the UBO.
|
||||
uint32_t array_index = size_index / 4;
|
||||
auto* vec_expr = ctx.dst->IndexAccessor(
|
||||
ctx.dst->MemberAccessor(get_ubo()->symbol, kBufferSizeMemberName),
|
||||
array_index);
|
||||
uint32_t vec_index = size_index % 4;
|
||||
auto* total_storage_buffer_size =
|
||||
ctx.dst->IndexAccessor(vec_expr, vec_index);
|
||||
|
||||
// Calculate actual array length
|
||||
// total_storage_buffer_size - array_offset
|
||||
// array_length = ----------------------------------------
|
||||
// array_stride
|
||||
auto* storage_buffer_type =
|
||||
storage_buffer_sem->Type()->UnwrapRef()->As<sem::Struct>();
|
||||
auto* array_member_sem = storage_buffer_type->Members().back();
|
||||
uint32_t array_offset = array_member_sem->Offset();
|
||||
uint32_t array_stride = array_member_sem->Size();
|
||||
auto* array_length = ctx.dst->Div(
|
||||
ctx.dst->Sub(total_storage_buffer_size, array_offset), array_stride);
|
||||
// Calculate actual array length
|
||||
// total_storage_buffer_size - array_offset
|
||||
// array_length = ----------------------------------------
|
||||
// array_stride
|
||||
auto* storage_buffer_type =
|
||||
storage_buffer_sem->Type()->UnwrapRef()->As<sem::Struct>();
|
||||
auto* array_member_sem = storage_buffer_type->Members().back();
|
||||
uint32_t array_offset = array_member_sem->Offset();
|
||||
uint32_t array_stride = array_member_sem->Size();
|
||||
auto* array_length =
|
||||
ctx.dst->Div(ctx.dst->Sub(total_storage_buffer_size, array_offset),
|
||||
array_stride);
|
||||
|
||||
ctx.Replace(call_expr, array_length);
|
||||
});
|
||||
ctx.Replace(call_expr, array_length);
|
||||
});
|
||||
|
||||
ctx.Clone();
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace transform {
|
|||
/// 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 `InlinePointerLets` and `Simplify`
|
||||
/// 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`.
|
||||
class ArrayLengthFromUniform
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -33,31 +32,19 @@ TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) {
|
|||
"error: missing transform data for "
|
||||
"tint::transform::ArrayLengthFromUniform";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(ArrayLengthFromUniformTest, Error_MissingInlinePointerLets) {
|
||||
TEST_F(ArrayLengthFromUniformTest, Error_MissingSimplifyPointers) {
|
||||
auto* src = "";
|
||||
|
||||
auto* expect =
|
||||
"error: tint::transform::ArrayLengthFromUniform depends on "
|
||||
"tint::transform::InlinePointerLets but the dependency was not run";
|
||||
"tint::transform::SimplifyPointers but the dependency was not run";
|
||||
|
||||
auto got = Run<Simplify, ArrayLengthFromUniform>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(ArrayLengthFromUniformTest, Error_MissingSimplify) {
|
||||
auto* src = "";
|
||||
|
||||
auto* expect =
|
||||
"error: tint::transform::ArrayLengthFromUniform depends on "
|
||||
"tint::transform::Simplify but the dependency was not run";
|
||||
|
||||
auto got = Run<InlinePointerLets, ArrayLengthFromUniform>(src);
|
||||
auto got = Run<ArrayLengthFromUniform>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -106,8 +93,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>({0}),
|
||||
|
@ -160,8 +146,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>({0}),
|
||||
|
@ -282,8 +267,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>({0, 1, 2, 3, 4}),
|
||||
|
@ -398,8 +382,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>({0, 2}),
|
||||
|
@ -428,8 +411,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(src, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>(),
|
||||
|
@ -500,8 +482,7 @@ fn main() {
|
|||
DataMap data;
|
||||
data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
|
||||
|
||||
auto got =
|
||||
Run<InlinePointerLets, Simplify, ArrayLengthFromUniform>(src, data);
|
||||
auto got = Run<SimplifyPointers, ArrayLengthFromUniform>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
EXPECT_EQ(std::unordered_set<uint32_t>({0}),
|
||||
|
|
|
@ -21,10 +21,9 @@
|
|||
#include "src/program_builder.h"
|
||||
#include "src/sem/expression.h"
|
||||
#include "src/sem/member_accessor_expression.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/utils/map.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/utils/hash.h"
|
||||
#include "src/utils/map.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::DecomposeStridedMatrix);
|
||||
|
||||
|
@ -119,7 +118,7 @@ bool DecomposeStridedMatrix::ShouldRun(const Program* program) {
|
|||
}
|
||||
|
||||
void DecomposeStridedMatrix::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||
if (!Requires<InlinePointerLets, Simplify>(ctx)) {
|
||||
if (!Requires<SimplifyPointers>(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
|
||||
#include "src/ast/disable_validation_decoration.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -35,17 +34,7 @@ TEST_F(DecomposeStridedMatrixTest, Empty) {
|
|||
auto* src = R"()";
|
||||
auto* expect = src;
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(DecomposeStridedMatrixTest, MissingDependencyInlinePointerLets) {
|
||||
auto* src = R"()";
|
||||
auto* expect =
|
||||
R"(error: tint::transform::DecomposeStridedMatrix depends on tint::transform::InlinePointerLets but the dependency was not run)";
|
||||
|
||||
auto got = Run<Simplify, DecomposeStridedMatrix>(src);
|
||||
auto got = Run<SimplifyPointers, DecomposeStridedMatrix>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -53,9 +42,9 @@ TEST_F(DecomposeStridedMatrixTest, MissingDependencyInlinePointerLets) {
|
|||
TEST_F(DecomposeStridedMatrixTest, MissingDependencySimplify) {
|
||||
auto* src = R"()";
|
||||
auto* expect =
|
||||
R"(error: tint::transform::DecomposeStridedMatrix depends on tint::transform::Simplify but the dependency was not run)";
|
||||
R"(error: tint::transform::DecomposeStridedMatrix depends on tint::transform::SimplifyPointers but the dependency was not run)";
|
||||
|
||||
auto got = Run<InlinePointerLets, DecomposeStridedMatrix>(src);
|
||||
auto got = Run<DecomposeStridedMatrix>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -120,8 +109,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -182,8 +171,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -245,8 +234,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -311,8 +300,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -373,8 +362,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -440,8 +429,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -502,8 +491,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -591,8 +580,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -648,8 +637,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
@ -706,8 +695,8 @@ fn f() {
|
|||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets, Simplify, DecomposeStridedMatrix>(
|
||||
Program(std::move(b)));
|
||||
auto got =
|
||||
Run<SimplifyPointers, DecomposeStridedMatrix>(Program(std::move(b)));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
|
|
@ -22,13 +22,12 @@
|
|||
#include "src/transform/decompose_memory_access.h"
|
||||
#include "src/transform/external_texture_transform.h"
|
||||
#include "src/transform/fold_trivial_single_use_lets.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/loop_to_for_loop.h"
|
||||
#include "src/transform/manager.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/single_entry_point.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
|
||||
|
@ -58,7 +57,7 @@ Output Glsl::Run(const Program* in, const DataMap& inputs) {
|
|||
manager.Add<ZeroInitWorkgroupMemory>();
|
||||
}
|
||||
manager.Add<CanonicalizeEntryPointIO>();
|
||||
manager.Add<InlinePointerLets>();
|
||||
manager.Add<SimplifyPointers>();
|
||||
|
||||
// Running SingleEntryPoint before RemovePhonies prevents variables
|
||||
// referenced only by phonies from being optimized out. Strictly
|
||||
|
@ -69,8 +68,6 @@ Output Glsl::Run(const Program* in, const DataMap& inputs) {
|
|||
data.Add<SingleEntryPoint::Config>(cfg->entry_point);
|
||||
}
|
||||
manager.Add<RemovePhonies>();
|
||||
// Simplify cleans up messy `*(&(expr))` expressions from InlinePointerLets.
|
||||
manager.Add<Simplify>();
|
||||
manager.Add<CalculateArrayLength>();
|
||||
manager.Add<ExternalTextureTransform>();
|
||||
manager.Add<PromoteInitializersToConstVar>();
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "src/program_builder.h"
|
||||
#include "src/sem/block_statement.h"
|
||||
#include "src/sem/function.h"
|
||||
#include "src/sem/statement.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/utils/scoped_assignment.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::InlinePointerLets);
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
/// Traverses the expression `expr` looking for non-literal array indexing
|
||||
/// expressions that would affect the computed address of a pointer expression.
|
||||
/// The function-like argument `cb` is called for each found.
|
||||
/// @param program the program that owns all the expression nodes
|
||||
/// @param expr the expression to traverse
|
||||
/// @param cb a function-like object with the signature
|
||||
/// `void(const ast::Expression*)`, which is called for each array index
|
||||
/// expression
|
||||
template <typename F>
|
||||
void CollectSavedArrayIndices(const Program* program,
|
||||
const ast::Expression* expr,
|
||||
F&& cb) {
|
||||
if (auto* a = expr->As<ast::IndexAccessorExpression>()) {
|
||||
CollectSavedArrayIndices(program, a->object, cb);
|
||||
|
||||
if (!a->index->Is<ast::LiteralExpression>()) {
|
||||
cb(a->index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
|
||||
CollectSavedArrayIndices(program, m->structure, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* u = expr->As<ast::UnaryOpExpression>()) {
|
||||
CollectSavedArrayIndices(program, u->expr, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: Other ast::Expression types can be safely ignored as they cannot be
|
||||
// used to generate a reference or pointer.
|
||||
// See https://gpuweb.github.io/gpuweb/wgsl/#forming-references-and-pointers
|
||||
}
|
||||
|
||||
// PtrLet represents a `let` declaration of a pointer type.
|
||||
struct PtrLet {
|
||||
// A map of ptr-let initializer sub-expression to the name of generated
|
||||
// variable that holds the saved value of this sub-expression, when resolved
|
||||
// at the point of the ptr-let declaration.
|
||||
std::unordered_map<const ast::Expression*, Symbol> saved_vars;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
InlinePointerLets::InlinePointerLets() = default;
|
||||
|
||||
InlinePointerLets::~InlinePointerLets() = default;
|
||||
|
||||
void InlinePointerLets::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||
// If not null, current_ptr_let is the current PtrLet being operated on.
|
||||
PtrLet* current_ptr_let = nullptr;
|
||||
// A map of the AST `let` variable to the PtrLet
|
||||
std::unordered_map<const ast::Variable*, std::unique_ptr<PtrLet>> ptr_lets;
|
||||
|
||||
// Register the ast::Expression transform handler.
|
||||
// This performs two different transformations:
|
||||
// * Identifiers that resolve to the pointer-typed `let` declarations are
|
||||
// replaced with the inlined (and recursively transformed) initializer
|
||||
// expression for the `let` declaration.
|
||||
// * Sub-expressions inside the pointer-typed `let` initializer expression
|
||||
// that have been hoisted to a saved variable are replaced with the saved
|
||||
// variable identifier.
|
||||
ctx.ReplaceAll([&](const ast::Expression* expr) -> const ast::Expression* {
|
||||
if (current_ptr_let) {
|
||||
// We're currently processing the initializer expression of a
|
||||
// pointer-typed `let` declaration. Look to see if we need to swap this
|
||||
// Expression with a saved variable.
|
||||
auto it = current_ptr_let->saved_vars.find(expr);
|
||||
if (it != current_ptr_let->saved_vars.end()) {
|
||||
return ctx.dst->Expr(it->second);
|
||||
}
|
||||
}
|
||||
if (auto* ident = expr->As<ast::IdentifierExpression>()) {
|
||||
if (auto* vu = ctx.src->Sem().Get<sem::VariableUser>(ident)) {
|
||||
auto* var = vu->Variable()->Declaration();
|
||||
auto it = ptr_lets.find(var);
|
||||
if (it != ptr_lets.end()) {
|
||||
// We've found an identifier that resolves to a `let` declaration.
|
||||
// We need to replace this identifier with the initializer expression
|
||||
// of the `let` declaration. Clone the initializer expression to make
|
||||
// a copy. Note that this will call back into this ReplaceAll()
|
||||
// handler for sub-expressions of the initializer.
|
||||
auto* ptr_let = it->second.get();
|
||||
// TINT_SCOPED_ASSIGNMENT provides a stack of PtrLet*, this is
|
||||
// required to handle the 'chaining' of inlined `let`s.
|
||||
TINT_SCOPED_ASSIGNMENT(current_ptr_let, ptr_let);
|
||||
return ctx.Clone(var->constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
// Find all the pointer-typed `let` declarations.
|
||||
// Note that these must be function-scoped, as module-scoped `let`s are not
|
||||
// permitted.
|
||||
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||
if (auto* let = node->As<ast::VariableDeclStatement>()) {
|
||||
if (!let->variable->is_const) {
|
||||
continue; // Not a `let` declaration. Ignore.
|
||||
}
|
||||
|
||||
auto* var = ctx.src->Sem().Get(let->variable);
|
||||
if (!var->Type()->Is<sem::Pointer>()) {
|
||||
continue; // Not a pointer type. Ignore.
|
||||
}
|
||||
|
||||
// We're dealing with a pointer-typed `let` declaration.
|
||||
auto ptr_let = std::make_unique<PtrLet>();
|
||||
TINT_SCOPED_ASSIGNMENT(current_ptr_let, ptr_let.get());
|
||||
|
||||
auto* block = ctx.src->Sem().Get(let)->Block()->Declaration();
|
||||
|
||||
// Scan the initializer expression for array index expressions that need
|
||||
// to be hoist to temporary "saved" variables.
|
||||
CollectSavedArrayIndices(
|
||||
ctx.src, var->Declaration()->constructor,
|
||||
[&](const ast::Expression* idx_expr) {
|
||||
// We have a sub-expression that needs to be saved.
|
||||
// Create a new variable
|
||||
auto saved_name = ctx.dst->Symbols().New(
|
||||
ctx.src->Symbols().NameFor(var->Declaration()->symbol) +
|
||||
"_save");
|
||||
auto* saved = ctx.dst->Decl(
|
||||
ctx.dst->Const(saved_name, nullptr, ctx.Clone(idx_expr)));
|
||||
// Place this variable after the pointer typed let. Order here is
|
||||
// important as order-of-operations needs to be preserved.
|
||||
// CollectSavedArrayIndices() visits the LHS of an index accessor
|
||||
// before the index expression.
|
||||
// Note that repeated calls to InsertAfter() with the same `after`
|
||||
// argument will result in nodes to inserted in the order the calls
|
||||
// are made (last call is inserted last).
|
||||
ctx.InsertAfter(block->statements, let, saved);
|
||||
// Record the substitution of `idx_expr` to the saved variable with
|
||||
// the symbol `saved_name`. This will be used by the ReplaceAll()
|
||||
// handler above.
|
||||
ptr_let->saved_vars.emplace(idx_expr, saved_name);
|
||||
});
|
||||
|
||||
// Record the pointer-typed `let` declaration.
|
||||
// This will be used by the ReplaceAll() handler above.
|
||||
ptr_lets.emplace(let->variable, std::move(ptr_let));
|
||||
// As the original `let` declaration will be fully inlined, there's no
|
||||
// need for the original declaration to exist. Remove it.
|
||||
RemoveStatement(ctx, let);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Clone();
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/simplify.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "src/program_builder.h"
|
||||
#include "src/sem/block_statement.h"
|
||||
#include "src/sem/function.h"
|
||||
#include "src/sem/statement.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/utils/scoped_assignment.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::Simplify);
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
Simplify::Simplify() = default;
|
||||
|
||||
Simplify::~Simplify() = default;
|
||||
|
||||
void Simplify::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||
ctx.ReplaceAll([&](const ast::Expression* expr) -> const ast::Expression* {
|
||||
if (auto* outer = expr->As<ast::UnaryOpExpression>()) {
|
||||
if (auto* inner = outer->expr->As<ast::UnaryOpExpression>()) {
|
||||
if (outer->op == ast::UnaryOp::kAddressOf &&
|
||||
inner->op == ast::UnaryOp::kIndirection) {
|
||||
// &(*(expr)) => expr
|
||||
return ctx.Clone(inner->expr);
|
||||
}
|
||||
if (outer->op == ast::UnaryOp::kIndirection &&
|
||||
inner->op == ast::UnaryOp::kAddressOf) {
|
||||
// *(&(expr)) => expr
|
||||
return ctx.Clone(inner->expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
ctx.Clone();
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_TRANSFORM_SIMPLIFY_H_
|
||||
#define SRC_TRANSFORM_SIMPLIFY_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "src/transform/transform.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
/// Simplify is a peephole optimizer Transform that simplifies ASTs by removing
|
||||
/// unnecessary operations.
|
||||
/// Simplify currently optimizes the following:
|
||||
/// `&(*(expr))` => `expr`
|
||||
/// `*(&(expr))` => `expr`
|
||||
class Simplify : public Castable<Simplify, Transform> {
|
||||
public:
|
||||
/// Constructor
|
||||
Simplify();
|
||||
|
||||
/// Destructor
|
||||
~Simplify() override;
|
||||
|
||||
protected:
|
||||
/// Runs the transform using the CloneContext built for transforming a
|
||||
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||
/// @param ctx the CloneContext primed with the input program and
|
||||
/// ProgramBuilder
|
||||
/// @param inputs optional extra transform-specific input data
|
||||
/// @param outputs optional extra transform-specific output data
|
||||
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) override;
|
||||
};
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TRANSFORM_SIMPLIFY_H_
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/program_builder.h"
|
||||
#include "src/sem/block_statement.h"
|
||||
#include "src/sem/function.h"
|
||||
#include "src/sem/statement.h"
|
||||
#include "src/sem/variable.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::SimplifyPointers);
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
namespace {
|
||||
|
||||
/// PointerOp describes either possible indirection or address-of action on an
|
||||
/// expression.
|
||||
struct PointerOp {
|
||||
/// Positive: Number of times the `expr` was dereferenced (*expr)
|
||||
/// Negative: Number of times the `expr` was 'addressed-of' (&expr)
|
||||
/// Zero: no pointer op on `expr`
|
||||
int indirections = 0;
|
||||
/// The expression being operated on
|
||||
const ast::Expression* expr = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// The PIMPL state for the SimplifyPointers transform
|
||||
struct SimplifyPointers::State {
|
||||
/// The clone context
|
||||
CloneContext& ctx;
|
||||
|
||||
/// Constructor
|
||||
/// @param context the clone context
|
||||
explicit State(CloneContext& context) : ctx(context) {}
|
||||
|
||||
/// Traverses the expression `expr` looking for non-literal array indexing
|
||||
/// expressions that would affect the computed address of a pointer
|
||||
/// expression. The function-like argument `cb` is called for each found.
|
||||
/// @param expr the expression to traverse
|
||||
/// @param cb a function-like object with the signature
|
||||
/// `void(const ast::Expression*)`, which is called for each array index
|
||||
/// expression
|
||||
template <typename F>
|
||||
static void CollectSavedArrayIndices(const ast::Expression* expr, F&& cb) {
|
||||
if (auto* a = expr->As<ast::IndexAccessorExpression>()) {
|
||||
CollectSavedArrayIndices(a->object, cb);
|
||||
if (!a->index->Is<ast::LiteralExpression>()) {
|
||||
cb(a->index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
|
||||
CollectSavedArrayIndices(m->structure, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* u = expr->As<ast::UnaryOpExpression>()) {
|
||||
CollectSavedArrayIndices(u->expr, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: Other ast::Expression types can be safely ignored as they cannot be
|
||||
// used to generate a reference or pointer.
|
||||
// See https://gpuweb.github.io/gpuweb/wgsl/#forming-references-and-pointers
|
||||
}
|
||||
|
||||
/// Reduce walks the expression chain, collapsing all address-of and
|
||||
/// indirection ops into a PointerOp.
|
||||
/// @param in the expression to walk
|
||||
/// @returns the reduced PointerOp
|
||||
PointerOp Reduce(const ast::Expression* in) const {
|
||||
PointerOp op{0, in};
|
||||
while (true) {
|
||||
if (auto* unary = op.expr->As<ast::UnaryOpExpression>()) {
|
||||
switch (unary->op) {
|
||||
case ast::UnaryOp::kIndirection:
|
||||
op.indirections++;
|
||||
op.expr = unary->expr;
|
||||
continue;
|
||||
case ast::UnaryOp::kAddressOf:
|
||||
op.indirections--;
|
||||
op.expr = unary->expr;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (auto* user = ctx.src->Sem().Get<sem::VariableUser>(op.expr)) {
|
||||
auto* var = user->Variable();
|
||||
if (var->Is<sem::LocalVariable>() && //
|
||||
var->Declaration()->is_const && //
|
||||
var->Type()->Is<sem::Pointer>()) {
|
||||
op.expr = var->Declaration()->constructor;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the transformation
|
||||
void Run() {
|
||||
// A map of saved expressions to their saved variable name
|
||||
std::unordered_map<const ast::Expression*, Symbol> saved_vars;
|
||||
|
||||
// Register the ast::Expression transform handler.
|
||||
// This performs two different transformations:
|
||||
// * Identifiers that resolve to the pointer-typed `let` declarations are
|
||||
// replaced with the recursively inlined initializer expression for the
|
||||
// `let` declaration.
|
||||
// * Sub-expressions inside the pointer-typed `let` initializer expression
|
||||
// that have been hoisted to a saved variable are replaced with the saved
|
||||
// variable identifier.
|
||||
ctx.ReplaceAll([&](const ast::Expression* expr) -> const ast::Expression* {
|
||||
// Look to see if we need to swap this Expression with a saved variable.
|
||||
auto it = saved_vars.find(expr);
|
||||
if (it != saved_vars.end()) {
|
||||
return ctx.dst->Expr(it->second);
|
||||
}
|
||||
|
||||
// Reduce the expression, folding away chains of address-of / indirections
|
||||
auto op = Reduce(expr);
|
||||
|
||||
// Clone the reduced root expression
|
||||
expr = ctx.CloneWithoutTransform(op.expr);
|
||||
|
||||
// And reapply the minimum number of address-of / indirections
|
||||
for (int i = 0; i < op.indirections; i++) {
|
||||
expr = ctx.dst->Deref(expr);
|
||||
}
|
||||
for (int i = 0; i > op.indirections; i--) {
|
||||
expr = ctx.dst->AddressOf(expr);
|
||||
}
|
||||
return expr;
|
||||
});
|
||||
|
||||
// Find all the pointer-typed `let` declarations.
|
||||
// Note that these must be function-scoped, as module-scoped `let`s are not
|
||||
// permitted.
|
||||
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||
if (auto* let = node->As<ast::VariableDeclStatement>()) {
|
||||
if (!let->variable->is_const) {
|
||||
continue; // Not a `let` declaration. Ignore.
|
||||
}
|
||||
|
||||
auto* var = ctx.src->Sem().Get(let->variable);
|
||||
if (!var->Type()->Is<sem::Pointer>()) {
|
||||
continue; // Not a pointer type. Ignore.
|
||||
}
|
||||
|
||||
// We're dealing with a pointer-typed `let` declaration.
|
||||
|
||||
// Scan the initializer expression for array index expressions that need
|
||||
// to be hoist to temporary "saved" variables.
|
||||
std::vector<const ast::VariableDeclStatement*> saved;
|
||||
CollectSavedArrayIndices(
|
||||
var->Declaration()->constructor,
|
||||
[&](const ast::Expression* idx_expr) {
|
||||
// We have a sub-expression that needs to be saved.
|
||||
// Create a new variable
|
||||
auto saved_name = ctx.dst->Symbols().New(
|
||||
ctx.src->Symbols().NameFor(var->Declaration()->symbol) +
|
||||
"_save");
|
||||
auto* decl = ctx.dst->Decl(
|
||||
ctx.dst->Const(saved_name, nullptr, ctx.Clone(idx_expr)));
|
||||
saved.emplace_back(decl);
|
||||
// Record the substitution of `idx_expr` to the saved variable
|
||||
// with the symbol `saved_name`. This will be used by the
|
||||
// ReplaceAll() handler above.
|
||||
saved_vars.emplace(idx_expr, saved_name);
|
||||
});
|
||||
|
||||
// Find the place to insert the saved declarations.
|
||||
// Special care needs to be made for lets declared as the initializer
|
||||
// part of for-loops. In this case the block will hold the for-loop
|
||||
// statement, not the let.
|
||||
if (!saved.empty()) {
|
||||
auto* stmt = ctx.src->Sem().Get(let);
|
||||
auto* block = stmt->Block();
|
||||
// Find the statement owned by the block (either the let decl or a
|
||||
// for-loop)
|
||||
while (block != stmt->Parent()) {
|
||||
stmt = stmt->Parent();
|
||||
}
|
||||
// Declare the stored variables just before stmt. Order here is
|
||||
// important as order-of-operations needs to be preserved.
|
||||
// CollectSavedArrayIndices() visits the LHS of an index accessor
|
||||
// before the index expression.
|
||||
for (auto* decl : saved) {
|
||||
// Note that repeated calls to InsertBefore() with the same `before`
|
||||
// argument will result in nodes to inserted in the order the
|
||||
// calls are made (last call is inserted last).
|
||||
ctx.InsertBefore(block->Declaration()->statements,
|
||||
stmt->Declaration(), decl);
|
||||
}
|
||||
}
|
||||
|
||||
// As the original `let` declaration will be fully inlined, there's no
|
||||
// need for the original declaration to exist. Remove it.
|
||||
RemoveStatement(ctx, let);
|
||||
}
|
||||
}
|
||||
ctx.Clone();
|
||||
}
|
||||
};
|
||||
|
||||
SimplifyPointers::SimplifyPointers() = default;
|
||||
|
||||
SimplifyPointers::~SimplifyPointers() = default;
|
||||
|
||||
void SimplifyPointers::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||
State(ctx).Run();
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_TRANSFORM_INLINE_POINTER_LETS_H_
|
||||
#define SRC_TRANSFORM_INLINE_POINTER_LETS_H_
|
||||
#ifndef SRC_TRANSFORM_SIMPLIFY_POINTERS_H_
|
||||
#define SRC_TRANSFORM_SIMPLIFY_POINTERS_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
@ -23,23 +23,25 @@
|
|||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
/// InlinePointerLets is a Transform that moves all usage of function-scope
|
||||
/// SimplifyPointers is a Transform that moves all usage of function-scope
|
||||
/// `let` statements of a pointer type into their places of usage.
|
||||
///
|
||||
/// Parameters of a pointer type are not adjusted.
|
||||
///
|
||||
/// Note: InlinePointerLets does not operate on module-scope `let`s, as these
|
||||
/// Note: SimplifyPointers does not operate on module-scope `let`s, as these
|
||||
/// cannot be pointers: https://gpuweb.github.io/gpuweb/wgsl/#module-constants
|
||||
/// `A module-scope let-declared constant must be of constructible type.`
|
||||
class InlinePointerLets : public Castable<InlinePointerLets, Transform> {
|
||||
class SimplifyPointers : public Castable<SimplifyPointers, Transform> {
|
||||
public:
|
||||
/// Constructor
|
||||
InlinePointerLets();
|
||||
SimplifyPointers();
|
||||
|
||||
/// Destructor
|
||||
~InlinePointerLets() override;
|
||||
~SimplifyPointers() override;
|
||||
|
||||
protected:
|
||||
struct State;
|
||||
|
||||
/// Runs the transform using the CloneContext built for transforming a
|
||||
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||
/// @param ctx the CloneContext primed with the input program and
|
||||
|
@ -52,4 +54,4 @@ class InlinePointerLets : public Castable<InlinePointerLets, Transform> {
|
|||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TRANSFORM_INLINE_POINTER_LETS_H_
|
||||
#endif // SRC_TRANSFORM_SIMPLIFY_POINTERS_H_
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
@ -24,18 +24,18 @@ namespace tint {
|
|||
namespace transform {
|
||||
namespace {
|
||||
|
||||
using InlinePointerLetsTest = TransformTest;
|
||||
using SimplifyPointersTest = TransformTest;
|
||||
|
||||
TEST_F(InlinePointerLetsTest, EmptyModule) {
|
||||
TEST_F(SimplifyPointersTest, EmptyModule) {
|
||||
auto* src = "";
|
||||
auto* expect = "";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, Basic) {
|
||||
TEST_F(SimplifyPointersTest, FoldPointer) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
|
@ -47,16 +47,68 @@ fn f() {
|
|||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let x : i32 = *(&(v));
|
||||
let x : i32 = v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, ComplexChain) {
|
||||
TEST_F(SimplifyPointersTest, AddressOfDeref) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let p : ptr<function, i32> = &(v);
|
||||
let x : ptr<function, i32> = &(*(p));
|
||||
let y : ptr<function, i32> = &(*(&(*(p))));
|
||||
let z : ptr<function, i32> = &(*(&(*(&(*(&(*(p))))))));
|
||||
var a = *x;
|
||||
var b = *y;
|
||||
var c = *z;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
var a = v;
|
||||
var b = v;
|
||||
var c = v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyPointersTest, DerefAddressOf) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let x : i32 = *(&(v));
|
||||
let y : i32 = *(&(*(&(v))));
|
||||
let z : i32 = *(&(*(&(*(&(*(&(v))))))));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let x : i32 = v;
|
||||
let y : i32 = v;
|
||||
let z : i32 = v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyPointersTest, ComplexChain) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var a : array<mat4x4<f32>, 4>;
|
||||
|
@ -70,16 +122,16 @@ fn f() {
|
|||
auto* expect = R"(
|
||||
fn f() {
|
||||
var a : array<mat4x4<f32>, 4>;
|
||||
let v : vec4<f32> = *(&((*(&((*(&(a)))[3])))[2]));
|
||||
let v : vec4<f32> = a[3][2];
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, SavedVars) {
|
||||
TEST_F(SimplifyPointersTest, SavedVars) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
i : i32;
|
||||
|
@ -115,7 +167,7 @@ fn arr() {
|
|||
var j : i32 = 0;
|
||||
let p_save = (i + j);
|
||||
i = 2;
|
||||
*(&(a[p_save].i)) = 4;
|
||||
a[p_save].i = 4;
|
||||
}
|
||||
|
||||
fn matrix() {
|
||||
|
@ -124,16 +176,16 @@ fn matrix() {
|
|||
var j : i32 = 0;
|
||||
let p_save_1 = (i + j);
|
||||
i = 2;
|
||||
*(&(m[p_save_1])) = vec3<f32>(4.0, 5.0, 6.0);
|
||||
m[p_save_1] = vec3<f32>(4.0, 5.0, 6.0);
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, DontSaveLiterals) {
|
||||
TEST_F(SimplifyPointersTest, DontSaveLiterals) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var arr : array<i32, 2>;
|
||||
|
@ -145,16 +197,16 @@ fn f() {
|
|||
auto* expect = R"(
|
||||
fn f() {
|
||||
var arr : array<i32, 2>;
|
||||
*(&(arr[1])) = 4;
|
||||
arr[1] = 4;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, SavedVarsChain) {
|
||||
TEST_F(SimplifyPointersTest, SavedVarsChain) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var arr : array<array<i32, 2>, 2>;
|
||||
|
@ -173,16 +225,51 @@ fn f() {
|
|||
let j : i32 = 1;
|
||||
let p_save = i;
|
||||
let q_save = j;
|
||||
*(&((*(&(arr[p_save])))[q_save])) = 12;
|
||||
arr[p_save][q_save] = 12;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(InlinePointerLetsTest, MultiSavedVarsInSinglePtrLetExpr) {
|
||||
TEST_F(SimplifyPointersTest, ForLoopInit) {
|
||||
auto* src = R"(
|
||||
fn foo() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main() {
|
||||
var arr = array<f32, 4>();
|
||||
for (let a = &arr[foo()]; ;) {
|
||||
let x = *a;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn foo() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main() {
|
||||
var arr = array<f32, 4>();
|
||||
let a_save = foo();
|
||||
for(; ; ) {
|
||||
let x = arr[a_save];
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyPointersTest, MultiSavedVarsInSinglePtrLetExpr) {
|
||||
auto* src = R"(
|
||||
fn x() -> i32 {
|
||||
return 1;
|
||||
|
@ -238,18 +325,18 @@ fn f() {
|
|||
let p_save = x();
|
||||
let p_save_1 = y();
|
||||
let p_save_2 = z();
|
||||
*(&(arr[p_save].a[p_save_1].a[p_save_2])) = 1;
|
||||
*(&(arr[p_save].a[p_save_1].a[p_save_2])) = 2;
|
||||
arr[p_save].a[p_save_1].a[p_save_2] = 1;
|
||||
arr[p_save].a[p_save_1].a[p_save_2] = 2;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/819): Enable when we support inter-scope shadowing.
|
||||
TEST_F(InlinePointerLetsTest, DISABLED_ModificationAfterInline) {
|
||||
TEST_F(SimplifyPointersTest, DISABLED_ModificationAfterInline) {
|
||||
auto* src = R"(
|
||||
fn x(p : ptr<function, i32>) -> i32 {
|
||||
return *p;
|
||||
|
@ -267,7 +354,7 @@ fn f() {
|
|||
|
||||
auto* expect = R"(<TODO>)";
|
||||
|
||||
auto got = Run<InlinePointerLets>(src);
|
||||
auto got = Run<SimplifyPointers>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/simplify.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/transform/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
using SimplifyTest = TransformTest;
|
||||
|
||||
TEST_F(SimplifyTest, EmptyModule) {
|
||||
auto* src = "";
|
||||
auto* expect = "";
|
||||
|
||||
auto got = Run<Simplify>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyTest, AddressOfDeref) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let p : ptr<function, i32> = &(v);
|
||||
let x : ptr<function, i32> = &(*(p));
|
||||
let y : ptr<function, i32> = &(*(&(*(p))));
|
||||
let z : ptr<function, i32> = &(*(&(*(&(*(&(*(p))))))));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let p : ptr<function, i32> = &(v);
|
||||
let x : ptr<function, i32> = p;
|
||||
let y : ptr<function, i32> = p;
|
||||
let z : ptr<function, i32> = p;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<Simplify>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyTest, DerefAddressOf) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let x : i32 = *(&(v));
|
||||
let y : i32 = *(&(*(&(v))));
|
||||
let z : i32 = *(&(*(&(*(&(*(&(v))))))));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let x : i32 = v;
|
||||
let y : i32 = v;
|
||||
let z : i32 = v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<Simplify>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SimplifyTest, NoChange) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let p : ptr<function, i32> = &(v);
|
||||
let x : i32 = *(p);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
var v : i32;
|
||||
let p : ptr<function, i32> = &(v);
|
||||
let x : i32 = *(p);
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<Simplify>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -51,14 +51,13 @@
|
|||
#include "src/transform/decompose_memory_access.h"
|
||||
#include "src/transform/external_texture_transform.h"
|
||||
#include "src/transform/fold_trivial_single_use_lets.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/loop_to_for_loop.h"
|
||||
#include "src/transform/manager.h"
|
||||
#include "src/transform/num_workgroups_from_uniform.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
#include "src/utils/defer.h"
|
||||
#include "src/utils/map.h"
|
||||
|
@ -151,9 +150,7 @@ SanitizedResult Sanitize(
|
|||
// assumes that num_workgroups builtins only appear as struct members and are
|
||||
// only accessed directly via member accessors.
|
||||
manager.Add<transform::NumWorkgroupsFromUniform>();
|
||||
manager.Add<transform::InlinePointerLets>();
|
||||
// Simplify cleans up messy `*(&(expr))` expressions from InlinePointerLets.
|
||||
manager.Add<transform::Simplify>();
|
||||
manager.Add<transform::SimplifyPointers>();
|
||||
manager.Add<transform::RemovePhonies>();
|
||||
// ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
|
||||
// it assumes that the form of the array length argument is &var.array.
|
||||
|
|
|
@ -60,13 +60,12 @@
|
|||
#include "src/transform/array_length_from_uniform.h"
|
||||
#include "src/transform/canonicalize_entry_point_io.h"
|
||||
#include "src/transform/external_texture_transform.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/manager.h"
|
||||
#include "src/transform/module_scope_var_to_entry_point_param.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/vectorize_scalar_matrix_constructors.h"
|
||||
#include "src/transform/wrap_arrays_in_structs.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
|
@ -164,10 +163,9 @@ SanitizedResult Sanitize(
|
|||
manager.Add<transform::VectorizeScalarMatrixConstructors>();
|
||||
manager.Add<transform::WrapArraysInStructs>();
|
||||
manager.Add<transform::PadArrayElements>();
|
||||
manager.Add<transform::InlinePointerLets>();
|
||||
manager.Add<transform::RemovePhonies>();
|
||||
manager.Add<transform::Simplify>();
|
||||
// ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
|
||||
manager.Add<transform::SimplifyPointers>();
|
||||
// ArrayLengthFromUniform must come after SimplifyPointers, as
|
||||
// it assumes that the form of the array length argument is &var.array.
|
||||
manager.Add<transform::ArrayLengthFromUniform>();
|
||||
manager.Add<transform::ModuleScopeVarToEntryPointParam>();
|
||||
|
|
|
@ -45,9 +45,8 @@
|
|||
#include "src/transform/external_texture_transform.h"
|
||||
#include "src/transform/fold_constants.h"
|
||||
#include "src/transform/for_loop_to_loop.h"
|
||||
#include "src/transform/inline_pointer_lets.h"
|
||||
#include "src/transform/manager.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/simplify_pointers.h"
|
||||
#include "src/transform/vectorize_scalar_matrix_constructors.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
#include "src/utils/defer.h"
|
||||
|
@ -270,8 +269,7 @@ SanitizedResult Sanitize(const Program* in,
|
|||
if (!disable_workgroup_init) {
|
||||
manager.Add<transform::ZeroInitWorkgroupMemory>();
|
||||
}
|
||||
manager.Add<transform::InlinePointerLets>(); // Required for arrayLength()
|
||||
manager.Add<transform::Simplify>(); // Required for arrayLength()
|
||||
manager.Add<transform::SimplifyPointers>(); // Required for arrayLength()
|
||||
manager.Add<transform::FoldConstants>();
|
||||
manager.Add<transform::ExternalTextureTransform>();
|
||||
manager.Add<transform::VectorizeScalarMatrixConstructors>();
|
||||
|
|
|
@ -314,7 +314,6 @@ tint_unittests_source_set("tint_unittests_transform_src") {
|
|||
"../src/transform/fold_constants_test.cc",
|
||||
"../src/transform/fold_trivial_single_use_lets_test.cc",
|
||||
"../src/transform/for_loop_to_loop_test.cc",
|
||||
"../src/transform/inline_pointer_lets_test.cc",
|
||||
"../src/transform/loop_to_for_loop_test.cc",
|
||||
"../src/transform/module_scope_var_to_entry_point_param_test.cc",
|
||||
"../src/transform/multiplanar_external_texture_test.cc",
|
||||
|
@ -324,7 +323,7 @@ tint_unittests_source_set("tint_unittests_transform_src") {
|
|||
"../src/transform/remove_phonies_test.cc",
|
||||
"../src/transform/renamer_test.cc",
|
||||
"../src/transform/robustness_test.cc",
|
||||
"../src/transform/simplify_test.cc",
|
||||
"../src/transform/simplify_pointers_test.cc",
|
||||
"../src/transform/single_entry_point_test.cc",
|
||||
"../src/transform/test_helper.h",
|
||||
"../src/transform/transform_test.cc",
|
||||
|
|
Loading…
Reference in New Issue