Implement phony assignment

Bug: tint:1213
Change-Id: Ib1ebc4947405c4ada7a9bdbc6bd5a36447bbd234
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67064
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2021-10-21 23:04:44 +00:00
committed by Tint LUCI CQ
parent 092326894e
commit 1aa98e62c0
47 changed files with 1081 additions and 25 deletions

View File

@@ -27,6 +27,7 @@
#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/single_entry_point.h"
#include "src/transform/zero_init_workgroup_memory.h"
@@ -58,6 +59,7 @@ Output Glsl::Run(const Program* in, const DataMap& inputs) {
}
manager.Add<CanonicalizeEntryPointIO>();
manager.Add<InlinePointerLets>();
manager.Add<RemovePhonies>();
// Simplify cleans up messy `*(&(expr))` expressions from InlinePointerLets.
manager.Add<Simplify>();
manager.Add<CalculateArrayLength>();

View File

@@ -0,0 +1,138 @@
// 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/remove_phonies.h"
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "src/ast/traverse_expressions.h"
#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/get_or_create.h"
#include "src/utils/scoped_assignment.h"
TINT_INSTANTIATE_TYPEINFO(tint::transform::RemovePhonies);
namespace tint {
namespace transform {
namespace {
struct SinkSignature {
std::vector<const sem::Type*> types;
bool operator==(const SinkSignature& other) const {
if (types.size() != other.types.size()) {
return false;
}
for (size_t i = 0; i < types.size(); i++) {
if (types[i] != other.types[i]) {
return false;
}
}
return true;
}
struct Hasher {
/// @param sig the CallTargetSignature to hash
/// @return the hash value
std::size_t operator()(const SinkSignature& sig) const {
size_t hash = tint::utils::Hash(sig.types.size());
for (auto* ty : sig.types) {
tint::utils::HashCombine(&hash, ty);
}
return hash;
}
};
};
} // namespace
RemovePhonies::RemovePhonies() = default;
RemovePhonies::~RemovePhonies() = default;
void RemovePhonies::Run(CloneContext& ctx, const DataMap&, DataMap&) {
auto& sem = ctx.src->Sem();
std::unordered_map<SinkSignature, Symbol, SinkSignature::Hasher> sinks;
for (auto* node : ctx.src->ASTNodes().Objects()) {
if (auto* stmt = node->As<ast::AssignmentStatement>()) {
if (stmt->lhs->Is<ast::PhonyExpression>()) {
std::vector<const ast::Expression*> side_effects;
if (!ast::TraverseExpressions(stmt->rhs, ctx.dst->Diagnostics(),
[&](const ast::CallExpression* call) {
side_effects.push_back(call);
return ast::TraverseAction::Skip;
})) {
return;
}
if (side_effects.empty()) {
// Phony assignment with no side effects.
// Just remove it.
RemoveStatement(ctx, stmt);
continue;
}
if (side_effects.size() == 1) {
if (auto* call = side_effects[0]->As<ast::CallExpression>()) {
// Phony assignment with single call side effect.
// Replace phony assignment with call.
ctx.Replace(
stmt, [&, call] { return ctx.dst->CallStmt(ctx.Clone(call)); });
continue;
}
}
// Phony assignment with multiple side effects.
// Generate a call to a dummy function with the side effects as
// arguments.
ctx.Replace(stmt, [&, side_effects] {
SinkSignature sig;
for (auto* arg : side_effects) {
sig.types.push_back(sem.Get(arg)->Type()->UnwrapRef());
}
auto sink = utils::GetOrCreate(sinks, sig, [&] {
auto name = ctx.dst->Symbols().New("phony_sink");
ast::VariableList params;
for (auto* ty : sig.types) {
auto* ast_ty = CreateASTTypeFor(ctx, ty);
params.push_back(
ctx.dst->Param("p" + std::to_string(params.size()), ast_ty));
}
ctx.dst->Func(name, params, ctx.dst->ty.void_(), {});
return name;
});
ast::ExpressionList args;
for (auto* arg : side_effects) {
args.push_back(ctx.Clone(arg));
}
return ctx.dst->CallStmt(ctx.dst->Call(sink, args));
});
}
}
}
ctx.Clone();
}
} // namespace transform
} // namespace tint

View File

