[spirv-writer] Fix implicit blocks with results
When an if instruction produces result values, we shouldn't emit
implicit blocks using OpUnreachable as they should actually branching
to the merge block.
Change-Id: I814946aeb924aa4630323cde30725b0d791ded12
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161141
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/spirv/writer/if_test.cc b/src/tint/lang/spirv/writer/if_test.cc
index a91ab22..5dd00ed 100644
--- a/src/tint/lang/spirv/writer/if_test.cc
+++ b/src/tint/lang/spirv/writer/if_test.cc
@@ -241,6 +241,34 @@
)");
}
+TEST_F(SpirvWriterTest, If_Phi_SingleValue_ImplicitFalse) {
+ auto* func = b.Function("foo", ty.i32());
+ b.Append(func->Block(), [&] {
+ auto* i = b.If(true);
+ i->SetResults(b.InstructionResult(ty.i32()));
+ b.Append(i->True(), [&] { //
+ b.ExitIf(i, 10_i);
+ });
+ b.Return(func, i);
+ });
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST("%12 = OpUndef %int");
+ EXPECT_INST(R"(
+ %4 = OpLabel
+ OpSelectionMerge %5 None
+ OpBranchConditional %true %6 %7
+ %6 = OpLabel
+ OpBranch %5
+ %7 = OpLabel
+ OpBranch %5
+ %5 = OpLabel
+ %10 = OpPhi %int %int_10 %6 %12 %7
+ OpReturnValue %10
+ OpFunctionEnd
+)");
+}
+
TEST_F(SpirvWriterTest, If_Phi_MultipleValue_0) {
auto* func = b.Function("foo", ty.i32());
b.Append(func->Block(), [&] {
diff --git a/src/tint/lang/spirv/writer/loop_test.cc b/src/tint/lang/spirv/writer/loop_test.cc
index 332c52a..c262b2c 100644
--- a/src/tint/lang/spirv/writer/loop_test.cc
+++ b/src/tint/lang/spirv/writer/loop_test.cc
@@ -572,5 +572,54 @@
)");
}
+TEST_F(SpirvWriterTest, Loop_Phi_NestedIfWithResultAndImplicitFalse_InContinuing) {
+ auto* func = b.Function("foo", ty.void_());
+
+ b.Append(func->Block(), [&] {
+ auto* loop = b.Loop();
+
+ b.Append(loop->Body(), [&] { //
+ b.Continue(loop);
+ });
+
+ b.Append(loop->Continuing(), [&] {
+ auto* if_ = b.If(true);
+ auto* cond = b.InstructionResult(ty.bool_());
+ if_->SetResults(Vector{cond});
+ b.Append(if_->True(), [&] { //
+ b.ExitIf(if_, true);
+ });
+ b.BreakIf(loop, cond);
+ });
+
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST("%15 = OpUndef %bool");
+ EXPECT_INST(R"(
+ %4 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpLoopMerge %8 %6 None
+ OpBranch %5
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %true %10 %11
+ %10 = OpLabel
+ OpBranch %9
+ %11 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ %14 = OpPhi %bool %true %10 %15 %11
+ OpBranchConditional %14 %8 %7
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
} // namespace
} // namespace tint::spirv::writer
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 78bca04..942c326 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -837,7 +837,11 @@
// If there are no instructions in the block, it's a dead end, so we shouldn't be able to
// get here to begin with.
if (block->IsEmpty()) {
- current_function_.push_inst(spv::Op::OpUnreachable, {});
+ if (!block->Parent()->Results().IsEmpty()) {
+ current_function_.push_inst(spv::Op::OpBranch, {GetMergeLabel(block->Parent())});
+ } else {
+ current_function_.push_inst(spv::Op::OpUnreachable, {});
+ }
return;
}
@@ -2163,6 +2167,13 @@
}
branches.Sort(); // Sort the branches by label to ensure deterministic output
+ // Also add phi nodes from implicit exit blocks.
+ inst->ForeachBlock([&](core::ir::Block* block) {
+ if (block->IsEmpty()) {
+ branches.Push(Branch{Label(block), nullptr});
+ }
+ });
+
OperandList ops{Type(ty), Value(result)};
for (auto& branch : branches) {
if (branch.value == nullptr) {