• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.ir.code;
5 
6 import com.android.tools.r8.errors.CompilationError;
7 import com.android.tools.r8.graph.DebugLocalInfo;
8 import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
9 import com.android.tools.r8.ir.conversion.IRBuilder;
10 import com.android.tools.r8.utils.CfgPrinter;
11 import com.android.tools.r8.utils.ListUtils;
12 import com.android.tools.r8.utils.StringUtils;
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 
19 public class Phi extends Value {
20 
21   private final BasicBlock block;
22   private final List<Value> operands = new ArrayList<>();
23 
24   // Trivial phis are eliminated during IR construction. When a trivial phi is eliminated
25   // we need to update all references to it. A phi can be referenced from phis, instructions
26   // and current definition mappings. This list contains the current definitions mappings that
27   // contain this phi.
28   private List<Map<Integer, Value>> definitionUsers = new ArrayList<>();
29 
30   // The computed out type is not always the same as 'this.type' because of the type
31   // confusion around null and constant zero. The null object can be used in a single
32   // context (if tests) and the single 0 can be used as null. A phi can therefore
33   // have either of the creation types 'single' and 'object' depending on the use that
34   // triggered the creation of the phi. We therefore have to delay the output type
35   // computation of the phi until all operands are known.
36   private MoveType outType = null;
37 
Phi(int number, BasicBlock block, MoveType type, DebugLocalInfo local)38   public Phi(int number, BasicBlock block, MoveType type, DebugLocalInfo local) {
39     super(number, type, local == null ? null : new DebugInfo(local, null));
40     this.block = block;
41     block.addPhi(this);
42   }
43 
44   @Override
isPhi()45   public boolean isPhi() {
46     return true;
47   }
48 
49   @Override
asPhi()50   public Phi asPhi() {
51     return this;
52   }
53 
getBlock()54   public BasicBlock getBlock() {
55     return block;
56   }
57 
addOperands(IRBuilder builder, int register)58   public void addOperands(IRBuilder builder, int register) {
59     // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
60     // period of time to break cycles. When the cycle has been resolved they are completed
61     // exactly once by adding the operands.
62     assert operands.isEmpty();
63     boolean canBeNull = false;
64     if (block.getPredecessors().size() == 0) {
65       throwUndefinedValueError();
66     }
67     for (BasicBlock pred : block.getPredecessors()) {
68       EdgeType edgeType = pred.getEdgeType(block);
69       // Since this read has been delayed we must provide the local info for the value.
70       Value operand = builder.readRegister(register, pred, edgeType, type, getLocalInfo());
71       canBeNull |= operand.canBeNull();
72       appendOperand(operand);
73     }
74     if (!canBeNull) {
75       markNeverNull();
76     }
77     removeTrivialPhi();
78   }
79 
addOperands(List<Value> operands)80   public void addOperands(List<Value> operands) {
81     // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
82     // period of time to break cycles. When the cycle has been resolved they are completed
83     // exactly once by adding the operands.
84     assert this.operands.isEmpty();
85     boolean canBeNull = false;
86     if (operands.size() == 0) {
87       throwUndefinedValueError();
88     }
89     for (Value operand : operands) {
90       canBeNull |= operand.canBeNull();
91       appendOperand(operand);
92     }
93     if (!canBeNull) {
94       markNeverNull();
95     }
96     removeTrivialPhi();
97   }
98 
throwUndefinedValueError()99   private void throwUndefinedValueError() {
100     throw new CompilationError(
101         "Undefined value encountered during compilation. "
102             + "This is typically caused by invalid dex input that uses a register "
103             + "that is not define on all control-flow paths leading to the use.");
104   }
105 
appendOperand(Value operand)106   private void appendOperand(Value operand) {
107     operands.add(operand);
108     operand.addPhiUser(this);
109   }
110 
getOperand(int predIndex)111   public Value getOperand(int predIndex) {
112     return operands.get(predIndex);
113   }
114 
getOperands()115   public List<Value> getOperands() {
116     return operands;
117   }
118 
removeOperand(int index)119   public void removeOperand(int index) {
120     operands.get(index).removePhiUser(this);
121     operands.remove(index);
122   }
123 
removeOperandsByIndex(List<Integer> operandsToRemove)124   public void removeOperandsByIndex(List<Integer> operandsToRemove) {
125     if (operandsToRemove.isEmpty()) {
126       return;
127     }
128     List<Value> copy = new ArrayList<>(operands);
129     operands.clear();
130     int current = 0;
131     for (int i : operandsToRemove) {
132       operands.addAll(copy.subList(current, i));
133       copy.get(i).removePhiUser(this);
134       current = i + 1;
135     }
136     operands.addAll(copy.subList(current, copy.size()));
137   }
138 
replace(int predIndex, Value newValue)139   public void replace(int predIndex, Value newValue) {
140     Value current = operands.get(predIndex);
141     operands.set(predIndex, newValue);
142     newValue.addPhiUser(this);
143     current.removePhiUser(this);
144   }
145 
146   // Removing the phi user from the current value leads to concurrent modification errors
147   // during trivial phi elimination. It is safe to not remove the phi user from current
148   // since current will be unreachable after trivial phi elimination.
149   // TODO(ager): can we unify the these replace methods and avoid the concurrent modification
150   // issue?
replaceTrivialPhi(Value current, Value newValue)151   private void replaceTrivialPhi(Value current, Value newValue) {
152     for (int i = 0; i < operands.size(); i++) {
153       if (operands.get(i) == current) {
154         operands.set(i, newValue);
155         newValue.addPhiUser(this);
156       }
157     }
158   }
159 
isTrivialPhi()160   public boolean isTrivialPhi() {
161     Value same = null;
162     for (Value op : operands) {
163       if (op == same || op == this) {
164         // Have only seen one value other than this.
165         continue;
166       }
167       if (same != null) {
168         // Merged at least two values and is therefore not trivial.
169         return false;
170       }
171       same = op;
172     }
173     return true;
174   }
175 
removeTrivialPhi()176   public void removeTrivialPhi() {
177     Value same = null;
178     for (Value op : operands) {
179       if (op == same || op == this) {
180         // Have only seen one value other than this.
181         continue;
182       }
183       if (same != null) {
184         // Merged at least two values and is therefore not trivial.
185         assert !isTrivialPhi();
186         return;
187       }
188       same = op;
189     }
190     assert isTrivialPhi();
191     // Removing this phi, so get rid of it as a phi user from all of the operands to avoid
192     // recursively getting back here with the same phi. If the phi has itself as an operand
193     // that also removes the self-reference.
194     for (Value op : operands) {
195       op.removePhiUser(this);
196     }
197     // Replace this phi with the unique value in all users.
198     for (Instruction user : uniqueUsers()) {
199       user.replaceValue(this, same);
200     }
201     for (Phi user : uniquePhiUsers()) {
202       user.replaceTrivialPhi(this, same);
203     }
204     if (debugUsers() != null) {
205       for (Instruction user : debugUsers()) {
206         user.replaceDebugPhi(this, same);
207       }
208     }
209     // If IR construction is taking place, update the definition users.
210     if (definitionUsers != null) {
211       for (Map<Integer, Value> user : definitionUsers) {
212         for (Map.Entry<Integer, Value> entry : user.entrySet()) {
213           if (entry.getValue() == this) {
214             entry.setValue(same);
215             if (same.isPhi()) {
216               same.asPhi().addDefinitionsUser(user);
217             }
218           }
219         }
220       }
221     }
222     // Try to simplify phi users that might now have become trivial.
223     for (Phi user : uniquePhiUsers()) {
224       user.removeTrivialPhi();
225     }
226     // Get rid of the phi itself.
227     block.removePhi(this);
228   }
229 
printPhi()230   public String printPhi() {
231     StringBuilder builder = new StringBuilder();
232     builder.append("v");
233     builder.append(number);
234     builder.append(" <- phi");
235     StringUtils.append(builder, ListUtils.map(operands, (Value operand) -> "v" + operand.number));
236     return builder.toString();
237   }
238 
print(CfgPrinter printer)239   public void print(CfgPrinter printer) {
240     int uses = numberOfPhiUsers() + numberOfUsers();
241     printer
242         .print("0 ")                 // bci
243         .append(uses)                // use
244         .append(" v").append(number) // tid
245         .append(" Phi");
246     for (Value operand : operands) {
247       printer.append(" v").append(operand.number);
248     }
249   }
250 
addDefinitionsUser(Map<Integer, Value> currentDefinitions)251   public void addDefinitionsUser(Map<Integer, Value> currentDefinitions) {
252     definitionUsers.add(currentDefinitions);
253   }
254 
removeDefinitionsUser(Map<Integer, Value> currentDefinitions)255   public void removeDefinitionsUser(Map<Integer, Value> currentDefinitions) {
256     definitionUsers.remove(currentDefinitions);
257   }
258 
clearDefinitionsUsers()259   public void clearDefinitionsUsers() {
260     definitionUsers = null;
261   }
262 
isSingleConstZero(Value value)263   private boolean isSingleConstZero(Value value) {
264     return value.definition != null && value.definition.isConstNumber() &&
265         value.definition.asConstNumber().isZero() &&
266         value.outType() == MoveType.SINGLE;
267   }
268 
computeOutType(Set<Phi> active)269   private MoveType computeOutType(Set<Phi> active) {
270     if (outType != null) {
271       return outType;
272     }
273     active.add(this);
274     // Go through non-phi operands first to determine if we have an operand that dictates the type.
275     for (Value operand : operands) {
276       // Since a constant zero can be either an integer or an Object (null) we skip them
277       // when computing types and rely on other operands to specify the actual type.
278       if (!operand.isPhi() && !isSingleConstZero(operand)) {
279         return operand.outType();
280       }
281     }
282     // We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
283     for (Value operand : operands) {
284       if (operand.isPhi() && !active.contains(operand)) {
285         MoveType phiType = operand.asPhi().computeOutType(active);
286         // TODO(zerny): If we had a CONST_ZERO type element, we could often avoid going through
287         // all phis. We would only have to recurse until we got a non CONST_ZERO out type.
288         if (phiType != MoveType.SINGLE) {
289           return phiType;
290         }
291       }
292     }
293     // All operands were the constant zero or phis with out type SINGLE and the out type is either
294     // object or single depending on the use. Since all inputs have out type SINGLE it is safe to
295     // return MoveType.SINGLE here.
296     assert type == MoveType.SINGLE || type == MoveType.OBJECT;
297     return MoveType.SINGLE;
298   }
299 
300   @Override
outType()301   public MoveType outType() {
302     if (outType != null) {
303       return outType;
304     }
305     return computeOutType(new HashSet<>());
306   }
307 
308   @Override
isConstant()309   public boolean isConstant() {
310     return false;
311   }
312 
needsRegister()313   public boolean needsRegister() {
314     return true;
315   }
316 }
317