# HG changeset patch # User Doug Simon # Date 1415702607 -3600 # Node ID 636d3aa761e49e5da871e3ffee764f110af7cbe9 # Parent 0950fa8782c7a72789cf2456fbfc64ad969ba9bf perform both capturing and replay when testing replay compilation and use deep object graph comparison to test compilation results diff -r 0950fa8782c7 -r 636d3aa761e4 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java Tue Nov 11 10:48:27 2014 +0100 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java Tue Nov 11 11:43:27 2014 +0100 @@ -40,6 +40,21 @@ private final Map, Fields> fieldsMap = new HashMap<>(); + public enum Mode { + Capturing, + Replaying + } + + private Mode mode = Mode.Capturing; + + public Mode getMode() { + return mode; + } + + public void setMode(Mode mode) { + this.mode = mode; + } + /** * Gets a descriptor for the fields in a class that can be used for serialization. */ @@ -76,6 +91,29 @@ )); // @formatter:on + private static void registerStaticField(Class declaringClass, String staticFieldName) { + try { + Field f = declaringClass.getDeclaredField(staticFieldName); + assert Modifier.isStatic(f.getModifiers()) : f; + assert Modifier.isFinal(f.getModifiers()) : f; + assert !f.getType().isPrimitive() : f; + f.setAccessible(true); + Object obj = f.get(null); + Field existing = SpecialStaticFields.put(obj, f); + assert existing == null; + } catch (Exception e) { + throw new GraalInternalError(e); + } + } + + /** + * Objects that should not be copied but retrieved from final static fields. + */ + private static final Map SpecialStaticFields = new IdentityHashMap<>(); + static { + registerStaticField(ArrayList.class, "DEFAULTCAPACITY_EMPTY_ELEMENTDATA"); + } + /** * Determines if a given class is a subclass of any class in a given collection of classes. */ @@ -187,7 +225,7 @@ private Object copyFieldOrElement(Deque worklist, Map copies, Object srcValue) { Object dstValue = srcValue; if (srcValue != null && !Proxy.isProxyClass(srcValue.getClass())) { - if (isAssignableTo(srcValue.getClass(), DontCopyClasses)) { + if (isAssignableTo(srcValue.getClass(), DontCopyClasses) || SpecialStaticFields.containsKey(srcValue)) { pool.put(srcValue, srcValue); return srcValue; } @@ -223,6 +261,9 @@ if (isAssignableTo(root.getClass(), DontCopyClasses)) { return root; } + if (SpecialStaticFields.containsKey(root)) { + return root; + } // System.out.printf("----- %s ------%n", s(obj)); assert pool.get(root) == null; Deque worklist = new IdentityLinkedList<>(); @@ -272,7 +313,11 @@ } else { Object value = pool.get(obj); if (value == null) { - value = copy(obj); + if (mode == Mode.Capturing) { + value = copy(obj); + } else { + throw new GraalInternalError("No captured state for %s", obj); + } } return (T) value; } diff -r 0950fa8782c7 -r 636d3aa761e4 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/DeepFieldsEquals.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/DeepFieldsEquals.java Tue Nov 11 11:43:27 2014 +0100 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.common.remote; + +import java.lang.reflect.*; +import java.util.*; + +import com.oracle.graal.compiler.common.*; + +/** + * Utility for structurally comparing two object graphs for equality. The comparison handles cycles + * in the graphs. It also {@linkplain Handler#unproxifyObject(Object) unproxifies} any proxy objects + * encountered. + */ +public class DeepFieldsEquals { + + /** + * Compares the object graphs rooted at {@code a} and {@code b} for equality. This comparison + * handles cycles in the graphs. + * + * @param a + * @param b + * @return a path to the first differing nodes in each graph or {@code null} if the graphs are + * equal + */ + public static String equals(Object a, Object b) { + return equals(a, b, new HashMap<>()); + } + + /** + * Compares the object graphs rooted at {@code a} and {@code b} for equality. This comparison + * handles cycles in the graphs. + * + * @param a + * @param b + * @param fieldsMap + * @return a path to the first differing nodes in each graph or {@code null} if the graphs are + * equal + */ + public static String equals(Object a, Object b, Map, Fields> fieldsMap) { + + Set visited = new HashSet<>(); + LinkedList worklist = new LinkedList<>(); + worklist.addFirst(new Item("", a, b)); + + while (!worklist.isEmpty()) { + Item item = worklist.removeFirst(); + visited.add(item); + + Object f1 = item.f1; + Object f2 = item.f2; + + if (f1 == f2) { + continue; + } + if (f1 == null || f2 == null) { + return String.format("%s: %s != %s", item.path, f1, f2); + } + Class f1Class = f1.getClass(); + Class f2Class = f2.getClass(); + if (f1Class != f2Class) { + return String.format("%s: %s != %s", item.path, f1Class, f2Class); + } + + Class componentType = f1Class.getComponentType(); + if (componentType != null) { + int f1Len = Array.getLength(f1); + int f2Len = Array.getLength(f2); + if (f1Len != f2Len) { + return String.format("%s.length: %s != %s", item.path, f1Len, f2Len); + } + if (componentType.isPrimitive()) { + for (int i = 0; i < f1Len; i++) { + Object e1 = Array.get(f1, i); + Object e2 = Array.get(f2, i); + if (!e1.equals(e2)) { + return String.format("%s[%d]: %s != %s", item.path, i, f1, f2); + } + } + } else { + for (int i = 0; i < f1Len; i++) { + String nextPath = item.path.length() == 0 ? "[" + i + "]" : item.path + "[" + i + "]"; + Item next = new Item(nextPath, Array.get(f1, i), Array.get(f2, i)); + if (!visited.contains(next)) { + worklist.addFirst(next); + } + } + } + } else { + Fields fields = fieldsMap.get(f1Class); + if (fields == null) { + fields = Fields.forClass(f1Class, Object.class, true, null); + fieldsMap.put(f1Class, fields); + } + + for (int i = 0; i < fields.getCount(); ++i) { + Class type = fields.getType(i); + Object e1 = fields.get(f1, i); + Object e2 = fields.get(f2, i); + if (type.isPrimitive()) { + if (!e1.equals(e2)) { + return String.format("%s.%s: %s != %s", item.path, fields.getName(i), e1, e2); + } + } else { + String nextPath = item.path.length() == 0 ? fields.getName(i) : item.path + "." + fields.getName(i); + Item next = new Item(nextPath, e1, e2); + if (!visited.contains(next)) { + worklist.addFirst(next); + } + } + } + } + } + return null; + } + + static class Item { + final String path; + final Object f1; + final Object f2; + + public Item(String path, Object f1, Object f2) { + this.path = path; + this.f1 = Handler.unproxifyObject(f1); + this.f2 = Handler.unproxifyObject(f2); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((f1 == null) ? 0 : System.identityHashCode(f1)); + result = prime * result + ((f2 == null) ? 0 : System.identityHashCode(f2)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Item) { + Item that = (Item) obj; + return this.f1 == that.f1 && this.f2 == that.f2; + } + return false; + } + + @Override + public String toString() { + return path; + } + } + +} diff -r 0950fa8782c7 -r 636d3aa761e4 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java Tue Nov 11 10:48:27 2014 +0100 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java Tue Nov 11 11:43:27 2014 +0100 @@ -41,6 +41,14 @@ return delegate; } + static Object unproxifyObject(Object obj) { + if (obj != null && Proxy.isProxyClass(obj.getClass())) { + Handler h = (Handler) Proxy.getInvocationHandler(obj); + return h.delegate; + } + return obj; + } + static Object[] unproxify(Object[] args) { if (args == null) { return args; diff -r 0950fa8782c7 -r 636d3aa761e4 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Nov 11 10:48:27 2014 +0100 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Nov 11 11:43:27 2014 +0100 @@ -44,6 +44,7 @@ import com.oracle.graal.compiler.GraalCompiler.Request; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.remote.*; +import com.oracle.graal.compiler.common.remote.Context.Mode; import com.oracle.graal.compiler.target.*; import com.oracle.graal.debug.*; import com.oracle.graal.debug.Debug.Scope; @@ -682,7 +683,7 @@ */ private static final boolean TEST_REPLAY = Boolean.getBoolean("graal.testReplay"); - protected CompilationResult replayCompile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) { + protected CompilationResult replayCompile(CompilationResult originalResult, ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) { StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner) : graph; lastCompiledGraph = graphToCompile; @@ -690,12 +691,32 @@ CallingConvention cc = getCallingConvention(getCodeCache(), Type.JavaCallee, graphToCompile.method(), false); try (Context c = new Context(); Debug.Scope s = Debug.scope("ReplayCompiling", new DebugDumpScope("REPLAY", true))) { try { - Request request = new GraalCompiler.Request<>(graphToCompile, null, cc, installedCodeOwner, getProviders(), getBackend(), getCodeCache().getTarget(), null, - getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, getProfilingInfo(graphToCompile), getSpeculationLog(), getSuites(), new CompilationResult(), - CompilationResultBuilderFactory.Default); + // Capturing compilation + Request capturingRequest = c.get(new GraalCompiler.Request<>(graphToCompile, null, cc, installedCodeOwner, getProviders(), getBackend(), getCodeCache().getTarget(), + null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, getProfilingInfo(graphToCompile), getSpeculationLog(), getSuites(), new CompilationResult(), + CompilationResultBuilderFactory.Default)); + CompilationResult capturingResult = GraalCompiler.compile(capturingRequest); + String path = DeepFieldsEquals.equals(originalResult, capturingResult); + if (path != null) { + DeepFieldsEquals.equals(originalResult, capturingResult); + Assert.fail("Capturing replay compilation result differs from original compilation result at " + path); + } + + c.setMode(Mode.Replaying); - request = c.get(request); - return GraalCompiler.compile(request); + // Replay compilation + Request replyRequest = new GraalCompiler.Request<>(graphToCompile, null, cc, capturingRequest.installedCodeOwner, capturingRequest.providers, + capturingRequest.backend, capturingRequest.target, null, capturingRequest.graphBuilderSuite, capturingRequest.optimisticOpts, capturingRequest.profilingInfo, + capturingRequest.speculationLog, capturingRequest.suites, new CompilationResult(), capturingRequest.factory); + + CompilationResult replayResult = GraalCompiler.compile(replyRequest); + path = DeepFieldsEquals.equals(originalResult, replayResult); + if (path != null) { + DeepFieldsEquals.equals(originalResult, replayResult); + Assert.fail("Capturing replay compilation result differs from original compilation result at " + path); + } + + return capturingResult; } catch (Throwable e) { e.printStackTrace(); throw e; @@ -721,7 +742,7 @@ OptimisticOptimizations.ALL, getProfilingInfo(graphToCompile), getSpeculationLog(), getSuites(), new CompilationResult(), CompilationResultBuilderFactory.Default); if (TEST_REPLAY && graph == null) { - replayCompile(installedCodeOwner, null); + replayCompile(res, installedCodeOwner, null); } return res; }