Home | History | Annotate | Download | only in code
      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 
     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 
     46     DebugData(DebugInfo info) {
     47       this(info.local, info.previousLocalValue);
     48     }
     49 
     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 
     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 
     82   public boolean isFixedRegisterValue() {
     83     return false;
     84   }
     85 
     86   public FixedRegisterValue asFixedRegisterValue() {
     87     return null;
     88   }
     89 
     90   public int getNumber() {
     91     return number;
     92   }
     93 
     94   public int requiredRegisters() {
     95     return type.requiredRegisters();
     96   }
     97 
     98   public DebugInfo getDebugInfo() {
     99     return debugData == null ? null : new DebugInfo(debugData.local, debugData.previousLocalValue);
    100   }
    101 
    102   public DebugLocalInfo getLocalInfo() {
    103     return debugData == null ? null : debugData.local;
    104   }
    105 
    106   public Value getPreviousLocalValue() {
    107     return debugData == null ? null : debugData.previousLocalValue;
    108   }
    109 
    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 
    118   public List<Instruction> getDebugLocalStarts() {
    119     return debugData.localStarts;
    120   }
    121 
    122   public List<Instruction> getDebugLocalEnds() {
    123     return debugData.localEnds;
    124   }
    125 
    126   public void addDebugLocalStart(Instruction start) {
    127     assert start != null;
    128     debugData.localStarts.add(start);
    129   }
    130 
    131   public void addDebugLocalEnd(Instruction end) {
    132     assert end != null;
    133     debugData.localEnds.add(end);
    134   }
    135 
    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 
    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 
    157   public boolean isLinked() {
    158     return nextConsecutive != null || previousConsecutive != null;
    159   }
    160 
    161   public Value getStartOfConsecutive() {
    162     Value current = this;
    163     while (current.getPreviousConsecutive() != null) {
    164       current = current.getPreviousConsecutive();
    165     }
    166     return current;
    167   }
    168 
    169   public Value getNextConsecutive() {
    170     return nextConsecutive;
    171   }
    172 
    173   public Value getPreviousConsecutive() {
    174     return previousConsecutive;
    175   }
    176 
    177   public Set<Instruction> uniqueUsers() {
    178     if (uniqueUsers != null) {
    179       return uniqueUsers;
    180     }
    181     return uniqueUsers = ImmutableSet.copyOf(users);
    182   }
    183 
    184   public Set<Phi> uniquePhiUsers() {
    185     if (uniquePhiUsers != null) {
    186       return uniquePhiUsers;
    187     }
    188     return uniquePhiUsers = ImmutableSet.copyOf(phiUsers);
    189   }
    190 
    191   public Set<Instruction> debugUsers() {
    192     if (debugData == null) {
    193       return null;
    194     }
    195     return Collections.unmodifiableSet(debugData.debugUsers);
    196   }
    197 
    198   public int numberOfUsers() {
    199     int size = users.size();
    200     if (size <= 1) {
    201       return size;
    202     }
    203     return uniqueUsers().size();
    204   }
    205 
    206   public int numberOfPhiUsers() {
    207     int size = phiUsers.size();
    208     if (size <= 1) {
    209       return size;
    210     }
    211     return uniquePhiUsers().size();
    212   }
    213 
    214   public int numberOfDebugUsers() {
    215     return debugData == null ? 0 : debugData.debugUsers.size();
    216   }
    217 
    218   public int numberOfAllUsers() {
    219     return numberOfUsers() + numberOfPhiUsers() + numberOfDebugUsers();
    220   }
    221 
    222   public void addUser(Instruction user) {
    223     users.add(user);
    224     uniqueUsers = null;
    225   }
    226 
    227   public void removeUser(Instruction user) {
    228     users.remove(user);
    229     uniqueUsers = null;
    230   }
    231 
    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 
    242   public void addPhiUser(Phi user) {
    243     phiUsers.add(user);
    244     uniquePhiUsers = null;
    245   }
    246 
    247   public void removePhiUser(Phi user) {
    248     phiUsers.remove(user);
    249     uniquePhiUsers = null;
    250   }
    251 
    252   public void addDebugUser(Instruction user) {
    253     if (isUninitializedLocal()) {
    254       return;
    255     }
    256     debugData.debugUsers.add(user);
    257   }
    258 
    259   public boolean isUninitializedLocal() {
    260     return definition != null && definition.isDebugLocalUninitialized();
    261   }
    262 
    263   public boolean isInitializedLocal() {
    264     return !isUninitializedLocal();
    265   }
    266 
    267   public void removeDebugUser(Instruction user) {
    268     debugData.debugUsers.remove(user);
    269   }
    270 
    271   public boolean hasUsersInfo() {
    272     return users != null;
    273   }
    274 
    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 
    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 
    325   public void setLiveIntervals(LiveIntervals intervals) {
    326     assert liveIntervals == null;
    327     liveIntervals = intervals;
    328   }
    329 
    330   public LiveIntervals getLiveIntervals() {
    331     return liveIntervals;
    332   }
    333 
    334   public boolean needsRegister() {
    335     assert needsRegister >= 0;
    336     assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister();
    337     return needsRegister > 0;
    338   }
    339 
    340   public void setNeedsRegister(boolean value) {
    341     assert needsRegister == -1 || (needsRegister > 0) == value;
    342     needsRegister = value ? 1 : 0;
    343   }
    344 
    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 
    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
    375   public int hashCode() {
    376     return number;
    377   }
    378 
    379   @Override
    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 
    410   public MoveType outType() {
    411     return type;
    412   }
    413 
    414   public ConstInstruction getConstInstruction() {
    415     assert isConstant();
    416     return definition.getOutConstantConstInstruction();
    417   }
    418 
    419   public boolean isConstant() {
    420     return definition.isOutConstant() && getLocalInfo() == null;
    421   }
    422 
    423   public boolean isPhi() {
    424     return false;
    425   }
    426 
    427   public Phi asPhi() {
    428     return null;
    429   }
    430 
    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    */
    439   public boolean isNeverNull() {
    440     return neverNull;
    441   }
    442 
    443   public boolean canBeNull() {
    444     return !neverNull;
    445   }
    446 
    447   public void markAsArgument() {
    448     assert !isArgument;
    449     assert !isThis;
    450     isArgument = true;
    451   }
    452 
    453   public boolean isArgument() {
    454     return isArgument;
    455   }
    456 
    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    */
    469   public boolean isThis() {
    470     return isThis;
    471   }
    472 
    473   public void setValueRange(LongInterval range) {
    474     valueRange = range;
    475   }
    476 
    477   public boolean hasValueRange() {
    478     return valueRange != null || isConstant();
    479   }
    480 
    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 
    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 
    504   public boolean isDead(InternalOptions options) {
    505     // Totally unused values are trivially dead.
    506     return numberOfAllUsers() == 0 || isDead(new HashSet<>(), options);
    507   }
    508 
    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