[ir] Add a NextIteration instruction.
This CL adds a `NextIteration` instruction in order to branch from a
continue block back to the start of a loop. The `next_iteration` is
clearer then the `break-if false` pattern that was there previously.
BreakIf is retained and used when an `ast::BreakIf` is encountered as
it's clearer then the replaced `if` structure.
Bug: tint:1718
Change-Id: Ie6ce0db51c244866e2e99118bc00e4cfd2b3dc74
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/134600
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index bff6c37..5970683 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1254,6 +1254,8 @@
"ir/loop.h",
"ir/module.cc",
"ir/module.h",
+ "ir/next_iteration.cc",
+ "ir/next_iteration.h",
"ir/return.cc",
"ir/return.h",
"ir/store.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index e5e529b..17fa2d3 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -764,6 +764,8 @@
ir/loop.h
ir/module.cc
ir/module.h
+ ir/next_iteration.cc
+ ir/next_iteration.h
ir/return.cc
ir/return.h
ir/store.cc
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 1daf458..7f2d9c3 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -205,6 +205,10 @@
return ir.values.Create<ir::Return>(func, args);
}
+ir::NextIteration* Builder::NextIteration(Loop* loop) {
+ return ir.values.Create<ir::NextIteration>(loop);
+}
+
ir::BreakIf* Builder::BreakIf(Value* condition, Loop* loop) {
return ir.values.Create<ir::BreakIf>(condition, loop);
}
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 8a4c021..b544cb2 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -37,6 +37,7 @@
#include "src/tint/ir/load.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
+#include "src/tint/ir/next_iteration.h"
#include "src/tint/ir/return.h"
#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
@@ -337,6 +338,11 @@
/// @returns the instruction
ir::Return* Return(Function* func, utils::VectorRef<Value*> args = {});
+ /// Creates a loop next iteration instruction
+ /// @param loop the loop being iterated
+ /// @returns the instruction
+ ir::NextIteration* NextIteration(Loop* loop);
+
/// Creates a loop break-if instruction
/// @param condition the break condition
/// @param loop the loop being iterated
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index d39438a..75cc2aa 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -33,6 +33,7 @@
#include "src/tint/ir/if.h"
#include "src/tint/ir/load.h"
#include "src/tint/ir/loop.h"
+#include "src/tint/ir/next_iteration.h"
#include "src/tint/ir/return.h"
#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
@@ -428,6 +429,9 @@
[&](const ir::ExitIf* ei) { out_ << "exit_if %b" << IdOf(ei->If()->Merge()); },
[&](const ir::ExitSwitch* es) { out_ << "exit_switch %b" << IdOf(es->Switch()->Merge()); },
[&](const ir::ExitLoop* el) { out_ << "exit_loop %b" << IdOf(el->Loop()->Merge()); },
+ [&](const ir::NextIteration* ni) {
+ out_ << "next_iteration %b" << IdOf(ni->Loop()->Start());
+ },
[&](const ir::BreakIf* bi) {
out_ << "break_if ";
EmitValue(bi->Condition());
diff --git a/src/tint/ir/from_program.cc b/src/tint/ir/from_program.cc
index 67c9f3f..4773f3f 100644
--- a/src/tint/ir/from_program.cc
+++ b/src/tint/ir/from_program.cc
@@ -575,7 +575,7 @@
}
// Branch back to the start node if the continue target didn't branch out already
if (NeedBranch()) {
- SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
+ SetBranch(builder_.NextIteration(loop_inst));
}
}
}
@@ -595,7 +595,7 @@
// Continue is always empty, just go back to the start
current_flow_block_ = loop_inst->Continuing();
- SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
+ SetBranch(builder_.NextIteration(loop_inst));
{
ControlStackScope scope(this, loop_inst);
@@ -676,7 +676,7 @@
if (stmt->continuing) {
current_flow_block_ = loop_inst->Continuing();
EmitStatement(stmt->continuing);
- SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
+ SetBranch(builder_.NextIteration(loop_inst));
}
}
diff --git a/src/tint/ir/from_program_test.cc b/src/tint/ir/from_program_test.cc
index f2b9c19..f9be668 100644
--- a/src/tint/ir/from_program_test.cc
+++ b/src/tint/ir/from_program_test.cc
@@ -381,7 +381,7 @@
EXPECT_EQ(1u, loop_flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(2u, loop_flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->Merge()->InboundBranches().Length());
@@ -411,7 +411,7 @@
# Continuing block
%b3 = block {
- break_if false %b2
+ next_iteration %b2
}
# Merge block
@@ -513,7 +513,7 @@
EXPECT_EQ(1u, loop_flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(0u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->Merge()->InboundBranches().Length());
@@ -521,21 +521,21 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = func():void [@compute @workgroup_size(1, 1, 1)] -> %b1 {
%b1 = block {
- loop [s: %b2, c: %b3, m: %b4]
+ loop [s: %b2, c: %b3]
%b2 = block {
- if true [t: %b5, f: %b6, m: %b7]
+ if true [t: %b4, f: %b5, m: %b6]
# True block
- %b5 = block {
+ %b4 = block {
ret
}
# False block
- %b6 = block {
- exit_if %b7
+ %b5 = block {
+ exit_if %b6
}
# Merge block
- %b7 = block {
+ %b6 = block {
continue %b3
}
@@ -543,14 +543,9 @@
# Continuing block
%b3 = block {
- break_if false %b2
+ next_iteration %b2
}
- # Merge block
- %b4 = block {
- ret
- }
-
}
}
)");
@@ -750,7 +745,7 @@
# Merge block
%b18 = block {
- break_if false %b5
+ next_iteration %b5
}
}
@@ -781,7 +776,7 @@
# Continuing block
%b3 = block {
- break_if false %b2
+ next_iteration %b2
}
# Merge block
@@ -812,7 +807,7 @@
EXPECT_EQ(1u, flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(2u, flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->Merge()->InboundBranches().Length());
@@ -842,7 +837,7 @@
# Continuing block
%b3 = block {
- break_if false %b2
+ next_iteration %b2
}
# Merge block
@@ -873,7 +868,7 @@
EXPECT_EQ(1u, flow->Start()->InboundBranches().Length());
EXPECT_EQ(0u, flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(2u, flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->Merge()->InboundBranches().Length());
@@ -903,7 +898,7 @@
# Continuing block
%b3 = block {
- break_if false %b2
+ next_iteration %b2
}
# Merge block
diff --git a/src/tint/ir/next_iteration.cc b/src/tint/ir/next_iteration.cc
new file mode 100644
index 0000000..0c021eb
--- /dev/null
+++ b/src/tint/ir/next_iteration.cc
@@ -0,0 +1,31 @@
+// Copyright 2023 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/tint/ir/next_iteration.h"
+
+#include "src/tint/ir/loop.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::NextIteration);
+
+namespace tint::ir {
+
+NextIteration::NextIteration(ir::Loop* loop) : Base(utils::Empty), loop_(loop) {
+ TINT_ASSERT(IR, loop_);
+ loop_->AddUsage(this);
+ loop_->Start()->AddInboundBranch(this);
+}
+
+NextIteration::~NextIteration() = default;
+
+} // namespace tint::ir
diff --git a/src/tint/ir/next_iteration.h b/src/tint/ir/next_iteration.h
new file mode 100644
index 0000000..f1211e3
--- /dev/null
+++ b/src/tint/ir/next_iteration.h
@@ -0,0 +1,45 @@
+// Copyright 2023 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_TINT_IR_NEXT_ITERATION_H_
+#define SRC_TINT_IR_NEXT_ITERATION_H_
+
+#include "src/tint/ir/branch.h"
+#include "src/tint/utils/castable.h"
+
+// Forward declarations
+namespace tint::ir {
+class Loop;
+} // namespace tint::ir
+
+namespace tint::ir {
+
+/// A next iteration instruction.
+class NextIteration : public utils::Castable<NextIteration, Branch> {
+ public:
+ /// Constructor
+ /// @param loop the loop being iterated
+ explicit NextIteration(ir::Loop* loop);
+ ~NextIteration() override;
+
+ /// @returns the loop being iterated
+ const ir::Loop* Loop() const { return loop_; }
+
+ private:
+ ir::Loop* loop_ = nullptr;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_NEXT_ITERATION_H_