@@ -0,0 +1,50 @@
// 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_REMOVE_PHONIES_H_
#define SRC_TRANSFORM_REMOVE_PHONIES_H_
#include <string>
#include <unordered_map>
#include "src/transform/transform.h"
namespace tint {
namespace transform {
/// RemovePhonies is a Transform that removes all phony-assignment statements,
/// while preserving function call expressions in the RHS of the assignment that
/// may have side-effects.
class RemovePhonies : public Castable<RemovePhonies, Transform> {
public:
/// Constructor
RemovePhonies();
/// Destructor
~RemovePhonies() 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_REMOVE_PHONIES_H_

View File

@@ -0,0 +1,226 @@
// 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/remove_phonies.h"
#include <memory>
#include <utility>
#include <vector>
#include "src/transform/test_helper.h"
namespace tint {
namespace transform {
namespace {
using RemovePhoniesTest = TransformTest;
TEST_F(RemovePhoniesTest, EmptyModule) {
auto* src = "";
auto* expect = "";
auto got = Run<RemovePhonies>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RemovePhoniesTest, NoSideEffects) {
auto* src = R"(
[[group(0), binding(0)]] var t : texture_2d<f32>;
fn f() {
var v : i32;
_ = &v;
_ = 1;
_ = 1 + 2;
_ = t;
}
)";
auto* expect = R"(
[[group(0), binding(0)]] var t : texture_2d<f32>;
fn f() {
var v : i32;
}
)";
auto got = Run<RemovePhonies>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RemovePhoniesTest, SingleSideEffects) {
auto* src = R"(
fn neg(a : i32) -> i32 {
return -(a);
}
fn add(a : i32, b : i32) -> i32 {
return (a + b);
}
fn f() {
_ = neg(1);
_ = add(2, 3);
_ = add(neg(4), neg(5));
}
)";
auto* expect = R"(
fn neg(a : i32) -> i32 {
return -(a);
}
fn add(a : i32, b : i32) -> i32 {
return (a + b);
}
fn f() {
neg(1);
add(2, 3);
add(neg(4), neg(5));
}
)";
auto got = Run<RemovePhonies>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RemovePhoniesTest, MultipleSideEffects) {
auto* src = R"(
fn neg(a : i32) -> i32 {
return -(a);
}
fn add(a : i32, b : i32) -> i32 {
return (a + b);
}
fn xor(a : u32, b : u32) -> u32 {
return (a ^ b);
}
fn f() {
_ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
_ = add(9, neg(10)) + neg(11);
_ = xor(12u, 13u) + xor(14u, 15u);
_ = neg(16) / neg(17) + add(18, 19);
}
)";
auto* expect = R"(
fn neg(a : i32) -> i32 {
return -(a);
}
fn add(a : i32, b : i32) -> i32 {
return (a + b);
}
fn xor(a : u32, b : u32) -> u32 {
return (a ^ b);
}
fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
}
fn phony_sink_1(p0 : i32, p1 : i32) {
}
fn phony_sink_2(p0 : u32, p1 : u32) {
}
fn f() {
phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
phony_sink_1(add(9, neg(10)), neg(11));
phony_sink_2(xor(12u, 13u), xor(14u, 15u));
phony_sink(neg(16), neg(17), add(18, 19));
}
)";
auto got = Run<RemovePhonies>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RemovePhoniesTest, ForLoop) {
auto* src = R"(
[[block]]
struct S {
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read_write> s : S;
fn x() -> i32 {
return 0;
}
fn y() -> i32 {
return 0;
}
fn z() -> i32 {
return 0;
}
fn f() {
for (_ = &s.arr; ;_ = &s.arr) {
}
for (_ = x(); ;_ = y() + z()) {
}
}
)";
auto* expect = R"(
[[block]]
struct S {
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read_write> s : S;
fn x() -> i32 {
return 0;
}
fn y() -> i32 {
return 0;
}
fn z() -> i32 {
return 0;
}
fn phony_sink(p0 : i32, p1 : i32) {
}
fn f() {
for(; ; ) {
}
for(x(); ; phony_sink(y(), z())) {
}
}
)";
auto got = Run<RemovePhonies>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint

View File

@@ -75,7 +75,7 @@ bool Transform::Requires(CloneContext& ctx,
return true;
}
void Transform::RemoveStatement(CloneContext& ctx, ast::Statement* stmt) {
void Transform::RemoveStatement(CloneContext& ctx, const ast::Statement* stmt) {
auto* sem = ctx.src->Sem().Get(stmt);
if (auto* block = tint::As<sem::BlockStatement>(sem->Parent())) {
ctx.Remove(block->Declaration()->statements, stmt);

View File

@@ -190,7 +190,7 @@ class Transform : public Castable<Transform> {
/// continuing of for-loops.
/// @param ctx the clone context
/// @param stmt the statement to remove when the program is cloned
static void RemoveStatement(CloneContext& ctx, ast::Statement* stmt);
static void RemoveStatement(CloneContext& ctx, const ast::Statement* stmt);
/// CreateASTTypeFor constructs new ast::Type nodes that reconstructs the
/// semantic type `ty`.