• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 The Khronos Group Inc.
2 // Copyright (c) 2017 Valve Corporation
3 // Copyright (c) 2017 LunarG Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "dead_branch_elim_pass.h"
18 
19 #include "cfa.h"
20 #include "iterator.h"
21 
22 namespace spvtools {
23 namespace opt {
24 
25 namespace {
26 
27 const uint32_t kEntryPointFunctionIdInIdx = 1;
28 const uint32_t kBranchCondConditionalIdInIdx = 0;
29 const uint32_t kBranchCondTrueLabIdInIdx = 1;
30 const uint32_t kBranchCondFalseLabIdInIdx = 2;
31 const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
32 const uint32_t kPhiVal0IdInIdx = 0;
33 const uint32_t kPhiLab0IdInIdx = 1;
34 const uint32_t kPhiVal1IdInIdx = 2;
35 const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
36 const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
37 
38 } // anonymous namespace
39 
MergeBlockIdIfAny(const ir::BasicBlock & blk,uint32_t * cbid) const40 uint32_t DeadBranchElimPass::MergeBlockIdIfAny(
41     const ir::BasicBlock& blk, uint32_t* cbid) const {
42   auto merge_ii = blk.cend();
43   --merge_ii;
44   uint32_t mbid = 0;
45   *cbid = 0;
46   if (merge_ii != blk.cbegin()) {
47     --merge_ii;
48     if (merge_ii->opcode() == SpvOpLoopMerge) {
49       mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
50       *cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
51     }
52     else if (merge_ii->opcode() == SpvOpSelectionMerge) {
53       mbid = merge_ii->GetSingleWordInOperand(
54           kSelectionMergeMergeBlockIdInIdx);
55     }
56   }
57   return mbid;
58 }
59 
ComputeStructuredSuccessors(ir::Function * func)60 void DeadBranchElimPass::ComputeStructuredSuccessors(ir::Function* func) {
61   // If header, make merge block first successor. If a loop header, make
62   // the second successor the continue target.
63   for (auto& blk : *func) {
64     uint32_t cbid;
65     uint32_t mbid = MergeBlockIdIfAny(blk, &cbid);
66     if (mbid != 0) {
67       block2structured_succs_[&blk].push_back(id2block_[mbid]);
68       if (cbid != 0)
69         block2structured_succs_[&blk].push_back(id2block_[cbid]);
70     }
71     // add true successors
72     blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) {
73       block2structured_succs_[&blk].push_back(id2block_[sbid]);
74     });
75   }
76 }
77 
ComputeStructuredOrder(ir::Function * func,std::list<ir::BasicBlock * > * order)78 void DeadBranchElimPass::ComputeStructuredOrder(
79     ir::Function* func, std::list<ir::BasicBlock*>* order) {
80   // Compute structured successors and do DFS
81   ComputeStructuredSuccessors(func);
82   auto ignore_block = [](cbb_ptr) {};
83   auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
84   auto get_structured_successors = [this](const ir::BasicBlock* block) {
85       return &(block2structured_succs_[block]); };
86   // TODO(greg-lunarg): Get rid of const_cast by making moving const
87   // out of the cfa.h prototypes and into the invoking code.
88   auto post_order = [&](cbb_ptr b) {
89       order->push_front(const_cast<ir::BasicBlock*>(b)); };
90 
91   spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
92       &*func->begin(), get_structured_successors, ignore_block, post_order,
93       ignore_edge);
94 }
95 
GetConstCondition(uint32_t condId,bool * condVal,bool * condIsConst)96 void DeadBranchElimPass::GetConstCondition(
97     uint32_t condId, bool* condVal, bool* condIsConst) {
98   ir::Instruction* cInst = def_use_mgr_->GetDef(condId);
99   switch (cInst->opcode()) {
100     case SpvOpConstantFalse: {
101       *condVal = false;
102       *condIsConst = true;
103     } break;
104     case SpvOpConstantTrue: {
105       *condVal = true;
106       *condIsConst = true;
107     } break;
108     case SpvOpLogicalNot: {
109       bool negVal;
110       (void)GetConstCondition(cInst->GetSingleWordInOperand(0),
111           &negVal, condIsConst);
112       if (*condIsConst)
113         *condVal = !negVal;
114     } break;
115     default: {
116       *condIsConst = false;
117     } break;
118   }
119 }
120 
AddBranch(uint32_t labelId,ir::BasicBlock * bp)121 void DeadBranchElimPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
122   std::unique_ptr<ir::Instruction> newBranch(
123     new ir::Instruction(SpvOpBranch, 0, 0,
124         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
125   def_use_mgr_->AnalyzeInstDefUse(&*newBranch);
126   bp->AddInstruction(std::move(newBranch));
127 }
128 
AddSelectionMerge(uint32_t labelId,ir::BasicBlock * bp)129 void DeadBranchElimPass::AddSelectionMerge(uint32_t labelId,
130     ir::BasicBlock* bp) {
131   std::unique_ptr<ir::Instruction> newMerge(
132     new ir::Instruction(SpvOpSelectionMerge, 0, 0,
133         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}},
134          {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0}}}));
135   def_use_mgr_->AnalyzeInstDefUse(&*newMerge);
136   bp->AddInstruction(std::move(newMerge));
137 }
138 
AddBranchConditional(uint32_t condId,uint32_t trueLabId,uint32_t falseLabId,ir::BasicBlock * bp)139 void DeadBranchElimPass::AddBranchConditional(uint32_t condId,
140     uint32_t trueLabId, uint32_t falseLabId, ir::BasicBlock* bp) {
141   std::unique_ptr<ir::Instruction> newBranchCond(
142     new ir::Instruction(SpvOpBranchConditional, 0, 0,
143         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {condId}},
144          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {trueLabId}},
145          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {falseLabId}}}));
146   def_use_mgr_->AnalyzeInstDefUse(&*newBranchCond);
147   bp->AddInstruction(std::move(newBranchCond));
148 }
149 
KillAllInsts(ir::BasicBlock * bp)150 void DeadBranchElimPass::KillAllInsts(ir::BasicBlock* bp) {
151   bp->ForEachInst([this](ir::Instruction* ip) {
152     def_use_mgr_->KillInst(ip);
153   });
154 }
155 
GetConstConditionalSelectionBranch(ir::BasicBlock * bp,ir::Instruction ** branchInst,ir::Instruction ** mergeInst,uint32_t * condId,bool * condVal)156 bool DeadBranchElimPass::GetConstConditionalSelectionBranch(ir::BasicBlock* bp,
157     ir::Instruction** branchInst, ir::Instruction** mergeInst,
158     uint32_t *condId, bool *condVal) {
159   auto ii = bp->end();
160   --ii;
161   *branchInst = &*ii;
162   if ((*branchInst)->opcode() != SpvOpBranchConditional)
163     return false;
164   if (ii == bp->begin())
165     return false;
166   --ii;
167   *mergeInst = &*ii;
168   if ((*mergeInst)->opcode() != SpvOpSelectionMerge)
169     return false;
170   bool condIsConst;
171   *condId = (*branchInst)->GetSingleWordInOperand(
172       kBranchCondConditionalIdInIdx);
173   (void) GetConstCondition(*condId, condVal, &condIsConst);
174   return condIsConst;
175 }
176 
HasNonPhiRef(uint32_t labelId)177 bool DeadBranchElimPass::HasNonPhiRef(uint32_t labelId) {
178   analysis::UseList* uses = def_use_mgr_->GetUses(labelId);
179   if (uses == nullptr)
180     return false;
181   for (auto u : *uses)
182     if (u.inst->opcode() != SpvOpPhi)
183       return true;
184   return false;
185 }
186 
EliminateDeadBranches(ir::Function * func)187 bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
188   // Traverse blocks in structured order
189   std::list<ir::BasicBlock*> structuredOrder;
190   ComputeStructuredOrder(func, &structuredOrder);
191   std::unordered_set<ir::BasicBlock*> elimBlocks;
192   bool modified = false;
193   for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
194     // Skip blocks that are already in the elimination set
195     if (elimBlocks.find(*bi) != elimBlocks.end())
196       continue;
197     // Skip blocks that don't have constant conditional branch preceded
198     // by OpSelectionMerge
199     ir::Instruction* br;
200     ir::Instruction* mergeInst;
201     uint32_t condId;
202     bool condVal;
203     if (!GetConstConditionalSelectionBranch(*bi, &br, &mergeInst, &condId,
204         &condVal))
205       continue;
206 
207     // Replace conditional branch with unconditional branch
208     const uint32_t trueLabId =
209         br->GetSingleWordInOperand(kBranchCondTrueLabIdInIdx);
210     const uint32_t falseLabId =
211         br->GetSingleWordInOperand(kBranchCondFalseLabIdInIdx);
212     const uint32_t mergeLabId =
213         mergeInst->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
214     const uint32_t liveLabId = condVal == true ? trueLabId : falseLabId;
215     const uint32_t deadLabId = condVal == true ? falseLabId : trueLabId;
216     AddBranch(liveLabId, *bi);
217     def_use_mgr_->KillInst(br);
218     def_use_mgr_->KillInst(mergeInst);
219 
220     // Iterate to merge block adding dead blocks to elimination set
221     auto dbi = bi;
222     ++dbi;
223     uint32_t dLabId = (*dbi)->id();
224     while (dLabId != mergeLabId) {
225       if (!HasNonPhiRef(dLabId)) {
226         // Kill use/def for all instructions and mark block for elimination
227         KillAllInsts(*dbi);
228         elimBlocks.insert(*dbi);
229       }
230       ++dbi;
231       dLabId = (*dbi)->id();
232     }
233 
234     // Process phi instructions in merge block.
235     // elimBlocks are now blocks which cannot precede merge block. Also,
236     // if eliminated branch is to merge label, remember the conditional block
237     // also cannot precede merge block.
238     uint32_t deadCondLabId = 0;
239     if (deadLabId == mergeLabId)
240       deadCondLabId = (*bi)->id();
241     (*dbi)->ForEachPhiInst([&elimBlocks, &deadCondLabId, this](
242         ir::Instruction* phiInst) {
243       const uint32_t phiLabId0 =
244           phiInst->GetSingleWordInOperand(kPhiLab0IdInIdx);
245       const bool useFirst =
246           elimBlocks.find(id2block_[phiLabId0]) == elimBlocks.end() &&
247           phiLabId0 != deadCondLabId;
248       const uint32_t phiValIdx =
249           useFirst ? kPhiVal0IdInIdx : kPhiVal1IdInIdx;
250       const uint32_t replId = phiInst->GetSingleWordInOperand(phiValIdx);
251       const uint32_t phiId = phiInst->result_id();
252       (void)def_use_mgr_->ReplaceAllUsesWith(phiId, replId);
253       def_use_mgr_->KillInst(phiInst);
254     });
255 
256     // If merge block has no predecessors, replace the new branch with
257     // a MergeSelection/BranchCondition using the original constant condition
258     // and the mergeblock as the false branch. This is done so the merge block
259     // is not orphaned, which could cause invalid control flow in certain case.
260     // TODO(greg-lunarg): Do this only in cases where invalid code is caused.
261     if (!HasNonPhiRef(mergeLabId)) {
262       auto eii = (*bi)->end();
263       --eii;
264       ir::Instruction* nbr = &*eii;
265       AddSelectionMerge(mergeLabId, *bi);
266       if (condVal == true)
267         AddBranchConditional(condId, liveLabId, mergeLabId, *bi);
268       else
269         AddBranchConditional(condId, mergeLabId, liveLabId, *bi);
270       def_use_mgr_->KillInst(nbr);
271     }
272     modified = true;
273   }
274 
275   // Erase dead blocks
276   for (auto ebi = func->begin(); ebi != func->end(); )
277     if (elimBlocks.find(&*ebi) != elimBlocks.end())
278       ebi = ebi.Erase();
279     else
280       ++ebi;
281   return modified;
282 }
283 
Initialize(ir::Module * module)284 void DeadBranchElimPass::Initialize(ir::Module* module) {
285 
286   module_ = module;
287 
288   // Initialize function and block maps
289   id2function_.clear();
290   id2block_.clear();
291   block2structured_succs_.clear();
292   for (auto& fn : *module_) {
293     // Initialize function and block maps.
294     id2function_[fn.result_id()] = &fn;
295     for (auto& blk : fn) {
296       id2block_[blk.id()] = &blk;
297     }
298   }
299 
300   def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
301 };
302 
ProcessImpl()303 Pass::Status DeadBranchElimPass::ProcessImpl() {
304   // Current functionality assumes structured control flow.
305   // TODO(greg-lunarg): Handle non-structured control-flow.
306   if (!module_->HasCapability(SpvCapabilityShader))
307     return Status::SuccessWithoutChange;
308 
309   bool modified = false;
310   for (const auto& e : module_->entry_points()) {
311     ir::Function* fn =
312         id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
313     modified = EliminateDeadBranches(fn) || modified;
314   }
315   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
316 }
317 
DeadBranchElimPass()318 DeadBranchElimPass::DeadBranchElimPass()
319     : module_(nullptr), def_use_mgr_(nullptr) {}
320 
Process(ir::Module * module)321 Pass::Status DeadBranchElimPass::Process(ir::Module* module) {
322   Initialize(module);
323   return ProcessImpl();
324 }
325 
326 }  // namespace opt
327 }  // namespace spvtools
328 
329