Merge changes Ic6865d1e,I8dae88db
* changes:
Only use JSRInlinerAdapter if necessary
CF frontend: Canonicalize debug local info
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index 733f2d9..a30c6e6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -216,9 +216,7 @@
// Current stack: ..., value1, value2, value1copy
state.pop();
// Output stack: ..., value1, value2
- throw new Unimplemented(
- "Building IR for CfStackInstruction " + opcode + " not supported");
- // break;
+ break;
}
}
}
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 a992371..3bd4cde 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -187,15 +187,44 @@
}
private void triggerDelayedParsingIfNeccessary() {
- if (context != null) {
- // The SecondVistor is in charge of setting the context to null.
- DexProgramClass owner = context.owner;
- new ClassReader(context.classCache).accept(new SecondVisitor(context, application),
- ClassReader.SKIP_FRAMES);
- assert verifyNoReparseContext(owner);
+ if (this.context != null) {
+ // The SecondVisitor is in charge of setting this.context to null.
+ ReparseContext context = this.context;
+ parseCode(context, false);
+ if (hasJsr(context)) {
+ System.out.println("JarCode: JSR encountered; reparse using JSRInlinerAdapter");
+ parseCode(context, true);
+ assert !hasJsr(context);
+ }
+ assert verifyNoReparseContext(context.owner);
}
}
+ private void parseCode(ReparseContext context, boolean useJsrInliner) {
+ SecondVisitor classVisitor = new SecondVisitor(context, application, useJsrInliner);
+ new ClassReader(context.classCache).accept(classVisitor, ClassReader.SKIP_FRAMES);
+ }
+
+ private boolean hasJsr(ReparseContext context) {
+ for (Code code : context.codeList) {
+ if (hasJsr(code.asJarCode().node)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasJsr(MethodNode node) {
+ Iterator<AbstractInsnNode> it = node.instructions.iterator();
+ while (it.hasNext()) {
+ int opcode = it.next().getOpcode();
+ if (opcode == Opcodes.JSR || opcode == Opcodes.RET) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Fills the MethodNodes of all the methods in the class and removes the ReparseContext.
*/
@@ -203,18 +232,24 @@
private final ReparseContext context;
private final JarApplicationReader application;
+ private final boolean useJsrInliner;
private int methodIndex = 0;
- public SecondVisitor(ReparseContext context, JarApplicationReader application) {
+ public SecondVisitor(
+ ReparseContext context, JarApplicationReader application, boolean useJsrInliner) {
super(Opcodes.ASM6);
this.context = context;
this.application = application;
+ this.useJsrInliner = useJsrInliner;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
- MethodNode node = new JSRInlinerAdapter(null, access, name, desc, signature, exceptions);
+ MethodNode node =
+ useJsrInliner
+ ? new JSRInlinerAdapter(null, access, name, desc, signature, exceptions)
+ : new MethodNode(Opcodes.ASM6, access, name, desc, signature, exceptions);
JarCode code = null;
MethodAccessFlags flags = JarClassFileReader.createMethodAccessFlags(name, access);
if (!flags.isAbstract() && !flags.isNative()) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 57932cc..bd6d3f7 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -64,6 +64,7 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -78,6 +79,12 @@
public class LazyCfCode extends Code {
+ private static class JsrEncountered extends RuntimeException {
+ public JsrEncountered(String s) {
+ super(s);
+ }
+ }
+
public LazyCfCode(
DexMethod method, Origin origin, ReparseContext context, JarApplicationReader application) {
@@ -107,17 +114,34 @@
@Override
public CfCode asCfCode() {
if (code == null) {
+ ReparseContext context = this.context;
assert context != null;
- // The SecondVistor is in charge of setting the context to null.
- DexProgramClass owner = context.owner;
- ClassReader classReader = new ClassReader(context.classCache);
- classReader.accept(new ClassCodeVisitor(context, application), ClassReader.EXPAND_FRAMES);
- assert verifyNoReparseContext(owner);
+ // The ClassCodeVisitor is in charge of setting this.context to null.
+ try {
+ parseCode(context, false);
+ } catch (JsrEncountered e) {
+ System.out.println("LazyCfCode: JSR encountered; reparse using JSRInlinerAdapter");
+ for (Code code : context.codeList) {
+ code.asLazyCfCode().code = null;
+ code.asLazyCfCode().context = context;
+ }
+ try {
+ parseCode(context, true);
+ } catch (JsrEncountered e1) {
+ throw new Unreachable(e1);
+ }
+ }
+ assert verifyNoReparseContext(context.owner);
}
assert code != null;
return code;
}
+ public void parseCode(ReparseContext context, boolean useJsrInliner) {
+ ClassCodeVisitor classVisitor = new ClassCodeVisitor(context, application, useJsrInliner);
+ new ClassReader(context.classCache).accept(classVisitor, ClassReader.EXPAND_FRAMES);
+ }
+
private void setCode(CfCode code) {
assert this.code == null;
assert this.context != null;
@@ -190,11 +214,14 @@
private final ReparseContext context;
private final JarApplicationReader application;
private int methodIndex = 0;
+ private boolean usrJsrInliner;
- ClassCodeVisitor(ReparseContext context, JarApplicationReader application) {
+ ClassCodeVisitor(
+ ReparseContext context, JarApplicationReader application, boolean useJsrInliner) {
super(Opcodes.ASM6);
this.context = context;
this.application = application;
+ this.usrJsrInliner = useJsrInliner;
}
@Override
@@ -206,6 +233,9 @@
DexMethod method = application.getMethod(context.owner.type, name, desc);
assert code.method == method;
MethodCodeVisitor methodVisitor = new MethodCodeVisitor(application, code);
+ if (!usrJsrInliner) {
+ return methodVisitor;
+ }
return new JSRInlinerAdapter(methodVisitor, access, name, desc, signature, exceptions);
}
return null;
@@ -220,6 +250,7 @@
private List<CfInstruction> instructions;
private List<CfTryCatch> tryCatchRanges;
private List<LocalVariableInfo> localVariables;
+ private final Map<DebugLocalInfo, DebugLocalInfo> canonicalDebugLocalInfo = new HashMap<>();
private Map<Label, CfLabel> labelMap;
private final LazyCfCode code;
private DexMethod method;
@@ -581,7 +612,7 @@
type = ValueType.OBJECT;
break;
case Opcodes.RET:
- throw new Unreachable("RET should be handled by the ASM jsr inliner");
+ throw new JsrEncountered("RET should be handled by the ASM jsr inliner");
default:
throw new Unreachable("Unexpected VarInsn opcode: " + opcode);
}
@@ -670,7 +701,7 @@
instructions.add(new CfIf(type, ValueType.OBJECT, target));
break;
case Opcodes.JSR:
- throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+ throw new JsrEncountered("JSR should be handled by the ASM jsr inliner");
default:
throw new Unreachable("Unexpected JumpInsn opcode: " + opcode);
}
@@ -782,14 +813,19 @@
public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end, int index) {
DebugLocalInfo debugLocalInfo =
- new DebugLocalInfo(
- factory.createString(name),
- factory.createType(desc),
- signature == null ? null : factory.createString(signature));
+ canonicalize(
+ new DebugLocalInfo(
+ factory.createString(name),
+ factory.createType(desc),
+ signature == null ? null : factory.createString(signature)));
localVariables.add(
new LocalVariableInfo(index, debugLocalInfo, getLabel(start), getLabel(end)));
}
+ private DebugLocalInfo canonicalize(DebugLocalInfo debugLocalInfo) {
+ return canonicalDebugLocalInfo.computeIfAbsent(debugLocalInfo, o -> debugLocalInfo);
+ }
+
@Override
public void visitLineNumber(int line, Label start) {
instructions.add(new CfPosition(getLabel(start), new Position(line, null, method, null)));
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
index 5e41a25..ec18fac 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
@@ -8,11 +8,15 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.utils.AndroidApp;
import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import org.junit.Test;
public class JumpSubroutineTests extends JasminTestBase {
@@ -22,10 +26,28 @@
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, main);
assertEquals(expected, artResult);
+ String cfFrontendResult = runOnJavaR8CfFrontend(builder, main);
+ assertEquals(expected, cfFrontendResult);
String dxArtResult = runOnArtDx(builder, main);
assertEquals(expected, dxArtResult);
}
+ private String runOnJavaR8CfFrontend(JasminBuilder builder, String main) throws Exception {
+ Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+ Path outputJar = temp.getRoot().toPath().resolve("output.jar");
+ builder.writeJar(inputJar, null);
+ ToolHelper.runR8(
+ R8Command.builder()
+ .addProgramFiles(inputJar)
+ .setOutput(outputJar, OutputMode.ClassFile)
+ .addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME))
+ .build(),
+ options -> options.enableCfFrontend = true);
+ ProcessResult processResult = ToolHelper.runJava(outputJar, main);
+ assertEquals(0, processResult.exitCode);
+ return processResult.stdout;
+ }
+
private void expectDxFailure(JasminBuilder builder) throws Exception {
// This expects this dx failure:
// Uncaught translation error: com.android.dex.util.ExceptionWithContext: returning from
@@ -387,6 +409,8 @@
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, clazz.name);
assertEquals(expected, artResult);
+ String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
+ assertEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@@ -1344,6 +1368,8 @@
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
+ String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
+ assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@@ -1392,6 +1418,8 @@
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
+ String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
+ assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@@ -1441,6 +1469,8 @@
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
+ String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
+ assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}