Inlining constraint visitor for jar code
Change-Id: I0203a5bc7d24294948565b7cb9aa081d767b59f2
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index d64eaf0..2c37c6a 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -30,6 +30,7 @@
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
@@ -173,17 +174,27 @@
}
public Constraint computeInliningConstraint(
- AppInfoWithLiveness appInfo, GraphLense graphLense, DexType invocationContext) {
+ DexEncodedMethod encodedMethod,
+ AppInfoWithLiveness appInfo,
+ GraphLense graphLense,
+ DexType invocationContext) {
InliningConstraintVisitor visitor =
- new InliningConstraintVisitor(application, appInfo, graphLense, method, invocationContext);
+ new InliningConstraintVisitor(
+ application, appInfo, graphLense, encodedMethod, invocationContext);
AbstractInsnNode insn = node.instructions.getFirst();
while (insn != null) {
insn.accept(visitor);
if (visitor.isFinished()) {
- break;
+ return visitor.getConstraint();
}
insn = insn.getNext();
}
+ for (TryCatchBlockNode block : node.tryCatchBlocks) {
+ visitor.accept(block);
+ if (visitor.isFinished()) {
+ return visitor.getConstraint();
+ }
+ }
return visitor.getConstraint();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 53bbb7e..23d9a73 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -959,7 +959,7 @@
private Invoke.Type invokeType(MethodInsnNode method) {
switch (method.getOpcode()) {
case Opcodes.INVOKEVIRTUAL:
- if (isCallToPolymorphicSignatureMethod(method)) {
+ if (isCallToPolymorphicSignatureMethod(method.owner, method.name)) {
return Invoke.Type.POLYMORPHIC;
}
return Invoke.Type.VIRTUAL;
@@ -2954,17 +2954,17 @@
return writer.toString();
}
- private boolean isCallToPolymorphicSignatureMethod(MethodInsnNode method) {
- if (method.owner.equals("java/lang/invoke/MethodHandle")) {
- switch (method.name) {
+ public static boolean isCallToPolymorphicSignatureMethod(String owner, String name) {
+ if (owner.equals("java/lang/invoke/MethodHandle")) {
+ switch (name) {
case "invoke":
case "invokeExact":
return true;
default :
return false;
}
- } else if (method.owner.equals("java/lang/invoke/VarHandle")) {
- switch (method.name) {
+ } else if (owner.equals("java/lang/invoke/VarHandle")) {
+ switch (name) {
case "compareAndExchange":
case "compareAndExchangeAcquire":
case "compareAndExchangeRelease":
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index 5652be8..4807b89 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -9,38 +9,42 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.ir.conversion.JarSourceCode;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.TryCatchBlockNode;
// This visitor can be used to determine if a piece of jar code has any instructions that the
// inliner would not be willing to inline. This can be used to determine if a method can be force
// inlined although its IR is still not available.
//
-// TODO(christofferqa): This class is incomplete. Still need to add support for ConstClass,
-// InstanceGet, InstanceOf, InstancePut, Monitor, MoveException, NewArrayEmpty, NewInstance,
-// StaticGet, StaticPut, etc.
+// Note that this class has only been implemented for the hooks in InliningConstraints that may
+// return a non-ALWAYS inlining constraint (e.g., InliningConstraints.forReturn is not called).
public class InliningConstraintVisitor extends MethodVisitor {
private final JarApplicationReader application;
private final AppInfoWithLiveness appInfo;
private final InliningConstraints inliningConstraints;
- private final DexMethod method;
+ private final DexEncodedMethod method;
private final DexType invocationContext;
- private Constraint constraint = Constraint.ALWAYS;
+ private Constraint constraint;
public InliningConstraintVisitor(
JarApplicationReader application,
AppInfoWithLiveness appInfo,
GraphLense graphLense,
- DexMethod method,
+ DexEncodedMethod method,
DexType invocationContext) {
super(ASM6);
this.application = application;
@@ -48,6 +52,10 @@
this.inliningConstraints = new InliningConstraints(appInfo, graphLense);
this.method = method;
this.invocationContext = invocationContext;
+
+ // Model a synchronized method as having a monitor instruction.
+ this.constraint =
+ method.accessFlags.isSynchronized() ? inliningConstraints.forMonitor() : Constraint.ALWAYS;
}
public Constraint getConstraint() {
@@ -64,11 +72,59 @@
return constraint == Constraint.NEVER;
}
+ public void accept(TryCatchBlockNode tryCatchBlock) {
+ // Model a try-catch as a move-exception instruction.
+ updateConstraint(inliningConstraints.forMoveException());
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ DexField field = application.getField(owner, name, desc);
+ switch (opcode) {
+ case Opcodes.GETFIELD:
+ updateConstraint(inliningConstraints.forInstanceGet(field, invocationContext));
+ break;
+
+ case Opcodes.PUTFIELD:
+ updateConstraint(inliningConstraints.forInstancePut(field, invocationContext));
+ break;
+
+ case Opcodes.GETSTATIC:
+ updateConstraint(inliningConstraints.forStaticGet(field, invocationContext));
+ break;
+
+ case Opcodes.PUTSTATIC:
+ updateConstraint(inliningConstraints.forStaticPut(field, invocationContext));
+ break;
+
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (cst instanceof Type && ((Type) cst).getSort() != Type.METHOD) {
+ DexType type = application.getType((Type) cst);
+ updateConstraint(inliningConstraints.forConstClass(type, invocationContext));
+ } else {
+ updateConstraint(inliningConstraints.forConstInstruction());
+ }
+ }
+
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
DexType ownerType = application.getTypeFromName(owner);
DexMethod target = application.getMethod(ownerType, name, desc);
switch (opcode) {
+ case Opcodes.INVOKEDYNAMIC:
+ if (JarSourceCode.isCallToPolymorphicSignatureMethod(owner, name)) {
+ updateConstraint(inliningConstraints.forInvokePolymorphic(target, invocationContext));
+ } else {
+ updateConstraint(inliningConstraints.forInvokeCustom());
+ }
+ break;
+
case Opcodes.INVOKEINTERFACE:
updateConstraint(inliningConstraints.forInvokeInterface(target, invocationContext));
break;
@@ -88,7 +144,7 @@
case Opcodes.INVOKEVIRTUAL:
// Instructions that target a private method in the same class are translated to
// invoke-direct.
- if (target.holder == method.holder) {
+ if (target.holder == method.method.holder) {
DexClass clazz = appInfo.definitionFor(target.holder);
if (clazz != null && clazz.lookupDirectMethod(target) != null) {
updateConstraint(inliningConstraints.forInvokeDirect(target, invocationContext));
@@ -105,10 +161,30 @@
}
@Override
+ public void visitInsn(int opcode) {
+ switch (opcode) {
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ updateConstraint(inliningConstraints.forMonitor());
+ break;
+
+ default:
+ // All instructions here lead to the inlining constraint ALWAYS.
+ }
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ DexType type = application.getTypeFromDescriptor(desc);
+ updateConstraint(inliningConstraints.forInvokeMultiNewArray(type, invocationContext));
+ }
+
+ @Override
public void visitTypeInsn(int opcode, String typeName) {
DexType type = application.getTypeFromName(typeName);
switch (opcode) {
case Opcodes.ANEWARRAY:
+ updateConstraint(inliningConstraints.forNewArrayEmpty(type, invocationContext));
break;
case Opcodes.CHECKCAST:
@@ -116,9 +192,11 @@
break;
case Opcodes.INSTANCEOF:
+ updateConstraint(inliningConstraints.forInstanceOf(type, invocationContext));
break;
case Opcodes.NEW:
+ updateConstraint(inliningConstraints.forNewInstance(type, invocationContext));
break;
default:
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index f38626e..2fbdab2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1373,6 +1373,7 @@
JarCode jarCode = method.getCode().asJarCode();
Constraint constraint =
jarCode.computeInliningConstraint(
+ method,
appInfo,
new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
invocationContext);