blob: b8988c13e95757f4275adff510881aeec2a77ebd [file] [log] [blame]
// Copyright (c) 2017, 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.analysis.type;
import static com.android.tools.r8.ir.analysis.type.TypeLatticeElement.fromDexType;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import java.util.ArrayDeque;
import java.util.Deque;
public class TypeAnalysis {
private enum Mode {
UNSET,
WIDENING, // initial analysis, including fixed-point iteration for phis.
NARROWING, // updating with more specific info, e.g., passing the return value of the inlinee.
}
private Mode mode = Mode.UNSET;
private final AppInfo appInfo;
private final DexEncodedMethod context;
private final Deque<Value> worklist = new ArrayDeque<>();
public TypeAnalysis(AppInfo appInfo, DexEncodedMethod encodedMethod) {
this.appInfo = appInfo;
this.context = encodedMethod;
}
private void analyze() {
while (!worklist.isEmpty()) {
analyzeValue(worklist.poll());
}
}
public void widening(DexEncodedMethod encodedMethod, IRCode code) {
mode = Mode.WIDENING;
assert worklist.isEmpty();
code.topologicallySortedBlocks().forEach(b -> analyzeBasicBlock(encodedMethod, b));
analyze();
}
public void widening(Iterable<Value> values) {
mode = Mode.WIDENING;
assert worklist.isEmpty();
values.forEach(this::enqueue);
analyze();
}
public void narrowing(Iterable<Value> values) {
mode = Mode.NARROWING;
assert worklist.isEmpty();
values.forEach(this::enqueue);
analyze();
}
private void enqueue(Value v) {
assert v != null;
if (!worklist.contains(v)) {
worklist.add(v);
}
}
private void analyzeBasicBlock(DexEncodedMethod encodedMethod, BasicBlock block) {
int argumentsSeen = encodedMethod.accessFlags.isStatic() ? 0 : -1;
for (Instruction instruction : block.getInstructions()) {
Value outValue = instruction.outValue();
if (outValue == null) {
continue;
}
// The type for Argument, a quasi instruction, can be inferred from the method signature.
if (instruction.isArgument()) {
TypeLatticeElement derived;
if (argumentsSeen < 0) {
// Receiver
derived = fromDexType(encodedMethod.method.holder, appInfo,
// Now we try inlining even when the receiver could be null.
encodedMethod != context);
} else {
DexType argType = encodedMethod.method.proto.parameters.values[argumentsSeen];
derived = fromDexType(argType, appInfo, true);
}
argumentsSeen++;
updateTypeOfValue(outValue, derived);
// Note that we don't need to enqueue the out value of arguments here because it's constant.
} else if (instruction.hasInvariantOutType()) {
TypeLatticeElement derived = instruction.evaluate(appInfo);
updateTypeOfValue(outValue, derived);
} else {
enqueue(outValue);
}
}
for (Phi phi : block.getPhis()) {
enqueue(phi);
}
}
private void analyzeValue(Value value) {
TypeLatticeElement derived =
value.isPhi()
? computePhiType(value.asPhi())
: value.definition.evaluate(appInfo);
updateTypeOfValue(value, derived);
}
private void updateTypeOfValue(Value value, TypeLatticeElement type) {
assert mode != Mode.UNSET;
TypeLatticeElement current = value.getTypeLattice();
if (current.equals(type)) {
return;
}
// TODO(b/72693244): This means some newly added Instruction's out value has not updated ever.
// An initial type lattice should be set somehow, and this line should be an assertion instead.
if (type.isBottom()) {
return;
}
if (mode == Mode.WIDENING) {
value.widening(appInfo, type);
} else {
assert mode == Mode.NARROWING;
value.narrowing(appInfo, type);
}
// propagate the type change to (instruction) users if any.
for (Instruction instruction : value.uniqueUsers()) {
Value outValue = instruction.outValue();
if (outValue != null) {
enqueue(outValue);
}
}
// Propagate the type change to phi users if any.
for (Phi phi : value.uniquePhiUsers()) {
enqueue(phi);
}
}
private TypeLatticeElement computePhiType(Phi phi) {
// Type of phi(v1, v2, ..., vn) is the least upper bound of all those n operands.
return TypeLatticeElement.join(
appInfo, phi.getOperands().stream().map(Value::getTypeLattice));
}
public static DexType getRefinedReceiverType(
AppInfoWithSubtyping appInfo, InvokeMethodWithReceiver invoke) {
DexType receiverType = invoke.getInvokedMethod().getHolder();
TypeLatticeElement lattice = invoke.getReceiver().getTypeLattice();
if (lattice.isClassType()) {
DexType refinedType = lattice.asClassTypeLatticeElement().getClassType();
if (refinedType.isSubtypeOf(receiverType, appInfo)) {
return refinedType;
}
}
return receiverType;
}
}