blob: 8892da04f98043a16150785e4e9f8bc5082c0297 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstRangeLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractInstruction;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
public abstract class Instruction
implements AbstractInstruction, InstructionOrPhi, TypeAndLocalInfoSupplier {
protected Value outValue = null;
protected final List<Value> inValues = new ArrayList<>();
private BasicBlock block = null;
private int number = -1;
private Set<Value> debugValues = null;
private Position position = null;
protected Instruction(Value outValue) {
setOutValue(outValue);
}
protected Instruction(Value outValue, Value inValue) {
addInValue(inValue);
setOutValue(outValue);
}
protected Instruction(Value outValue, List<? extends Value> inValues) {
if (inValues != null) {
for (Value v : inValues) {
addInValue(v);
}
}
setOutValue(outValue);
}
public abstract int opcode();
public abstract <T> T accept(InstructionVisitor<T> visitor);
final boolean hasPosition() {
return position != null;
}
public final Position getPosition() {
assert position != null;
return position;
}
public void setPosition(Position position) {
assert this.position == null;
this.position = position;
}
public void forceOverwritePosition(Position position) {
assert position != null;
assert this.position != null;
this.position = position;
}
public String getPositionAsString() {
return position == null ? "???" : position.toString();
}
public Value getOperand(int index) {
return inValues().get(index);
}
public Value getFirstOperand() {
return getOperand(0);
}
public List<Value> inValues() {
return inValues;
}
protected void addInValue(Value value) {
if (value != null) {
inValues.add(value);
assert value.hasUsersInfo();
if (value.hasUsersInfo()) {
value.addUser(this);
}
}
}
public boolean ignoreCompatRules() {
return false;
}
public boolean hasInValueWithLocalInfo() {
return hasInValueThatMatches(Value::hasLocalInfo);
}
public boolean hasInValueThatMatches(Predicate<Value> predicate) {
for (Value value : inValues()) {
if (predicate.test(value)) {
return true;
}
}
return false;
}
public boolean hasOutValue() {
return outValue != null;
}
public boolean hasUnusedOutValue() {
return !hasUsedOutValue();
}
public boolean hasUsedOutValue() {
return hasOutValue() && outValue().hasAnyUsers();
}
public Value outValue() {
return outValue;
}
public Value clearOutValue() {
return setOutValue(null);
}
// Returns the previous out-value, if any.
public Value setOutValue(Value newOutValue) {
Value previousOutValue = outValue();
outValue = newOutValue;
if (newOutValue != null) {
newOutValue.definition = this;
}
return previousOutValue;
}
public Value swapOutValue(Value newOutValue) {
Value oldOutValue = outValue;
outValue = null;
setOutValue(newOutValue);
if (oldOutValue != null) {
oldOutValue.definition = null;
}
return oldOutValue;
}
public AbstractValue getAbstractValue(
AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context) {
assert hasOutValue();
return UnknownValue.getInstance();
}
@Override
public TypeElement getOutType() {
if (hasOutValue()) {
return outValue().getType();
}
return null;
}
public void addDebugValue(Value value) {
assert value.hasLocalInfo();
if (debugValues == null) {
debugValues = Sets.newIdentityHashSet();
}
debugValues.add(value);
}
public static void clearUserInfo(Instruction instruction) {
if (instruction.outValue != null) {
instruction.outValue.clearUsersInfo();
}
instruction.inValues.forEach(Value::clearUsersInfo);
if (instruction.debugValues != null) {
instruction.debugValues.forEach(Value::clearUsersInfo);
instruction.debugValues = null;
}
}
public final ValueType outType() {
return outValue.outType();
}
public abstract void buildDex(DexBuilder builder);
public abstract void buildCf(CfBuilder builder);
public void replaceValue(Value oldValue, Value newValue) {
for (int i = 0; i < inValues.size(); i++) {
if (oldValue == inValues.get(i)) {
inValues.set(i, newValue);
newValue.addUser(this);
}
}
oldValue.removeUser(this);
}
public void replaceValue(int index, Value newValue) {
Value oldValue = inValues.get(index);
inValues.set(index, newValue);
newValue.addUser(this);
oldValue.removeUser(this);
}
public void replaceDebugValue(Value oldValue, Value newValue) {
assert oldValue.hasLocalInfo();
assert newValue.hasLocalInfo();
assert newValue.getLocalInfo() == oldValue.getLocalInfo()
: "Replacing debug values with inconsistent locals "
+ oldValue.getLocalInfo()
+ " and "
+ newValue.getLocalInfo()
+ ". This is likely a code transformation bug "
+ "that has not taken local information into account";
boolean removed = debugValues.remove(oldValue);
assert removed;
if (removed && newValue.hasLocalInfo()) {
newValue.addDebugLocalEnd(this);
}
}
public void moveDebugValues(Instruction target) {
if (debugValues == null) {
return;
}
for (Value value : debugValues) {
value.replaceDebugUser(this, target);
}
debugValues.clear();
}
public void removeDebugValue(Value value) {
assert value.hasLocalInfo();
if (debugValues != null) {
assert debugValues.contains(value);
if (debugValues.remove(value)) {
value.removeDebugUser(this);
}
return;
}
assert false;
}
public Value removeDebugValue(DebugLocalInfo localInfo) {
if (debugValues != null) {
Iterator<Value> it = debugValues.iterator();
while (it.hasNext()) {
Value value = it.next();
if (value.hasLocalInfo() && value.getLocalInfo() == localInfo) {
it.remove();
value.removeDebugUser(this);
return value;
}
}
}
return null;
}
public void clearDebugValues() {
if (debugValues != null) {
for (Value debugValue : debugValues) {
debugValue.removeDebugUser(this);
}
debugValues.clear();
}
}
/**
* Returns the basic block containing this instruction.
*/
@Override
public BasicBlock getBlock() {
assert block != null;
return block;
}
/**
* Set the basic block of this instruction. See IRBuilder.
*/
public void setBlock(BasicBlock block) {
assert block != null;
this.block = block;
}
/**
* Clear the basic block of this instruction. Use when removing an instruction from a block.
*/
public void clearBlock() {
assert block != null;
block = null;
}
public void removeOrReplaceByDebugLocalRead(IRCode code) {
getBlock().listIterator(code, this).removeOrReplaceByDebugLocalRead();
}
public void replace(Instruction newInstruction, IRCode code) {
replace(newInstruction, code, null);
}
public void replace(Instruction newInstruction, IRCode code, Set<Value> affectedValues) {
getBlock().listIterator(code, this).replaceCurrentInstruction(newInstruction, affectedValues);
}
/**
* Returns true if the instruction is in the IR and therefore has a block.
*/
public boolean hasBlock() {
return block != null;
}
public String getInstructionName() {
return getClass().getSimpleName();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(getInstructionName());
for (int i = builder.length(); i < 20; i++) {
builder.append(" ");
}
builder.append(" ");
if (outValue != null) {
builder.append(outValue);
builder.append(" <- ");
}
if (!inValues.isEmpty()) {
StringUtils.append(builder, inValues, ", ", BraceType.NONE);
}
return builder.toString();
}
public void print(CfgPrinter printer) {
int uses = 0;
String value;
if (outValue == null) {
value = printer.makeUnusedValue();
} else {
if (outValue.hasUsersInfo()) {
uses = outValue.uniqueUsers().size() + outValue.uniquePhiUsers().size();
}
value = "v" + outValue.getNumber();
}
printer
.print(0) // bci
.sp().append(uses) // use
.sp().append(value) // tid
.sp().append(getClass().getSimpleName());
for (Value in : inValues) {
printer.append(" v").append(in.getNumber());
}
}
public void printLIR(CfgPrinter printer) {
// TODO(ager): Improve the instruction printing. Use different name for values so that the
// HIR and LIR values are not confused in the c1 visualizer.
printer.print(number).sp().append(toString());
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
assert number != -1;
this.number = number;
}
public void clearNumber() {
this.number = -1;
}
/**
* Compare equality of two class-equivalent instructions modulo their values and positions.
*/
public abstract boolean identicalNonValueNonPositionParts(Instruction other);
public boolean identicalNonValueParts(Instruction other) {
assert getClass() == other.getClass();
return position.equals(other.position) && identicalNonValueNonPositionParts(other);
}
private boolean identicalInputAfterRegisterAllocation(
Value a,
int aInstrNumber,
Instruction bInstr,
Value b,
int bInstrNumber,
RegisterAllocator allocator) {
boolean aIsStackValue = a instanceof StackValue;
boolean aIsStackValues = a instanceof StackValues;
if (aIsStackValue != b instanceof StackValue || aIsStackValues != b instanceof StackValues) {
return false;
}
if (aIsStackValue) {
return identicalStackValuePair((StackValue) a, (StackValue) b);
}
if (aIsStackValues) {
return identicalStackValuesPair((StackValues) a, (StackValues) b);
}
if (needsValueInRegister(a) != bInstr.needsValueInRegister(b)) {
return false;
}
// If the value is needed in a register or one of the instructions is a two-addr instruction,
// the register for the value is used and it needs to be the same.
if (needsValueInRegister(a) || isTwoAddr(allocator) || bInstr.isTwoAddr(allocator)) {
// Only one of the instructions have a register assigned and one of them will use a
// register, so the instructions are not identical.
if (!a.needsRegister() || !b.needsRegister()) {
return false;
}
// Check if the allocated registers are identical.
if (allocator.getRegisterForValue(a, aInstrNumber)
!= allocator.getRegisterForValue(b, bInstrNumber)) {
return false;
}
} else {
ConstNumber aNum = a.getConstInstruction().asConstNumber();
ConstNumber bNum = b.getConstInstruction().asConstNumber();
if (!aNum.identicalNonValueNonPositionParts(bNum)) {
return false;
}
}
return a.outType() == b.outType();
}
private boolean identicalOutputAfterRegisterAllocation(
Value a, int aInstrNumber, Value b, int bInstrNumber, RegisterAllocator allocator) {
boolean aIsStackValue = a instanceof StackValue;
boolean aIsStackValues = a instanceof StackValues;
if (aIsStackValue != b instanceof StackValue || aIsStackValues != b instanceof StackValues) {
return false;
}
if (aIsStackValue) {
return identicalStackValuePair((StackValue) a, (StackValue) b);
}
if (aIsStackValues) {
return identicalStackValuesPair((StackValues) a, (StackValues) b);
}
if (a.needsRegister() != b.needsRegister()) {
return false;
}
if (a.needsRegister()) {
if (allocator.getRegisterForValue(a, aInstrNumber)
!= allocator.getRegisterForValue(b, bInstrNumber)) {
return false;
}
} else {
ConstNumber aNum = a.getConstInstruction().asConstNumber();
ConstNumber bNum = b.getConstInstruction().asConstNumber();
if (!aNum.identicalNonValueNonPositionParts(bNum)) {
return false;
}
}
return a.outType() == b.outType();
}
public boolean identicalAfterRegisterAllocation(
Instruction other, RegisterAllocator allocator, MethodConversionOptions conversionOptions) {
if (other.getClass() != getClass()) {
return false;
}
// In debug mode or if the instruction can throw we must account for positions, in release mode
// we do want to share non-throwing instructions even if their positions differ.
if (instructionTypeCanThrow() || allocator.options().debug) {
if (!identicalNonValueParts(other)) {
return false;
}
} else if (!identicalNonValueNonPositionParts(other)) {
return false;
}
if (isInvokeDirect() && !asInvokeDirect().sameConstructorReceiverValue(other.asInvoke())) {
return false;
}
if (outValue != null) {
if (other.outValue == null) {
return false;
}
if (!identicalOutputAfterRegisterAllocation(
outValue, getNumber(), other.outValue, other.getNumber(), allocator)) {
return false;
}
} else if (other.outValue != null) {
return false;
}
// Check that all input values have the same type and allocated registers.
if (inValues.size() != other.inValues.size()) {
return false;
}
for (int j = 0; j < inValues.size(); j++) {
Value in0 = inValues.get(j);
Value in1 = other.inValues.get(j);
if (!identicalInputAfterRegisterAllocation(in0, getNumber(), other, in1, other.getNumber(),
allocator)) {
return false;
}
}
// Finally check that the dex instructions for the generated code actually are the same.
InternalOptions options = allocator.options();
if (conversionOptions.isGeneratingDex()
&& !DexBuilder.identicalInstructionsAfterBuildingDexCode(
this, other, allocator, conversionOptions)) {
return false;
}
return true;
}
private boolean identicalStackValuePair(StackValue a, StackValue b) {
return a.getHeight() == b.getHeight() && a.outType() == b.outType();
}
private boolean identicalStackValuesPair(StackValues a, StackValues b) {
StackValue[] aStackValues = a.getStackValues();
StackValue[] bStackValues = b.getStackValues();
if (aStackValues.length != bStackValues.length) {
return false;
}
for (int i = 0; i < aStackValues.length; ++i) {
if (!identicalStackValuePair(aStackValues[i], bStackValues[i])) {
return false;
}
}
return true;
}
public boolean isAllowedAfterThrowingInstruction() {
return false;
}
public boolean isBlockLocalInstructionWithoutSideEffects(
AppView<?> appView, ProgramMethod context) {
return definesBlockLocalValue() && !instructionMayHaveSideEffects(appView, context);
}
private boolean definesBlockLocalValue() {
return !definesValueWithNonLocalUsages();
}
private boolean definesValueWithNonLocalUsages() {
if (hasOutValue()) {
Value outValue = outValue();
if (outValue.numberOfPhiUsers() > 0) {
return true;
}
for (Instruction user : outValue.uniqueUsers()) {
if (user.getBlock() != getBlock()) {
return true;
}
}
}
return false;
}
public boolean instructionTypeCanBeCanonicalized() {
return false;
}
/** Returns true if this instruction may throw an exception. */
@Override
public boolean instructionTypeCanThrow() {
return false;
}
public boolean instructionInstanceCanThrow() {
return instructionTypeCanThrow();
}
public boolean instructionMayHaveSideEffects(AppView<?> appView, ProgramMethod context) {
return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
}
public boolean instructionMayHaveSideEffects(
AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow();
}
/**
* Returns true if this instruction may trigger another method to execute either directly or
* indirectly (e.g., via class initialization).
*/
public abstract boolean instructionMayTriggerMethodInvocation(
AppView<?> appView, ProgramMethod context);
public boolean instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
return instructionInstanceCanThrow();
}
/** Returns true is this instruction can be treated as dead code if its outputs are not used. */
public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
return instructionMayHaveSideEffects(appView, code.context())
? DeadInstructionResult.notDead()
: DeadInstructionResult.deadIfOutValueIsDead();
}
/**
* Returns an abstraction of the set of fields that may possibly be read as a result of executing
* this instruction.
*/
public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
if (instructionMayTriggerMethodInvocation(appView, context)
&& instructionMayHaveSideEffects(appView, context)) {
return UnknownFieldSet.getInstance();
}
return EmptyFieldSet.getInstance();
}
/**
* Returns true if this instruction need this value in a register.
*/
public boolean needsValueInRegister(Value value) {
return true;
}
public boolean isTwoAddr(RegisterAllocator allocator) {
return false;
}
/**
* Returns true if the out value of this instruction is a constant.
*
* @return whether the out value of this instruction is a constant.
*/
public boolean isOutConstant() {
return false;
}
/**
* Returns the ConstInstruction defining the constant out value if the out value is constant.
*
* @return ConstInstruction or null.
*/
public ConstInstruction getOutConstantConstInstruction() {
return null;
}
public abstract int maxInValueRegister();
public abstract int maxOutValueRegister();
@Override
public DebugLocalInfo getLocalInfo() {
return outValue == null ? null : outValue.getLocalInfo();
}
public Set<Value> getDebugValues() {
return debugValues != null ? debugValues : ImmutableSet.of();
}
@Override
public boolean isInstruction() {
return true;
}
@Override
public Instruction asInstruction() {
return this;
}
public boolean isRecordFieldValues() {
return false;
}
public RecordFieldValues asRecordFieldValues() {
return null;
}
public boolean isArrayAccess() {
return false;
}
public ArrayAccess asArrayAccess() {
return null;
}
public boolean isArrayGet() {
return false;
}
public ArrayGet asArrayGet() {
return null;
}
public boolean isArrayLength() {
return false;
}
public ArrayLength asArrayLength() {
return null;
}
public boolean isArrayPut() {
return false;
}
public ArrayPut asArrayPut() {
return null;
}
public boolean isArgument() {
return false;
}
public Argument asArgument() {
return null;
}
public boolean isUnusedArgument() {
return false;
}
public UnusedArgument asUnusedArgument() {
return null;
}
public boolean isArithmeticBinop() {
return false;
}
public ArithmeticBinop asArithmeticBinop() {
return null;
}
public boolean isAssume() {
return false;
}
public Assume asAssume() {
return null;
}
public final boolean isAssumeWithDynamicTypeAssumption() {
return isAssume() && asAssume().hasDynamicTypeAssumption();
}
public final boolean isAssumeWithNonNullAssumption() {
return isAssume() && asAssume().hasNonNullAssumption();
}
public boolean isBinop() {
return false;
}
public Binop asBinop() {
return null;
}
public boolean isUnop() {
return false;
}
public Unop asUnop() {
return null;
}
public boolean isCheckCast() {
return false;
}
public CheckCast asCheckCast() {
return null;
}
public boolean isSafeCheckCast() {
return false;
}
public SafeCheckCast asSafeCheckCast() {
return null;
}
public boolean isConstNumber() {
return false;
}
public ConstNumber asConstNumber() {
return null;
}
public boolean isConstInstruction() {
return false;
}
public ConstInstruction asConstInstruction() {
return null;
}
public boolean isConstClass() {
return false;
}
public ConstClass asConstClass() {
return null;
}
public boolean isConstMethodHandle() {
return false;
}
public ConstMethodHandle asConstMethodHandle() {
return null;
}
public boolean isConstMethodType() {
return false;
}
public ConstMethodType asConstMethodType() {
return null;
}
public boolean isConstString() {
return false;
}
public ConstString asConstString() {
return null;
}
public boolean isDexItemBasedConstString() {
return false;
}
public DexItemBasedConstString asDexItemBasedConstString() {
return null;
}
public boolean isCmp() {
return false;
}
public Cmp asCmp() {
return null;
}
public boolean isDup() {
return false;
}
public Dup asDup() {
return null;
}
public boolean isDup2() {
return false;
}
public Dup2 asDup2() {
return null;
}
public boolean isJumpInstruction() {
return false;
}
public JumpInstruction asJumpInstruction() {
return null;
}
public boolean isFieldInstruction() {
return false;
}
public FieldInstruction asFieldInstruction() {
return null;
}
public boolean isGoto() {
return false;
}
public Goto asGoto() {
return null;
}
public boolean isIf() {
return false;
}
public If asIf() {
return null;
}
public boolean isSwitch() {
return false;
}
public Switch asSwitch() {
return null;
}
public boolean isIntSwitch() {
return false;
}
public IntSwitch asIntSwitch() {
return null;
}
public boolean isStringSwitch() {
return false;
}
public StringSwitch asStringSwitch() {
return null;
}
public boolean isInstanceFieldInstruction() {
return false;
}
public InstanceFieldInstruction asInstanceFieldInstruction() {
return null;
}
public boolean isInstanceGet() {
return false;
}
public InstanceGet asInstanceGet() {
return null;
}
public boolean isInstanceOf() {
return false;
}
public InstanceOf asInstanceOf() {
return null;
}
public boolean isFieldGet() {
return false;
}
public FieldGet asFieldGet() {
return null;
}
public boolean isFieldPut() {
return false;
}
public FieldPut asFieldPut() {
return null;
}
public boolean isInstancePut() {
return false;
}
public InstancePut asInstancePut() {
return null;
}
public boolean isInvoke() {
return false;
}
public Invoke asInvoke() {
return null;
}
public boolean isMonitor() {
return false;
}
public boolean isMonitorEnter() {
return false;
}
public Monitor asMonitor() {
return null;
}
public boolean isMove() {
return false;
}
public Move asMove() {
return null;
}
public boolean isNewArrayEmpty() {
return false;
}
public boolean isNewArrayEmptyOrInvokeNewArray() {
return isNewArrayEmpty() || isInvokeNewArray();
}
public NewArrayEmpty asNewArrayEmpty() {
return null;
}
public boolean isNewArrayFilledData() {
return false;
}
public NewArrayFilledData asNewArrayFilledData() {
return null;
}
public boolean isNeg() {
return false;
}
public Neg asNeg() {
return null;
}
public boolean isNewInstance() {
return false;
}
public NewInstance asNewInstance() {
return null;
}
public boolean isNewUnboxedEnumInstance() {
return false;
}
public NewUnboxedEnumInstance asNewUnboxedEnumInstance() {
return null;
}
public boolean isNot() {
return false;
}
public Not asNot() {
return null;
}
public boolean isNumberConversion() {
return false;
}
public NumberConversion asNumberConversion() {
return null;
}
public boolean isReturn() {
return false;
}
public Return asReturn() {
return null;
}
public boolean isThrow() {
return false;
}
public Throw asThrow() {
return null;
}
public boolean isStaticFieldInstruction() {
return false;
}
public boolean isStaticGet() {
return false;
}
public StaticGet asStaticGet() {
return null;
}
public boolean isStaticPut() {
return false;
}
public StaticPut asStaticPut() {
return null;
}
public boolean isInitClass() {
return false;
}
public InitClass asInitClass() {
return null;
}
public boolean isAdd() {
return false;
}
public Add asAdd() {
return null;
}
public boolean isSub() {
return false;
}
public Sub asSub() {
return null;
}
public boolean isMul() {
return false;
}
public Mul asMul() {
return null;
}
public boolean isDiv() {
return false;
}
public Div asDiv() {
return null;
}
public boolean isRem() {
return false;
}
public Rem asRem() {
return null;
}
public boolean isLogicalBinop() {
return false;
}
public LogicalBinop asLogicalBinop() {
return null;
}
public boolean isShl() {
return false;
}
public Shl asShl() {
return null;
}
public boolean isShr() {
return false;
}
public Shr asShr() {
return null;
}
public boolean isUshr() {
return false;
}
public Ushr asUshr() {
return null;
}
public boolean isAnd() {
return false;
}
public And asAnd() {
return null;
}
public boolean isOr() {
return false;
}
public Or asOr() {
return null;
}
public boolean isXor() {
return false;
}
public Xor asXor() {
return null;
}
public boolean isMoveException() {
return false;
}
public MoveException asMoveException() {
return null;
}
public boolean isDebugInstruction() {
return isDebugPosition()
|| isDebugLocalsChange()
|| isDebugLocalRead()
|| isDebugLocalWrite()
|| isDebugLocalUninitialized();
}
public boolean isDebugPosition() {
return false;
}
public DebugPosition asDebugPosition() {
return null;
}
public boolean isDebugLocalsChange() {
return false;
}
public DebugLocalsChange asDebugLocalsChange() {
return null;
}
public boolean isDebugLocalUninitialized() {
return false;
}
public DebugLocalUninitialized asDebugLocalUninitialized() {
return null;
}
public boolean isDebugLocalWrite() {
return false;
}
public DebugLocalWrite asDebugLocalWrite() {
return null;
}
public boolean isInvokeMethodWithDynamicDispatch() {
return isInvokeInterface() || isInvokeVirtual();
}
public boolean isInvokeMethod() {
return false;
}
public InvokeMethod asInvokeMethod() {
return null;
}
public boolean isInvokeMethodWithReceiver() {
return false;
}
public InvokeMethodWithReceiver asInvokeMethodWithReceiver() {
return null;
}
public boolean isInvokeNewArray() {
return false;
}
public InvokeNewArray asInvokeNewArray() {
return null;
}
public boolean isInvokeMultiNewArray() {
return false;
}
public InvokeMultiNewArray asInvokeMultiNewArray() {
return null;
}
public boolean isInvokeCustom() {
return false;
}
public InvokeCustom asInvokeCustom() {
return null;
}
public boolean isInvokeConstructor(DexItemFactory dexItemFactory) {
return false;
}
public boolean isInvokeDirect() {
return false;
}
public InvokeDirect asInvokeDirect() {
return null;
}
public boolean isInvokeInterface() {
return false;
}
public InvokeInterface asInvokeInterface() {
return null;
}
public boolean isInvokeStatic() {
return false;
}
public InvokeStatic asInvokeStatic() {
return null;
}
public boolean isInvokeSuper() {
return false;
}
public InvokeSuper asInvokeSuper() {
return null;
}
public boolean isInvokeVirtual() {
return false;
}
public InvokeVirtual asInvokeVirtual() {
return null;
}
public boolean isInvokePolymorphic() {
return false;
}
public InvokePolymorphic asInvokePolymorphic() {
return null;
}
public boolean isDebugLocalRead() {
return false;
}
public DebugLocalRead asDebugLocalRead() {
return null;
}
public boolean isPop() {
return false;
}
public Pop asPop() {
return null;
}
public boolean isStore() {
return false;
}
public Store asStore() {
return null;
}
public boolean isSwap() {
return false;
}
public Swap asSwap() {
return null;
}
public boolean isLoad() {
return false;
}
public Load asLoad() {
return null;
}
public boolean canBeFolded() {
return false;
}
/** Returns true if the out-value could be an alias of an in-value.
*
* <p>This is a conservative version of {@link #isIntroducingAnAlias()} so that other analyses,
* e.g., escape analysis, can propagate or track aliased values in a conservative manner.
*/
public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
return false;
}
/** Returns true if the out-value is an alias of an in-value. */
public boolean isIntroducingAnAlias() {
return false;
}
/** Returns the in-value that is an alias for the out-value. */
public Value getAliasForOutValue() {
assert isIntroducingAnAlias();
return null;
}
public boolean isCreatingArray() {
return isNewArrayEmpty()
|| isNewArrayFilledData()
|| isInvokeNewArray()
|| isInvokeMultiNewArray()
|| isRecordFieldValues();
}
public boolean isCreatingInstanceOrArray() {
return isNewInstance() || isCreatingArray();
}
/**
* Returns the inlining constraint for this method when used in the context of the given type.
*
* <p>The type is used to judge visibility constraints and also for dispatch decisions.
*/
public abstract ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context);
public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
assert outValue == null || !outValue.getType().isReferenceType();
throw new Unreachable("Instruction without object outValue cannot compute verification type");
}
public abstract boolean hasInvariantOutType();
public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
if (outValue.hasValueRange()) {
return new ConstRangeLatticeElement(outValue);
}
return Bottom.getInstance();
}
// TODO(b/72693244): maybe rename to computeOutType once TypeVerificationHelper is gone?
public TypeElement evaluate(AppView<?> appView) {
assert outValue == null;
throw new Unimplemented(
"Implement type lattice evaluation for: " + getInstructionName());
}
public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
if (outValue != null) {
TypeElement outTypeElement = outValue.getType();
if (outTypeElement.isArrayType()) {
DexType outBaseType =
outTypeElement
.asArrayType()
.toDexType(appView.dexItemFactory())
.toBaseType(appView.dexItemFactory());
assert appView.graphLens().lookupType(outBaseType) == outBaseType;
} else if (outTypeElement.isClassType()) {
DexType outType = outTypeElement.asClassType().getClassType();
assert appView.graphLens().lookupType(outType) == outType;
}
}
return true;
}
/**
* Indicates whether the instruction throws a NullPointerException if the object denoted by the
* given value is null at runtime execution.
*
* @param value the value representing an object that may be null at runtime execution.
* @param appView where pre-defined descriptors are retrieved
* @param context
* @return true if the instruction throws NullPointerException if value is null at runtime, false
* otherwise.
*/
public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
return false;
}
// Any instructions that override `throwsNpeIfValueIsNull` should override this too.
// `throw` instruction is also throwing if the input is null. However, this util and the below
// one are used to insert Assume instruction if input is known to be non-null or replace the
// instruction with `throw null`. In either case, nullability of `throw` does not add anything
// new: `throw X` with null reference X has the same effect as `throw null`; `throw X` with
// non-null reference X ends, hence having non-null X after that instruction is non-sense.
public boolean throwsOnNullInput() {
return false;
}
// Any instructions that override `throwsOnNullInput` should override this too.
public Value getNonNullInput() {
throw new Unreachable("Should conform to throwsOnNullInput.");
}
/**
* Indicates whether the instruction triggers the class initialization (i.e. the <clinit> method)
* of the given class at runtime execution.
*
* @return true if the instruction triggers intialization of the class at runtime, false
* otherwise.
*/
public boolean definitelyTriggersClassInitialization(
DexType clazz,
ProgramMethod context,
AppView<AppInfoWithLiveness> appView,
Query mode,
AnalysisAssumption assumption) {
return false;
}
public boolean verifyValidPositionInfo(boolean debug) {
assert position != null;
assert !debug || getPosition().isSome();
assert !instructionTypeCanThrow()
|| isConstString()
|| isDexItemBasedConstString()
|| getPosition().isSome()
|| getPosition().isSyntheticNone();
return true;
}
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return false;
}
public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
}
public void registerUse(UseRegistry registry, ProgramMethod context) {
internalRegisterUse(registry, context);
}
void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
// Intentionally empty.
}
public static class SideEffectAssumption {
public static final SideEffectAssumption NONE = new SideEffectAssumption();
public static final SideEffectAssumption CLASS_ALREADY_INITIALIZED =
new SideEffectAssumption() {
@Override
public boolean canAssumeClassIsAlreadyInitialized() {
return true;
}
};
public static final SideEffectAssumption IGNORE_RECEIVER_FIELD_ASSIGNMENTS =
new SideEffectAssumption() {
@Override
public boolean canIgnoreInstanceFieldAssignmentsToReceiver() {
return true;
}
};
public static final SideEffectAssumption INVOKED_METHOD_DOES_NOT_HAVE_SIDE_EFFECTS =
new SideEffectAssumption() {
@Override
public boolean canAssumeInvokedMethodDoesNotHaveSideEffects() {
return true;
}
};
public static final SideEffectAssumption RECEIVER_NOT_NULL =
new SideEffectAssumption() {
@Override
public boolean canAssumeReceiverIsNotNull() {
return true;
}
};
public boolean canAssumeClassIsAlreadyInitialized() {
return false;
}
public boolean canAssumeInvokedMethodDoesNotHaveSideEffects() {
return false;
}
public boolean canIgnoreInstanceFieldAssignmentsToReceiver() {
return false;
}
public boolean canAssumeReceiverIsNotNull() {
return false;
}
public SideEffectAssumption join(SideEffectAssumption other) {
return new SideEffectAssumption() {
@Override
public boolean canAssumeClassIsAlreadyInitialized() {
return SideEffectAssumption.this.canAssumeClassIsAlreadyInitialized()
|| other.canAssumeClassIsAlreadyInitialized();
}
@Override
public boolean canAssumeInvokedMethodDoesNotHaveSideEffects() {
return SideEffectAssumption.this.canAssumeInvokedMethodDoesNotHaveSideEffects()
|| other.canAssumeInvokedMethodDoesNotHaveSideEffects();
}
@Override
public boolean canAssumeReceiverIsNotNull() {
return SideEffectAssumption.this.canAssumeInvokedMethodDoesNotHaveSideEffects()
|| other.canAssumeInvokedMethodDoesNotHaveSideEffects();
}
};
}
}
public abstract static class BuilderBase<B extends BuilderBase<B, I>, I extends Instruction> {
protected Value outValue;
protected Position position;
public abstract I build();
public abstract B self();
final I amend(I instruction) {
if (position != null) {
instruction.setPosition(position);
}
return instruction;
}
public B setOutValue(Value outValue) {
this.outValue = outValue;
return self();
}
public B setFreshOutValue(ValueFactory factory, TypeElement type) {
return setFreshOutValue(factory, type, null);
}
public B setFreshOutValue(ValueFactory factory, TypeElement type, DebugLocalInfo localInfo) {
return setOutValue(factory.createValue(type, localInfo));
}
public B setPosition(Position position) {
this.position = position;
return self();
}
public B setPosition(Instruction other) {
return setPosition(other.getPosition());
}
}
}