/src/spirv-tools/source/opt/block_merge_util.cpp
Line | Count | Source |
1 | | // Copyright (c) 2017 The Khronos Group Inc. |
2 | | // Copyright (c) 2017 Valve Corporation |
3 | | // Copyright (c) 2017 LunarG Inc. |
4 | | // Copyright (c) 2019 Google LLC |
5 | | // |
6 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | // you may not use this file except in compliance with the License. |
8 | | // You may obtain a copy of the License at |
9 | | // |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // |
12 | | // Unless required by applicable law or agreed to in writing, software |
13 | | // distributed under the License is distributed on an "AS IS" BASIS, |
14 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | // See the License for the specific language governing permissions and |
16 | | // limitations under the License. |
17 | | |
18 | | #include "block_merge_util.h" |
19 | | |
20 | | namespace spvtools { |
21 | | namespace opt { |
22 | | namespace blockmergeutil { |
23 | | namespace { |
24 | | |
25 | | // Returns true if |block| contains a merge instruction. |
26 | 1.63M | bool IsHeader(BasicBlock* block) { return block->GetMergeInst() != nullptr; } |
27 | | |
28 | | // Returns true if |id| contains a merge instruction. |
29 | 20.9k | bool IsHeader(IRContext* context, uint32_t id) { |
30 | 20.9k | return IsHeader( |
31 | 20.9k | context->get_instr_block(context->get_def_use_mgr()->GetDef(id))); |
32 | 20.9k | } |
33 | | |
34 | | // Returns true if |id| is the merge target of a merge instruction. |
35 | 2.18M | bool IsMerge(IRContext* context, uint32_t id) { |
36 | 2.18M | return !context->get_def_use_mgr()->WhileEachUse( |
37 | 2.18M | id, [](Instruction* user, uint32_t index) { |
38 | 1.34M | spv::Op op = user->opcode(); |
39 | 1.34M | if ((op == spv::Op::OpLoopMerge || op == spv::Op::OpSelectionMerge) && |
40 | 114k | index == 0u) { |
41 | 92.0k | return false; |
42 | 92.0k | } |
43 | 1.24M | return true; |
44 | 1.34M | }); |
45 | 2.18M | } |
46 | | |
47 | | // Returns true if |block| is the merge target of a merge instruction. |
48 | 1.09M | bool IsMerge(IRContext* context, BasicBlock* block) { |
49 | 1.09M | return IsMerge(context, block->id()); |
50 | 1.09M | } |
51 | | |
52 | | // Returns true if |id| is the continue target of a merge instruction. |
53 | 1.15M | bool IsContinue(IRContext* context, uint32_t id) { |
54 | 1.15M | return !context->get_def_use_mgr()->WhileEachUse( |
55 | 1.20M | id, [](Instruction* user, uint32_t index) { |
56 | 1.20M | spv::Op op = user->opcode(); |
57 | 1.20M | if (op == spv::Op::OpLoopMerge && index == 1u) { |
58 | 12.8k | return false; |
59 | 12.8k | } |
60 | 1.19M | return true; |
61 | 1.20M | }); |
62 | 1.15M | } |
63 | | |
64 | | // Removes any OpPhi instructions in |block|, which should have exactly one |
65 | | // predecessor, replacing uses of OpPhi ids with the ids associated with the |
66 | | // predecessor. |
67 | 538k | void EliminateOpPhiInstructions(IRContext* context, BasicBlock* block) { |
68 | 538k | block->ForEachPhiInst([context](Instruction* phi) { |
69 | 0 | assert(2 == phi->NumInOperands() && |
70 | 0 | "A block can only have one predecessor for block merging to make " |
71 | 0 | "sense."); |
72 | 0 | context->ReplaceAllUsesWith(phi->result_id(), |
73 | 0 | phi->GetSingleWordInOperand(0)); |
74 | 0 | context->KillInst(phi); |
75 | 0 | }); |
76 | 538k | } |
77 | | |
78 | | } // Anonymous namespace |
79 | | |
80 | 1.33M | bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { |
81 | | // Find block with single successor which has no other predecessors. |
82 | 1.33M | auto ii = block->end(); |
83 | 1.33M | --ii; |
84 | 1.33M | Instruction* br = &*ii; |
85 | 1.33M | if (br->opcode() != spv::Op::OpBranch) { |
86 | 126k | return false; |
87 | 126k | } |
88 | | |
89 | 1.21M | const uint32_t lab_id = br->GetSingleWordInOperand(0); |
90 | 1.21M | if (context->cfg()->preds(lab_id).size() != 1) { |
91 | 121k | return false; |
92 | 121k | } |
93 | | |
94 | 1.09M | bool pred_is_merge = IsMerge(context, block); |
95 | 1.09M | bool succ_is_merge = IsMerge(context, lab_id); |
96 | 1.09M | if (pred_is_merge && succ_is_merge) { |
97 | | // Cannot merge two merges together. |
98 | 907 | return false; |
99 | 907 | } |
100 | | |
101 | | // Note: This means that the instructions in a break block will execute as if |
102 | | // they were still diverged according to the loop iteration. This restricts |
103 | | // potential transformations an implementation may perform on the IR to match |
104 | | // shader author expectations. Similarly, instructions in the loop construct |
105 | | // cannot be moved into the continue construct unless it can be proven that |
106 | | // invocations are always converged. |
107 | 1.08M | if (succ_is_merge && context->get_feature_mgr()->HasExtension( |
108 | 3.49k | kSPV_KHR_maximal_reconvergence)) { |
109 | 0 | return false; |
110 | 0 | } |
111 | | |
112 | 1.08M | if (pred_is_merge && IsContinue(context, lab_id)) { |
113 | | // Cannot merge a continue target with a merge block. |
114 | 9.15k | return false; |
115 | 9.15k | } |
116 | | |
117 | 1.08M | Instruction* merge_inst = block->GetMergeInst(); |
118 | 1.08M | const bool pred_is_header = IsHeader(block); |
119 | 1.08M | if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { |
120 | 20.9k | bool succ_is_header = IsHeader(context, lab_id); |
121 | 20.9k | if (pred_is_header && succ_is_header) { |
122 | | // Cannot merge two headers together when the successor is not the merge |
123 | | // block of the predecessor. |
124 | 3.75k | return false; |
125 | 3.75k | } |
126 | | |
127 | | // If this is a header block and the successor is not its merge, we must |
128 | | // be careful about which blocks we are willing to merge together. |
129 | | // OpLoopMerge must be followed by a conditional or unconditional branch. |
130 | | // The merge must be a loop merge because a selection merge cannot be |
131 | | // followed by an unconditional branch. |
132 | 17.2k | BasicBlock* succ_block = context->get_instr_block(lab_id); |
133 | 17.2k | spv::Op succ_term_op = succ_block->terminator()->opcode(); |
134 | 17.2k | assert(merge_inst->opcode() == spv::Op::OpLoopMerge); |
135 | 17.2k | if (succ_term_op != spv::Op::OpBranch && |
136 | 10.9k | succ_term_op != spv::Op::OpBranchConditional) { |
137 | 34 | return false; |
138 | 34 | } |
139 | 17.2k | } |
140 | | |
141 | 1.07M | if (succ_is_merge || IsContinue(context, lab_id)) { |
142 | 7.22k | auto* struct_cfg = context->GetStructuredCFGAnalysis(); |
143 | 7.22k | auto switch_block_id = struct_cfg->ContainingSwitch(block->id()); |
144 | 7.22k | if (switch_block_id) { |
145 | 1.27k | auto switch_merge_id = struct_cfg->SwitchMergeBlock(switch_block_id); |
146 | 1.27k | const auto* switch_inst = |
147 | 1.27k | &*block->GetParent()->FindBlock(switch_block_id)->tail(); |
148 | 2.82k | for (uint32_t i = 1; i < switch_inst->NumInOperands(); i += 2) { |
149 | 1.58k | auto target_id = switch_inst->GetSingleWordInOperand(i); |
150 | 1.58k | if (target_id == block->id() && target_id != switch_merge_id) { |
151 | | // Case constructs must be structurally dominated by the OpSwitch. |
152 | | // Since the successor is the merge/continue for another construct, |
153 | | // merging the blocks would break that requirement. |
154 | 39 | return false; |
155 | 39 | } |
156 | 1.58k | } |
157 | 1.27k | } |
158 | 7.22k | } |
159 | | |
160 | 1.07M | return true; |
161 | 1.07M | } |
162 | | |
163 | | void MergeWithSuccessor(IRContext* context, Function* func, |
164 | 538k | Function::iterator bi) { |
165 | 538k | assert(CanMergeWithSuccessor(context, &*bi) && |
166 | 538k | "Precondition failure for MergeWithSuccessor: it must be legal to " |
167 | 538k | "merge the block and its successor."); |
168 | | |
169 | 538k | auto ii = bi->end(); |
170 | 538k | --ii; |
171 | 538k | Instruction* br = &*ii; |
172 | 538k | const uint32_t lab_id = br->GetSingleWordInOperand(0); |
173 | 538k | Instruction* merge_inst = bi->GetMergeInst(); |
174 | 538k | bool pred_is_header = IsHeader(&*bi); |
175 | | |
176 | | // Merge blocks. |
177 | 538k | context->KillInst(br); |
178 | 538k | auto sbi = bi; |
179 | 1.08M | for (; sbi != func->end(); ++sbi) |
180 | 1.08M | if (sbi->id() == lab_id) break; |
181 | | // If bi is sbi's only predecessor, it dominates sbi and thus |
182 | | // sbi must follow bi in func's ordering. |
183 | 538k | assert(sbi != func->end()); |
184 | | |
185 | 538k | if (sbi->tail()->opcode() == spv::Op::OpSwitch && |
186 | 1.57k | sbi->MergeBlockIdIfAny() != 0) { |
187 | 1.57k | context->InvalidateAnalyses(IRContext::Analysis::kAnalysisStructuredCFG); |
188 | 1.57k | } |
189 | | |
190 | | // Update the inst-to-block mapping for the instructions in sbi. |
191 | 1.20M | for (auto& inst : *sbi) { |
192 | 1.20M | context->set_instr_block(&inst, &*bi); |
193 | 1.20M | } |
194 | | |
195 | 538k | EliminateOpPhiInstructions(context, &*sbi); |
196 | | |
197 | | // Now actually move the instructions. |
198 | 538k | bi->AddInstructions(&*sbi); |
199 | | |
200 | 538k | if (merge_inst) { |
201 | 9.13k | if (pred_is_header && lab_id == merge_inst->GetSingleWordInOperand(0u)) { |
202 | | // Merging the header and merge blocks, so remove the structured control |
203 | | // flow declaration. |
204 | 544 | context->KillInst(merge_inst); |
205 | 8.58k | } else { |
206 | | // Move OpLine/OpNoLine information to merge_inst. This solves |
207 | | // the validation error that OpLine is placed between OpLoopMerge |
208 | | // and OpBranchConditional. |
209 | 8.58k | auto terminator = bi->terminator(); |
210 | 8.58k | auto& vec = terminator->dbg_line_insts(); |
211 | 8.58k | if (vec.size() > 0) { |
212 | 9 | merge_inst->ClearDbgLineInsts(); |
213 | 9 | auto& new_vec = merge_inst->dbg_line_insts(); |
214 | 9 | new_vec.insert(new_vec.end(), vec.begin(), vec.end()); |
215 | 9 | terminator->ClearDbgLineInsts(); |
216 | 9 | for (auto& l_inst : new_vec) |
217 | 26 | context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); |
218 | 9 | } |
219 | | // Clear debug scope of terminator to avoid DebugScope |
220 | | // emitted between terminator and merge. |
221 | 8.58k | terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); |
222 | | // Move the merge instruction to just before the terminator. |
223 | 8.58k | merge_inst->InsertBefore(terminator); |
224 | 8.58k | } |
225 | 9.13k | } |
226 | 538k | context->ReplaceAllUsesWith(lab_id, bi->id()); |
227 | 538k | context->KillInst(sbi->GetLabelInst()); |
228 | 538k | (void)sbi.Erase(); |
229 | 538k | } |
230 | | |
231 | | } // namespace blockmergeutil |
232 | | } // namespace opt |
233 | | } // namespace spvtools |