• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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