Maintain per-file PC encoding cutoffs.
Bug: b/231903117
Change-Id: I22bb038f292639b14e950d17485d8e91547412f9
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 173ddd1..667e5b0 100644
--- a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
+++ b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
@@ -30,14 +30,17 @@
public class DebugRepresentation {
+ public static final int NO_PC_ENCODING = -1;
+ public static final int ALWAYS_PC_ENCODING = Integer.MAX_VALUE;
+
public interface DebugRepresentationPredicate {
- boolean useDexPcEncoding(DexProgramClass holder, DexEncodedMethod method);
+ int getDexPcEncodingCutoff(DexProgramClass holder, DexEncodedMethod method);
}
public static DebugRepresentationPredicate none(InternalOptions options) {
assert !options.canUseDexPc2PcAsDebugInformation();
- return (holder, method) -> false;
+ return (holder, method) -> NO_PC_ENCODING;
}
public static DebugRepresentationPredicate fromFiles(
@@ -45,8 +48,8 @@
if (!options.canUseDexPc2PcAsDebugInformation()) {
return none(options);
}
- if (options.canUseNativeDexPcInsteadOfDebugInfo() || options.testing.forcePcBasedEncoding) {
- return (holder, method) -> true;
+ if (options.canUseNativeDexPcInsteadOfDebugInfo()) {
+ return (holder, method) -> ALWAYS_PC_ENCODING;
}
// TODO(b/220999985): Avoid the need to maintain a class-to-file map.
Map<DexProgramClass, VirtualFile> classMapping = new IdentityHashMap<>();
@@ -55,11 +58,11 @@
}
return (holder, method) -> {
if (!isPcCandidate(method)) {
- return false;
+ return NO_PC_ENCODING;
}
VirtualFile file = classMapping.get(holder);
DebugRepresentation cutoffs = file.getDebugRepresentation();
- return cutoffs.usesPcEncoding(method);
+ return cutoffs.getDexPcEncodingCutoff(method);
};
}
@@ -72,8 +75,7 @@
public static void computeForFile(
VirtualFile file, GraphLens graphLens, NamingLens namingLens, InternalOptions options) {
if (!options.canUseDexPc2PcAsDebugInformation()
- || options.canUseNativeDexPcInsteadOfDebugInfo()
- || options.testing.forcePcBasedEncoding) {
+ || options.canUseNativeDexPcInsteadOfDebugInfo()) {
return;
}
// First collect all of the per-pc costs
@@ -106,26 +108,28 @@
}
}
// Second compute the cost of converting to a pc encoding.
- paramCountToCosts.forEach((ignored, summary) -> summary.computeConversionCosts());
+ paramCountToCosts.forEach((ignored, summary) -> summary.computeConversionCosts(options));
// The result is stored on the virtual files for thread safety.
// TODO(b/220999985): Consider just passing this to the line number optimizer once fixed.
file.setDebugRepresentation(new DebugRepresentation(paramCountToCosts));
}
- private boolean usesPcEncoding(DexEncodedMethod method) {
+ private int getDexPcEncodingCutoff(DexEncodedMethod method) {
DexCode code = method.getCode().asDexCode();
DexDebugInfo debugInfo = code.getDebugInfo();
int paramCount = debugInfo.getParameterCount();
CostSummary conversionInfo = paramToInfo.get(paramCount);
- if (conversionInfo.cutoff < 0) {
- return false;
+ if (conversionInfo == null || conversionInfo.cutoff < 0) {
+ // We expect all methods calling this to have computed conversion info.
+ assert conversionInfo != null;
+ return NO_PC_ENCODING;
}
Instruction lastInstruction = getLastExecutableInstruction(code);
if (lastInstruction == null) {
- return false;
+ return NO_PC_ENCODING;
}
int maxPc = lastInstruction.getOffset();
- return maxPc <= conversionInfo.cutoff;
+ return maxPc <= conversionInfo.cutoff ? conversionInfo.cutoff : NO_PC_ENCODING;
}
@Override
@@ -189,7 +193,8 @@
maxPc = Math.max(maxPc, pc);
}
- private void computeConversionCosts() {
+ private void computeConversionCosts(InternalOptions options) {
+ boolean forcePcBasedEncoding = options.testing.forcePcBasedEncoding;
assert !pcToCost.isEmpty();
// Point at which it is estimated that conversion to PC-encoding is viable.
int currentConvertedPc = -1;
@@ -212,7 +217,7 @@
// If the estimated cost is larger we convert. The order here could be either way as
// both the normal cost and converted cost are estimates. Canonicalization could reduce
// the former and compaction could reduce the latter.
- if (normalOutstandingCost > costToConvert) {
+ if (forcePcBasedEncoding || normalOutstandingCost > costToConvert) {
normalConvertedCost += normalOutstandingCost;
normalOutstandingCost = 0;
currentConvertedPc = currentPc;
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 78f612a..e1d4ce4 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -76,10 +76,9 @@
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
-import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
@@ -360,12 +359,10 @@
private interface PcBasedDebugInfoRecorder {
/** Callback to record a code object with a given max instruction PC and parameter count. */
- void recordPcMappingFor(DexCode code, int lastInstructionPc, int parameterCount);
+ void recordPcMappingFor(DexCode code, int parameterCount, int maxEncodingPc);
/** Callback to record a code object with only a single "line". */
- void recordSingleLineFor(DexCode code, int parameterCount);
-
- void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc);
+ void recordSingleLineFor(DexCode code, int parameterCount, int maxEncodingPc);
/**
* Install the correct debug info objects.
@@ -378,11 +375,33 @@
private static class Pc2PcMappingSupport implements PcBasedDebugInfoRecorder {
- // Some DEX VMs require matching parameter count in methods and debug info.
- // Record the max pc for each parameter count so we can share the param count objects.
- private Int2IntMap paramToMaxPc = new Int2IntOpenHashMap();
+ private static class UpdateInfo {
+ final DexCode code;
+ final int paramCount;
+ final int maxEncodingPc;
- private final List<Pair<Integer, DexCode>> codesToUpdate = new ArrayList<>();
+ public UpdateInfo(DexCode code, int paramCount, int maxEncodingPc) {
+ this.code = code;
+ this.paramCount = paramCount;
+ this.maxEncodingPc = maxEncodingPc;
+ }
+
+ // Used as key when building the shared debug info map.
+ // Only param and max-pc are part of the key.
+
+ @Override
+ public boolean equals(Object o) {
+ UpdateInfo that = (UpdateInfo) o;
+ return paramCount == that.paramCount && maxEncodingPc != that.maxEncodingPc;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(paramCount, maxEncodingPc);
+ }
+ }
+
+ private final List<UpdateInfo> codesToUpdate = new ArrayList<>();
// We can only drop single-line debug info if it is OK to lose the source-file info.
// This list is null if we must retain single-line entries.
@@ -393,46 +412,29 @@
}
@Override
- public void recordPcMappingFor(DexCode code, int lastInstructionPc, int parameterCount) {
- codesToUpdate.add(new Pair<>(parameterCount, code));
- int existing = paramToMaxPc.getOrDefault(parameterCount, -1);
- if (existing < lastInstructionPc) {
- paramToMaxPc.put(parameterCount, lastInstructionPc);
- }
+ public void recordPcMappingFor(DexCode code, int parameterCount, int maxEncodingPc) {
+ codesToUpdate.add(new UpdateInfo(code, parameterCount, maxEncodingPc));
}
@Override
- public void recordSingleLineFor(DexCode code, int parameterCount) {
+ public void recordSingleLineFor(DexCode code, int parameterCount, int maxEncodingPc) {
if (singleLineCodesToClear != null) {
singleLineCodesToClear.add(code);
return;
}
- int lastInstructionPc = ArrayUtils.last(code.instructions).getOffset();
- recordPcMappingFor(code, lastInstructionPc, parameterCount);
- }
-
- @Override
- public void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc) {
- if (singleLineCodesToClear != null) {
- singleLineCodesToClear.add(code);
- return;
- }
- recordPcMappingFor(code, lastInstructionPc, parameterCount);
+ recordPcMappingFor(code, parameterCount, maxEncodingPc);
}
@Override
public void updateDebugInfoInCodeObjects() {
- Int2ReferenceMap<DexDebugInfo> debugInfos =
- new Int2ReferenceOpenHashMap<>(paramToMaxPc.size());
+ Object2ReferenceMap<UpdateInfo, DexDebugInfo> debugInfos =
+ new Object2ReferenceOpenHashMap<>();
codesToUpdate.forEach(
entry -> {
- int parameterCount = entry.getFirst();
- DexCode code = entry.getSecond();
DexDebugInfo debugInfo =
debugInfos.computeIfAbsent(
- parameterCount,
- key -> buildPc2PcDebugInfo(paramToMaxPc.get(key), parameterCount));
- code.setDebugInfo(debugInfo);
+ entry, key -> buildPc2PcDebugInfo(key.maxEncodingPc, key.paramCount));
+ entry.code.setDebugInfo(debugInfo);
});
if (singleLineCodesToClear != null) {
singleLineCodesToClear.forEach(c -> c.setDebugInfo(null));
@@ -447,23 +449,18 @@
private static class NativePcSupport implements PcBasedDebugInfoRecorder {
@Override
- public void recordPcMappingFor(DexCode code, int lastInstructionPc, int length) {
+ public void recordPcMappingFor(DexCode code, int length, int maxEncodingPc) {
// Strip the info in full as the runtime will emit the PC directly.
code.setDebugInfo(null);
}
@Override
- public void recordSingleLineFor(DexCode code, int parameterCount) {
+ public void recordSingleLineFor(DexCode code, int parameterCount, int maxEncodingPc) {
// Strip the info at once as it does not conflict with any PC mapping update.
code.setDebugInfo(null);
}
@Override
- public void recordSingleLineFor(DexCode code, int parameterCount, int lastInstructionPc) {
- recordSingleLineFor(code, parameterCount);
- }
-
- @Override
public void updateDebugInfoInCodeObjects() {
// Already null out the info so nothing to do.
}
@@ -572,14 +569,15 @@
kotlinRemapper.currentMethod = definition;
List<MappedPosition> mappedPositions;
Code code = definition.getCode();
- boolean canUseDexPc =
- methods.size() == 1 && representation.useDexPcEncoding(clazz, definition);
+ int pcEncodingCutoff =
+ methods.size() == 1 ? representation.getDexPcEncodingCutoff(clazz, definition) : -1;
+ boolean canUseDexPc = pcEncodingCutoff > 0;
if (code != null) {
if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
if (canUseDexPc) {
mappedPositions =
optimizeDexCodePositionsForPc(
- definition, appView, kotlinRemapper, pcBasedDebugInfo);
+ definition, appView, kotlinRemapper, pcBasedDebugInfo, pcEncodingCutoff);
} else {
mappedPositions =
optimizeDexCodePositions(
@@ -751,7 +749,7 @@
&& definition.getCode().asDexCode().getDebugInfo()
== DexDebugInfoForSingleLineMethod.getInstance()) {
pcBasedDebugInfo.recordSingleLineFor(
- definition.getCode().asDexCode(), method.getParameters().size());
+ definition.getCode().asDexCode(), method.getParameters().size(), pcEncodingCutoff);
}
} // for each method of the group
} // for each method group, grouped by name
@@ -1133,7 +1131,8 @@
DexEncodedMethod method,
AppView<?> appView,
PositionRemapper positionRemapper,
- PcBasedDebugInfoRecorder debugInfoProvider) {
+ PcBasedDebugInfoRecorder debugInfoProvider,
+ int pcEncodingCutoff) {
List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
DexCode dexCode = method.getCode().asDexCode();
@@ -1191,9 +1190,9 @@
&& !mappedPositions.get(0).isOutlineCaller()) {
dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
debugInfoProvider.recordSingleLineFor(
- dexCode, method.getParameters().size(), lastInstructionPc);
+ dexCode, method.getParameters().size(), pcEncodingCutoff);
} else {
- debugInfoProvider.recordPcMappingFor(dexCode, lastInstructionPc, debugInfo.parameters.length);
+ debugInfoProvider.recordPcMappingFor(dexCode, debugInfo.parameters.length, pcEncodingCutoff);
}
return mappedPositions;
}