/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 | 798k | BasicBlock* BasicBlock::Clone(IRContext* context) const { |
32 | 798k | Instruction* label_clone = GetLabelInst()->Clone(context); |
33 | 798k | if (!label_clone) { |
34 | 0 | return nullptr; |
35 | 0 | } |
36 | 798k | BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone)); |
37 | 3.98M | for (const auto& inst : insts_) { |
38 | | // Use the incoming context |
39 | 3.98M | Instruction* inst_clone = inst.Clone(context); |
40 | 3.98M | if (!inst_clone) { |
41 | 0 | delete clone; |
42 | 0 | return nullptr; |
43 | 0 | } |
44 | 3.98M | clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone)); |
45 | 3.98M | } |
46 | | |
47 | 798k | if (context->AreAnalysesValid( |
48 | 798k | IRContext::Analysis::kAnalysisInstrToBlockMapping)) { |
49 | 1.82M | for (auto& inst : *clone) { |
50 | 1.82M | context->set_instr_block(&inst, clone); |
51 | 1.82M | } |
52 | 509k | } |
53 | | |
54 | 798k | return clone; |
55 | 798k | } |
56 | | |
57 | 24.9M | const Instruction* BasicBlock::GetMergeInst() const { |
58 | 24.9M | const Instruction* result = nullptr; |
59 | | // If it exists, the merge instruction immediately precedes the |
60 | | // terminator. |
61 | 24.9M | auto iter = ctail(); |
62 | 24.9M | if (iter != cbegin()) { |
63 | 20.4M | --iter; |
64 | 20.4M | const auto opcode = iter->opcode(); |
65 | 20.4M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { |
66 | 8.36M | result = &*iter; |
67 | 8.36M | } |
68 | 20.4M | } |
69 | 24.9M | return result; |
70 | 24.9M | } |
71 | | |
72 | 25.9M | Instruction* BasicBlock::GetMergeInst() { |
73 | 25.9M | Instruction* result = nullptr; |
74 | | // If it exists, the merge instruction immediately precedes the |
75 | | // terminator. |
76 | 25.9M | auto iter = tail(); |
77 | 25.9M | if (iter != begin()) { |
78 | 22.2M | --iter; |
79 | 22.2M | const auto opcode = iter->opcode(); |
80 | 22.2M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { |
81 | 13.1M | result = &*iter; |
82 | 13.1M | } |
83 | 22.2M | } |
84 | 25.9M | return result; |
85 | 25.9M | } |
86 | | |
87 | 24.9M | const Instruction* BasicBlock::GetLoopMergeInst() const { |
88 | 24.9M | if (auto* merge = GetMergeInst()) { |
89 | 8.36M | if (merge->opcode() == spv::Op::OpLoopMerge) { |
90 | 728k | return merge; |
91 | 728k | } |
92 | 8.36M | } |
93 | 24.1M | return nullptr; |
94 | 24.9M | } |
95 | | |
96 | 10.5M | Instruction* BasicBlock::GetLoopMergeInst() { |
97 | 10.5M | if (auto* merge = GetMergeInst()) { |
98 | 4.08M | if (merge->opcode() == spv::Op::OpLoopMerge) { |
99 | 868k | return merge; |
100 | 868k | } |
101 | 4.08M | } |
102 | 9.66M | return nullptr; |
103 | 10.5M | } |
104 | | |
105 | 101k | void BasicBlock::KillAllInsts(bool killLabel) { |
106 | 405k | ForEachInst([killLabel](Instruction* ip) { |
107 | 405k | if (killLabel || ip->opcode() != spv::Op::OpLabel) { |
108 | 388k | ip->context()->KillInst(ip); |
109 | 388k | } |
110 | 405k | }); |
111 | 101k | } |
112 | | |
113 | | void BasicBlock::ForEachSuccessorLabel( |
114 | 21.6M | const std::function<void(const uint32_t)>& f) const { |
115 | 25.4M | WhileEachSuccessorLabel([f](const uint32_t l) { |
116 | 25.4M | f(l); |
117 | 25.4M | return true; |
118 | 25.4M | }); |
119 | 21.6M | } |
120 | | |
121 | | bool BasicBlock::WhileEachSuccessorLabel( |
122 | 27.9M | const std::function<bool(const uint32_t)>& f) const { |
123 | 27.9M | const auto br = &insts_.back(); |
124 | 27.9M | switch (br->opcode()) { |
125 | 20.0M | case spv::Op::OpBranch: |
126 | 20.0M | return f(br->GetOperand(0).words[0]); |
127 | 5.80M | case spv::Op::OpBranchConditional: |
128 | 6.71M | case spv::Op::OpSwitch: { |
129 | 6.71M | bool is_first = true; |
130 | 19.2M | return br->WhileEachInId([&is_first, &f](const uint32_t* idp) { |
131 | 19.2M | if (!is_first) return f(*idp); |
132 | 6.71M | is_first = false; |
133 | 6.71M | return true; |
134 | 19.2M | }); |
135 | 5.80M | } |
136 | 1.13M | default: |
137 | 1.13M | return true; |
138 | 27.9M | } |
139 | 27.9M | } |
140 | | |
141 | | void BasicBlock::ForEachSuccessorLabel( |
142 | 97.2k | const std::function<void(uint32_t*)>& f) { |
143 | 97.2k | auto br = &insts_.back(); |
144 | 97.2k | switch (br->opcode()) { |
145 | 85.3k | case spv::Op::OpBranch: { |
146 | 85.3k | uint32_t tmp_id = br->GetOperand(0).words[0]; |
147 | 85.3k | f(&tmp_id); |
148 | 85.3k | if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id}); |
149 | 85.3k | } break; |
150 | 11.7k | case spv::Op::OpBranchConditional: |
151 | 11.8k | case spv::Op::OpSwitch: { |
152 | 11.8k | bool is_first = true; |
153 | 35.7k | br->ForEachInId([&is_first, &f](uint32_t* idp) { |
154 | 35.7k | if (!is_first) f(idp); |
155 | 35.7k | is_first = false; |
156 | 35.7k | }); |
157 | 11.8k | } break; |
158 | 16 | default: |
159 | 16 | break; |
160 | 97.2k | } |
161 | 97.2k | } |
162 | | |
163 | 703k | bool BasicBlock::IsSuccessor(const BasicBlock* block) const { |
164 | 703k | uint32_t succId = block->id(); |
165 | 703k | bool isSuccessor = false; |
166 | 915k | ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) { |
167 | 915k | if (label == succId) isSuccessor = true; |
168 | 915k | }); |
169 | 703k | return isSuccessor; |
170 | 703k | } |
171 | | |
172 | | void BasicBlock::ForMergeAndContinueLabel( |
173 | 2.74M | const std::function<void(const uint32_t)>& f) { |
174 | 2.74M | auto ii = insts_.end(); |
175 | 2.74M | --ii; |
176 | 2.74M | if (ii == insts_.begin()) return; |
177 | 1.48M | --ii; |
178 | 1.48M | if (ii->opcode() == spv::Op::OpSelectionMerge || |
179 | 966k | ii->opcode() == spv::Op::OpLoopMerge) { |
180 | 779k | ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); }); |
181 | 649k | } |
182 | 1.48M | } |
183 | | |
184 | 22.5M | uint32_t BasicBlock::MergeBlockIdIfAny() const { |
185 | 22.5M | auto merge_ii = cend(); |
186 | 22.5M | --merge_ii; |
187 | 22.5M | uint32_t mbid = 0; |
188 | 22.5M | if (merge_ii != cbegin()) { |
189 | 16.6M | --merge_ii; |
190 | 16.6M | if (merge_ii->opcode() == spv::Op::OpLoopMerge) { |
191 | 1.23M | mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); |
192 | 15.3M | } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) { |
193 | 5.53M | mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); |
194 | 5.53M | } |
195 | 16.6M | } |
196 | | |
197 | 22.5M | return mbid; |
198 | 22.5M | } |
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 | 4.40M | uint32_t BasicBlock::ContinueBlockIdIfAny() const { |
207 | 4.40M | auto merge_ii = cend(); |
208 | 4.40M | --merge_ii; |
209 | 4.40M | uint32_t cbid = 0; |
210 | 4.40M | if (merge_ii != cbegin()) { |
211 | 3.76M | --merge_ii; |
212 | 3.76M | if (merge_ii->opcode() == spv::Op::OpLoopMerge) { |
213 | 580k | cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); |
214 | 580k | } |
215 | 3.76M | } |
216 | 4.40M | return cbid; |
217 | 4.40M | } |
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.2k | iterator iter) { |
247 | 10.2k | assert(!insts_.empty()); |
248 | | |
249 | 10.2k | std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>( |
250 | 10.2k | MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id, |
251 | 10.2k | std::initializer_list<Operand>{})); |
252 | 10.2k | BasicBlock* new_block = new_block_temp.get(); |
253 | 10.2k | function_->InsertBasicBlockAfter(std::move(new_block_temp), this); |
254 | | |
255 | 10.2k | new_block->insts_.Splice(new_block->end(), &insts_, iter, end()); |
256 | 10.2k | assert(new_block->GetParent() == GetParent() && |
257 | 10.2k | "The parent should already be set appropriately."); |
258 | | |
259 | 10.2k | context->AnalyzeDefUse(new_block->GetLabelInst()); |
260 | | |
261 | | // Update the phi nodes in the successor blocks to reference the new block id. |
262 | 10.2k | const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel( |
263 | 12.9k | [new_block, this, context](const uint32_t label) { |
264 | 12.9k | BasicBlock* target_bb = context->get_instr_block(label); |
265 | 12.9k | target_bb->ForEachPhiInst( |
266 | 12.9k | [this, new_block, context](Instruction* phi_inst) { |
267 | 796 | bool changed = false; |
268 | 2.38k | for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) { |
269 | 1.59k | if (phi_inst->GetSingleWordInOperand(i) == this->id()) { |
270 | 796 | changed = true; |
271 | 796 | phi_inst->SetInOperand(i, {new_block->id()}); |
272 | 796 | } |
273 | 1.59k | } |
274 | | |
275 | 796 | if (changed) { |
276 | 796 | context->UpdateDefUse(phi_inst); |
277 | 796 | } |
278 | 796 | }); |
279 | 12.9k | }); |
280 | | |
281 | 10.2k | if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { |
282 | 10.2k | context->set_instr_block(new_block->GetLabelInst(), new_block); |
283 | 87.4k | new_block->ForEachInst([new_block, context](Instruction* inst) { |
284 | 87.4k | context->set_instr_block(inst, new_block); |
285 | 87.4k | }); |
286 | 10.2k | } |
287 | | |
288 | 10.2k | return new_block; |
289 | 10.2k | } |
290 | | |
291 | | } // namespace opt |
292 | | } // namespace spvtools |