• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 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 // This file implements conditional constant propagation as described in
16 //
17 //      Constant propagation with conditional branches,
18 //      Wegman and Zadeck, ACM TOPLAS 13(2):181-210.
19 
20 #include "source/opt/ccp_pass.h"
21 
22 #include <algorithm>
23 #include <limits>
24 
25 #include "source/opt/fold.h"
26 #include "source/opt/function.h"
27 #include "source/opt/module.h"
28 #include "source/opt/propagator.h"
29 
30 namespace spvtools {
31 namespace opt {
32 
33 namespace {
34 
35 // This SSA id is never defined nor referenced in the IR.  It is a special ID
36 // which represents varying values.  When an ID is found to have a varying
37 // value, its entry in the |values_| table maps to kVaryingSSAId.
38 const uint32_t kVaryingSSAId = std::numeric_limits<uint32_t>::max();
39 
40 }  // namespace
41 
IsVaryingValue(uint32_t id) const42 bool CCPPass::IsVaryingValue(uint32_t id) const { return id == kVaryingSSAId; }
43 
MarkInstructionVarying(Instruction * instr)44 SSAPropagator::PropStatus CCPPass::MarkInstructionVarying(Instruction* instr) {
45   assert(instr->result_id() != 0 &&
46          "Instructions with no result cannot be marked varying.");
47   values_[instr->result_id()] = kVaryingSSAId;
48   return SSAPropagator::kVarying;
49 }
50 
VisitPhi(Instruction * phi)51 SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) {
52   uint32_t meet_val_id = 0;
53 
54   // Implement the lattice meet operation. The result of this Phi instruction is
55   // interesting only if the meet operation over arguments coming through
56   // executable edges yields the same constant value.
57   for (uint32_t i = 2; i < phi->NumOperands(); i += 2) {
58     if (!propagator_->IsPhiArgExecutable(phi, i)) {
59       // Ignore arguments coming through non-executable edges.
60       continue;
61     }
62     uint32_t phi_arg_id = phi->GetSingleWordOperand(i);
63     auto it = values_.find(phi_arg_id);
64     if (it != values_.end()) {
65       // We found an argument with a constant value.  Apply the meet operation
66       // with the previous arguments.
67       if (it->second == kVaryingSSAId) {
68         // The "constant" value is actually a placeholder for varying. Return
69         // varying for this phi.
70         return MarkInstructionVarying(phi);
71       } else if (meet_val_id == 0) {
72         // This is the first argument we find.  Initialize the result to its
73         // constant value id.
74         meet_val_id = it->second;
75       } else if (it->second == meet_val_id) {
76         // The argument is the same constant value already computed. Continue
77         // looking.
78         continue;
79       } else {
80         // We either found a varying value, or another constant value different
81         // from the previous computed meet value.  This Phi will never be
82         // constant.
83         return MarkInstructionVarying(phi);
84       }
85     } else {
86       // The incoming value has no recorded value and is therefore not
87       // interesting. A not interesting value joined with any other value is the
88       // other value.
89       continue;
90     }
91   }
92 
93   // If there are no incoming executable edges, the meet ID will still be 0. In
94   // that case, return not interesting to evaluate the Phi node again.
95   if (meet_val_id == 0) {
96     return SSAPropagator::kNotInteresting;
97   }
98 
99   // All the operands have the same constant value represented by |meet_val_id|.
100   // Set the Phi's result to that value and declare it interesting.
101   values_[phi->result_id()] = meet_val_id;
102   return SSAPropagator::kInteresting;
103 }
104 
ComputeLatticeMeet(Instruction * instr,uint32_t val2)105 uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) {
106   // Given two values val1 and val2, the meet operation in the constant
107   // lattice uses the following rules:
108   //
109   // meet(val1, UNDEFINED) = val1
110   // meet(val1, VARYING)   = VARYING
111   // meet(val1, val2)      = val1     if val1 == val2
112   // meet(val1, val2)      = VARYING  if val1 != val2
113   //
114   // When two different values meet, the result is always varying because CCP
115   // does not allow lateral transitions in the lattice.  This prevents
116   // infinite cycles during propagation.
117   auto val1_it = values_.find(instr->result_id());
118   if (val1_it == values_.end()) {
119     return val2;
120   }
121 
122   uint32_t val1 = val1_it->second;
123   if (IsVaryingValue(val1)) {
124     return val1;
125   } else if (IsVaryingValue(val2)) {
126     return val2;
127   } else if (val1 != val2) {
128     return kVaryingSSAId;
129   }
130   return val2;
131 }
132 
VisitAssignment(Instruction * instr)133 SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
134   assert(instr->result_id() != 0 &&
135          "Expecting an instruction that produces a result");
136 
137   // If this is a copy operation, and the RHS is a known constant, assign its
138   // value to the LHS.
139   if (instr->opcode() == SpvOpCopyObject) {
140     uint32_t rhs_id = instr->GetSingleWordInOperand(0);
141     auto it = values_.find(rhs_id);
142     if (it != values_.end()) {
143       if (IsVaryingValue(it->second)) {
144         return MarkInstructionVarying(instr);
145       } else {
146         uint32_t new_val = ComputeLatticeMeet(instr, it->second);
147         values_[instr->result_id()] = new_val;
148         return IsVaryingValue(new_val) ? SSAPropagator::kVarying
149                                        : SSAPropagator::kInteresting;
150       }
151     }
152     return SSAPropagator::kNotInteresting;
153   }
154 
155   // Instructions with a RHS that cannot produce a constant are always varying.
156   if (!instr->IsFoldable()) {
157     return MarkInstructionVarying(instr);
158   }
159 
160   // See if the RHS of the assignment folds into a constant value.
161   auto map_func = [this](uint32_t id) {
162     auto it = values_.find(id);
163     if (it == values_.end() || IsVaryingValue(it->second)) {
164       return id;
165     }
166     return it->second;
167   };
168   Instruction* folded_inst =
169       context()->get_instruction_folder().FoldInstructionToConstant(instr,
170                                                                     map_func);
171 
172   if (folded_inst != nullptr) {
173     // We do not want to change the body of the function by adding new
174     // instructions.  When folding we can only generate new constants.
175     assert((folded_inst->IsConstant() ||
176             IsSpecConstantInst(folded_inst->opcode())) &&
177            "CCP is only interested in constant values.");
178     uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id());
179     values_[instr->result_id()] = new_val;
180     return IsVaryingValue(new_val) ? SSAPropagator::kVarying
181                                    : SSAPropagator::kInteresting;
182   }
183 
184   // Conservatively mark this instruction as varying if any input id is varying.
185   if (!instr->WhileEachInId([this](uint32_t* op_id) {
186         auto iter = values_.find(*op_id);
187         if (iter != values_.end() && IsVaryingValue(iter->second)) return false;
188         return true;
189       })) {
190     return MarkInstructionVarying(instr);
191   }
192 
193   // If not, see if there is a least one unknown operand to the instruction.  If
194   // so, we might be able to fold it later.
195   if (!instr->WhileEachInId([this](uint32_t* op_id) {
196         auto it = values_.find(*op_id);
197         if (it == values_.end()) return false;
198         return true;
199       })) {
200     return SSAPropagator::kNotInteresting;
201   }
202 
203   // Otherwise, we will never be able to fold this instruction, so mark it
204   // varying.
205   return MarkInstructionVarying(instr);
206 }
207 
VisitBranch(Instruction * instr,BasicBlock ** dest_bb) const208 SSAPropagator::PropStatus CCPPass::VisitBranch(Instruction* instr,
209                                                BasicBlock** dest_bb) const {
210   assert(instr->IsBranch() && "Expected a branch instruction.");
211 
212   *dest_bb = nullptr;
213   uint32_t dest_label = 0;
214   if (instr->opcode() == SpvOpBranch) {
215     // An unconditional jump always goes to its unique destination.
216     dest_label = instr->GetSingleWordInOperand(0);
217   } else if (instr->opcode() == SpvOpBranchConditional) {
218     // For a conditional branch, determine whether the predicate selector has a
219     // known value in |values_|.  If it does, set the destination block
220     // according to the selector's boolean value.
221     uint32_t pred_id = instr->GetSingleWordOperand(0);
222     auto it = values_.find(pred_id);
223     if (it == values_.end() || IsVaryingValue(it->second)) {
224       // The predicate has an unknown value, either branch could be taken.
225       return SSAPropagator::kVarying;
226     }
227 
228     // Get the constant value for the predicate selector from the value table.
229     // Use it to decide which branch will be taken.
230     uint32_t pred_val_id = it->second;
231     const analysis::Constant* c = const_mgr_->FindDeclaredConstant(pred_val_id);
232     assert(c && "Expected to find a constant declaration for a known value.");
233     // Undef values should have returned as varying above.
234     assert(c->AsBoolConstant() || c->AsNullConstant());
235     if (c->AsNullConstant()) {
236       dest_label = instr->GetSingleWordOperand(2u);
237     } else {
238       const analysis::BoolConstant* val = c->AsBoolConstant();
239       dest_label = val->value() ? instr->GetSingleWordOperand(1)
240                                 : instr->GetSingleWordOperand(2);
241     }
242   } else {
243     // For an OpSwitch, extract the value taken by the switch selector and check
244     // which of the target literals it matches.  The branch associated with that
245     // literal is the taken branch.
246     assert(instr->opcode() == SpvOpSwitch);
247     if (instr->GetOperand(0).words.size() != 1) {
248       // If the selector is wider than 32-bits, return varying. TODO(dnovillo):
249       // Add support for wider constants.
250       return SSAPropagator::kVarying;
251     }
252     uint32_t select_id = instr->GetSingleWordOperand(0);
253     auto it = values_.find(select_id);
254     if (it == values_.end() || IsVaryingValue(it->second)) {
255       // The selector has an unknown value, any of the branches could be taken.
256       return SSAPropagator::kVarying;
257     }
258 
259     // Get the constant value for the selector from the value table. Use it to
260     // decide which branch will be taken.
261     uint32_t select_val_id = it->second;
262     const analysis::Constant* c =
263         const_mgr_->FindDeclaredConstant(select_val_id);
264     assert(c && "Expected to find a constant declaration for a known value.");
265     // TODO: support 64-bit integer switches.
266     uint32_t constant_cond = 0;
267     if (const analysis::IntConstant* val = c->AsIntConstant()) {
268       constant_cond = val->words()[0];
269     } else {
270       // Undef values should have returned varying above.
271       assert(c->AsNullConstant());
272       constant_cond = 0;
273     }
274 
275     // Start assuming that the selector will take the default value;
276     dest_label = instr->GetSingleWordOperand(1);
277     for (uint32_t i = 2; i < instr->NumOperands(); i += 2) {
278       if (constant_cond == instr->GetSingleWordOperand(i)) {
279         dest_label = instr->GetSingleWordOperand(i + 1);
280         break;
281       }
282     }
283   }
284 
285   assert(dest_label && "Destination label should be set at this point.");
286   *dest_bb = context()->cfg()->block(dest_label);
287   return SSAPropagator::kInteresting;
288 }
289 
VisitInstruction(Instruction * instr,BasicBlock ** dest_bb)290 SSAPropagator::PropStatus CCPPass::VisitInstruction(Instruction* instr,
291                                                     BasicBlock** dest_bb) {
292   *dest_bb = nullptr;
293   if (instr->opcode() == SpvOpPhi) {
294     return VisitPhi(instr);
295   } else if (instr->IsBranch()) {
296     return VisitBranch(instr, dest_bb);
297   } else if (instr->result_id()) {
298     return VisitAssignment(instr);
299   }
300   return SSAPropagator::kVarying;
301 }
302 
ReplaceValues()303 bool CCPPass::ReplaceValues() {
304   // Even if we make no changes to the function's IR, propagation may have
305   // created new constants.  Even if those constants cannot be replaced in
306   // the IR, the constant definition itself is a change.  To reflect this,
307   // we check whether the next ID to be given by the module is different than
308   // the original bound ID. If that happens, new instructions were added to the
309   // module during propagation.
310   //
311   // See https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 and
312   // https://github.com/KhronosGroup/SPIRV-Tools/issues/3991 for details.
313   bool changed_ir = (context()->module()->IdBound() > original_id_bound_);
314 
315   for (const auto& it : values_) {
316     uint32_t id = it.first;
317     uint32_t cst_id = it.second;
318     if (!IsVaryingValue(cst_id) && id != cst_id) {
319       context()->KillNamesAndDecorates(id);
320       changed_ir |= context()->ReplaceAllUsesWith(id, cst_id);
321     }
322   }
323 
324   return changed_ir;
325 }
326 
PropagateConstants(Function * fp)327 bool CCPPass::PropagateConstants(Function* fp) {
328   if (fp->IsDeclaration()) {
329     return false;
330   }
331 
332   // Mark function parameters as varying.
333   fp->ForEachParam([this](const Instruction* inst) {
334     values_[inst->result_id()] = kVaryingSSAId;
335   });
336 
337   const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) {
338     return VisitInstruction(instr, dest_bb);
339   };
340 
341   propagator_ =
342       std::unique_ptr<SSAPropagator>(new SSAPropagator(context(), visit_fn));
343 
344   if (propagator_->Run(fp)) {
345     return ReplaceValues();
346   }
347 
348   return false;
349 }
350 
Initialize()351 void CCPPass::Initialize() {
352   const_mgr_ = context()->get_constant_mgr();
353 
354   // Populate the constant table with values from constant declarations in the
355   // module.  The values of each OpConstant declaration is the identity
356   // assignment (i.e., each constant is its own value).
357   for (const auto& inst : get_module()->types_values()) {
358     // Record compile time constant ids. Treat all other global values as
359     // varying.
360     if (inst.IsConstant()) {
361       values_[inst.result_id()] = inst.result_id();
362     } else {
363       values_[inst.result_id()] = kVaryingSSAId;
364     }
365   }
366 
367   original_id_bound_ = context()->module()->IdBound();
368 }
369 
Process()370 Pass::Status CCPPass::Process() {
371   Initialize();
372 
373   // Process all entry point functions.
374   ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); };
375   bool modified = context()->ProcessReachableCallTree(pfn);
376   return modified ? Pass::Status::SuccessWithChange
377                   : Pass::Status::SuccessWithoutChange;
378 }
379 
380 }  // namespace opt
381 }  // namespace spvtools
382