Ensure pc2pc encoding of debug info has non-zero line info.
Bug: b/235319568
Change-Id: I151b52dcfdbe269a43f0dec1d3d1f0fa198c63cf
diff --git a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
index 516abb9..dcc31f7 100644
--- a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
+++ b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
@@ -464,8 +464,12 @@
}
public static Instruction getLastExecutableInstruction(DexCode code) {
+ return getLastExecutableInstruction(code.instructions);
+ }
+
+ public static Instruction getLastExecutableInstruction(Instruction[] instructions) {
Instruction lastInstruction = null;
- for (Instruction instruction : code.instructions) {
+ for (Instruction instruction : instructions) {
if (!instruction.isPayload()) {
lastInstruction = instruction;
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 7a3ccf5..d7266cd 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InstructionFactory;
+import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.ApplicationReaderMap;
import com.android.tools.r8.graph.ClassAccessFlags;
@@ -129,6 +130,13 @@
// Mapping from offset to dex item;
private Int2ReferenceMap<Object> offsetMap = new Int2ReferenceOpenHashMap<>();
+ // Mapping from offset to cached debug info that is not pc2pc based.
+ // This is a secondary map that is used for each debug info item that structurally looks like
+ // a pc2pc encoding but which is referenced from methods that don't fit within the encoding.
+ // This can happen because the two overlap in representation.
+ private Int2ReferenceMap<EventBasedDebugInfo> nonPcBasedDebugInfo =
+ new Int2ReferenceOpenHashMap<>();
+
// Factory to canonicalize certain dexitems.
private final DexItemFactory dexItemFactory;
@@ -519,13 +527,36 @@
parameters);
}
- private DexDebugInfo debugInfoAt(int offset) {
- return (DexDebugInfo) cacheAt(offset, this::parseDebugInfo);
+ private DexDebugInfo debugInfoAt(int offset, Instruction[] instructions) {
+ DexDebugInfo debugInfo = (DexDebugInfo) cacheAt(offset, this::parseDebugInfoAllowPc2PcEncoding);
+ // If the debug information matches a pc2pc encoding check that the instructions are within
+ // the max-pc bound of this method. If not, the info is not an actual pc encoding. Re-read the
+ // info as a normal event based encoding (and cache it to preserve sharing).
+ if (debugInfo != null && debugInfo.isPcBasedInfo()) {
+ PcBasedDebugInfo pcBasedInfo = debugInfo.asPcBasedInfo();
+ int maxPc = pcBasedInfo.getMaxPc();
+ Instruction last = DebugRepresentation.getLastExecutableInstruction(instructions);
+ if (last.getOffset() > maxPc) {
+ return nonPcBasedDebugInfo.computeIfAbsent(
+ offset, this::parseDebugInfoDisallowPc2PcEncoding);
+ }
+ }
+ return debugInfo;
}
- private DexDebugInfo parseDebugInfo() {
+ private DexDebugInfo parseDebugInfoAllowPc2PcEncoding() {
+ return parseDebugInfo(true);
+ }
+
+ private EventBasedDebugInfo parseDebugInfoDisallowPc2PcEncoding(int offset) {
+ dexReader.position(offset);
+ EventBasedDebugInfo debugInfo = parseDebugInfo(false).asEventBasedInfo();
+ return debugInfo;
+ }
+
+ private DexDebugInfo parseDebugInfo(boolean allowPc2PcEncoding) {
int start = dexReader.getUleb128();
- boolean isPcBasedDebugInfo = start == 0;
+ boolean isPcBasedDebugInfo = allowPc2PcEncoding && start == PcBasedDebugInfo.START_LINE;
int parametersSize = dexReader.getUleb128();
DexString[] parameters = new DexString[parametersSize];
for (int i = 0; i < parametersSize; i++) {
@@ -983,13 +1014,15 @@
}
}
}
- // Store and restore offset information around reading debug info.
- int saved = dexReader.position();
- DexDebugInfo debugInfo = debugInfoAt(debugInfoOff);
- dexReader.position(saved);
InstructionFactory factory = new InstructionFactory();
Instruction[] instructions =
factory.readSequenceFrom(ShortBuffer.wrap(code), 0, code.length, indexedItems);
+
+ // Store and restore offset information around reading debug info.
+ int saved = dexReader.position();
+ DexDebugInfo debugInfo = debugInfoAt(debugInfoOff, instructions);
+ dexReader.position(saved);
+
return new DexCode(registerSize, insSize, outsSize, instructions, tries, handlers, debugInfo);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 0b944c3..a7c9b0d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -85,7 +85,7 @@
}
public static class PcBasedDebugInfo extends DexDebugInfo implements DexDebugInfoForWriting {
- private static final int START_LINE = 0;
+ public static final int START_LINE = 1;
private final int parameterCount;
private final int maxPc;
@@ -302,9 +302,15 @@
assert DebugRepresentation.verifyLastExecutableInstructionWithinBound(
code, pcBasedDebugInfo.maxPc);
// Generate a line event at each throwing instruction.
- List<DexDebugEvent> events = new ArrayList<>(code.instructions.length);
+ Instruction[] instructions = code.instructions;
+ return forceConvertToEventBasedDebugInfo(pcBasedDebugInfo, instructions, factory);
+ }
+
+ public static EventBasedDebugInfo forceConvertToEventBasedDebugInfo(
+ PcBasedDebugInfo pcBasedDebugInfo, Instruction[] instructions, DexItemFactory factory) {
+ List<DexDebugEvent> events = new ArrayList<>(instructions.length);
int delta = 0;
- for (Instruction instruction : code.instructions) {
+ for (Instruction instruction : instructions) {
if (instruction.canThrow()) {
DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory);
delta = 0;
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 f75f98e..3cff666 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -371,6 +371,8 @@
* items to be installed.
*/
void updateDebugInfoInCodeObjects();
+
+ int getPcEncoding(int pc);
}
private static class Pc2PcMappingSupport implements PcBasedDebugInfoRecorder {
@@ -411,8 +413,10 @@
singleLineCodesToClear = allowDiscardingSourceFile ? new ArrayList<>() : null;
}
- private int getLastInstructionOffset(DexCode code) {
- return DebugRepresentation.getLastExecutableInstruction(code).getOffset();
+ @Override
+ public int getPcEncoding(int pc) {
+ assert pc >= 0;
+ return pc + 1;
}
private boolean cantAddToClearSet(ProgramMethod method) {
@@ -464,6 +468,12 @@
private static class NativePcSupport implements PcBasedDebugInfoRecorder {
+ @Override
+ public int getPcEncoding(int pc) {
+ assert pc >= 0;
+ return pc;
+ }
+
private void clearDebugInfo(ProgramMethod method) {
// Always strip the info in full as the runtime will emit the PC directly.
method.getDefinition().getCode().asDexCode().setDebugInfo(null);
@@ -1175,6 +1185,7 @@
singleOriginalLine.set(false);
}
remapAndAddForPc(
+ debugInfoProvider,
lastPosition.getFirst(),
getCurrentPc(),
lastPosition.getSecond(),
@@ -1194,6 +1205,7 @@
int lastInstructionPc = DebugRepresentation.getLastExecutableInstruction(dexCode).getOffset();
if (lastPosition.getSecond() != null) {
remapAndAddForPc(
+ debugInfoProvider,
lastPosition.getFirst(),
lastInstructionPc + 1,
lastPosition.getSecond(),
@@ -1274,6 +1286,7 @@
}
private static void remapAndAddForPc(
+ PcBasedDebugInfoRecorder debugInfoProvider,
int startPc,
int endPc,
Position position,
@@ -1288,7 +1301,7 @@
oldPosition.getMethod(),
oldPosition.getLine(),
oldPosition.getCallerPosition(),
- currentPc,
+ debugInfoProvider.getPcEncoding(currentPc),
// Outline info is placed exactly on the positions that relate to it so we should
// only emit it for the first entry.
firstEntry && oldPosition.isOutline(),