/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 | 650k | bool IsHeader(BasicBlock* block) { return block->GetMergeInst() != nullptr; } |
27 | | |
28 | | // Returns true if |id| contains a merge instruction. |
29 | 22.3k | bool IsHeader(IRContext* context, uint32_t id) { |
30 | 22.3k | return IsHeader( |
31 | 22.3k | context->get_instr_block(context->get_def_use_mgr()->GetDef(id))); |
32 | 22.3k | } |
33 | | |
34 | | // Returns true if |id| is the merge target of a merge instruction. |
35 | 862k | bool IsMerge(IRContext* context, uint32_t id) { |
36 | 862k | return !context->get_def_use_mgr()->WhileEachUse( |
37 | 862k | id, [](Instruction* user, uint32_t index) { |
38 | 685k | spv::Op op = user->opcode(); |
39 | 685k | if ((op == spv::Op::OpLoopMerge || op == spv::Op::OpSelectionMerge) && |
40 | 120k | index == 0u) { |
41 | 105k | return false; |
42 | 105k | } |
43 | 580k | return true; |
44 | 685k | }); |
45 | 862k | } |
46 | | |
47 | | // Returns true if |block| is the merge target of a merge instruction. |
48 | 431k | bool IsMerge(IRContext* context, BasicBlock* block) { |
49 | 431k | return IsMerge(context, block->id()); |
50 | 431k | } |
51 | | |
52 | | // Returns true if |id| is the continue target of a merge instruction. |
53 | 511k | bool IsContinue(IRContext* context, uint32_t id) { |
54 | 511k | return !context->get_def_use_mgr()->WhileEachUse( |
55 | 568k | id, [](Instruction* user, uint32_t index) { |
56 | 568k | spv::Op op = user->opcode(); |
57 | 568k | if (op == spv::Op::OpLoopMerge && index == 1u) { |
58 | 14.0k | return false; |
59 | 14.0k | } |
60 | 554k | return true; |
61 | 568k | }); |
62 | 511k | } |
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 | 207k | void EliminateOpPhiInstructions(IRContext* context, BasicBlock* block) { |
68 | 207k | 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 | 207k | } |
77 | | |
78 | | } // Anonymous namespace |
79 | | |
80 | 707k | bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { |
81 | | // Find block with single successor which has no other predecessors. |
82 | 707k | auto ii = block->end(); |
83 | 707k | --ii; |
84 | 707k | Instruction* br = &*ii; |
85 | 707k | if (br->opcode() != spv::Op::OpBranch) { |
86 | 136k | return false; |
87 | 136k | } |
88 | | |
89 | 571k | const uint32_t lab_id = br->GetSingleWordInOperand(0); |
90 | 571k | if (context->cfg()->preds(lab_id).size() != 1) { |
91 | 140k | return false; |
92 | 140k | } |
93 | | |
94 | 431k | bool pred_is_merge = IsMerge(context, block); |
95 | 431k | bool succ_is_merge = IsMerge(context, lab_id); |
96 | 431k | if (pred_is_merge && succ_is_merge) { |
97 | | // Cannot merge two merges together. |
98 | 1.07k | return false; |
99 | 1.07k | } |
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 | 430k | if (succ_is_merge && context->get_feature_mgr()->HasExtension( |
108 | 3.87k | kSPV_KHR_maximal_reconvergence)) { |
109 | 0 | return false; |
110 | 0 | } |
111 | | |
112 | 430k | if (pred_is_merge && IsContinue(context, lab_id)) { |
113 | | // Cannot merge a continue target with a merge block. |
114 | 10.4k | return false; |
115 | 10.4k | } |
116 | | |
117 | 419k | Instruction* merge_inst = block->GetMergeInst(); |
118 | 419k | const bool pred_is_header = IsHeader(block); |
119 | 419k | if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { |
120 | 22.3k | bool succ_is_header = IsHeader(context, lab_id); |
121 | 22.3k | 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 | 4.32k | return false; |
125 | 4.32k | } |
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 | 18.0k | BasicBlock* succ_block = context->get_instr_block(lab_id); |
133 | 18.0k | spv::Op succ_term_op = succ_block->terminator()->opcode(); |
134 | 18.0k | assert(merge_inst->opcode() == spv::Op::OpLoopMerge); |
135 | 18.0k | if (succ_term_op != spv::Op::OpBranch && |
136 | 12.5k | succ_term_op != spv::Op::OpBranchConditional) { |
137 | 28 | return false; |
138 | 28 | } |
139 | 18.0k | } |
140 | | |
141 | 415k | if (succ_is_merge || IsContinue(context, lab_id)) { |
142 | 7.54k | auto* struct_cfg = context->GetStructuredCFGAnalysis(); |
143 | 7.54k | auto switch_block_id = struct_cfg->ContainingSwitch(block->id()); |
144 | 7.54k | if (switch_block_id) { |
145 | 1.09k | auto switch_merge_id = struct_cfg->SwitchMergeBlock(switch_block_id); |
146 | 1.09k | const auto* switch_inst = |
147 | 1.09k | &*block->GetParent()->FindBlock(switch_block_id)->tail(); |
148 | 2.73k | for (uint32_t i = 1; i < switch_inst->NumInOperands(); i += 2) { |
149 | 1.67k | auto target_id = switch_inst->GetSingleWordInOperand(i); |
150 | 1.67k | 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 | 30 | return false; |
155 | 30 | } |
156 | 1.67k | } |
157 | 1.09k | } |
158 | 7.54k | } |
159 | | |
160 | 415k | return true; |
161 | 415k | } |
162 | | |
163 | | void MergeWithSuccessor(IRContext* context, Function* func, |
164 | 207k | Function::iterator bi) { |
165 | 207k | assert(CanMergeWithSuccessor(context, &*bi) && |
166 | 207k | "Precondition failure for MergeWithSuccessor: it must be legal to " |
167 | 207k | "merge the block and its successor."); |
168 | | |
169 | 207k | auto ii = bi->end(); |
170 | 207k | --ii; |
171 | 207k | Instruction* br = &*ii; |
172 | 207k | const uint32_t lab_id = br->GetSingleWordInOperand(0); |
173 | 207k | Instruction* merge_inst = bi->GetMergeInst(); |
174 | 207k | bool pred_is_header = IsHeader(&*bi); |
175 | | |
176 | | // Merge blocks. |
177 | 207k | context->KillInst(br); |
178 | 207k | auto sbi = bi; |
179 | 421k | for (; sbi != func->end(); ++sbi) |
180 | 421k | 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 | 207k | assert(sbi != func->end()); |
184 | | |
185 | 207k | if (sbi->tail()->opcode() == spv::Op::OpSwitch && |
186 | 2.01k | sbi->MergeBlockIdIfAny() != 0) { |
187 | 2.01k | context->InvalidateAnalyses(IRContext::Analysis::kAnalysisStructuredCFG); |
188 | 2.01k | } |
189 | | |
190 | | // Update the inst-to-block mapping for the instructions in sbi. |
191 | 442k | for (auto& inst : *sbi) { |
192 | 442k | context->set_instr_block(&inst, &*bi); |
193 | 442k | } |
194 | | |
195 | 207k | EliminateOpPhiInstructions(context, &*sbi); |
196 | | |
197 | | // Now actually move the instructions. |
198 | 207k | bi->AddInstructions(&*sbi); |
199 | | |
200 | 207k | if (merge_inst) { |
201 | 9.67k | 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 | 659 | context->KillInst(merge_inst); |
205 | 9.01k | } 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 | 9.01k | auto terminator = bi->terminator(); |
210 | 9.01k | auto& vec = terminator->dbg_line_insts(); |
211 | 9.01k | if (vec.size() > 0) { |
212 | 0 | merge_inst->ClearDbgLineInsts(); |
213 | 0 | auto& new_vec = merge_inst->dbg_line_insts(); |
214 | 0 | new_vec.insert(new_vec.end(), vec.begin(), vec.end()); |
215 | 0 | terminator->ClearDbgLineInsts(); |
216 | 0 | for (auto& l_inst : new_vec) |
217 | 0 | context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); |
218 | 0 | } |
219 | | // Clear debug scope of terminator to avoid DebugScope |
220 | | // emitted between terminator and merge. |
221 | 9.01k | terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); |
222 | | // Move the merge instruction to just before the terminator. |
223 | 9.01k | merge_inst->InsertBefore(terminator); |
224 | 9.01k | } |
225 | 9.67k | } |
226 | 207k | context->ReplaceAllUsesWith(lab_id, bi->id()); |
227 | 207k | context->KillInst(sbi->GetLabelInst()); |
228 | 207k | (void)sbi.Erase(); |
229 | 207k | } |
230 | | |
231 | | } // namespace blockmergeutil |
232 | | } // namespace opt |
233 | | } // namespace spvtools |