Fix OpLine bug of merge-blocks pass (#3130)
As explained in #3118, spirv-opt merge-blocks pass causes a
spirv-val error when an OpBranch has an OpLine in front of it.
OpLoopMerge
OpBranch ; Will be killed by merge-blocks pass
OpLabel ; Will be killed by merge-blocks pass
OpLine ; will be placed between OpLoopMerge and OpBranch - error!
OpBranch
To fix this issue, this commit moves line info of OpBranch to
OpLoopMerge.
Fixes #3118
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 263a069..14b5d36 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -171,8 +171,17 @@
// flow declaration.
context->KillInst(merge_inst);
} else {
+ // Move OpLine/OpNoLine information to merge_inst. This solves
+ // the validation error that OpLine is placed between OpLoopMerge
+ // and OpBranchConditional.
+ auto terminator = bi->terminator();
+ auto& vec = terminator->dbg_line_insts();
+ auto& new_vec = merge_inst->dbg_line_insts();
+ new_vec.insert(new_vec.end(), vec.begin(), vec.end());
+ terminator->clear_dbg_line_insts();
+
// Move the merge instruction to just before the terminator.
- merge_inst->InsertBefore(bi->terminator());
+ merge_inst->InsertBefore(terminator);
}
}
context->ReplaceAllUsesWith(lab_id, bi->id());
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index d1c4ce1..322e0aa 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -172,6 +172,9 @@
return dbg_line_insts_;
}
+ // Clear line-related debug instructions attached to this instruction.
+ void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
+
// Same semantics as in the base class except the list the InstructionList
// containing |pos| will now assume ownership of |this|.
// inline void MoveBefore(Instruction* pos);
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index 11fba73..f1460c5 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -423,6 +423,42 @@
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
+TEST_F(BlockMergeTest, MergeContinueWithOpLine) {
+ const std::string text = R"(
+; CHECK: OpBranch [[header:%\w+]]
+; CHECK: [[header]] = OpLabel
+; CHECK-NEXT: OpLogicalAnd
+; CHECK-NEXT: OpLine {{%\w+}} 1 1
+; CHECK-NEXT: OpLoopMerge {{%\w+}} [[header]] None
+; CHECK-NEXT: OpBranch [[header]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%src = OpString "test.shader"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%entry = OpLabel
+OpBranch %header
+%header = OpLabel
+OpLoopMerge %merge %continue None
+OpBranch %continue
+%continue = OpLabel
+%op = OpLogicalAnd %bool %true %false
+OpLine %src 1 1
+OpBranch %header
+%merge = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
TEST_F(BlockMergeTest, TwoHeadersCannotBeMerged) {
const std::string text = R"(
; CHECK: OpBranch [[loop_header:%\w+]]