Extend LineNumberOptimizer for CF and enable CF in its test.
Change-Id: Ide6fdb38ff5ee9450ada614f6bf3b25cb543122b
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 7508ea0..644f2c2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -35,6 +35,10 @@
return position;
}
+ public CfLabel getLabel() {
+ return label;
+ }
+
@Override
public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
state.setPosition(position);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 00a94a9..f276cb2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -87,6 +87,8 @@
private List<InvokeDirect> thisInitializers;
private Map<NewInstance, CfLabel> newInstanceLabels;
+ private InternalOptions options;
+
// Internal abstraction of the stack values and height.
private static class Stack {
int maxHeight = 0;
@@ -123,6 +125,7 @@
GraphLense graphLense,
InternalOptions options,
AppInfoWithSubtyping appInfo) {
+ this.options = options;
computeInitializers();
types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
splitExceptionalBlocks();
@@ -379,7 +382,10 @@
private void updatePositionAndLocals(Instruction instruction) {
Position position = instruction.getPosition();
boolean didLocalsChange = localsChanged();
- boolean didPositionChange = position.isSome() && position != currentPosition;
+ boolean didPositionChange =
+ position.isSome()
+ && position != currentPosition
+ && (options.debug || instruction.instructionTypeCanThrow());
if (!didLocalsChange && !didPositionChange) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index f10ff69..7d7baf8 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
@@ -119,17 +122,15 @@
// PositionRemapper is a stateful function which takes a position (represented by a
// DexDebugPositionState) and returns a remapped Position.
private interface PositionRemapper {
- Position createRemappedPosition(DexDebugPositionState positionState);
+ Position createRemappedPosition(
+ int line, DexString file, DexMethod method, Position callerPosition);
}
private static class IdentityPositionRemapper implements PositionRemapper {
@Override
- public Position createRemappedPosition(DexDebugPositionState positionState) {
- return new Position(
- positionState.getCurrentLine(),
- positionState.getCurrentFile(),
- positionState.getCurrentMethod(),
- positionState.getCurrentCallerPosition());
+ public Position createRemappedPosition(
+ int line, DexString file, DexMethod method, Position callerPosition) {
+ return new Position(line, file, method, callerPosition);
}
}
@@ -137,13 +138,9 @@
private int nextLineNumber = 1;
@Override
- public Position createRemappedPosition(DexDebugPositionState positionState) {
- Position newPosition =
- new Position(
- nextLineNumber,
- positionState.getCurrentFile(),
- positionState.getCurrentMethod(),
- null);
+ public Position createRemappedPosition(
+ int line, DexString file, DexMethod method, Position callerPosition) {
+ Position newPosition = new Position(nextLineNumber, file, method, null);
++nextLineNumber;
return newPosition;
}
@@ -252,57 +249,14 @@
for (DexEncodedMethod method : methods) {
List<MappedPosition> mappedPositions = new ArrayList<>();
- if (doesContainPositions(method)) {
- // Do the actual processing for each method.
- DexCode dexCode = method.getCode().asDexCode();
- DexDebugInfo debugInfo = dexCode.getDebugInfo();
- List<DexDebugEvent> processedEvents = new ArrayList<>();
-
- // Our pipeline will be:
- // [debugInfo.events] -> eventFilter -> positionRemapper -> positionEventEmitter ->
- // [processedEvents]
- PositionEventEmitter positionEventEmitter =
- new PositionEventEmitter(
- application.dexItemFactory, method.method, processedEvents);
-
- EventFilter eventFilter =
- new EventFilter(
- debugInfo.startLine,
- method.method,
- processedEvents::add,
- positionState -> {
- int currentLine = positionState.getCurrentLine();
- assert currentLine >= 0;
- Position position = positionRemapper.createRemappedPosition(positionState);
- mappedPositions.add(
- new MappedPosition(
- positionState.getCurrentMethod(),
- currentLine,
- positionState.getCurrentCallerPosition(),
- position.line));
- positionEventEmitter.emitPositionEvents(
- positionState.getCurrentPc(), position);
- });
- for (DexDebugEvent event : debugInfo.events) {
- event.accept(eventFilter);
+ Code code = method.getCode();
+ if (code != null) {
+ if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
+ optimizeDexCodePositions(
+ method, application, positionRemapper, mappedPositions, identityMapping);
+ } else if (code.isCfCode() && doesContainPositions(code.asCfCode())) {
+ optimizeCfCodePositions(method, positionRemapper, mappedPositions);
}
-
- DexDebugInfo optimizedDebugInfo =
- new DexDebugInfo(
- positionEventEmitter.getStartLine(),
- debugInfo.parameters,
- processedEvents.toArray(new DexDebugEvent[processedEvents.size()]));
-
- // TODO(tamaskenez) Remove this as soon as we have external tests testing not only the
- // remapping but whether the non-positional debug events remain intact.
- if (identityMapping) {
- assert optimizedDebugInfo.startLine == debugInfo.startLine;
- assert optimizedDebugInfo.events.length == debugInfo.events.length;
- for (int i = 0; i < debugInfo.events.length; ++i) {
- assert optimizedDebugInfo.events[i].equals(debugInfo.events[i]);
- }
- }
- dexCode.setDebugInfo(optimizedDebugInfo);
}
MethodSignature originalSignature = MethodSignature.fromDexMethod(method.method);
@@ -382,21 +336,33 @@
return classNameMapperBuilder.build();
}
+ private static int getMethodStartLine(DexEncodedMethod method) {
+ Code code = method.getCode();
+ if (code == null) {
+ return 0;
+ }
+ if (code.isDexCode()) {
+ DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
+ return dexDebugInfo == null ? 0 : dexDebugInfo.startLine;
+ } else if (code.isCfCode()) {
+ List<CfInstruction> instructions = code.asCfCode().getInstructions();
+ for (CfInstruction instruction : instructions) {
+ if (!(instruction instanceof CfPosition)) {
+ continue;
+ }
+ return ((CfPosition) instruction).getPosition().line;
+ }
+ }
+ return 0;
+ }
+
// Sort by startline, then DexEncodedMethod.slowCompare.
// Use startLine = 0 if no debuginfo.
private static void sortMethods(List<DexEncodedMethod> methods) {
methods.sort(
(lhs, rhs) -> {
- Code lhsCode = lhs.getCode();
- Code rhsCode = rhs.getCode();
- DexCode lhsDexCode =
- lhsCode == null || !lhsCode.isDexCode() ? null : lhsCode.asDexCode();
- DexCode rhsDexCode =
- rhsCode == null || !rhsCode.isDexCode() ? null : rhsCode.asDexCode();
- DexDebugInfo lhsDebugInfo = lhsDexCode == null ? null : lhsDexCode.getDebugInfo();
- DexDebugInfo rhsDebugInfo = rhsDexCode == null ? null : rhsDexCode.getDebugInfo();
- int lhsStartLine = lhsDebugInfo == null ? 0 : lhsDebugInfo.startLine;
- int rhsStartLine = rhsDebugInfo == null ? 0 : rhsDebugInfo.startLine;
+ int lhsStartLine = getMethodStartLine(lhs);
+ int rhsStartLine = getMethodStartLine(rhs);
int startLineDiff = lhsStartLine - rhsStartLine;
if (startLineDiff != 0) return startLineDiff;
return DexEncodedMethod.slowCompare(lhs, rhs);
@@ -453,10 +419,19 @@
private static boolean doesContainPositions(DexEncodedMethod method) {
Code code = method.getCode();
- if (code == null || !code.isDexCode()) {
+ if (code == null) {
return false;
}
- DexDebugInfo debugInfo = code.asDexCode().getDebugInfo();
+ if (code.isDexCode()) {
+ return doesContainPositions(code.asDexCode());
+ } else if (code.isCfCode()) {
+ return doesContainPositions(code.asCfCode());
+ }
+ return false;
+ }
+
+ private static boolean doesContainPositions(DexCode dexCode) {
+ DexDebugInfo debugInfo = dexCode.getDebugInfo();
if (debugInfo == null) {
return false;
}
@@ -467,4 +442,114 @@
}
return false;
}
+
+ private static boolean doesContainPositions(CfCode cfCode) {
+ List<CfInstruction> instructions = cfCode.getInstructions();
+ for (CfInstruction instruction : instructions) {
+ if (instruction instanceof CfPosition) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void optimizeDexCodePositions(
+ DexEncodedMethod method,
+ DexApplication application,
+ PositionRemapper positionRemapper,
+ List<MappedPosition> mappedPositions,
+ boolean identityMapping) {
+ // Do the actual processing for each method.
+ DexCode dexCode = method.getCode().asDexCode();
+ DexDebugInfo debugInfo = dexCode.getDebugInfo();
+ List<DexDebugEvent> processedEvents = new ArrayList<>();
+
+ // Our pipeline will be:
+ // [debugInfo.events] -> eventFilter -> positionRemapper -> positionEventEmitter ->
+ // [processedEvents]
+ PositionEventEmitter positionEventEmitter =
+ new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
+
+ EventFilter eventFilter =
+ new EventFilter(
+ debugInfo.startLine,
+ method.method,
+ processedEvents::add,
+ positionState -> {
+ int currentLine = positionState.getCurrentLine();
+ assert currentLine >= 0;
+ Position position =
+ positionRemapper.createRemappedPosition(
+ positionState.getCurrentLine(),
+ positionState.getCurrentFile(),
+ positionState.getCurrentMethod(),
+ positionState.getCurrentCallerPosition());
+ mappedPositions.add(
+ new MappedPosition(
+ positionState.getCurrentMethod(),
+ currentLine,
+ positionState.getCurrentCallerPosition(),
+ position.line));
+ positionEventEmitter.emitPositionEvents(positionState.getCurrentPc(), position);
+ });
+ for (DexDebugEvent event : debugInfo.events) {
+ event.accept(eventFilter);
+ }
+
+ DexDebugInfo optimizedDebugInfo =
+ new DexDebugInfo(
+ positionEventEmitter.getStartLine(),
+ debugInfo.parameters,
+ processedEvents.toArray(new DexDebugEvent[processedEvents.size()]));
+
+ // TODO(b/111253214) Remove this as soon as we have external tests testing not only the
+ // remapping but whether the non-positional debug events remain intact.
+ if (identityMapping) {
+ assert optimizedDebugInfo.startLine == debugInfo.startLine;
+ assert optimizedDebugInfo.events.length == debugInfo.events.length;
+ for (int i = 0; i < debugInfo.events.length; ++i) {
+ assert optimizedDebugInfo.events[i].equals(debugInfo.events[i]);
+ }
+ }
+ dexCode.setDebugInfo(optimizedDebugInfo);
+ }
+
+ private static void optimizeCfCodePositions(
+ DexEncodedMethod method,
+ PositionRemapper positionRemapper,
+ List<MappedPosition> mappedPositions) {
+ // Do the actual processing for each method.
+ CfCode oldCode = method.getCode().asCfCode();
+ List<CfInstruction> oldInstructions = oldCode.getInstructions();
+ List<CfInstruction> newInstructions = new ArrayList<>(oldInstructions.size());
+ for (int i = 0; i < oldInstructions.size(); ++i) {
+ CfInstruction oldInstruction = oldInstructions.get(i);
+ CfInstruction newInstruction;
+ if (oldInstruction instanceof CfPosition) {
+ CfPosition cfPosition = (CfPosition) oldInstruction;
+ Position oldPosition = cfPosition.getPosition();
+ Position newPosition =
+ positionRemapper.createRemappedPosition(
+ oldPosition.line, oldPosition.file, oldPosition.method, oldPosition.callerPosition);
+ mappedPositions.add(
+ new MappedPosition(
+ oldPosition.method,
+ oldPosition.line,
+ oldPosition.callerPosition,
+ newPosition.line));
+ newInstruction = new CfPosition(cfPosition.getLabel(), newPosition);
+ } else {
+ newInstruction = oldInstruction;
+ }
+ newInstructions.add(newInstruction);
+ }
+ method.setCode(
+ new CfCode(
+ oldCode.getMethod(),
+ oldCode.getMaxStack(),
+ oldCode.getMaxLocals(),
+ newInstructions,
+ oldCode.getTryCatchRanges(),
+ oldCode.getLocalVariables()));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index d944a75..6e1c05f 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -7,12 +7,18 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
+import java.util.Collection;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
/** Tests source file and line numbers on inlined methods. */
+@RunWith(Parameterized.class)
public class LineNumberOptimizationTest extends DebugTestBase {
private static final int[] ORIGINAL_LINE_NUMBERS = {20, 7, 8, 28, 8, 20, 21, 12, 21, 22, 16, 22};
@@ -26,25 +32,53 @@
private static final String FILE2 = CLASS2 + ".java";
private static final String MAIN_SIGNATURE = "([Ljava/lang/String;)V";
+ private RuntimeKind runtimeKind;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> setup() {
+ return ImmutableList.of(
+ new Object[] {"CF", RuntimeKind.CF}, new Object[] {"DEX", RuntimeKind.DEX});
+ }
+
+ public LineNumberOptimizationTest(String name, RuntimeKind runtimeKind) {
+ this.runtimeKind = runtimeKind;
+ }
+
private static DebugTestConfig makeConfig(
LineNumberOptimization lineNumberOptimization,
boolean writeProguardMap,
- boolean dontOptimizeByEnablingDebug)
+ boolean dontOptimizeByEnablingDebug,
+ RuntimeKind runtimeKind)
throws Exception {
- AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
Path outdir = temp.newFolder().toPath();
Path outjar = outdir.resolve("r8_compiled.jar");
- Path proguardMapPath = writeProguardMap ? outdir.resolve("proguard.map") : null;
+
R8Command.Builder builder =
R8Command.builder()
.addProgramFiles(DEBUGGEE_JAR)
- .setMinApiLevel(minSdk.getLevel())
- .addLibraryFiles(ToolHelper.getAndroidJar(minSdk))
- .setMode(dontOptimizeByEnablingDebug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
- .setOutput(outjar, OutputMode.DexIndexed);
- if (proguardMapPath != null) {
- builder.setProguardMapOutputPath(proguardMapPath);
+ .setMode(dontOptimizeByEnablingDebug ? CompilationMode.DEBUG : CompilationMode.RELEASE);
+ DebugTestConfig config = null;
+
+ if (runtimeKind == RuntimeKind.CF) {
+ builder.setOutput(outjar, OutputMode.ClassFile);
+ config = new CfDebugTestConfig(outjar);
+ } else {
+ assert (runtimeKind == RuntimeKind.DEX);
+ AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
+ builder
+ .setMinApiLevel(minSdk.getLevel())
+ .addLibraryFiles(ToolHelper.getAndroidJar(minSdk))
+ .setOutput(outjar, OutputMode.DexIndexed);
+ config = new D8DebugTestConfig();
}
+
+ config.addPaths(outjar);
+ if (writeProguardMap) {
+ Path proguardMapPath = outdir.resolve("proguard.map");
+ builder.setProguardMapOutputPath(proguardMapPath);
+ config.setProguardMap(proguardMapPath);
+ }
+
ToolHelper.runR8(
builder.build(),
options -> {
@@ -53,46 +87,52 @@
}
options.enableInlining = false;
});
- DebugTestConfig config = new D8DebugTestConfig();
- config.addPaths(outjar);
- config.setProguardMap(proguardMapPath);
+
return config;
}
@Test
public void testIdentityCompilation() throws Throwable {
// Compilation will fail if the identity translation does.
- makeConfig(LineNumberOptimization.IDENTITY_MAPPING, true, false);
+ makeConfig(LineNumberOptimization.IDENTITY_MAPPING, true, false, runtimeKind);
}
@Test
public void testNotOptimized() throws Throwable {
- testRelease(makeConfig(LineNumberOptimization.OFF, false, false), ORIGINAL_LINE_NUMBERS);
+ testRelease(
+ makeConfig(LineNumberOptimization.OFF, false, false, runtimeKind), ORIGINAL_LINE_NUMBERS);
}
@Test
public void testNotOptimizedWithMap() throws Throwable {
- testRelease(makeConfig(LineNumberOptimization.OFF, true, false), ORIGINAL_LINE_NUMBERS);
+ testRelease(
+ makeConfig(LineNumberOptimization.OFF, true, false, runtimeKind), ORIGINAL_LINE_NUMBERS);
}
@Test
public void testNotOptimizedByEnablingDebug() throws Throwable {
- testDebug(makeConfig(LineNumberOptimization.OFF, false, true), ORIGINAL_LINE_NUMBERS_DEBUG);
+ testDebug(
+ makeConfig(LineNumberOptimization.OFF, false, true, runtimeKind),
+ ORIGINAL_LINE_NUMBERS_DEBUG);
}
@Test
public void testNotOptimizedByEnablingDebugWithMap() throws Throwable {
- testDebug(makeConfig(LineNumberOptimization.OFF, true, true), ORIGINAL_LINE_NUMBERS_DEBUG);
+ testDebug(
+ makeConfig(LineNumberOptimization.OFF, true, true, runtimeKind),
+ ORIGINAL_LINE_NUMBERS_DEBUG);
}
@Test
public void testOptimized() throws Throwable {
- testRelease(makeConfig(LineNumberOptimization.ON, false, false), OPTIMIZED_LINE_NUMBERS);
+ testRelease(
+ makeConfig(LineNumberOptimization.ON, false, false, runtimeKind), OPTIMIZED_LINE_NUMBERS);
}
@Test
public void testOptimizedWithMap() throws Throwable {
- testRelease(makeConfig(LineNumberOptimization.ON, true, false), ORIGINAL_LINE_NUMBERS);
+ testRelease(
+ makeConfig(LineNumberOptimization.ON, true, false, runtimeKind), ORIGINAL_LINE_NUMBERS);
}
private void testDebug(DebugTestConfig config, int[] lineNumbers) throws Throwable {