[LIR] Move LIR to after synthetic finalization
This change requires that LIR code can be hashed and compared to create
a deterministic order for each code object.
Bug: b/225838009
Change-Id: I79a36e36c76b95d352ef0e194b33ddabd1648caf
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a306d434..bbaa2b9 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -710,15 +710,15 @@
appView.getArtProfileCollection().withoutMissingItems(appView));
appView.setStartupProfile(appView.getStartupProfile().withoutMissingItems(appView));
- // TODO(b/225838009): Support LIR in synthetic finalization (needs hashing and compareTo).
- PrimaryR8IRConverter.finalizeLirToOutputFormat(appView, timing, executorService);
-
if (appView.appInfo().hasLiveness()) {
SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService, timing);
} else {
SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService, timing);
}
+ // TODO(b/225838009): Move further down.
+ PrimaryR8IRConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+
// Read any -applymapping input to allow for repackaging to not relocate the classes.
timing.begin("read -applymapping file");
appView.loadApplyMappingSeedMapper();
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 2f84316..b40ef2b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.lightir.LirConstant;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -193,6 +195,21 @@
return new HashBuilder().build();
}
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.CALL_SITE;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexCallSite) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
+
private final class HashBuilder {
private ByteArrayOutputStream bytes;
private ObjectOutputStream out;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 5452bf2..3e1856d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -295,6 +295,9 @@
// This call is to remain order consistent with the 'withNullableItem' code.
return visitor.visitBool(code1 != null, code2 != null);
}
+ if (code1.isLirCode() && code2.isLirCode()) {
+ return code1.asLirCode().acceptCompareTo(code2.asLirCode(), visitor);
+ }
if (code1.isCfWritableCode() && code2.isCfWritableCode()) {
return code1.asCfWritableCode().acceptCompareTo(code2.asCfWritableCode(), visitor);
}
@@ -309,6 +312,8 @@
if (code == null) {
// The null code does not contribute to the hash. This should be distinct from non-null as
// code otherwise has a non-empty instruction payload.
+ } else if (code.isLirCode()) {
+ code.asLirCode().acceptHashing(visitor);
} else if (code.isCfWritableCode()) {
code.asCfWritableCode().acceptHashing(visitor);
} else {
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 9fbbdb1..7628bd6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.structural.CompareToVisitor;
@@ -50,10 +51,18 @@
}
@Override
- public void acceptHashing(HashingVisitor visitor) {
- visitor.visitDexType(holder);
- visitor.visitDexString(name);
- getReferencedTypes().forEach(visitor::visitDexType);
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.FIELD;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexField) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
}
@Override
@@ -177,6 +186,11 @@
}
@Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitDexField(this);
+ }
+
+ @Override
public boolean match(DexField field) {
return field.name == name && field.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 1ad1e1b..a8c8ddf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
@@ -63,9 +64,22 @@
@Override
public void acceptHashing(HashingVisitor visitor) {
- visitor.visitDexType(holder);
- visitor.visitDexString(name);
- getReferencedTypes().forEach(visitor::visitDexType);
+ visitor.visitDexMethod(this);
+ }
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.METHOD;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexMethod) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
}
public DexType getArgumentType(int argumentIndex, boolean isStatic) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index a61182e..e48785c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Objects;
@@ -18,6 +20,21 @@
public class DexMethodHandle extends IndexedDexItem
implements NamingLensComparable<DexMethodHandle>, LirConstant {
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.METHOD_HANDLE;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexMethodHandle) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
+
public enum MethodHandleType {
STATIC_PUT((short) 0x00),
STATIC_GET((short) 0x01),
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 3e9d428..6ca01ec 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.Iterables;
@@ -17,6 +19,7 @@
public static final DexProto SENTINEL = new DexProto(null, null, null);
+ // TODO(b/172206529): Consider removing shorty.
public final DexString shorty;
public final DexType returnType;
public final DexTypeList parameters;
@@ -28,10 +31,7 @@
}
private static void specify(StructuralSpecification<DexProto, ?> spec) {
- spec.withItem(DexProto::getReturnType)
- .withItem(p -> p.parameters)
- // TODO(b/172206529): Consider removing shorty.
- .withItem(p1 -> p1.shorty);
+ spec.withItem(DexProto::getReturnType).withItem(p -> p.parameters);
}
@Override
@@ -137,4 +137,19 @@
builder.append(lens.lookupDescriptor(returnType));
return builder.toString();
}
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.PROTO;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexProto) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index f00c125..eea89b2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -98,6 +98,21 @@
visitor.visitDexString(this);
}
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.STRING;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexString) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ visitor.visitDexString(this);
+ }
+
public ThrowingCharIterator<UTFDataFormatException> iterator() {
return new ThrowingCharIterator<UTFDataFormatException>() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index c7f8aa2..204d05e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
@@ -111,6 +112,21 @@
}
@Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.TYPE;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((DexType) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
+
+ @Override
public DexType getContextType() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index f633d53..d26c44c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -4,11 +4,19 @@
package com.android.tools.r8.ir.code;
-public class IRMetadata {
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
+
+public class IRMetadata implements StructuralItem<IRMetadata> {
private long first;
private long second;
+ private static void specify(StructuralSpecification<IRMetadata, ?> spec) {
+ spec.withLong(s -> s.first).withLong(s -> s.second);
+ }
+
public IRMetadata() {}
private IRMetadata(long first, long second) {
@@ -20,6 +28,16 @@
return new IRMetadata(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
}
+ @Override
+ public IRMetadata self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<IRMetadata> getStructuralMapping() {
+ return IRMetadata::specify;
+ }
+
private boolean get(int bit) {
if (bit < 64) {
return isAnySetInFirst(1L << bit);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index cc457a3..d21044f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -40,6 +40,11 @@
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -102,7 +107,8 @@
* <p>The instruction encoding assumes the instruction operand payload size is u1, so the data
* payload is stored in the constant pool instead.
*/
- public static class FillArrayPayload extends InstructionPayload {
+ public static class FillArrayPayload extends InstructionPayload
+ implements StructuralItem<FillArrayPayload> {
public final int element_width;
public final long size;
public final short[] data;
@@ -112,20 +118,80 @@
this.size = size;
this.data = data;
}
+
+ @Override
+ public FillArrayPayload self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<FillArrayPayload> getStructuralMapping() {
+ return FillArrayPayload::specify;
+ }
+
+ private static void specify(StructuralSpecification<FillArrayPayload, ?> spec) {
+ spec.withInt(s -> s.element_width).withLong(s -> s.size).withShortArray(s -> s.data);
+ }
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.FILL_ARRAY;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((FillArrayPayload) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
}
- public static class IntSwitchPayload extends InstructionPayload {
+ public static class IntSwitchPayload extends InstructionPayload
+ implements StructuralItem<IntSwitchPayload> {
public final int[] keys;
public final int[] targets;
+ private static void specify(StructuralSpecification<IntSwitchPayload, ?> spec) {
+ spec.withIntArray(s -> s.keys).withIntArray(s -> s.targets);
+ }
+
public IntSwitchPayload(int[] keys, int[] targets) {
assert keys.length == targets.length;
this.keys = keys;
this.targets = targets;
}
+
+ @Override
+ public IntSwitchPayload self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<IntSwitchPayload> getStructuralMapping() {
+ return IntSwitchPayload::specify;
+ }
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.INT_SWITCH;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((IntSwitchPayload) other, visitor);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
}
- public static class StringSwitchPayload extends InstructionPayload {
+ public static class StringSwitchPayload extends InstructionPayload
+ implements StructuralItem<StringSwitchPayload> {
public final int[] keys;
public final int[] targets;
@@ -134,6 +200,35 @@
this.keys = keys;
this.targets = targets;
}
+
+ private static void specify(StructuralSpecification<StringSwitchPayload, ?> spec) {
+ spec.withIntArray(s -> s.keys).withIntArray(s -> s.targets);
+ }
+
+ @Override
+ public StringSwitchPayload self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<StringSwitchPayload> getStructuralMapping() {
+ return StringSwitchPayload::specify;
+ }
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.STRING_SWITCH;
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ acceptHashing(visitor);
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return acceptCompareTo((StringSwitchPayload) other, visitor);
+ }
}
public static class NameComputationPayload extends InstructionPayload {
@@ -142,6 +237,19 @@
public NameComputationPayload(NameComputationInfo<?> nameComputationInfo) {
this.nameComputationInfo = nameComputationInfo;
}
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.NAME_COMPUTATION;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return 0;
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {}
}
public static class RecordFieldValuesPayload extends InstructionPayload {
@@ -150,6 +258,21 @@
public RecordFieldValuesPayload(DexField[] fields) {
this.fields = fields;
}
+
+ @Override
+ public LirConstantOrder getLirConstantOrder() {
+ return LirConstantOrder.RECORD_FIELD_VALUES;
+ }
+
+ @Override
+ public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
+ return visitor.visitItemArray(fields, ((RecordFieldValuesPayload) other).fields);
+ }
+
+ @Override
+ public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
+ visitor.visitItemArray(fields);
+ }
}
public LirBuilder(
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index 00930a8..e99b870 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -30,21 +30,34 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LirConstant.LirConstantStructuralAcceptor;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.ComparatorUtils;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.RetracerForCodePrinting;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAcceptor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
-public class LirCode<EV> extends Code implements Iterable<LirInstructionView> {
+public class LirCode<EV> extends Code
+ implements StructuralItem<LirCode<EV>>, Iterable<LirInstructionView> {
- public abstract static class PositionEntry {
+ public abstract static class PositionEntry implements StructuralItem<PositionEntry> {
private final int fromInstructionIndex;
@@ -57,6 +70,37 @@
}
public abstract Position getPosition(DexMethod method);
+
+ abstract int getOrder();
+
+ abstract int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor);
+
+ abstract void internalAcceptHashing(HashingVisitor visitor);
+
+ @Override
+ public final PositionEntry self() {
+ return this;
+ }
+
+ @Override
+ public final StructuralMapping<PositionEntry> getStructuralMapping() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public final int acceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
+ int diff = visitor.visitInt(getOrder(), other.getOrder());
+ if (diff != 0) {
+ return diff;
+ }
+ return internalAcceptCompareTo(other, visitor);
+ }
+
+ @Override
+ public final void acceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(getOrder());
+ internalAcceptHashing(visitor);
+ }
}
public static class LinePositionEntry extends PositionEntry {
@@ -75,6 +119,21 @@
public Position getPosition(DexMethod method) {
return SourcePosition.builder().setMethod(method).setLine(line).build();
}
+
+ @Override
+ int getOrder() {
+ return 0;
+ }
+
+ @Override
+ int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
+ return visitor.visitInt(line, ((LinePositionEntry) other).line);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(line);
+ }
}
public static class StructuredPositionEntry extends PositionEntry {
@@ -89,9 +148,24 @@
public Position getPosition(DexMethod method) {
return position;
}
+
+ @Override
+ int getOrder() {
+ return 1;
+ }
+
+ @Override
+ int internalAcceptCompareTo(PositionEntry other, CompareToVisitor visitor) {
+ return position.acceptCompareTo(((StructuredPositionEntry) other).position, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ position.acceptHashing(visitor);
+ }
}
- public static class TryCatchTable {
+ public static class TryCatchTable implements StructuralItem<TryCatchTable> {
private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
public TryCatchTable(Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers) {
@@ -107,9 +181,47 @@
public void forEachHandler(BiConsumer<Integer, CatchHandlers<Integer>> fn) {
tryCatchHandlers.forEach(fn);
}
+
+ @Override
+ public TryCatchTable self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<TryCatchTable> getStructuralMapping() {
+ return TryCatchTable::specify;
+ }
+
+ private static void specify(StructuralSpecification<TryCatchTable, ?> spec) {
+ spec.withInt2CustomItemMap(
+ s -> s.tryCatchHandlers,
+ new StructuralAcceptor<>() {
+ @Override
+ public int acceptCompareTo(
+ CatchHandlers<Integer> item1,
+ CatchHandlers<Integer> item2,
+ CompareToVisitor visitor) {
+ int diff = visitor.visitItemCollection(item1.getGuards(), item2.getGuards());
+ if (diff != 0) {
+ return diff;
+ }
+ return ComparatorUtils.compareLists(item1.getAllTargets(), item2.getAllTargets());
+ }
+
+ @Override
+ public void acceptHashing(CatchHandlers<Integer> item, HashingVisitor visitor) {
+ List<Integer> targets = item.getAllTargets();
+ for (int i = 0; i < targets.size(); i++) {
+ visitor.visitDexType(item.getGuard(i));
+ visitor.visitInt(targets.get(i));
+ }
+ }
+ });
+ }
}
- public static class DebugLocalInfoTable<EV> {
+ public static class DebugLocalInfoTable<EV> implements StructuralItem<DebugLocalInfoTable<EV>> {
+ // TODO(b/225838009): Once EV is removed use an int2ref map here.
private final Map<EV, DebugLocalInfo> valueToLocalMap;
private final Int2ReferenceMap<int[]> instructionToEndUseMap;
@@ -135,6 +247,71 @@
public void forEachLocalDefinition(BiConsumer<EV, DebugLocalInfo> fn) {
valueToLocalMap.forEach(fn);
}
+
+ @Override
+ public DebugLocalInfoTable<EV> self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<DebugLocalInfoTable<EV>> getStructuralMapping() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int acceptCompareTo(DebugLocalInfoTable<EV> other, CompareToVisitor visitor) {
+ int size = valueToLocalMap.size();
+ int diff = Integer.compare(size, other.valueToLocalMap.size());
+ if (diff != 0) {
+ return diff;
+ }
+ if ((instructionToEndUseMap == null) != (other.instructionToEndUseMap == null)) {
+ return instructionToEndUseMap == null ? -1 : 1;
+ }
+ if (instructionToEndUseMap != null) {
+ assert other.instructionToEndUseMap != null;
+ diff =
+ ComparatorUtils.compareInt2ReferenceMap(
+ instructionToEndUseMap,
+ other.instructionToEndUseMap,
+ ComparatorUtils::compareIntArray);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ // We know EV is only instantiated with Integer so this is safe.
+ assert !(valueToLocalMap instanceof Int2ReferenceMap);
+ Int2ReferenceOpenHashMap<DebugLocalInfo> map1 = new Int2ReferenceOpenHashMap<>(size);
+ Int2ReferenceOpenHashMap<DebugLocalInfo> map2 = new Int2ReferenceOpenHashMap<>(size);
+ valueToLocalMap.forEach((k, v) -> map1.put((int) k, v));
+ other.valueToLocalMap.forEach((k, v) -> map2.put((int) k, v));
+ return ComparatorUtils.compareInt2ReferenceMap(
+ map1, map2, (i1, i2) -> i1.acceptCompareTo(i2, visitor));
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(valueToLocalMap.size());
+ ArrayList<Integer> keys = new ArrayList<>(valueToLocalMap.size());
+ valueToLocalMap.forEach((k, v) -> keys.add((int) k));
+ keys.sort(Integer::compareTo);
+ for (int key : keys) {
+ visitor.visitInt(key);
+ valueToLocalMap.get(key).acceptHashing(visitor);
+ }
+ if (instructionToEndUseMap != null) {
+ // Instead of sorting the end map we just sum the keys and values which is commutative.
+ IntBox keySum = new IntBox();
+ IntBox valueSum = new IntBox();
+ instructionToEndUseMap.forEach(
+ (k, v) -> {
+ keySum.increment(k);
+ valueSum.increment(Arrays.hashCode(v));
+ });
+ visitor.visitInt(keySum.get());
+ visitor.visitInt(valueSum.get());
+ }
+ }
}
private final LirStrategyInfo<EV> strategyInfo;
@@ -171,6 +348,20 @@
return new LirBuilder<>(method, strategy, options);
}
+ private static <EV> void specify(StructuralSpecification<LirCode<EV>, ?> spec) {
+ // strategyInfo is compiler meta-data (constant for a given compilation unit).
+ // useDexEstimationStrategy is compiler meta-data (constant for a given compilation unit).
+ spec.withItem(c -> c.irMetadata)
+ .withCustomItemArray(c -> c.constants, LirConstantStructuralAcceptor.getInstance())
+ .withItemArray(c -> c.positionTable)
+ .withInt(c -> c.argumentCount)
+ .withByteArray(c -> c.instructions)
+ .withInt(c -> c.instructionCount)
+ .withNullableItem(c -> c.tryCatchTable)
+ .withNullableItem(c -> c.debugLocalInfoTable)
+ .withAssert(c -> c.metadataMap == null);
+ }
+
/** Should be constructed using {@link LirBuilder}. */
LirCode(
IRMetadata irMetadata,
@@ -205,6 +396,16 @@
}
@Override
+ public LirCode<EV> self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<LirCode<EV>> getStructuralMapping() {
+ return LirCode::specify;
+ }
+
+ @Override
protected int computeHashCode() {
throw new Unreachable("LIR code should not be subject to hashing.");
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirConstant.java b/src/main/java/com/android/tools/r8/lightir/LirConstant.java
index 6c3dc4d..41a7115 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirConstant.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirConstant.java
@@ -4,5 +4,68 @@
package com.android.tools.r8.lightir;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAcceptor;
+
/** Interface for items that can be put in the LIR constant pool. */
-public interface LirConstant {}
+public interface LirConstant {
+
+ enum LirConstantOrder {
+ // DexItem derived constants.
+ STRING,
+ TYPE,
+ FIELD,
+ METHOD,
+ PROTO,
+ METHOD_HANDLE,
+ CALL_SITE,
+ // Payload constants.
+ INT_SWITCH,
+ STRING_SWITCH,
+ FILL_ARRAY,
+ NAME_COMPUTATION,
+ RECORD_FIELD_VALUES
+ }
+
+ class LirConstantStructuralAcceptor implements StructuralAcceptor<LirConstant> {
+ private static final LirConstantStructuralAcceptor INSTANCE =
+ new LirConstantStructuralAcceptor();
+
+ public static LirConstantStructuralAcceptor getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public int acceptCompareTo(LirConstant item1, LirConstant item2, CompareToVisitor visitor) {
+ int diff =
+ visitor.visitInt(
+ item1.getLirConstantOrder().ordinal(), item2.getLirConstantOrder().ordinal());
+ if (diff != 0) {
+ return diff;
+ }
+ return item1.internalLirConstantAcceptCompareTo(item2, visitor);
+ }
+
+ @Override
+ public void acceptHashing(LirConstant item, HashingVisitor visitor) {
+ visitor.visitInt(item.getLirConstantOrder().ordinal());
+ item.internalLirConstantAcceptHashing(visitor);
+ }
+ }
+
+ /** Total order on the subtypes to ensure compareTo order on the general type. */
+ LirConstantOrder getLirConstantOrder();
+
+ /**
+ * Implementation of compareTo on the actual subtypes.
+ *
+ * <p>The implementation can assume that 'other' is of the same subtype, since it must hold that
+ *
+ * <pre>this.getOrder() == other.getOrder()</pre>
+ */
+ int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor);
+
+ /** Implementation of hashing on the actual subtypes. */
+ void internalLirConstantAcceptHashing(HashingVisitor visitor);
+}
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
index 59e398c..34a9076 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
@@ -15,8 +15,14 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
-public class ClassNameComputationInfo extends NameComputationInfo<DexType> {
+public class ClassNameComputationInfo extends NameComputationInfo<DexType>
+ implements StructuralItem<ClassNameComputationInfo> {
public enum ClassNameMapping {
NONE,
@@ -102,6 +108,10 @@
private final int arrayDepth;
private final ClassNameMapping mapping;
+ public static void specify(StructuralSpecification<ClassNameComputationInfo, ?> spec) {
+ spec.withInt(s -> s.arrayDepth).withInt(s -> s.mapping.ordinal());
+ }
+
private ClassNameComputationInfo(ClassNameMapping mapping) {
this(mapping, 0);
}
@@ -139,6 +149,31 @@
}
@Override
+ public ClassNameComputationInfo self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<ClassNameComputationInfo> getStructuralMapping() {
+ return ClassNameComputationInfo::specify;
+ }
+
+ @Override
+ Order getOrder() {
+ return Order.CLASSNAME;
+ }
+
+ @Override
+ int internalAcceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor) {
+ return StructuralItem.super.acceptCompareTo((ClassNameComputationInfo) other, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ StructuralItem.super.acceptHashing(visitor);
+ }
+
+ @Override
public boolean needsToComputeName() {
return mapping.needsToComputeClassName();
}
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
index f9f8cc0..a99056e 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/FieldNameComputationInfo.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
public class FieldNameComputationInfo extends NameComputationInfo<DexField> {
@@ -29,6 +31,22 @@
}
@Override
+ Order getOrder() {
+ return Order.FIELDNAME;
+ }
+
+ @Override
+ int internalAcceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor) {
+ assert this == other;
+ return 0;
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ // Nothing to hash.
+ }
+
+ @Override
public boolean needsToComputeName() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
index 11014f6..02e29aa 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
@@ -9,9 +9,18 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
public abstract class NameComputationInfo<T extends DexReference> {
+ enum Order {
+ CLASSNAME,
+ FIELDNAME,
+ RECORD_MATCH,
+ RECORD_MISMATCH
+ }
+
public final DexString computeNameFor(
DexReference reference,
DexDefinitionSupplier definitions,
@@ -39,6 +48,25 @@
abstract DexString internalComputeNameFor(
T reference, DexDefinitionSupplier definitions, NamingLens namingLens);
+ abstract Order getOrder();
+
+ public int acceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor) {
+ int diff = visitor.visitInt(getOrder().ordinal(), other.getOrder().ordinal());
+ if (diff != 0) {
+ return diff;
+ }
+ return internalAcceptCompareTo(other, visitor);
+ }
+
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(getOrder().ordinal());
+ internalAcceptHashing(visitor);
+ }
+
+ abstract int internalAcceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor);
+
+ abstract void internalAcceptHashing(HashingVisitor visitor);
+
public abstract boolean needsToComputeName();
public abstract boolean needsToRegisterReference();
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/RecordFieldNamesComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/RecordFieldNamesComputationInfo.java
index 253aa47..9d93047 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/RecordFieldNamesComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/RecordFieldNamesComputationInfo.java
@@ -14,7 +14,14 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAcceptor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntFunction;
@@ -43,7 +50,8 @@
}
private static class MissMatchingRecordFieldNamesComputationInfo
- extends RecordFieldNamesComputationInfo {
+ extends RecordFieldNamesComputationInfo
+ implements StructuralItem<MissMatchingRecordFieldNamesComputationInfo> {
private final String[] fieldNames;
@@ -52,6 +60,27 @@
this.fieldNames = fieldNames;
}
+ private static void specify(
+ StructuralSpecification<MissMatchingRecordFieldNamesComputationInfo, ?> spec) {
+ spec.withItemArray(s -> s.fields)
+ .withCustomItem(
+ s -> s.fieldNames,
+ new StructuralAcceptor<>() {
+ @Override
+ public int acceptCompareTo(
+ String[] item1, String[] item2, CompareToVisitor visitor) {
+ return ComparatorUtils.arrayComparator(String::compareTo).compare(item1, item2);
+ }
+
+ @Override
+ public void acceptHashing(String[] item, HashingVisitor visitor) {
+ for (String s : item) {
+ visitor.visitJavaString(s);
+ }
+ }
+ });
+ }
+
@Override
public DexString internalComputeNameFor(
DexType type,
@@ -60,15 +89,47 @@
NamingLens namingLens) {
return internalComputeNameFor(type, definitions, graphLens, i -> fieldNames[i]);
}
+
+ @Override
+ Order getOrder() {
+ return Order.RECORD_MISMATCH;
+ }
+
+ @Override
+ public MissMatchingRecordFieldNamesComputationInfo self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<MissMatchingRecordFieldNamesComputationInfo> getStructuralMapping() {
+ return MissMatchingRecordFieldNamesComputationInfo::specify;
+ }
+
+ @Override
+ int internalAcceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor) {
+ return StructuralItem.super.acceptCompareTo(
+ (MissMatchingRecordFieldNamesComputationInfo) other, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ StructuralItem.super.acceptHashing(visitor);
+ }
}
private static class MatchingRecordFieldNamesComputationInfo
- extends RecordFieldNamesComputationInfo {
+ extends RecordFieldNamesComputationInfo
+ implements StructuralItem<MatchingRecordFieldNamesComputationInfo> {
public MatchingRecordFieldNamesComputationInfo(DexField[] fields) {
super(fields);
}
+ private static void specify(
+ StructuralSpecification<MatchingRecordFieldNamesComputationInfo, ?> spec) {
+ spec.withItemArray(s -> s.fields);
+ }
+
@Override
public DexString internalComputeNameFor(
DexType type,
@@ -86,6 +147,32 @@
.name
.toString());
}
+
+ @Override
+ Order getOrder() {
+ return Order.RECORD_MATCH;
+ }
+
+ @Override
+ public MatchingRecordFieldNamesComputationInfo self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<MatchingRecordFieldNamesComputationInfo> getStructuralMapping() {
+ return MatchingRecordFieldNamesComputationInfo::specify;
+ }
+
+ @Override
+ int internalAcceptCompareTo(NameComputationInfo<?> other, CompareToVisitor visitor) {
+ return StructuralItem.super.acceptCompareTo(
+ (MatchingRecordFieldNamesComputationInfo) other, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ StructuralItem.super.acceptHashing(visitor);
+ }
}
static DexString dexStringFromFieldNames(List<String> fieldNames, DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java b/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java
index 59cac42..36ce846 100644
--- a/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.errors.Unreachable;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -14,13 +16,19 @@
}
public static <T> Comparator<List<T>> listComparator(Comparator<T> comparator) {
- return (List<T> xs, List<T> ys) -> {
- int diff = Integer.compare(xs.size(), ys.size());
- for (int i = 0; i < xs.size() && diff == 0; i++) {
- diff = comparator.compare(xs.get(i), ys.get(i));
- }
- return diff;
- };
+ return (List<T> xs, List<T> ys) -> compareLists(xs, ys, comparator);
+ }
+
+ public static <T extends Comparable<T>> int compareLists(List<T> xs, List<T> ys) {
+ return compareLists(xs, ys, T::compareTo);
+ }
+
+ public static <T> int compareLists(List<T> xs, List<T> ys, Comparator<T> comparator) {
+ int diff = Integer.compare(xs.size(), ys.size());
+ for (int i = 0; i < xs.size() && diff == 0; i++) {
+ diff = comparator.compare(xs.get(i), ys.get(i));
+ }
+ return diff;
}
// Compare pair-wise integers in sequenced order, i.e., (A1, A2), (B1, B2), (C1, C2), ...
@@ -68,4 +76,52 @@
throw new Unreachable();
};
}
+
+ public static <S> Comparator<Int2ReferenceMap<S>> int2ReferenceMapComparator(
+ Comparator<S> comparator) {
+ return (map1, map2) -> compareInt2ReferenceMap(map1, map2, comparator);
+ }
+
+ public static <S> int compareInt2ReferenceMap(
+ Int2ReferenceMap<S> map1, Int2ReferenceMap<S> map2, Comparator<S> comparator) {
+ int sizeDiff = Integer.compare(map1.size(), map2.size());
+ if (sizeDiff != 0) {
+ return sizeDiff;
+ }
+ if (map1.isEmpty()) {
+ assert map2.isEmpty();
+ return 0;
+ }
+ Integer minMissing1 = findSmallestMissingKey(map1, map2);
+ Integer minMissing2 = findSmallestMissingKey(map2, map1);
+ if (minMissing1 != null) {
+ assert minMissing2 != null;
+ return minMissing1.compareTo(minMissing2);
+ }
+ // Keys are equal so compare the values point-wise sorted by the keys.
+ ArrayList<Integer> keys = new ArrayList<>(map1.keySet());
+ keys.sort(Integer::compareTo);
+ for (int key : keys) {
+ S item1 = map1.get(key);
+ S item2 = map2.get(key);
+ int diff = comparator.compare(item1, item2);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
+ }
+
+ private static <S> Integer findSmallestMissingKey(
+ Int2ReferenceMap<S> map1, Int2ReferenceMap<S> map2) {
+ boolean hasMissing = false;
+ int missing = Integer.MAX_VALUE;
+ for (int key : map1.keySet()) {
+ if (!map2.containsKey(key)) {
+ missing = hasMissing ? Math.min(missing, key) : key;
+ hasMissing = true;
+ }
+ }
+ return hasMissing ? missing : null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
index c721599..a476214 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -174,6 +174,22 @@
}
@Override
+ public ItemSpecification<T> withByteArray(Function<T, byte[]> getter) {
+ if (order == 0) {
+ byte[] is1 = getter.apply(item1);
+ byte[] is2 = getter.apply(item2);
+ int minLength = Math.min(is1.length, is2.length);
+ for (int i = 0; i < minLength && order == 0; i++) {
+ order = parent.visitInt(is1[i], is2[i]);
+ }
+ if (order == 0) {
+ order = parent.visitInt(is1.length, is2.length);
+ }
+ }
+ return this;
+ }
+
+ @Override
public ItemSpecification<T> withShortArray(Function<T, short[]> getter) {
if (order == 0) {
short[] is1 = getter.apply(item1);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
index 244d404..c13e0b8 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -85,6 +85,11 @@
}
@Override
+ public HashCodeVisitor<T> withByteArray(Function<T, byte[]> getter) {
+ return amend(Arrays.hashCode(getter.apply(item)));
+ }
+
+ @Override
public HashCodeVisitor<T> withShortArray(Function<T, short[]> getter) {
return amend(Arrays.hashCode(getter.apply(item)));
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
index 538ddcd..7054a40 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
@@ -36,6 +36,8 @@
visitItemIterator(items.iterator(), S::acceptHashing);
}
+ public abstract void visitJavaString(String string);
+
public abstract void visitDexString(DexString string);
public abstract void visitDexType(DexType type);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index 303f212..b355673 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -62,6 +63,11 @@
}
@Override
+ public void visitJavaString(String string) {
+ hash.putBytes(string.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
public void visitDexString(DexString string) {
hash.putBytes(string.content);
}
@@ -139,6 +145,15 @@
}
@Override
+ public ItemSpecification<T> withByteArray(Function<T, byte[]> getter) {
+ byte[] ints = getter.apply(item);
+ for (int i = 0; i < ints.length; i++) {
+ parent.visitInt(ints[i]);
+ }
+ return this;
+ }
+
+ @Override
public ItemSpecification<T> withShortArray(Function<T, short[]> getter) {
short[] ints = getter.apply(item);
for (int i = 0; i < ints.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
index 69d8796..1c0104d 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.utils.structural;
import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -59,6 +62,11 @@
return withCustomItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
}
+ public final <S> V withCustomItemArray(Function<T, S[]> getter, StructuralAcceptor<S> acceptor) {
+ return withCustomItemIterator(
+ getter.andThen(a -> Arrays.asList(a).iterator()), acceptor, acceptor);
+ }
+
/**
* Specification for a "specified" item.
*
@@ -112,6 +120,45 @@
});
}
+ private <S> V withInt2CustomItemMap(
+ Function<T, Int2ReferenceMap<S>> getter,
+ CompareToAccept<S> compare,
+ HashingAccept<S> hasher) {
+ return withCustomItem(
+ getter,
+ new StructuralAcceptor<>() {
+ @Override
+ public int acceptCompareTo(
+ Int2ReferenceMap<S> map1, Int2ReferenceMap<S> map2, CompareToVisitor visitor) {
+ return ComparatorUtils.compareInt2ReferenceMap(
+ map1, map2, (s1, s2) -> compare.acceptCompareTo(s1, s2, visitor));
+ }
+
+ @Override
+ public void acceptHashing(Int2ReferenceMap<S> map, HashingVisitor visitor) {
+ // We might want to optimize this to avoid sorting. Potentiality compute the min-max
+ // range and use a fori, or collect some number of the smallest keys and hash just
+ // those.
+ ArrayList<Integer> keys = new ArrayList<>(map.keySet());
+ keys.sort(Integer::compareTo);
+ for (int key : keys) {
+ visitor.visitInt(key);
+ hasher.acceptHashing(map.get(key), visitor);
+ }
+ }
+ });
+ }
+
+ public final <S> V withInt2CustomItemMap(
+ Function<T, Int2ReferenceMap<S>> getter, StructuralAcceptor<S> acceptor) {
+ return withInt2CustomItemMap(getter, acceptor, acceptor);
+ }
+
+ public final <S extends StructuralItem<S>> V withInt2ItemMap(
+ Function<T, Int2ReferenceMap<S>> getter) {
+ return withInt2CustomItemMap(getter, S::acceptCompareTo, S::acceptHashing);
+ }
+
/**
* Helper to declare an assert on the item.
*
@@ -131,6 +178,8 @@
public abstract V withIntArray(Function<T, int[]> getter);
+ public abstract V withByteArray(Function<T, byte[]> getter);
+
public abstract V withShortArray(Function<T, short[]> getter);
public abstract V withDexReference(Function<T, DexReference> getter);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index ed2dd4e..128178a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -334,9 +334,9 @@
Set<MethodReference> expectedSynthetics =
ImmutableSet.of(
SyntheticItemsTestUtils.syntheticBackportMethod(
- User1.class, 0, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
+ User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
SyntheticItemsTestUtils.syntheticBackportMethod(
- User1.class, 1, Character.class.getMethod("compare", char.class, char.class)),
+ User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
SyntheticItemsTestUtils.syntheticBackportMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 2577312..592281a 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -252,7 +252,7 @@
// int hashCode()
ClassSubject hashCodeHelperClassSubject =
- inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0));
+ inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1));
assertThat(hashCodeHelperClassSubject, notIf(isPresent(), canUseRecords));
MethodSubject hashCodeHelperMethodSubject = hashCodeHelperClassSubject.uniqueMethod();
@@ -269,7 +269,7 @@
// String toString()
ClassSubject toStringHelperClassSubject =
- inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1));
+ inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0));
assertThat(toStringHelperClassSubject, notIf(isPresent(), canUseRecords));
MethodSubject toStringHelperMethodSubject = toStringHelperClassSubject.uniqueMethod();