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:
Ben Clayton 2021-11-23 20:45:51 +00:00
parent f83ada8b5d
commit e524ee1813
20 changed files with 454 additions and 588 deletions

View File

@ -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",

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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

View File

@ -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}),

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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>();

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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));
}

View File

@ -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

View File

@ -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.

View File

@ -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>();

View File

@ -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>();

View File

@ -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",