[ir] Track BreakIf as a loop exit
This fixes an issue where the SPIR-V writer would not add OpPhi
operands for results coming from a break_if instruction, as it was not
considered to be an 'exit' from a loop.
Change-Id: I0723d209d078e52e969e79986e4adbfbdde3d885
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/190463
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/break_if.cc b/src/tint/lang/core/ir/break_if.cc
index efb2990..72ad55b 100644
--- a/src/tint/lang/core/ir/break_if.cc
+++ b/src/tint/lang/core/ir/break_if.cc
@@ -55,6 +55,7 @@
if (loop_) {
loop_->Body()->AddInboundSiblingBranch(this);
+ SetControlInstruction(loop_);
}
}
@@ -72,6 +73,7 @@
loop_->Body()->RemoveInboundSiblingBranch(this);
}
loop_ = loop;
+ SetControlInstruction(loop);
if (loop) {
loop->Body()->AddInboundSiblingBranch(this);
}
diff --git a/src/tint/lang/core/ir/break_if.h b/src/tint/lang/core/ir/break_if.h
index 38bf9d8..47533ec 100644
--- a/src/tint/lang/core/ir/break_if.h
+++ b/src/tint/lang/core/ir/break_if.h
@@ -30,7 +30,7 @@
#include <string>
-#include "src/tint/lang/core/ir/terminator.h"
+#include "src/tint/lang/core/ir/exit.h"
#include "src/tint/lang/core/ir/value.h"
#include "src/tint/utils/containers/const_propagating_ptr.h"
#include "src/tint/utils/rtti/castable.h"
@@ -42,8 +42,8 @@
namespace tint::core::ir {
-/// A break-if iteration instruction.
-class BreakIf final : public Castable<BreakIf, Terminator> {
+/// A break-if terminator instruction.
+class BreakIf final : public Castable<BreakIf, Exit> {
public:
/// The offset in Operands() for the condition
static constexpr size_t kConditionOperandOffset = 0;
diff --git a/src/tint/lang/core/ir/break_if_test.cc b/src/tint/lang/core/ir/break_if_test.cc
index d472c47..c32f00c 100644
--- a/src/tint/lang/core/ir/break_if_test.cc
+++ b/src/tint/lang/core/ir/break_if_test.cc
@@ -113,5 +113,20 @@
EXPECT_EQ(0u, args.Length());
}
+TEST_F(IR_BreakIfTest, SetLoop) {
+ auto* loop1 = b.Loop();
+ auto* loop2 = b.Loop();
+ auto* cond = b.Constant(true);
+ auto* arg1 = b.Constant(1_u);
+ auto* arg2 = b.Constant(2_u);
+
+ auto* brk = b.BreakIf(loop1, cond, arg1, arg2);
+ EXPECT_THAT(loop1->Exits(), testing::ElementsAre(brk));
+
+ brk->SetLoop(loop2);
+ EXPECT_TRUE(loop1->Exits().IsEmpty());
+ EXPECT_THAT(loop2->Exits(), testing::ElementsAre(brk));
+}
+
} // namespace
} // namespace tint::core::ir
diff --git a/src/tint/lang/spirv/writer/loop_test.cc b/src/tint/lang/spirv/writer/loop_test.cc
index a5640fa..1631549 100644
--- a/src/tint/lang/spirv/writer/loop_test.cc
+++ b/src/tint/lang/spirv/writer/loop_test.cc
@@ -664,5 +664,69 @@
)");
}
+TEST_F(SpirvWriterTest, Loop_ExitValue_BreakIf) {
+ auto* func = b.Function("foo", ty.i32());
+ b.Append(func->Block(), [&] {
+ auto* result = b.InstructionResult(ty.i32());
+ auto* loop = b.Loop();
+ loop->SetResults(Vector{result});
+ b.Append(loop->Body(), [&] { //
+ auto* if_ = b.If(false);
+ b.Append(if_->True(), [&] { //
+ b.ExitLoop(loop, 1_i);
+ });
+ b.Continue(loop);
+
+ b.Append(loop->Continuing(), [&] { //
+ b.BreakIf(loop, true, Empty, 42_i);
+ });
+ });
+ b.Return(func, result);
+ });
+
+ EXPECT_EQ(IR(), R"(
+%foo = func():i32 {
+ $B1: {
+ %2:i32 = loop [b: $B2, c: $B3] { # loop_1
+ $B2: { # body
+ if false [t: $B4] { # if_1
+ $B4: { # true
+ exit_loop 1i # loop_1
+ }
+ }
+ continue # -> $B3
+ }
+ $B3: { # continuing
+ break_if true exit_loop: [ 42i ] # -> [t: exit_loop loop_1, f: $B2]
+ }
+ }
+ ret %2
+ }
+}
+)");
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(
+ %4 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpLoopMerge %8 %6 None
+ OpBranch %5
+ %5 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %false %10 %9
+ %10 = OpLabel
+ OpBranch %8
+ %9 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranchConditional %true %8 %7
+ %8 = OpLabel
+ %14 = OpPhi %int %int_42 %6 %int_1 %10
+ OpReturnValue %14
+ OpFunctionEnd
+)");
+}
+
} // namespace
} // namespace tint::spirv::writer