• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC
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/if_conversion.h"
16 
17 #include <memory>
18 #include <vector>
19 
20 #include "source/opt/value_number_table.h"
21 
22 namespace spvtools {
23 namespace opt {
24 
Process()25 Pass::Status IfConversion::Process() {
26   if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
27     return Status::SuccessWithoutChange;
28   }
29 
30   const ValueNumberTable& vn_table = *context()->GetValueNumberTable();
31   bool modified = false;
32   std::vector<Instruction*> to_kill;
33   for (auto& func : *get_module()) {
34     DominatorAnalysis* dominators = context()->GetDominatorAnalysis(&func);
35     for (auto& block : func) {
36       // Check if it is possible for |block| to have phis that can be
37       // transformed.
38       BasicBlock* common = nullptr;
39       if (!CheckBlock(&block, dominators, &common)) continue;
40 
41       // Get an insertion point.
42       auto iter = block.begin();
43       while (iter != block.end() && iter->opcode() == spv::Op::OpPhi) {
44         ++iter;
45       }
46 
47       InstructionBuilder builder(
48           context(), &*iter,
49           IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
50       block.ForEachPhiInst([this, &builder, &modified, &common, &to_kill,
51                             dominators, &block, &vn_table](Instruction* phi) {
52         // This phi is not compatible, but subsequent phis might be.
53         if (!CheckType(phi->type_id())) return;
54 
55         // We cannot transform cases where the phi is used by another phi in the
56         // same block due to instruction ordering restrictions.
57         // TODO(alan-baker): If all inappropriate uses could also be
58         // transformed, we could still remove this phi.
59         if (!CheckPhiUsers(phi, &block)) return;
60 
61         // Identify the incoming values associated with the true and false
62         // branches. If |then_block| dominates |inc0| or if the true edge
63         // branches straight to this block and |common| is |inc0|, then |inc0|
64         // is on the true branch. Otherwise the |inc1| is on the true branch.
65         BasicBlock* inc0 = GetIncomingBlock(phi, 0u);
66         Instruction* branch = common->terminator();
67         uint32_t condition = branch->GetSingleWordInOperand(0u);
68         BasicBlock* then_block = GetBlock(branch->GetSingleWordInOperand(1u));
69         Instruction* true_value = nullptr;
70         Instruction* false_value = nullptr;
71         if ((then_block == &block && inc0 == common) ||
72             dominators->Dominates(then_block, inc0)) {
73           true_value = GetIncomingValue(phi, 0u);
74           false_value = GetIncomingValue(phi, 1u);
75         } else {
76           true_value = GetIncomingValue(phi, 1u);
77           false_value = GetIncomingValue(phi, 0u);
78         }
79 
80         BasicBlock* true_def_block = context()->get_instr_block(true_value);
81         BasicBlock* false_def_block = context()->get_instr_block(false_value);
82 
83         uint32_t true_vn = vn_table.GetValueNumber(true_value);
84         uint32_t false_vn = vn_table.GetValueNumber(false_value);
85         if (true_vn != 0 && true_vn == false_vn) {
86           Instruction* inst_to_use = nullptr;
87 
88           // Try to pick an instruction that is not in a side node.  If we can't
89           // pick either the true for false branch as long as they can be
90           // legally moved.
91           if (!true_def_block ||
92               dominators->Dominates(true_def_block, &block)) {
93             inst_to_use = true_value;
94           } else if (!false_def_block ||
95                      dominators->Dominates(false_def_block, &block)) {
96             inst_to_use = false_value;
97           } else if (CanHoistInstruction(true_value, common, dominators)) {
98             inst_to_use = true_value;
99           } else if (CanHoistInstruction(false_value, common, dominators)) {
100             inst_to_use = false_value;
101           }
102 
103           if (inst_to_use != nullptr) {
104             modified = true;
105             HoistInstruction(inst_to_use, common, dominators);
106             context()->KillNamesAndDecorates(phi);
107             context()->ReplaceAllUsesWith(phi->result_id(),
108                                           inst_to_use->result_id());
109           }
110           return;
111         }
112 
113         // If either incoming value is defined in a block that does not dominate
114         // this phi, then we cannot eliminate the phi with a select.
115         // TODO(alan-baker): Perform code motion where it makes sense to enable
116         // the transform in this case.
117         if (true_def_block && !dominators->Dominates(true_def_block, &block))
118           return;
119 
120         if (false_def_block && !dominators->Dominates(false_def_block, &block))
121           return;
122 
123         analysis::Type* data_ty =
124             context()->get_type_mgr()->GetType(true_value->type_id());
125         if (analysis::Vector* vec_data_ty = data_ty->AsVector()) {
126           condition = SplatCondition(vec_data_ty, condition, &builder);
127         }
128 
129         Instruction* select = builder.AddSelect(phi->type_id(), condition,
130                                                 true_value->result_id(),
131                                                 false_value->result_id());
132         context()->get_def_use_mgr()->AnalyzeInstDefUse(select);
133         select->UpdateDebugInfoFrom(phi);
134         context()->ReplaceAllUsesWith(phi->result_id(), select->result_id());
135         to_kill.push_back(phi);
136         modified = true;
137 
138         return;
139       });
140     }
141   }
142 
143   for (auto inst : to_kill) {
144     context()->KillInst(inst);
145   }
146 
147   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
148 }
149 
CheckBlock(BasicBlock * block,DominatorAnalysis * dominators,BasicBlock ** common)150 bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators,
151                               BasicBlock** common) {
152   const std::vector<uint32_t>& preds = cfg()->preds(block->id());
153 
154   // TODO(alan-baker): Extend to more than two predecessors
155   if (preds.size() != 2) return false;
156 
157   BasicBlock* inc0 = context()->get_instr_block(preds[0]);
158   if (dominators->Dominates(block, inc0)) return false;
159 
160   BasicBlock* inc1 = context()->get_instr_block(preds[1]);
161   if (dominators->Dominates(block, inc1)) return false;
162 
163   if (inc0 == inc1) {
164     // If the predecessor blocks are the same, then there is only 1 value for
165     // the OpPhi.  Other transformation should be able to simplify that.
166     return false;
167   }
168   // All phis will have the same common dominator, so cache the result
169   // for this block. If there is no common dominator, then we cannot transform
170   // any phi in this basic block.
171   *common = dominators->CommonDominator(inc0, inc1);
172   if (!*common || cfg()->IsPseudoEntryBlock(*common)) return false;
173   Instruction* branch = (*common)->terminator();
174   if (branch->opcode() != spv::Op::OpBranchConditional) return false;
175   auto merge = (*common)->GetMergeInst();
176   if (!merge || merge->opcode() != spv::Op::OpSelectionMerge) return false;
177   if (spv::SelectionControlMask(merge->GetSingleWordInOperand(1)) ==
178       spv::SelectionControlMask::DontFlatten) {
179     return false;
180   }
181   if ((*common)->MergeBlockIdIfAny() != block->id()) return false;
182 
183   return true;
184 }
185 
CheckPhiUsers(Instruction * phi,BasicBlock * block)186 bool IfConversion::CheckPhiUsers(Instruction* phi, BasicBlock* block) {
187   return get_def_use_mgr()->WhileEachUser(
188       phi, [block, this](Instruction* user) {
189         if (user->opcode() == spv::Op::OpPhi &&
190             context()->get_instr_block(user) == block)
191           return false;
192         return true;
193       });
194 }
195 
SplatCondition(analysis::Vector * vec_data_ty,uint32_t cond,InstructionBuilder * builder)196 uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty,
197                                       uint32_t cond,
198                                       InstructionBuilder* builder) {
199   // If the data inputs to OpSelect are vectors, the condition for
200   // OpSelect must be a boolean vector with the same number of
201   // components. So splat the condition for the branch into a vector
202   // type.
203   analysis::Bool bool_ty;
204   analysis::Vector bool_vec_ty(&bool_ty, vec_data_ty->element_count());
205   uint32_t bool_vec_id =
206       context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty);
207   std::vector<uint32_t> ids(vec_data_ty->element_count(), cond);
208   return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id();
209 }
210 
CheckType(uint32_t id)211 bool IfConversion::CheckType(uint32_t id) {
212   Instruction* type = get_def_use_mgr()->GetDef(id);
213   spv::Op op = type->opcode();
214   if (spvOpcodeIsScalarType(op) || op == spv::Op::OpTypePointer ||
215       op == spv::Op::OpTypeVector)
216     return true;
217   return false;
218 }
219 
GetBlock(uint32_t id)220 BasicBlock* IfConversion::GetBlock(uint32_t id) {
221   return context()->get_instr_block(get_def_use_mgr()->GetDef(id));
222 }
223 
GetIncomingBlock(Instruction * phi,uint32_t predecessor)224 BasicBlock* IfConversion::GetIncomingBlock(Instruction* phi,
225                                            uint32_t predecessor) {
226   uint32_t in_index = 2 * predecessor + 1;
227   return GetBlock(phi->GetSingleWordInOperand(in_index));
228 }
229 
GetIncomingValue(Instruction * phi,uint32_t predecessor)230 Instruction* IfConversion::GetIncomingValue(Instruction* phi,
231                                             uint32_t predecessor) {
232   uint32_t in_index = 2 * predecessor;
233   return get_def_use_mgr()->GetDef(phi->GetSingleWordInOperand(in_index));
234 }
235 
HoistInstruction(Instruction * inst,BasicBlock * target_block,DominatorAnalysis * dominators)236 void IfConversion::HoistInstruction(Instruction* inst, BasicBlock* target_block,
237                                     DominatorAnalysis* dominators) {
238   BasicBlock* inst_block = context()->get_instr_block(inst);
239   if (!inst_block) {
240     // This is in the header, and dominates everything.
241     return;
242   }
243 
244   if (dominators->Dominates(inst_block, target_block)) {
245     // Already in position.  No work to do.
246     return;
247   }
248 
249   assert(inst->IsOpcodeCodeMotionSafe() &&
250          "Trying to move an instruction that is not safe to move.");
251 
252   // First hoist all instructions it depends on.
253   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
254   inst->ForEachInId(
255       [this, target_block, def_use_mgr, dominators](uint32_t* id) {
256         Instruction* operand_inst = def_use_mgr->GetDef(*id);
257         HoistInstruction(operand_inst, target_block, dominators);
258       });
259 
260   Instruction* insertion_pos = target_block->terminator();
261   if ((insertion_pos)->PreviousNode()->opcode() == spv::Op::OpSelectionMerge) {
262     insertion_pos = insertion_pos->PreviousNode();
263   }
264   inst->RemoveFromList();
265   insertion_pos->InsertBefore(std::unique_ptr<Instruction>(inst));
266   context()->set_instr_block(inst, target_block);
267 }
268 
CanHoistInstruction(Instruction * inst,BasicBlock * target_block,DominatorAnalysis * dominators)269 bool IfConversion::CanHoistInstruction(Instruction* inst,
270                                        BasicBlock* target_block,
271                                        DominatorAnalysis* dominators) {
272   BasicBlock* inst_block = context()->get_instr_block(inst);
273   if (!inst_block) {
274     // This is in the header, and dominates everything.
275     return true;
276   }
277 
278   if (dominators->Dominates(inst_block, target_block)) {
279     // Already in position.  No work to do.
280     return true;
281   }
282 
283   if (!inst->IsOpcodeCodeMotionSafe()) {
284     return false;
285   }
286 
287   // Check all instruction |inst| depends on.
288   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
289   return inst->WhileEachInId(
290       [this, target_block, def_use_mgr, dominators](uint32_t* id) {
291         Instruction* operand_inst = def_use_mgr->GetDef(*id);
292         return CanHoistInstruction(operand_inst, target_block, dominators);
293       });
294 }
295 
296 }  // namespace opt
297 }  // namespace spvtools
298