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
Clone(IRContext * context) const31 BasicBlock* BasicBlock::Clone(IRContext* context) const {
32 BasicBlock* clone = new BasicBlock(
33 std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
34 for (const auto& inst : insts_) {
35 // Use the incoming context
36 clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
37 }
38
39 if (context->AreAnalysesValid(
40 IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
41 for (auto& inst : *clone) {
42 context->set_instr_block(&inst, clone);
43 }
44 }
45
46 return clone;
47 }
48
GetMergeInst() const49 const Instruction* BasicBlock::GetMergeInst() const {
50 const Instruction* result = nullptr;
51 // If it exists, the merge instruction immediately precedes the
52 // terminator.
53 auto iter = ctail();
54 if (iter != cbegin()) {
55 --iter;
56 const auto opcode = iter->opcode();
57 if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
58 result = &*iter;
59 }
60 }
61 return result;
62 }
63
GetMergeInst()64 Instruction* BasicBlock::GetMergeInst() {
65 Instruction* result = nullptr;
66 // If it exists, the merge instruction immediately precedes the
67 // terminator.
68 auto iter = tail();
69 if (iter != begin()) {
70 --iter;
71 const auto opcode = iter->opcode();
72 if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
73 result = &*iter;
74 }
75 }
76 return result;
77 }
78
GetLoopMergeInst() const79 const Instruction* BasicBlock::GetLoopMergeInst() const {
80 if (auto* merge = GetMergeInst()) {
81 if (merge->opcode() == spv::Op::OpLoopMerge) {
82 return merge;
83 }
84 }
85 return nullptr;
86 }
87
GetLoopMergeInst()88 Instruction* BasicBlock::GetLoopMergeInst() {
89 if (auto* merge = GetMergeInst()) {
90 if (merge->opcode() == spv::Op::OpLoopMerge) {
91 return merge;
92 }
93 }
94 return nullptr;
95 }
96
KillAllInsts(bool killLabel)97 void BasicBlock::KillAllInsts(bool killLabel) {
98 ForEachInst([killLabel](Instruction* ip) {
99 if (killLabel || ip->opcode() != spv::Op::OpLabel) {
100 ip->context()->KillInst(ip);
101 }
102 });
103 }
104
ForEachSuccessorLabel(const std::function<void (const uint32_t)> & f) const105 void BasicBlock::ForEachSuccessorLabel(
106 const std::function<void(const uint32_t)>& f) const {
107 WhileEachSuccessorLabel([f](const uint32_t l) {
108 f(l);
109 return true;
110 });
111 }
112
WhileEachSuccessorLabel(const std::function<bool (const uint32_t)> & f) const113 bool BasicBlock::WhileEachSuccessorLabel(
114 const std::function<bool(const uint32_t)>& f) const {
115 const auto br = &insts_.back();
116 switch (br->opcode()) {
117 case spv::Op::OpBranch:
118 return f(br->GetOperand(0).words[0]);
119 case spv::Op::OpBranchConditional:
120 case spv::Op::OpSwitch: {
121 bool is_first = true;
122 return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
123 if (!is_first) return f(*idp);
124 is_first = false;
125 return true;
126 });
127 }
128 default:
129 return true;
130 }
131 }
132
ForEachSuccessorLabel(const std::function<void (uint32_t *)> & f)133 void BasicBlock::ForEachSuccessorLabel(
134 const std::function<void(uint32_t*)>& f) {
135 auto br = &insts_.back();
136 switch (br->opcode()) {
137 case spv::Op::OpBranch: {
138 uint32_t tmp_id = br->GetOperand(0).words[0];
139 f(&tmp_id);
140 if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id});
141 } break;
142 case spv::Op::OpBranchConditional:
143 case spv::Op::OpSwitch: {
144 bool is_first = true;
145 br->ForEachInId([&is_first, &f](uint32_t* idp) {
146 if (!is_first) f(idp);
147 is_first = false;
148 });
149 } break;
150 default:
151 break;
152 }
153 }
154
IsSuccessor(const BasicBlock * block) const155 bool BasicBlock::IsSuccessor(const BasicBlock* block) const {
156 uint32_t succId = block->id();
157 bool isSuccessor = false;
158 ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) {
159 if (label == succId) isSuccessor = true;
160 });
161 return isSuccessor;
162 }
163
ForMergeAndContinueLabel(const std::function<void (const uint32_t)> & f)164 void BasicBlock::ForMergeAndContinueLabel(
165 const std::function<void(const uint32_t)>& f) {
166 auto ii = insts_.end();
167 --ii;
168 if (ii == insts_.begin()) return;
169 --ii;
170 if (ii->opcode() == spv::Op::OpSelectionMerge ||
171 ii->opcode() == spv::Op::OpLoopMerge) {
172 ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); });
173 }
174 }
175
MergeBlockIdIfAny() const176 uint32_t BasicBlock::MergeBlockIdIfAny() const {
177 auto merge_ii = cend();
178 --merge_ii;
179 uint32_t mbid = 0;
180 if (merge_ii != cbegin()) {
181 --merge_ii;
182 if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
183 mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
184 } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) {
185 mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
186 }
187 }
188
189 return mbid;
190 }
191
MergeBlockId() const192 uint32_t BasicBlock::MergeBlockId() const {
193 uint32_t mbid = MergeBlockIdIfAny();
194 assert(mbid && "Expected block to have a corresponding merge block");
195 return mbid;
196 }
197
ContinueBlockIdIfAny() const198 uint32_t BasicBlock::ContinueBlockIdIfAny() const {
199 auto merge_ii = cend();
200 --merge_ii;
201 uint32_t cbid = 0;
202 if (merge_ii != cbegin()) {
203 --merge_ii;
204 if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
205 cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
206 }
207 }
208 return cbid;
209 }
210
ContinueBlockId() const211 uint32_t BasicBlock::ContinueBlockId() const {
212 uint32_t cbid = ContinueBlockIdIfAny();
213 assert(cbid && "Expected block to have a corresponding continue target");
214 return cbid;
215 }
216
operator <<(std::ostream & str,const BasicBlock & block)217 std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
218 str << block.PrettyPrint();
219 return str;
220 }
221
Dump() const222 void BasicBlock::Dump() const {
223 std::cerr << "Basic block #" << id() << "\n" << *this << "\n ";
224 }
225
PrettyPrint(uint32_t options) const226 std::string BasicBlock::PrettyPrint(uint32_t options) const {
227 std::ostringstream str;
228 ForEachInst([&str, options](const Instruction* inst) {
229 str << inst->PrettyPrint(options);
230 if (!spvOpcodeIsBlockTerminator(inst->opcode())) {
231 str << std::endl;
232 }
233 });
234 return str.str();
235 }
236
SplitBasicBlock(IRContext * context,uint32_t label_id,iterator iter)237 BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id,
238 iterator iter) {
239 assert(!insts_.empty());
240
241 std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>(
242 MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id,
243 std::initializer_list<Operand>{}));
244 BasicBlock* new_block = new_block_temp.get();
245 function_->InsertBasicBlockAfter(std::move(new_block_temp), this);
246
247 new_block->insts_.Splice(new_block->end(), &insts_, iter, end());
248 assert(new_block->GetParent() == GetParent() &&
249 "The parent should already be set appropriately.");
250
251 context->AnalyzeDefUse(new_block->GetLabelInst());
252
253 // Update the phi nodes in the successor blocks to reference the new block id.
254 const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel(
255 [new_block, this, context](const uint32_t label) {
256 BasicBlock* target_bb = context->get_instr_block(label);
257 target_bb->ForEachPhiInst(
258 [this, new_block, context](Instruction* phi_inst) {
259 bool changed = false;
260 for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) {
261 if (phi_inst->GetSingleWordInOperand(i) == this->id()) {
262 changed = true;
263 phi_inst->SetInOperand(i, {new_block->id()});
264 }
265 }
266
267 if (changed) {
268 context->UpdateDefUse(phi_inst);
269 }
270 });
271 });
272
273 if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
274 context->set_instr_block(new_block->GetLabelInst(), new_block);
275 new_block->ForEachInst([new_block, context](Instruction* inst) {
276 context->set_instr_block(inst, new_block);
277 });
278 }
279
280 return new_block;
281 }
282
283 } // namespace opt
284 } // namespace spvtools
285