mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-16 00:17:03 +00:00
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:
committed by
Tint LUCI CQ
parent
092326894e
commit
1aa98e62c0
@@ -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>();
|
||||
|
||||
138
src/transform/remove_phonies.cc
Normal file
138
src/transform/remove_phonies.cc
Normal 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
|
||||
50
src/transform/remove_phonies.h
Normal file
50
src/transform/remove_phonies.h
Normal 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_
|
||||
226
src/transform/remove_phonies_test.cc
Normal file
226
src/transform/remove_phonies_test.cc
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user