Fix non determinism of D8 with synthetics
Before we would be generating different hashes for the synthetic methods.
Bug: b/359616078
Change-Id: I0cc31845cf773cdd475810160bcfda1b621a28f1
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 3340e46..adecc39 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -26,6 +26,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.ReachabilitySensitiveValue;
+import com.android.tools.r8.utils.structural.HasherWrapper;
+import com.android.tools.r8.utils.structural.StructuralItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -186,11 +188,6 @@
directMethods.add(method);
}
}
- long checksum =
- 7 * (long) directMethods.hashCode()
- + 11 * (long) virtualMethods.hashCode()
- + 13 * (long) staticFields.hashCode()
- + 17 * (long) instanceFields.hashCode();
C clazz =
getClassKind()
.create(
@@ -214,7 +211,7 @@
DexEncodedMethod.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
- c -> checksum,
+ c -> getChecksum(),
null,
ReachabilitySensitiveValue.DISABLED);
if (useSortedMethodBacking) {
@@ -224,4 +221,17 @@
clazz.setVirtualMethods(virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY));
return clazz;
}
+
+ private long getChecksum() {
+ return 7 * hashEntries(virtualMethods, directMethods)
+ + 13 * hashEntries(instanceFields, staticFields);
+ }
+
+ private <S extends StructuralItem<S>> long hashEntries(List<S>... entryLists) {
+ HasherWrapper hasherWrapper = HasherWrapper.murmur3128Hasher();
+ for (List<S> entryList : entryLists) {
+ entryList.stream().sorted().forEach(e -> e.hash(hasherWrapper));
+ }
+ return hasherWrapper.hash().hashCode();
+ }
}
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapDeterminismTest.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapDeterminismTest.java
index 6afaa95..1ba438d 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapDeterminismTest.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapDeterminismTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -51,6 +52,18 @@
assertTrue(Files.exists(logDirectory.resolve("0.log")));
}
+ @Test
+ public void testD8DeterminismWithChecksums() throws Exception {
+ Path logDirectory = temp.newFolder().toPath();
+ Path ref = compileD8WithChecksums(1, logDirectory);
+ for (int i = 2; i <= ITERATIONS; i++) {
+ Path next = compileD8WithChecksums(i, logDirectory);
+ assertProgramsEqual(ref, next);
+ }
+ // Check that setting the determinism checker wrote a log file.
+ assertTrue(Files.exists(logDirectory.resolve("0.log")));
+ }
+
private Path compile(int iteration, Path logDirectory) throws Exception {
System.out.println("= compiling " + iteration + "/" + ITERATIONS + " ======================");
Path out = temp.newFolder().toPath().resolve("out.jar");
@@ -71,4 +84,25 @@
.compile();
return out;
}
+
+ private Path compileD8WithChecksums(int iteration, Path logDirectory) throws Exception {
+ System.out.println("= compiling d8 " + iteration + "/" + ITERATIONS + " =====================");
+ Path out = temp.newFolder().toPath().resolve("out.jar");
+ testForD8(Backend.DEX)
+ .addProgramFiles(ToolHelper.getR8WithRelocatedDeps())
+ .addLibraryProvider(JdkClassFileProvider.fromSystemJdk())
+ .addOptionsModification(
+ options -> {
+ options
+ .getTestingOptions()
+ .setDeterminismChecker(DeterminismChecker.createWithFileBacking(logDirectory));
+ // Ensure that we generate the same check sums, see b/359616078
+ options.encodeChecksums = true;
+ })
+ .allowStdoutMessages()
+ .allowStderrMessages()
+ .setProgramConsumer(new ArchiveConsumer(out))
+ .compile();
+ return out;
+ }
}