[transform] Add ForLoopToLoop
Transforms a for-loop into a loop. Will be required by the SPIR-V writer. Bug: tint:952 Change-Id: Iba859bd144d702cee85374f2cfcb94cd7465ca98 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57202 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
2ba8315b02
commit
97bddeee20
|
@ -570,6 +570,8 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"transform/fold_constants.h",
|
"transform/fold_constants.h",
|
||||||
"transform/fold_trivial_single_use_lets.cc",
|
"transform/fold_trivial_single_use_lets.cc",
|
||||||
"transform/fold_trivial_single_use_lets.h",
|
"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.cc",
|
||||||
"transform/inline_pointer_lets.h",
|
"transform/inline_pointer_lets.h",
|
||||||
"transform/loop_to_for_loop.cc",
|
"transform/loop_to_for_loop.cc",
|
||||||
|
|
|
@ -295,6 +295,8 @@ set(TINT_LIB_SRCS
|
||||||
transform/fold_constants.h
|
transform/fold_constants.h
|
||||||
transform/fold_trivial_single_use_lets.cc
|
transform/fold_trivial_single_use_lets.cc
|
||||||
transform/fold_trivial_single_use_lets.h
|
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.cc
|
||||||
transform/inline_pointer_lets.h
|
transform/inline_pointer_lets.h
|
||||||
transform/loop_to_for_loop.cc
|
transform/loop_to_for_loop.cc
|
||||||
|
@ -882,6 +884,7 @@ if(${TINT_BUILD_TESTS})
|
||||||
transform/first_index_offset_test.cc
|
transform/first_index_offset_test.cc
|
||||||
transform/fold_constants_test.cc
|
transform/fold_constants_test.cc
|
||||||
transform/fold_trivial_single_use_lets_test.cc
|
transform/fold_trivial_single_use_lets_test.cc
|
||||||
|
transform/for_loop_to_loop_test.cc
|
||||||
transform/inline_pointer_lets_test.cc
|
transform/inline_pointer_lets_test.cc
|
||||||
transform/loop_to_for_loop_test.cc
|
transform/loop_to_for_loop_test.cc
|
||||||
transform/pad_array_elements_test.cc
|
transform/pad_array_elements_test.cc
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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/for_loop_to_loop.h"
|
||||||
|
|
||||||
|
#include "src/ast/break_statement.h"
|
||||||
|
#include "src/program_builder.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::ForLoopToLoop);
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
ForLoopToLoop::ForLoopToLoop() = default;
|
||||||
|
|
||||||
|
ForLoopToLoop::~ForLoopToLoop() = default;
|
||||||
|
|
||||||
|
void ForLoopToLoop::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||||
|
ctx.ReplaceAll([&](ast::ForLoopStatement* for_loop) -> ast::Statement* {
|
||||||
|
ast::StatementList stmts;
|
||||||
|
if (auto* cond = for_loop->condition()) {
|
||||||
|
// !condition
|
||||||
|
auto* not_cond = ctx.dst->create<ast::UnaryOpExpression>(
|
||||||
|
ast::UnaryOp::kNot, ctx.Clone(cond));
|
||||||
|
|
||||||
|
// { break; }
|
||||||
|
auto* break_body = ctx.dst->Block(ctx.dst->create<ast::BreakStatement>());
|
||||||
|
|
||||||
|
// if (!condition) { break; }
|
||||||
|
stmts.emplace_back(ctx.dst->If(not_cond, break_body));
|
||||||
|
}
|
||||||
|
for (auto* stmt : for_loop->body()->statements()) {
|
||||||
|
stmts.emplace_back(ctx.Clone(stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::BlockStatement* continuing = nullptr;
|
||||||
|
if (auto* cont = for_loop->continuing()) {
|
||||||
|
continuing = ctx.dst->Block(ctx.Clone(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* body = ctx.dst->Block(stmts);
|
||||||
|
auto* loop = ctx.dst->create<ast::LoopStatement>(body, continuing);
|
||||||
|
|
||||||
|
if (auto* init = for_loop->initializer()) {
|
||||||
|
return ctx.dst->Block(ctx.Clone(init), loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loop;
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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_FOR_LOOP_TO_LOOP_H_
|
||||||
|
#define SRC_TRANSFORM_FOR_LOOP_TO_LOOP_H_
|
||||||
|
|
||||||
|
#include "src/transform/transform.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
|
||||||
|
/// ForLoopToLoop is a Transform that converts a for-loop statement into a loop
|
||||||
|
/// statement. This is required by the SPIR-V writer.
|
||||||
|
class ForLoopToLoop : public Castable<ForLoopToLoop, Transform> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
ForLoopToLoop();
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~ForLoopToLoop() 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_FOR_LOOP_TO_LOOP_H_
|
|
@ -0,0 +1,341 @@
|
||||||
|
// 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/for_loop_to_loop.h"
|
||||||
|
|
||||||
|
#include "src/transform/test_helper.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ForLoopToLoopTest = TransformTest;
|
||||||
|
|
||||||
|
TEST_F(ForLoopToLoopTest, EmptyModule) {
|
||||||
|
auto* src = "";
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test an empty for loop.
|
||||||
|
TEST_F(ForLoopToLoopTest, Empty) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop with non-empty body.
|
||||||
|
TEST_F(ForLoopToLoopTest, Body) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (;;) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop declaring a variable in the initializer statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, InitializerStatementDecl) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (var i: i32;;) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
var i : i32;
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop declaring and initializing a variable in the initializer
|
||||||
|
// statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, InitializerStatementDeclEqual) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (var i: i32 = 0;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
var i : i32 = 0;
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop declaring a const variable in the initializer statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, InitializerStatementConstDecl) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (let i: i32 = 0;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
let i : i32 = 0;
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop assigning a variable in the initializer statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, InitializerStatementAssignment) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var i: i32;
|
||||||
|
for (i = 0;;) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var i : i32;
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop calling a function in the initializer statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, InitializerStatementFuncCall) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn a(x : i32, y : i32) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var b : i32;
|
||||||
|
var c : i32;
|
||||||
|
for (a(b,c);;) { }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn a(x : i32, y : i32) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var b : i32;
|
||||||
|
var c : i32;
|
||||||
|
{
|
||||||
|
a(b, c);
|
||||||
|
loop {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop with a break condition
|
||||||
|
TEST_F(ForLoopToLoopTest, BreakCondition) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
for (; 0 == 1;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
if (!((0 == 1))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop assigning a variable in the continuing statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, ContinuingAssignment) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var x: i32;
|
||||||
|
for (;;x = 2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var x : i32;
|
||||||
|
loop {
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop calling a function in the continuing statement.
|
||||||
|
TEST_F(ForLoopToLoopTest, ContinuingFuncCall) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn a(x : i32, y : i32) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var b : i32;
|
||||||
|
var c : i32;
|
||||||
|
for (;;a(b,c)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn a(x : i32, y : i32) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var b : i32;
|
||||||
|
var c : i32;
|
||||||
|
loop {
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
a(b, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a for loop with all statements non-empty.
|
||||||
|
TEST_F(ForLoopToLoopTest, All) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var a : i32;
|
||||||
|
for(var i : i32 = 0; i < 4; i = i + 1) {
|
||||||
|
if (a == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
a = a + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var a : i32;
|
||||||
|
{
|
||||||
|
var i : i32 = 0;
|
||||||
|
loop {
|
||||||
|
if (!((i < 4))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((a == 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
a = (a + 2);
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
i = (i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<ForLoopToLoop>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -15,9 +15,6 @@
|
||||||
#ifndef SRC_TRANSFORM_LOOP_TO_FOR_LOOP_H_
|
#ifndef SRC_TRANSFORM_LOOP_TO_FOR_LOOP_H_
|
||||||
#define SRC_TRANSFORM_LOOP_TO_FOR_LOOP_H_
|
#define SRC_TRANSFORM_LOOP_TO_FOR_LOOP_H_
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "src/transform/transform.h"
|
#include "src/transform/transform.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
|
|
@ -286,6 +286,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
|
||||||
"../src/transform/first_index_offset_test.cc",
|
"../src/transform/first_index_offset_test.cc",
|
||||||
"../src/transform/fold_constants_test.cc",
|
"../src/transform/fold_constants_test.cc",
|
||||||
"../src/transform/fold_trivial_single_use_lets_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/inline_pointer_lets_test.cc",
|
||||||
"../src/transform/loop_to_for_loop_test.cc",
|
"../src/transform/loop_to_for_loop_test.cc",
|
||||||
"../src/transform/pad_array_elements_test.cc",
|
"../src/transform/pad_array_elements_test.cc",
|
||||||
|
|
Loading…
Reference in New Issue