/src/spirv-tools/source/opt/basic_block.cpp
Line | Count | Source |
1 | | // Copyright (c) 2016 Google Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include "source/opt/basic_block.h" |
16 | | |
17 | | #include <ostream> |
18 | | |
19 | | #include "source/opt/ir_context.h" |
20 | | #include "source/opt/reflect.h" |
21 | | #include "source/util/make_unique.h" |
22 | | |
23 | | namespace spvtools { |
24 | | namespace opt { |
25 | | namespace { |
26 | | constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1; |
27 | | constexpr uint32_t kLoopMergeMergeBlockIdInIdx = 0; |
28 | | constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0; |
29 | | } // namespace |
30 | | |
31 | 998k | BasicBlock* BasicBlock::Clone(IRContext* context) const { |
32 | 998k | Instruction* label_clone = GetLabelInst()->Clone(context); |
33 | 998k | if (!label_clone) { |
34 | 0 | return nullptr; |
35 | 0 | } |
36 | 998k | BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone)); |
37 | 4.84M | for (const auto& inst : insts_) { |
38 | | // Use the incoming context |
39 | 4.84M | Instruction* inst_clone = inst.Clone(context); |
40 | 4.84M | if (!inst_clone) { |
41 | 0 | delete clone; |
42 | 0 | return nullptr; |
43 | 0 | } |
44 | 4.84M | clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone)); |
45 | 4.84M | } |
46 | | |
47 | 998k | if (context->AreAnalysesValid( |
48 | 998k | IRContext::Analysis::kAnalysisInstrToBlockMapping)) { |
49 | 2.31M | for (auto& inst : *clone) { |
50 | 2.31M | context->set_instr_block(&inst, clone); |
51 | 2.31M | } |
52 | 645k | } |
53 | | |
54 | 998k | return clone; |
55 | 998k | } |
56 | | |
57 | 28.5M | const Instruction* BasicBlock::GetMergeInst() const { |
58 | 28.5M | const Instruction* result = nullptr; |
59 | | // If it exists, the merge instruction immediately precedes the |
60 | | // terminator. |
61 | 28.5M | auto iter = ctail(); |
62 | 28.5M | if (iter != cbegin()) { |
63 | 23.3M | --iter; |
64 | 23.3M | const auto opcode = iter->opcode(); |
65 | 23.3M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { |
66 | 9.28M | result = &*iter; |
67 | 9.28M | } |
68 | 23.3M | } |
69 | 28.5M | return result; |
70 | 28.5M | } |
71 | | |
72 | 35.0M | Instruction* BasicBlock::GetMergeInst() { |
73 | 35.0M | Instruction* result = nullptr; |
74 | | // If it exists, the merge instruction immediately precedes the |
75 | | // terminator. |
76 | 35.0M | auto iter = tail(); |
77 | 35.0M | if (iter != begin()) { |
78 | 28.3M | --iter; |
79 | 28.3M | const auto opcode = iter->opcode(); |
80 | 28.3M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { |
81 | 15.9M | result = &*iter; |
82 | 15.9M | } |
83 | 28.3M | } |
84 | 35.0M | return result; |
85 | 35.0M | } |
86 | | |
87 | 28.5M | const Instruction* BasicBlock::GetLoopMergeInst() const { |
88 | 28.5M | if (auto* merge = GetMergeInst()) { |
89 | 9.28M | if (merge->opcode() == spv::Op::OpLoopMerge) { |
90 | 788k | return merge; |
91 | 788k | } |
92 | 9.28M | } |
93 | 27.7M | return nullptr; |
94 | 28.5M | } |
95 | | |
96 | 12.0M | Instruction* BasicBlock::GetLoopMergeInst() { |
97 | 12.0M | if (auto* merge = GetMergeInst()) { |
98 | 4.57M | if (merge->opcode() == spv::Op::OpLoopMerge) { |
99 | 985k | return merge; |
100 | 985k | } |
101 | 4.57M | } |
102 | 11.0M | return nullptr; |
103 | 12.0M | } |
104 | | |
105 | 133k | void BasicBlock::KillAllInsts(bool killLabel) { |
106 | 537k | ForEachInst([killLabel](Instruction* ip) { |
107 | 537k | if (killLabel || ip->opcode() != spv::Op::OpLabel) { |
108 | 518k | ip->context()->KillInst(ip); |
109 | 518k | } |
110 | 537k | }); |
111 | 133k | } |
112 | | |
113 | | void BasicBlock::ForEachSuccessorLabel( |
114 | 51.8M | const std::function<void(const uint32_t)>& f) const { |
115 | 59.9M | WhileEachSuccessorLabel([f](const uint32_t l) { |
116 | 59.9M | f(l); |
117 | 59.9M | return true; |
118 | 59.9M | }); |
119 | 51.8M | } |
120 | | |
121 | | bool BasicBlock::WhileEachSuccessorLabel( |
122 | 58.6M | const std::function<bool(const uint32_t)>& f) const { |
123 | 58.6M | const auto br = &insts_.back(); |
124 | 58.6M | switch (br->opcode()) { |
125 | 42.1M | case spv::Op::OpBranch: |
126 | 42.1M | return f(br->GetOperand(0).words[0]); |
127 | 11.7M | case spv::Op::OpBranchConditional: |
128 | 13.4M | case spv::Op::OpSwitch: { |
129 | 13.4M | bool is_first = true; |
130 | 39.1M | return br->WhileEachInId([&is_first, &f](const uint32_t* idp) { |
131 | 39.1M | if (!is_first) return f(*idp); |
132 | 13.4M | is_first = false; |
133 | 13.4M | return true; |
134 | 39.1M | }); |
135 | 11.7M | } |
136 | 3.08M | default: |
137 | 3.08M | return true; |
138 | 58.6M | } |
139 | 58.6M | } |
140 | | |
141 | | void BasicBlock::ForEachSuccessorLabel( |
142 | 117k | const std::function<void(uint32_t*)>& f) { |
143 | 117k | auto br = &insts_.back(); |
144 | 117k | switch (br->opcode()) { |
145 | 103k | case spv::Op::OpBranch: { |
146 | 103k | uint32_t tmp_id = br->GetOperand(0).words[0]; |
147 | 103k | f(&tmp_id); |
148 | 103k | if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id}); |
149 | 103k | } break; |
150 | 13.4k | case spv::Op::OpBranchConditional: |
151 | 13.7k | case spv::Op::OpSwitch: { |
152 | 13.7k | bool is_first = true; |
153 | 41.3k | br->ForEachInId([&is_first, &f](uint32_t* idp) { |
154 | 41.3k | if (!is_first) f(idp); |
155 | 41.3k | is_first = false; |
156 | 41.3k | }); |
157 | 13.7k | } break; |
158 | 23 | default: |
159 | 23 | break; |
160 | 117k | } |
161 | 117k | } |
162 | | |
163 | 773k | bool BasicBlock::IsSuccessor(const BasicBlock* block) const { |
164 | 773k | uint32_t succId = block->id(); |
165 | 773k | bool isSuccessor = false; |
166 | 1.00M | ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) { |
167 | 1.00M | if (label == succId) isSuccessor = true; |
168 | 1.00M | }); |
169 | 773k | return isSuccessor; |
170 | 773k | } |
171 | | |
172 | | void BasicBlock::ForMergeAndContinueLabel( |
173 | 3.13M | const std::function<void(const uint32_t)>& f) { |
174 | 3.13M | auto ii = insts_.end(); |
175 | 3.13M | --ii; |
176 | 3.13M | if (ii == insts_.begin()) return; |
177 | 1.67M | --ii; |
178 | 1.67M | if (ii->opcode() == spv::Op::OpSelectionMerge || |
179 | 1.09M | ii->opcode() == spv::Op::OpLoopMerge) { |
180 | 855k | ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); }); |
181 | 716k | } |
182 | 1.67M | } |
183 | | |
184 | 31.3M | uint32_t BasicBlock::MergeBlockIdIfAny() const { |
185 | 31.3M | auto merge_ii = cend(); |
186 | 31.3M | --merge_ii; |
187 | 31.3M | uint32_t mbid = 0; |
188 | 31.3M | if (merge_ii != cbegin()) { |
189 | 22.0M | --merge_ii; |
190 | 22.0M | if (merge_ii->opcode() == spv::Op::OpLoopMerge) { |
191 | 1.67M | mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); |
192 | 20.3M | } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) { |
193 | 7.14M | mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); |
194 | 7.14M | } |
195 | 22.0M | } |
196 | | |
197 | 31.3M | return mbid; |
198 | 31.3M | } |
199 | | |
200 | 0 | uint32_t BasicBlock::MergeBlockId() const { |
201 | 0 | uint32_t mbid = MergeBlockIdIfAny(); |
202 | 0 | assert(mbid && "Expected block to have a corresponding merge block"); |
203 | 0 | return mbid; |
204 | 0 | } |
205 | | |
206 | 6.36M | uint32_t BasicBlock::ContinueBlockIdIfAny() const { |
207 | 6.36M | auto merge_ii = cend(); |
208 | 6.36M | --merge_ii; |
209 | 6.36M | uint32_t cbid = 0; |
210 | 6.36M | if (merge_ii != cbegin()) { |
211 | 5.57M | --merge_ii; |
212 | 5.57M | if (merge_ii->opcode() == spv::Op::OpLoopMerge) { |
213 | 963k | cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); |
214 | 963k | } |
215 | 5.57M | } |
216 | 6.36M | return cbid; |
217 | 6.36M | } |
218 | | |
219 | 0 | uint32_t BasicBlock::ContinueBlockId() const { |
220 | 0 | uint32_t cbid = ContinueBlockIdIfAny(); |
221 | 0 | assert(cbid && "Expected block to have a corresponding continue target"); |
222 | 0 | return cbid; |
223 | 0 | } |
224 | | |
225 | 0 | std::ostream& operator<<(std::ostream& str, const BasicBlock& block) { |
226 | 0 | str << block.PrettyPrint(); |
227 | 0 | return str; |
228 | 0 | } |
229 | | |
230 | 0 | void BasicBlock::Dump() const { |
231 | 0 | std::cerr << "Basic block #" << id() << "\n" << *this << "\n "; |
232 | 0 | } |
233 | | |
234 | 0 | std::string BasicBlock::PrettyPrint(uint32_t options) const { |
235 | 0 | std::ostringstream str; |
236 | 0 | ForEachInst([&str, options](const Instruction* inst) { |
237 | 0 | str << inst->PrettyPrint(options); |
238 | 0 | if (!spvOpcodeIsBlockTerminator(inst->opcode())) { |
239 | 0 | str << std::endl; |
240 | 0 | } |
241 | 0 | }); |
242 | 0 | return str.str(); |
243 | 0 | } |
244 | | |
245 | | BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id, |
246 | 10.6k | iterator iter) { |
247 | 10.6k | assert(!insts_.empty()); |
248 | | |
249 | 10.6k | std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>( |
250 | 10.6k | MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id, |
251 | 10.6k | std::initializer_list<Operand>{})); |
252 | 10.6k | BasicBlock* new_block = new_block_temp.get(); |
253 | 10.6k | function_->InsertBasicBlockAfter(std::move(new_block_temp), this); |
254 | | |
255 | 10.6k | new_block->insts_.Splice(new_block->end(), &insts_, iter, end()); |
256 | 10.6k | assert(new_block->GetParent() == GetParent() && |
257 | 10.6k | "The parent should already be set appropriately."); |
258 | | |
259 | 10.6k | context->AnalyzeDefUse(new_block->GetLabelInst()); |
260 | | |
261 | | // Update the phi nodes in the successor blocks to reference the new block id. |
262 | 10.6k | const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel( |
263 | 13.5k | [new_block, this, context](const uint32_t label) { |
264 | 13.5k | BasicBlock* target_bb = context->get_instr_block(label); |
265 | 13.5k | target_bb->ForEachPhiInst( |
266 | 13.5k | [this, new_block, context](Instruction* phi_inst) { |
267 | 821 | bool changed = false; |
268 | 2.46k | for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) { |
269 | 1.64k | if (phi_inst->GetSingleWordInOperand(i) == this->id()) { |
270 | 821 | changed = true; |
271 | 821 | phi_inst->SetInOperand(i, {new_block->id()}); |
272 | 821 | } |
273 | 1.64k | } |
274 | | |
275 | 821 | if (changed) { |
276 | 821 | context->UpdateDefUse(phi_inst); |
277 | 821 | } |
278 | 821 | }); |
279 | 13.5k | }); |
280 | | |
281 | 10.6k | if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { |
282 | 10.6k | context->set_instr_block(new_block->GetLabelInst(), new_block); |
283 | 93.8k | new_block->ForEachInst([new_block, context](Instruction* inst) { |
284 | 93.8k | context->set_instr_block(inst, new_block); |
285 | 93.8k | }); |
286 | 10.6k | } |
287 | | |
288 | 10.6k | return new_block; |
289 | 10.6k | } |
290 | | |
291 | | } // namespace opt |
292 | | } // namespace spvtools |