[ir] Add BreakIf instruction.
This CL adds a BreakIf instruction to the IR to instruct a loop to
go break based on a condition or to iterate the loop.
Bug: tint:1718
Change-Id: I70e65736e59ae189ddb2ea9b05c4b084291314f0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/134463
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index adf0e48..344607d 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1214,6 +1214,8 @@
"ir/block_param.h",
"ir/branch.cc",
"ir/branch.h",
+ "ir/break_if.cc",
+ "ir/break_if.h",
"ir/builder.cc",
"ir/builder.h",
"ir/builtin.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 39bc28b..ff69240 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -722,6 +722,8 @@
ir/block_param.h
ir/branch.cc
ir/branch.h
+ ir/break_if.cc
+ ir/break_if.h
ir/builder.cc
ir/builder.h
ir/builtin.cc
diff --git a/src/tint/ir/break_if.cc b/src/tint/ir/break_if.cc
new file mode 100644
index 0000000..f19fb79
--- /dev/null
+++ b/src/tint/ir/break_if.cc
@@ -0,0 +1,35 @@
+// 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/break_if.h"
+
+#include "src/tint/ir/loop.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::BreakIf);
+
+namespace tint::ir {
+
+BreakIf::BreakIf(Value* condition, ir::Loop* loop)
+ : Base(utils::Empty), condition_(condition), loop_(loop) {
+ TINT_ASSERT(IR, condition_);
+ TINT_ASSERT(IR, loop_);
+ condition_->AddUsage(this);
+ loop_->AddUsage(this);
+ loop_->Start()->AddInboundBranch(this);
+ loop_->Merge()->AddInboundBranch(this);
+}
+
+BreakIf::~BreakIf() = default;
+
+} // namespace tint::ir
diff --git a/src/tint/ir/break_if.h b/src/tint/ir/break_if.h
new file mode 100644
index 0000000..47fd4e8
--- /dev/null
+++ b/src/tint/ir/break_if.h
@@ -0,0 +1,51 @@
+// 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_BREAK_IF_H_
+#define SRC_TINT_IR_BREAK_IF_H_
+
+#include "src/tint/ir/branch.h"
+#include "src/tint/ir/value.h"
+#include "src/tint/utils/castable.h"
+
+// Forward declarations
+namespace tint::ir {
+class Loop;
+} // namespace tint::ir
+
+namespace tint::ir {
+
+/// A break-if iteration instruction.
+class BreakIf : public utils::Castable<BreakIf, Branch> {
+ public:
+ /// Constructor
+ /// @param condition the break condition
+ /// @param loop the loop containing the break-if
+ BreakIf(Value* condition, ir::Loop* loop);
+ ~BreakIf() override;
+
+ /// @returns the break condition
+ const Value* Condition() const { return condition_; }
+
+ /// @returns the loop containing the break-if
+ const ir::Loop* Loop() const { return loop_; }
+
+ private:
+ Value* condition_ = nullptr;
+ ir::Loop* loop_ = nullptr;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_BREAK_IF_H_
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 7c94648..4bdb53d 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -213,6 +213,10 @@
return ir.values.Create<ir::Return>(func, args);
}
+ir::BreakIf* Builder::BreakIf(Value* condition, Loop* loop) {
+ return ir.values.Create<ir::BreakIf>(condition, loop);
+}
+
ir::Continue* Builder::Continue(Loop* loop) {
return ir.values.Create<ir::Continue>(loop);
}
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index a63515e..9a2fe18 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -21,6 +21,7 @@
#include "src/tint/ir/binary.h"
#include "src/tint/ir/bitcast.h"
#include "src/tint/ir/block_param.h"
+#include "src/tint/ir/break_if.h"
#include "src/tint/ir/builtin.h"
#include "src/tint/ir/constant.h"
#include "src/tint/ir/construct.h"
@@ -337,6 +338,12 @@
/// @returns the instruction
ir::Return* Return(Function* func, utils::VectorRef<Value*> args = {});
+ /// Creates a loop break-if instruction
+ /// @param condition the break condition
+ /// @param loop the loop being iterated
+ /// @returns the instruction
+ ir::BreakIf* BreakIf(Value* condition, Loop* loop);
+
/// Creates a continue instruction
/// @param loop the loop being continued
/// @returns the instruction
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index ae89902..6097726 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -21,6 +21,7 @@
#include "src/tint/ir/binary.h"
#include "src/tint/ir/bitcast.h"
#include "src/tint/ir/block.h"
+#include "src/tint/ir/break_if.h"
#include "src/tint/ir/builtin.h"
#include "src/tint/ir/construct.h"
#include "src/tint/ir/continue.h"
@@ -414,6 +415,10 @@
out_ << "ret";
} else if (auto* cont = b->As<ir::Continue>()) {
out_ << "continue %b" << IdOf(cont->Loop()->Continuing());
+ } else if (auto* bi = b->As<ir::BreakIf>()) {
+ out_ << "break_if ";
+ EmitValue(bi->Condition());
+ out_ << " %b" << IdOf(bi->Loop()->Start());
} else {
out_ << "br %b" << IdOf(b->To());
if (b->To()->Is<RootTerminator>()) {
diff --git a/src/tint/ir/from_program.cc b/src/tint/ir/from_program.cc
index 444950e..a4d95e0 100644
--- a/src/tint/ir/from_program.cc
+++ b/src/tint/ir/from_program.cc
@@ -590,7 +590,9 @@
EmitBlock(stmt->continuing);
}
// Branch back to the start node if the continue target didn't branch out already
- BranchToIfNeeded(loop_inst->Start());
+ if (NeedBranch()) {
+ SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
+ }
}
}
@@ -608,7 +610,8 @@
current_flow_block_->Instructions().Push(loop_inst);
// Continue is always empty, just go back to the start
- loop_inst->Continuing()->Instructions().Push(builder_.Branch(loop_inst->Start()));
+ current_flow_block_ = loop_inst->Continuing();
+ SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
{
ControlStackScope scope(this, loop_inst);
@@ -681,7 +684,7 @@
if (stmt->continuing) {
current_flow_block_ = loop_inst->Continuing();
EmitStatement(stmt->continuing);
- loop_inst->Continuing()->Instructions().Push(builder_.Branch(loop_inst->Start()));
+ SetBranch(builder_.BreakIf(builder_.Constant(false), loop_inst));
}
}
@@ -772,31 +775,14 @@
}
void EmitBreakIf(const ast::BreakIfStatement* stmt) {
+ auto* current_control = FindEnclosingControl(ControlFlags::kExcludeSwitch);
+
// Emit the break-if condition into the end of the preceding block
- auto reg = EmitExpression(stmt->condition);
- if (!reg) {
+ auto cond = EmitExpression(stmt->condition);
+ if (!cond) {
return;
}
- auto* if_inst = builder_.CreateIf(reg.Get());
- current_flow_block_->Instructions().Push(if_inst);
-
- auto* current_control = FindEnclosingControl(ControlFlags::kExcludeSwitch);
- TINT_ASSERT(IR, current_control);
- TINT_ASSERT(IR, current_control->Is<Loop>());
-
- auto* loop = current_control->As<Loop>();
-
- current_flow_block_ = if_inst->True();
- BranchTo(loop->Merge());
-
- current_flow_block_ = if_inst->False();
- BranchTo(if_inst->Merge());
-
- current_flow_block_ = if_inst->Merge();
-
- // The `break-if` has to be the last item in the continuing block. The false branch of
- // the `break-if` will always take us back to the start of the loop.
- BranchTo(loop->Start());
+ SetBranch(builder_.BreakIf(cond.Get(), current_control->As<ir::Loop>()));
}
utils::Result<Value*> EmitExpression(const ast::Expression* expr) {
diff --git a/src/tint/ir/from_program_test.cc b/src/tint/ir/from_program_test.cc
index 0b23ec5..e0f161d 100644
--- a/src/tint/ir/from_program_test.cc
+++ b/src/tint/ir/from_program_test.cc
@@ -385,7 +385,7 @@
EXPECT_EQ(2u, loop_flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(2u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(2u, if_flow->Merge()->InboundBranches().Length());
@@ -416,7 +416,7 @@
# Continuing block
%b3 = block {
- br %b2
+ break_if false %b2
}
# Merge block
@@ -440,16 +440,12 @@
auto m = res.Move();
auto* loop_flow = FindSingleValue<ir::Loop>(m);
- auto* break_if_flow = FindSingleValue<ir::If>(m);
ASSERT_EQ(1u, m.functions.Length());
EXPECT_EQ(2u, loop_flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
- EXPECT_EQ(1u, break_if_flow->True()->InboundBranches().Length());
- EXPECT_EQ(1u, break_if_flow->False()->InboundBranches().Length());
- EXPECT_EQ(2u, break_if_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m),
R"(%test_function = func():void [@compute @workgroup_size(1, 1, 1)] -> %b1 {
@@ -461,23 +457,7 @@
# Continuing block
%b3 = block {
- if true [t: %b5, f: %b6, m: %b7]
- # True block
- %b5 = block {
- br %b4
- }
-
- # False block
- %b6 = block {
- br %b7
- }
-
- # Merge block
- %b7 = block {
- br %b2
- }
-
-
+ break_if true %b2
}
# Merge block
@@ -511,23 +491,7 @@
# Continuing block
%b3 = block {
- if true [t: %b5, f: %b6, m: %b7]
- # True block
- %b5 = block {
- br %b4
- }
-
- # False block
- %b6 = block {
- br %b7
- }
-
- # Merge block
- %b7 = block {
- br %b2
- }
-
-
+ break_if true %b2
}
# Merge block
@@ -557,7 +521,7 @@
EXPECT_EQ(2u, loop_flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(0u, 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(2u, if_flow->Merge()->InboundBranches().Length());
@@ -565,20 +529,20 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = func():void [@compute @workgroup_size(1, 1, 1)] -> %b1 {
%b1 = block {
- loop [s: %b2, c: %b3]
+ loop [s: %b2, c: %b3, m: %b4]
%b2 = block {
- if true [t: %b4, f: %b5, m: %b6]
+ if true [t: %b5, f: %b6, m: %b7]
# True block
- %b4 = block {
+ %b5 = block {
ret
}
# False block
- %b5 = block {
- br %b6
+ %b6 = block {
+ br %b7
}
# Merge block
- %b6 = block {
+ %b7 = block {
continue %b3
}
@@ -587,9 +551,13 @@
# Continuing block
%b3 = block {
- br %b2
+ break_if false %b2
}
+ # Merge block
+ %b4 = block {
+ ret
+ }
}
@@ -792,28 +760,12 @@
# Continuing block
%b17 = block {
- if true [t: %b19, f: %b20, m: %b21]
- # True block
- %b19 = block {
- br %b18
- }
-
- # False block
- %b20 = block {
- br %b21
- }
-
- # Merge block
- %b21 = block {
- br %b16
- }
-
-
+ break_if true %b16
}
# Merge block
%b18 = block {
- br %b5
+ break_if false %b5
}
@@ -824,19 +776,19 @@
# Merge block
%b7 = block {
- if true [t: %b22, f: %b23, m: %b24]
+ if true [t: %b19, f: %b20, m: %b21]
# True block
- %b22 = block {
+ %b19 = block {
br %b4
}
# False block
- %b23 = block {
- br %b24
+ %b20 = block {
+ br %b21
}
# Merge block
- %b24 = block {
+ %b21 = block {
continue %b3
}
@@ -848,7 +800,7 @@
# Continuing block
%b3 = block {
- br %b2
+ break_if false %b2
}
# Merge block
@@ -880,7 +832,7 @@
EXPECT_EQ(2u, flow->Start()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(2u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(2u, if_flow->Merge()->InboundBranches().Length());
@@ -911,7 +863,7 @@
# Continuing block
%b3 = block {
- br %b2
+ break_if false %b2
}
# Merge block
@@ -943,7 +895,7 @@
EXPECT_EQ(2u, flow->Start()->InboundBranches().Length());
EXPECT_EQ(0u, flow->Continuing()->InboundBranches().Length());
- EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
+ EXPECT_EQ(2u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->False()->InboundBranches().Length());
EXPECT_EQ(2u, if_flow->Merge()->InboundBranches().Length());
@@ -973,7 +925,7 @@
# Continuing block
%b3 = block {
- br %b2
+ break_if false %b2
}
# Merge block