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