• 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.dex.Constants;
7 import com.android.tools.r8.graph.DebugLocalInfo;
8 import com.android.tools.r8.ir.regalloc.LiveIntervals;
9 import com.android.tools.r8.utils.InternalOptions;
10 import com.android.tools.r8.utils.LongInterval;
11 import com.google.common.collect.ImmutableSet;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.LinkedList;
16 import java.util.List;
17 import java.util.Set;
18 
19 public class Value {
20 
21   /**
22    * Immutable view of the debug info associated with an SSA value.
23    *
24    * Used during IR building and to construct replacement values.
25    */
26   public static class DebugInfo {
27     private final DebugLocalInfo local;
28     private final Value previousLocalValue;
29 
DebugInfo(DebugLocalInfo local, Value previousLocalValue)30     public DebugInfo(DebugLocalInfo local, Value previousLocalValue) {
31       assert local != null;
32       this.local = local;
33       this.previousLocalValue = previousLocalValue;
34     }
35   }
36 
37   // Actual internal data for the debug information of locals.
38   // This is wrapped in a class to avoid multiple pointers in the value structure.
39   private static class DebugData {
40     final DebugLocalInfo local;
41     Value previousLocalValue;
42     Set<Instruction> debugUsers = new HashSet<>();
43     List<Instruction> localStarts = new ArrayList<>();
44     List<Instruction> localEnds = new ArrayList<>();
45 
DebugData(DebugInfo info)46     DebugData(DebugInfo info) {
47       this(info.local, info.previousLocalValue);
48     }
49 
DebugData(DebugLocalInfo local, Value previousLocalValue)50     DebugData(DebugLocalInfo local, Value previousLocalValue) {
51       assert previousLocalValue == null || !previousLocalValue.isUninitializedLocal();
52       this.local = local;
53       this.previousLocalValue = previousLocalValue;
54     }
55   }
56 
57   public static final Value UNDEFINED = new Value(-1, MoveType.OBJECT, null);
58 
59   protected final int number;
60   protected final MoveType type;
61   public Instruction definition = null;
62   private LinkedList<Instruction> users = new LinkedList<>();
63   private Set<Instruction> uniqueUsers = null;
64   private LinkedList<Phi> phiUsers = new LinkedList<>();
65   private Set<Phi> uniquePhiUsers = null;
66   private Value nextConsecutive = null;
67   private Value previousConsecutive = null;
68   private LiveIntervals liveIntervals;
69   private int needsRegister = -1;
70   private boolean neverNull = false;
71   private boolean isThis = false;
72   private boolean isArgument = false;
73   private LongInterval valueRange;
74   private final DebugData debugData;
75 
Value(int number, MoveType type, DebugInfo debugInfo)76   public Value(int number, MoveType type, DebugInfo debugInfo) {
77     this.number = number;
78     this.type = type;
79     this.debugData = debugInfo == null ? null : new DebugData(debugInfo);
80   }
81 
isFixedRegisterValue()82   public boolean isFixedRegisterValue() {
83     return false;
84   }
85 
asFixedRegisterValue()86   public FixedRegisterValue asFixedRegisterValue() {
87     return null;
88   }
89 
getNumber()90   public int getNumber() {
91     return number;
92   }
93 
requiredRegisters()94   public int requiredRegisters() {
95     return type.requiredRegisters();
96   }
97 
getDebugInfo()98   public DebugInfo getDebugInfo() {
99     return debugData == null ? null : new DebugInfo(debugData.local, debugData.previousLocalValue);
100   }
101 
getLocalInfo()102   public DebugLocalInfo getLocalInfo() {
103     return debugData == null ? null : debugData.local;
104   }
105 
getPreviousLocalValue()106   public Value getPreviousLocalValue() {
107     return debugData == null ? null : debugData.previousLocalValue;
108   }
109 
replacePreviousLocalValue(Value value)110   public void replacePreviousLocalValue(Value value) {
111     if (value == null || value.isUninitializedLocal()) {
112       debugData.previousLocalValue = null;
113     } else {
114       debugData.previousLocalValue = value;
115     }
116   }
117 
getDebugLocalStarts()118   public List<Instruction> getDebugLocalStarts() {
119     return debugData.localStarts;
120   }
121 
getDebugLocalEnds()122   public List<Instruction> getDebugLocalEnds() {
123     return debugData.localEnds;
124   }
125 
addDebugLocalStart(Instruction start)126   public void addDebugLocalStart(Instruction start) {
127     assert start != null;
128     debugData.localStarts.add(start);
129   }
130 
addDebugLocalEnd(Instruction end)131   public void addDebugLocalEnd(Instruction end) {
132     assert end != null;
133     debugData.localEnds.add(end);
134   }
135 
linkTo(Value other)136   public void linkTo(Value other) {
137     assert nextConsecutive == null || nextConsecutive == other;
138     assert other.previousConsecutive == null || other.previousConsecutive == this;
139     other.previousConsecutive = this;
140     nextConsecutive = other;
141   }
142 
replaceLink(Value newArgument)143   public void replaceLink(Value newArgument) {
144     assert isLinked();
145     if (previousConsecutive != null) {
146       previousConsecutive.nextConsecutive = newArgument;
147       newArgument.previousConsecutive = previousConsecutive;
148       previousConsecutive = null;
149     }
150     if (nextConsecutive != null) {
151       nextConsecutive.previousConsecutive = newArgument;
152       newArgument.nextConsecutive = nextConsecutive;
153       nextConsecutive = null;
154     }
155   }
156 
isLinked()157   public boolean isLinked() {
158     return nextConsecutive != null || previousConsecutive != null;
159   }
160 
getStartOfConsecutive()161   public Value getStartOfConsecutive() {
162     Value current = this;
163     while (current.getPreviousConsecutive() != null) {
164       current = current.getPreviousConsecutive();
165     }
166     return current;
167   }
168 
getNextConsecutive()169   public Value getNextConsecutive() {
170     return nextConsecutive;
171   }
172 
getPreviousConsecutive()173   public Value getPreviousConsecutive() {
174     return previousConsecutive;
175   }
176 
uniqueUsers()177   public Set<Instruction> uniqueUsers() {
178     if (uniqueUsers != null) {
179       return uniqueUsers;
180     }
181     return uniqueUsers = ImmutableSet.copyOf(users);
182   }
183 
uniquePhiUsers()184   public Set<Phi> uniquePhiUsers() {
185     if (uniquePhiUsers != null) {
186       return uniquePhiUsers;
187     }
188     return uniquePhiUsers = ImmutableSet.copyOf(phiUsers);
189   }
190 
debugUsers()191   public Set<Instruction> debugUsers() {
192     if (debugData == null) {
193       return null;
194     }
195     return Collections.unmodifiableSet(debugData.debugUsers);
196   }
197 
numberOfUsers()198   public int numberOfUsers() {
199     int size = users.size();
200     if (size <= 1) {
201       return size;
202     }
203     return uniqueUsers().size();
204   }
205 
numberOfPhiUsers()206   public int numberOfPhiUsers() {
207     int size = phiUsers.size();
208     if (size <= 1) {
209       return size;
210     }
211     return uniquePhiUsers().size();
212   }
213 
numberOfDebugUsers()214   public int numberOfDebugUsers() {
215     return debugData == null ? 0 : debugData.debugUsers.size();
216   }
217 
numberOfAllUsers()218   public int numberOfAllUsers() {
219     return numberOfUsers() + numberOfPhiUsers() + numberOfDebugUsers();
220   }
221 
addUser(Instruction user)222   public void addUser(Instruction user) {
223     users.add(user);
224     uniqueUsers = null;
225   }
226 
removeUser(Instruction user)227   public void removeUser(Instruction user) {
228     users.remove(user);
229     uniqueUsers = null;
230   }
231 
clearUsers()232   public void clearUsers() {
233     users.clear();
234     uniqueUsers = null;
235     phiUsers.clear();
236     uniquePhiUsers = null;
237     if (debugData != null) {
238       debugData.debugUsers.clear();
239     }
240   }
241 
addPhiUser(Phi user)242   public void addPhiUser(Phi user) {
243     phiUsers.add(user);
244     uniquePhiUsers = null;
245   }
246 
removePhiUser(Phi user)247   public void removePhiUser(Phi user) {
248     phiUsers.remove(user);
249     uniquePhiUsers = null;
250   }
251 
addDebugUser(Instruction user)252   public void addDebugUser(Instruction user) {
253     if (isUninitializedLocal()) {
254       return;
255     }
256     debugData.debugUsers.add(user);
257   }
258 
isUninitializedLocal()259   public boolean isUninitializedLocal() {
260     return definition != null && definition.isDebugLocalUninitialized();
261   }
262 
isInitializedLocal()263   public boolean isInitializedLocal() {
264     return !isUninitializedLocal();
265   }
266 
removeDebugUser(Instruction user)267   public void removeDebugUser(Instruction user) {
268     debugData.debugUsers.remove(user);
269   }
270 
hasUsersInfo()271   public boolean hasUsersInfo() {
272     return users != null;
273   }
274 
clearUsersInfo()275   public void clearUsersInfo() {
276     users = null;
277     uniqueUsers = null;
278     phiUsers = null;
279     uniquePhiUsers = null;
280     if (debugData != null) {
281       debugData.debugUsers = null;
282     }
283   }
284 
replaceUsers(Value newValue)285   public void replaceUsers(Value newValue) {
286     if (this == newValue) {
287       return;
288     }
289     for (Instruction user : uniqueUsers()) {
290       user.inValues.replaceAll(v -> {
291         if (v == this) {
292           newValue.addUser(user);
293           return newValue;
294         }
295         return v;
296       });
297     }
298     for (Phi user : uniquePhiUsers()) {
299       user.getOperands().replaceAll(v -> {
300         if (v == this) {
301           newValue.addPhiUser(user);
302           return newValue;
303         }
304         return v;
305       });
306     }
307     if (debugData != null) {
308       for (Instruction user : debugUsers()) {
309         user.getDebugValues().replaceAll(v -> {
310           if (v == this) {
311             newValue.addDebugUser(user);
312             return newValue;
313           }
314           return v;
315         });
316         if (user.getPreviousLocalValue() == this) {
317           newValue.addDebugUser(user);
318           user.replacePreviousLocalValue(newValue);
319         }
320       }
321     }
322     clearUsers();
323   }
324 
setLiveIntervals(LiveIntervals intervals)325   public void setLiveIntervals(LiveIntervals intervals) {
326     assert liveIntervals == null;
327     liveIntervals = intervals;
328   }
329 
getLiveIntervals()330   public LiveIntervals getLiveIntervals() {
331     return liveIntervals;
332   }
333 
needsRegister()334   public boolean needsRegister() {
335     assert needsRegister >= 0;
336     assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister();
337     return needsRegister > 0;
338   }
339 
setNeedsRegister(boolean value)340   public void setNeedsRegister(boolean value) {
341     assert needsRegister == -1 || (needsRegister > 0) == value;
342     needsRegister = value ? 1 : 0;
343   }
344 
computeNeedsRegister()345   public void computeNeedsRegister() {
346     assert needsRegister < 0;
347     setNeedsRegister(internalComputeNeedsRegister());
348   }
349 
350   public boolean internalComputeNeedsRegister() {
351     if (!isConstant()) {
352       return true;
353     }
354     if (numberOfPhiUsers() > 0) {
355       return true;
356     }
357     for (Instruction user : uniqueUsers()) {
358       if (user.needsValueInRegister(this)) {
359         return true;
360       }
361     }
362     return false;
363   }
364 
hasRegisterConstraint()365   public boolean hasRegisterConstraint() {
366     for (Instruction instruction : uniqueUsers()) {
367       if (instruction.maxInValueRegister() != Constants.U16BIT_MAX) {
368         return true;
369       }
370     }
371     return false;
372   }
373 
374   @Override
hashCode()375   public int hashCode() {
376     return number;
377   }
378 
379   @Override
toString()380   public String toString() {
381     StringBuilder builder = new StringBuilder();
382     builder.append("v");
383     builder.append(number);
384     boolean isConstant = definition != null && definition.isConstNumber();
385     boolean hasLocalInfo = getLocalInfo() != null;
386     if (isConstant || hasLocalInfo) {
387       builder.append("(");
388       if (isConstant) {
389         ConstNumber constNumber = getConstInstruction().asConstNumber();
390         if (constNumber.outType() == MoveType.SINGLE) {
391           builder.append((int) constNumber.getRawValue());
392         } else {
393           builder.append(constNumber.getRawValue());
394         }
395       }
396       if (isConstant && hasLocalInfo) {
397         builder.append(", ");
398       }
399       if (hasLocalInfo) {
400         builder.append(getLocalInfo());
401       }
402       builder.append(")");
403     }
404     if (valueRange != null) {
405       builder.append(valueRange);
406     }
407     return builder.toString();
408   }
409 
outType()410   public MoveType outType() {
411     return type;
412   }
413 
getConstInstruction()414   public ConstInstruction getConstInstruction() {
415     assert isConstant();
416     return definition.getOutConstantConstInstruction();
417   }
418 
isConstant()419   public boolean isConstant() {
420     return definition.isOutConstant() && getLocalInfo() == null;
421   }
422 
isPhi()423   public boolean isPhi() {
424     return false;
425   }
426 
asPhi()427   public Phi asPhi() {
428     return null;
429   }
430 
markNeverNull()431   public void markNeverNull() {
432     assert !neverNull;
433     neverNull = true;
434   }
435 
436   /**
437    * Returns whether this value is know to never be <code>null</code>.
438    */
isNeverNull()439   public boolean isNeverNull() {
440     return neverNull;
441   }
442 
canBeNull()443   public boolean canBeNull() {
444     return !neverNull;
445   }
446 
markAsArgument()447   public void markAsArgument() {
448     assert !isArgument;
449     assert !isThis;
450     isArgument = true;
451   }
452 
isArgument()453   public boolean isArgument() {
454     return isArgument;
455   }
456 
markAsThis()457   public void markAsThis() {
458     assert isArgument;
459     assert !isThis;
460     isThis = true;
461     markNeverNull();
462   }
463 
464   /**
465    * Returns whether this value is known to be the receiver (this argument) in a method body.
466    * <p>
467    * For a receiver value {@link #isNeverNull()} is guarenteed to be <code>true</code> as well.
468    */
isThis()469   public boolean isThis() {
470     return isThis;
471   }
472 
setValueRange(LongInterval range)473   public void setValueRange(LongInterval range) {
474     valueRange = range;
475   }
476 
hasValueRange()477   public boolean hasValueRange() {
478     return valueRange != null || isConstant();
479   }
480 
isValueInRange(int value)481   public boolean isValueInRange(int value) {
482     if (isConstant()) {
483       return value == getConstInstruction().asConstNumber().getIntValue();
484     } else {
485       return valueRange != null && valueRange.containsValue(value);
486     }
487   }
488 
getValueRange()489   public LongInterval getValueRange() {
490     if (isConstant()) {
491       if (type == MoveType.SINGLE) {
492         int value = getConstInstruction().asConstNumber().getIntValue();
493         return new LongInterval(value, value);
494       } else {
495         assert type == MoveType.WIDE;
496         long value = getConstInstruction().asConstNumber().getLongValue();
497         return new LongInterval(value, value);
498       }
499     } else {
500       return valueRange;
501     }
502   }
503 
isDead(InternalOptions options)504   public boolean isDead(InternalOptions options) {
505     // Totally unused values are trivially dead.
506     return numberOfAllUsers() == 0 || isDead(new HashSet<>(), options);
507   }
508 
isDead(Set<Value> active, InternalOptions options)509   protected boolean isDead(Set<Value> active, InternalOptions options) {
510     // If the value has debug users we cannot eliminate it since it represents a value in a local
511     // variable that should be visible in the debugger.
512     if (numberOfDebugUsers() != 0) {
513       return false;
514     }
515     // This is a candidate for a dead value. Guard against looping by adding it to the set of
516     // currently active values.
517     active.add(this);
518     for (Instruction instruction : uniqueUsers()) {
519       if (!instruction.canBeDeadCode(null, options)) {
520         return false;
521       }
522       Value outValue = instruction.outValue();
523       // Instructions with no out value cannot be dead code by the current definition
524       // (unused out value). They typically side-effect input values or deals with control-flow.
525       assert outValue != null;
526       if (!active.contains(outValue) && !outValue.isDead(active, options)) {
527         return false;
528       }
529     }
530     for (Phi phi : uniquePhiUsers()) {
531       if (!active.contains(phi) && !phi.isDead(active, options)) {
532         return false;
533       }
534     }
535     return true;
536   }
537 }
538