Merge "BasicBlock bugfix: Remember to update Phi.definitionUsers"
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 163c20b..60703de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -495,6 +495,8 @@
public void removePhi(Phi phi) {
phis.remove(phi);
+ assert currentDefinitions == null || !currentDefinitions.containsValue(phi)
+ : "Attempt to remove Phi " + phi + " which is present in currentDefinitions";
}
public void add(Instruction next) {
@@ -678,7 +680,17 @@
public void replaceCurrentDefinitions(Value oldValue, Value newValue) {
assert oldValue.definition.getBlock() == this;
assert !oldValue.isUsed();
- currentDefinitions.replaceAll((index, value) -> value == oldValue ? newValue : value);
+ for (Entry<Integer, Value> entry : currentDefinitions.entrySet()) {
+ if (entry.getValue() == oldValue) {
+ if (oldValue.isPhi()) {
+ oldValue.asPhi().removeDefinitionsUser(currentDefinitions);
+ }
+ entry.setValue(newValue);
+ if (newValue.isPhi()) {
+ newValue.asPhi().addDefinitionsUser(currentDefinitions);
+ }
+ }
+ }
}
public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) {
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
new file mode 100644
index 0000000..98db705
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2018, 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;
+
+public class PhiDefinitionsTest {
+
+ public static Class[] CLASSES = {
+ PhiDefinitionsTest.class,
+ MethodWriter.class,
+ };
+
+ static class MethodWriter {
+ public int exceptionCount;
+ }
+
+ public static void main(String[] args) {
+ if (args.length >= 42) {
+ System.out.println(new PhiDefinitionsTest().readMethod(args.length));
+ }
+ }
+
+ private int readMethod(int u) {
+ u += 6;
+ int exception = 0;
+ for (int i = count(u); i > 0; --i) {
+ exception = count(u);
+ for (int j = count(); j > 0; --j) {
+ read(exception);
+ exception += 2;
+ }
+ u += 6 + count(u + 4);
+ }
+ u += 2;
+ MethodWriter mv = visitMethod();
+ if (cond() && cond() && cond()) {
+ MethodWriter mw = mv;
+ boolean sameExceptions = false;
+ if (count() == mw.exceptionCount) {
+ sameExceptions = true;
+ for (int j = count(); j >= 0; --j) {
+ exception -= 2;
+ }
+ }
+ if (cond()) {
+ return u;
+ }
+ }
+ return u;
+ }
+
+ private native MethodWriter visitMethod();
+
+ private native boolean cond();
+
+ private native String read(int i);
+
+ private native int count(int arg);
+
+ private native int count();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
new file mode 100644
index 0000000..5a93117
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
@@ -0,0 +1,509 @@
+// Copyright (c) 2018, 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;
+
+import org.objectweb.asm.*;
+
+public class PhiDefinitionsTestDump implements Opcodes {
+
+ private static final String SIMPLE_NAME = "PhiDefinitionsTest";
+ static final String INTERNAL_NAME = "com/android/tools/r8/ir/" + SIMPLE_NAME;
+ private static final String INNER_SIMPLE_NAME = "MethodWriter";
+ static final String INNER_INTERNAL_NAME = INTERNAL_NAME + "$" + INNER_SIMPLE_NAME;
+
+ // static String INTERNAL_NAME = "com/android/tools/r8/ir/PhiDefinitionsTest";
+ // private static String INNER_SIMPLE_NAME = "MethodWriter";
+ // static String INNER_INTERNAL_NAME = INTERNAL_NAME + '$' + INNER_SIMPLE_NAME;
+
+ public static byte[] dump() throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ AnnotationVisitor av0;
+
+ cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, INTERNAL_NAME, null, "java/lang/Object", null);
+
+ cw.visitSource(SIMPLE_NAME + ".java", null);
+
+ cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+ method0(cw);
+ method1(cw);
+ method2(cw);
+ nativeMethod(cw, "visitMethod", "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";");
+ nativeMethod(cw, "cond", "()Z");
+ nativeMethod(cw, "read", "(I)Ljava/lang/String;");
+ nativeMethod(cw, "count", "(I)I");
+ nativeMethod(cw, "count", "()I");
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private static void nativeMethod(ClassWriter cw, String name, String desc) {
+ MethodVisitor mv;
+ mv = cw.visitMethod(ACC_PRIVATE + ACC_NATIVE, name, desc, null, null);
+ mv.visitEnd();
+ }
+
+ private static void method0(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ Label[] labels = new Label[2];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(6, labels[0]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(labels[1]);
+ mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[1], 0);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+
+ private static void method1(ClassWriter cw) {
+ MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ Label[] labels = new Label[4];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(18, labels[0]);
+ mv.visitLdcInsn(new Integer(42));
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitJumpInsn(IF_ICMPLT, labels[1]);
+ mv.visitLabel(labels[2]);
+ mv.visitLineNumber(19, labels[2]);
+ mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitVarInsn(ASTORE, 1);
+ mv.visitTypeInsn(NEW, INTERNAL_NAME);
+ mv.visitVarInsn(ASTORE, 2);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "<init>", "()V", false);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "readMethod", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 2);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
+ mv.visitLabel(labels[1]);
+ mv.visitLineNumber(21, labels[1]);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(labels[3]);
+ mv.visitLocalVariable("args", "[Ljava/lang/String;", null, labels[0], labels[3], 0);
+ mv.visitMaxs(2, 4);
+ mv.visitEnd();
+ }
+
+ private static void method2(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "readMethod", "(I)I", null, null);
+ Label[] labels = new Label[31];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(24, labels[0]);
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitInsn(POP);
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitVarInsn(ISTORE, 2);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[1]);
+ mv.visitLineNumber(25, labels[1]);
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[2]);
+ mv.visitLineNumber(26, labels[2]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[3]);
+ mv.visitFrame(
+ Opcodes.F_APPEND,
+ 3,
+ new Object[] {Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER},
+ 0,
+ null);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitJumpInsn(IFLE, labels[4]);
+ mv.visitLabel(labels[5]);
+ mv.visitLineNumber(27, labels[5]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[6]);
+ mv.visitLineNumber(28, labels[6]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[7]);
+ mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitJumpInsn(IFLE, labels[8]);
+ mv.visitLabel(labels[9]);
+ mv.visitLineNumber(29, labels[9]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "read", "(I)Ljava/lang/String;", false);
+ mv.visitInsn(POP);
+ mv.visitLabel(labels[10]);
+ mv.visitLineNumber(30, labels[10]);
+ mv.visitInsn(ICONST_2);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_2);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[11]);
+ mv.visitLineNumber(28, labels[11]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[7]);
+ mv.visitLabel(labels[8]);
+ mv.visitLineNumber(32, labels[8]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.TOP,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[12]);
+ mv.visitLineNumber(26, labels[12]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[3]);
+ mv.visitLabel(labels[4]);
+ mv.visitLineNumber(34, labels[4]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 5,
+ new Object[] {INTERNAL_NAME, Opcodes.TOP, Opcodes.TOP, Opcodes.INTEGER, Opcodes.INTEGER},
+ 0,
+ new Object[] {});
+ mv.visitInsn(ICONST_2);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_2);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[13]);
+ mv.visitLineNumber(35, labels[13]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(
+ INVOKESPECIAL,
+ INTERNAL_NAME,
+ "visitMethod",
+ "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";",
+ false);
+ mv.visitVarInsn(ASTORE, 2);
+ mv.visitLabel(labels[14]);
+ mv.visitLineNumber(36, labels[14]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitLabel(labels[16]);
+ mv.visitLineNumber(37, labels[16]);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitVarInsn(ASTORE, 3);
+ mv.visitLabel(labels[17]);
+ mv.visitLineNumber(38, labels[17]);
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[18]);
+ mv.visitLineNumber(39, labels[18]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitFieldInsn(GETFIELD, INNER_INTERNAL_NAME, "exceptionCount", "I");
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitJumpInsn(IF_ICMPNE, labels[19]);
+ mv.visitLabel(labels[20]);
+ mv.visitLineNumber(40, labels[20]);
+ mv.visitInsn(ICONST_1);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[21]);
+ mv.visitLineNumber(41, labels[21]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[22]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 7,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitJumpInsn(IFLT, labels[23]);
+ mv.visitLabel(labels[24]);
+ mv.visitLineNumber(42, labels[24]);
+ mv.visitLdcInsn(new Integer(-2));
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitLabel(labels[25]);
+ mv.visitLineNumber(41, labels[25]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[22]);
+ mv.visitLabel(labels[23]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 7,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.TOP,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[26]);
+ mv.visitLabel(labels[19]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[26]);
+ mv.visitLineNumber(45, labels[26]);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[27]);
+ mv.visitLabel(labels[28]);
+ mv.visitLineNumber(46, labels[28]);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IRETURN);
+ mv.visitLabel(labels[27]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitJumpInsn(GOTO, labels[29]);
+ mv.visitLabel(labels[15]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 5,
+ new Object[] {
+ INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.TOP, Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[29]);
+ mv.visitLineNumber(49, labels[29]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 4,
+ new Object[] {INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.INTEGER},
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IRETURN);
+ mv.visitLabel(labels[30]);
+ mv.visitLocalVariable("u", "I", null, labels[0], labels[3], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[2], labels[3], 3);
+ mv.visitLocalVariable("exception", "I", null, labels[3], labels[7], 4);
+ mv.visitLocalVariable("j", "I", null, labels[7], labels[8], 4);
+ mv.visitLocalVariable("i", "I", null, labels[3], labels[4], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[7], labels[4], 5);
+ mv.visitLocalVariable("u", "I", null, labels[3], labels[13], 3);
+ mv.visitLocalVariable("exception", "I", null, labels[4], labels[22], 4);
+ mv.visitLocalVariable("j", "I", null, labels[22], labels[23], 4);
+ mv.visitLocalVariable("exception", "I", null, labels[22], labels[19], 6);
+ mv.visitLocalVariable("exception", "I", null, labels[19], labels[26], 4);
+ mv.visitLocalVariable("sameExceptions", "Z", null, labels[18], labels[26], 5);
+ mv.visitLocalVariable(
+ "mw", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[17], labels[27], 3);
+ mv.visitLocalVariable("sameExceptions", "Z", null, labels[26], labels[27], 4);
+ mv.visitLocalVariable("exception", "I", null, labels[26], labels[15], 5);
+ mv.visitLocalVariable("exception", "I", null, labels[15], labels[29], 4);
+ mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[30], 0);
+ mv.visitLocalVariable("u", "I", null, labels[13], labels[30], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[29], labels[30], 3);
+ mv.visitLocalVariable(
+ "mv", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[14], labels[30], 2);
+ mv.visitMaxs(3, 8);
+ mv.visitEnd();
+ }
+
+ public static byte[] dumpInner() throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+ AnnotationVisitor av0;
+
+ cw.visit(V1_8, ACC_SUPER, INNER_INTERNAL_NAME, null, "java/lang/Object", null);
+
+ cw.visitSource(SIMPLE_NAME + ".java", null);
+
+ cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+ {
+ fv = cw.visitField(ACC_PUBLIC, "exceptionCount", "I", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "()V", null, null);
+ mv.visitCode();
+ Label l0 = new Label();
+ mv.visitLabel(l0);
+ mv.visitLineNumber(13, l0);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ Label l1 = new Label();
+ mv.visitLabel(l1);
+ mv.visitLocalVariable(
+ "this", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, l0, l1, 0);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
new file mode 100644
index 0000000..f9541e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2018, 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;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+public class PhiDefinitionsTestRunner extends TestBase {
+
+ private ProcessResult runInput;
+ private String className = PhiDefinitionsTest.class.getName();
+
+ private Path writeAndRunOriginal() throws IOException {
+ Path originalJar = temp.getRoot().toPath().resolve("originput.jar");
+ ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(originalJar);
+ for (Class clazz : PhiDefinitionsTest.CLASSES) {
+ String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
+ consumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+ }
+ consumer.finished(null);
+ runOriginalJar(originalJar);
+ return originalJar;
+ }
+
+ private void runOriginalJar(Path originalJar) throws IOException {
+ runInput = ToolHelper.runJava(originalJar, className);
+ if (runInput.exitCode != 0) {
+ System.out.println(runInput);
+ }
+ assertEquals(0, runInput.exitCode);
+ }
+
+ private Path writeAndRunInputJar() throws Exception {
+ Path originalJar = writeAndRunOriginal();
+ Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+ build(originalJar, new ClassFileConsumer.ArchiveConsumer(inputJar));
+ runCf(inputJar, className);
+ return inputJar;
+ }
+
+ private Path writeAndRunDumpJar() throws Exception {
+ Path dumpJar = temp.getRoot().toPath().resolve("dump.jar");
+ ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(dumpJar);
+ String desc = 'L' + PhiDefinitionsTestDump.INTERNAL_NAME + ';';
+ consumer.accept(PhiDefinitionsTestDump.dump(), desc, null);
+ String innerDesc = 'L' + PhiDefinitionsTestDump.INNER_INTERNAL_NAME + ';';
+ consumer.accept(PhiDefinitionsTestDump.dumpInner(), innerDesc, null);
+ consumer.finished(null);
+ runOriginalJar(dumpJar);
+ return dumpJar;
+ }
+
+ @Test
+ public void testCf() throws Exception {
+ Path outCf = temp.getRoot().toPath().resolve("cf.zip");
+ build(writeAndRunInputJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+ runCf(outCf, className);
+ }
+
+ @Test
+ public void testDex() throws Exception {
+ Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+ build(writeAndRunInputJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+ runDex(outDex, className);
+ }
+
+ @Test
+ public void testCfDump() throws Exception {
+ Path outCf = temp.getRoot().toPath().resolve("dump-cf.zip");
+ build(writeAndRunDumpJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+ runCf(outCf, className);
+ }
+
+ @Test
+ public void testDexDump() throws Exception {
+ Path outDex = temp.getRoot().toPath().resolve("dump-dex.zip");
+ build(writeAndRunDumpJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+ runDex(outDex, className);
+ }
+
+ private void runCf(Path outCf, String className) throws Exception {
+ ProcessResult runCf = ToolHelper.runJava(outCf, className);
+ assertEquals(runInput.toString(), runCf.toString());
+ }
+
+ private void runDex(Path outDex, String className) throws Exception {
+ ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), className);
+ assertEquals(runInput.stdout, runDex.stdout);
+ assertEquals(runInput.exitCode, runDex.exitCode);
+ }
+
+ private void build(Path inputJar, ProgramConsumer consumer) throws Exception {
+ Builder builder =
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .setProgramConsumer(consumer)
+ .addProgramFiles(inputJar);
+ if (consumer instanceof ClassFileConsumer) {
+ builder.addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME));
+ } else {
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
+ }
+ // TODO(b/75997473): Enable inlining when supported by CF backend
+ ToolHelper.runR8(
+ builder.build(),
+ options -> {
+ options.enableInlining = false;
+ options.invalidDebugInfoFatal = true;
+ });
+ }
+}