changeset 18305:da76d42c397e

preliminary code for managing and testing replay/remote compilation
author Doug Simon <doug.simon@oracle.com>
date Fri, 07 Nov 2014 14:50:43 +0100
parents bf586af6fa0c
children 229dc0d72f2f 56cc1a799a60
files graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Invocation.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/ProxyUtil.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java
diffstat 5 files changed, 566 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java	Fri Nov 07 14:50:43 2014 +0100
@@ -0,0 +1,316 @@
+/*
+ * 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 sun.awt.util.*;
+
+import com.oracle.graal.api.code.Register.RegisterCategory;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+
+/**
+ * Manages a context for replay or remote compilation.
+ */
+public class Context implements AutoCloseable {
+
+    private static final ThreadLocal<Context> currentContext = new ThreadLocal<>();
+
+    private final Map<Class<?>, Fields> fieldsMap = new HashMap<>();
+
+    /**
+     * Gets a descriptor for the fields in a class that can be used for serialization.
+     */
+    private Fields fieldsFor(Class<?> c) {
+        Fields fields = fieldsMap.get(c);
+        if (fields == null) {
+            fields = Fields.forClass(c, Object.class, true, null);
+            fieldsMap.put(c, fields);
+        }
+        return fields;
+    }
+
+    /**
+     * Classes whose values are subject to special serialization handling.
+     */
+    // @formatter:off
+    private static final Set<Class<?>> DontCopyClasses = new HashSet<>(Arrays.asList(
+        Enum.class,
+        Integer.class,
+        Boolean.class,
+        Short.class,
+        Byte.class,
+        Character.class,
+        Float.class,
+        Long.class,
+        Double.class,
+        String.class,
+        Method.class,
+        Class.class,
+        Field.class,
+        Constructor.class,
+        RegisterCategory.class,
+        NamedLocationIdentity.class
+    ));
+    // @formatter:on
+
+    /**
+     * Determines if a given class is a subclass of any class in a given collection of classes.
+     */
+    private static boolean isAssignableTo(Class<?> from, Collection<Class<?>> to) {
+        return to.stream().anyMatch(c -> c.isAssignableFrom(from));
+    }
+
+    /**
+     * Gets a string representing the identity of an object.
+     */
+    private static String id(Object o) {
+        if (o == null) {
+            return "null";
+        }
+        return String.format("%s@%x", MetaUtil.getSimpleName(o.getClass(), true), System.identityHashCode(o));
+    }
+
+    /**
+     * Process an object graph copy operation iteratively using a worklist.
+     */
+    private void copy0(Deque<Object> worklist, Map<Object, Object> copies) {
+        // 1. Traverse object graph, making uninitialized copies of
+        // objects that need to be copied (other object values are transferred 'as is')
+        while (!worklist.isEmpty()) {
+            Object obj = worklist.pollFirst();
+            // System.out.printf("worklist-: %s%n", s(obj));
+            assert copies.get(obj) == copies : id(obj) + " -> " + id(copies.get(obj));
+            assert pool.get(obj) == null;
+            Class<? extends Object> clazz = obj.getClass();
+            Class<?> componentType = clazz.getComponentType();
+            if (componentType != null) {
+                if (componentType.isPrimitive()) {
+                    if (obj instanceof int[]) {
+                        copies.put(obj, ((int[]) obj).clone());
+                    } else if (obj instanceof byte[]) {
+                        copies.put(obj, ((byte[]) obj).clone());
+                    } else if (obj instanceof char[]) {
+                        copies.put(obj, ((char[]) obj).clone());
+                    } else if (obj instanceof short[]) {
+                        copies.put(obj, ((short[]) obj).clone());
+                    } else if (obj instanceof float[]) {
+                        copies.put(obj, ((float[]) obj).clone());
+                    } else if (obj instanceof long[]) {
+                        copies.put(obj, ((long[]) obj).clone());
+                    } else if (obj instanceof double[]) {
+                        copies.put(obj, ((double[]) obj).clone());
+                    } else {
+                        copies.put(obj, ((boolean[]) obj).clone());
+                    }
+                } else {
+                    Object[] o = (Object[]) obj;
+                    Object[] c = o.clone();
+                    copies.put(obj, c);
+                    // System.out.printf("m+: %s%n", s(obj));
+                    for (int i = 0; i < c.length; i++) {
+                        c[i] = copyFieldOrElement(worklist, copies, o[i]);
+                    }
+                }
+            } else {
+                assert !isAssignableTo(clazz, DontCopyClasses);
+                Object c;
+                try {
+                    c = UnsafeAccess.unsafe.allocateInstance(clazz);
+                    copies.put(obj, c);
+                    // System.out.printf("m+: %s%n", s(obj));
+
+                    Fields fields = fieldsFor(clazz);
+                    fields.copy(obj, c, (i, o) -> copyFieldOrElement(worklist, copies, o));
+                } catch (InstantiationException e) {
+                    throw new GraalInternalError(e);
+                }
+            }
+        }
+
+        // 2. Initialize fields of copied objects
+        for (Map.Entry<Object, Object> e : copies.entrySet()) {
+            Object src = e.getKey();
+            Object dst = e.getValue();
+            assert dst != copies : id(src);
+            pool.put(src, dst);
+            if (src instanceof Object[]) {
+                Object[] srcArr = (Object[]) src;
+                Object[] dstArr = (Object[]) dst;
+                for (int i = 0; i < srcArr.length; i++) {
+                    Object dstElement = copies.get(srcArr[i]);
+                    if (dstElement != null) {
+                        dstArr[i] = dstElement;
+                    }
+                }
+            } else {
+                Fields fields = fieldsFor(src.getClass());
+                assert !Proxy.isProxyClass(dst.getClass());
+                for (int index = 0; index < fields.getCount(); index++) {
+                    if (!fields.getType(index).isPrimitive()) {
+                        Object value = copies.get(fields.getObject(src, index));
+                        if (value != null) {
+                            fields.set(dst, index, value);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Given the value of an object field or object array element, returns the value that should be
+     * written into the copy of the object or array.
+     */
+    private Object copyFieldOrElement(Deque<Object> worklist, Map<Object, Object> copies, Object srcValue) {
+        Object dstValue = srcValue;
+        if (srcValue != null && !Proxy.isProxyClass(srcValue.getClass())) {
+            if (isAssignableTo(srcValue.getClass(), DontCopyClasses)) {
+                pool.put(srcValue, srcValue);
+                return srcValue;
+            }
+            dstValue = pool.get(srcValue);
+            if (dstValue == null) {
+                if (srcValue instanceof Remote) {
+                    dstValue = get(srcValue);
+                } else {
+                    dstValue = copies.get(srcValue);
+                    if (dstValue == null) {
+                        assert !worklist.contains(srcValue) : id(srcValue);
+                        // System.out.printf("worklist+: %s%n", s(srcValue));
+                        worklist.add(srcValue);
+                        copies.put(srcValue, copies);
+                    } else if (dstValue == copies) {
+                        dstValue = null;
+                    }
+                }
+            }
+        }
+        return dstValue;
+    }
+
+    /**
+     * Copies an object graph. This operation does not copy:
+     * <ul>
+     * <li>objects whose type is assignable to one of {@link #DontCopyClasses}</li>
+     * <li>proxy objects</li>
+     * </ul>
+     * In addition, copies in {@link #pool} are re-used.
+     */
+    private Object copy(Object root) {
+        if (isAssignableTo(root.getClass(), DontCopyClasses)) {
+            return root;
+        }
+        // System.out.printf("----- %s ------%n", s(obj));
+        assert pool.get(root) == null;
+        Deque<Object> worklist = new IdentityLinkedList<>();
+        worklist.add(root);
+        IdentityHashMap<Object, Object> copies = new IdentityHashMap<>();
+        copies.put(root, copies);
+        copy0(worklist, copies);
+        return pool.get(root);
+    }
+
+    /**
+     * Creates an opens a context for a remote compilation request or a replay compilation
+     * capturing. This should be used in conjunction with the try-finally statement:
+     *
+     * <pre>
+     * try (Context c = new Context()) {
+     *     ...
+     * }
+     * </pre>
+     *
+     * Open one context can be active at any time for a thread.
+     */
+    public Context() {
+        assert currentContext.get() == null : currentContext.get();
+        currentContext.set(this);
+    }
+
+    private final Map<Object, Object> proxies = new IdentityHashMap<>();
+    private final Map<Object, Object> pool = new IdentityHashMap<>();
+
+    /**
+     * Gets the value of a given object within this context.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T get(Object obj) {
+        if (obj == null || Proxy.isProxyClass(obj.getClass())) {
+            return (T) obj;
+        }
+        if (obj instanceof Remote) {
+            Object proxy = proxies.get(obj);
+            if (proxy == null) {
+                Class<?>[] interfaces = ProxyUtil.getAllInterfaces(obj.getClass());
+                proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), interfaces, new Handler<>(obj, this));
+                proxies.put(obj, proxy);
+            }
+            return (T) proxy;
+        } else {
+            Object value = pool.get(obj);
+            if (value == null) {
+                value = copy(obj);
+            }
+            return (T) value;
+        }
+    }
+
+    public void close() {
+        assert currentContext.get() == this : currentContext.get();
+        currentContext.set(null);
+    }
+
+    /**
+     * Checks that a given value is valid within the {@linkplain #getCurrent() current context} (if
+     * any).
+     */
+    public static boolean check(Object o) {
+        if (o != null) {
+            Context c = currentContext.get();
+            if (c != null) {
+                if (o instanceof Remote) {
+                    if (!Proxy.isProxyClass(o.getClass())) {
+                        throw new GraalInternalError("Expecting proxy, found instance of %s", o.getClass());
+                    }
+                } else {
+                    if (!Proxy.isProxyClass(o.getClass())) {
+                        throw new GraalInternalError("Expecting instance of %s, found proxy", o.getClass());
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets the currently active context for the calling thread.
+     *
+     * @return {@code null} if there is no context active on the calling thread
+     */
+    public static Context getCurrent() {
+        return currentContext.get();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java	Fri Nov 07 14:50:43 2014 +0100
@@ -0,0 +1,89 @@
+/*
+ * 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.api.meta.*;
+import com.oracle.graal.api.meta.Remote.PureFunction;
+
+@SuppressWarnings("unused")
+public class Handler<T> implements InvocationHandler {
+
+    private final T delegate;
+    private final Context context;
+
+    Map<Invocation, Object> constantDataInvocations = new HashMap<>();
+
+    public Handler(T delegate, Context context) {
+        this.delegate = delegate;
+        this.context = context;
+    }
+
+    public T getDelegate() {
+        return delegate;
+    }
+
+    static Object[] unproxify(Object[] args) {
+        if (args == null) {
+            return args;
+        }
+        Object[] res = args;
+        for (int i = 0; i < args.length; i++) {
+            Object arg = args[i];
+            if (arg != null && Proxy.isProxyClass(arg.getClass())) {
+                Handler<?> h = (Handler<?>) Proxy.getInvocationHandler(arg);
+                if (res == args) {
+                    res = args.clone();
+                }
+                res[i] = h.delegate;
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] a) throws Throwable {
+        Object[] args = unproxify(a);
+        boolean isConstantData = method.getAnnotation(PureFunction.class) != null;
+        Invocation invocation = new Invocation(method, delegate, args);
+        if (isConstantData) {
+            if (constantDataInvocations.containsKey(invocation)) {
+                Object result = constantDataInvocations.get(invocation);
+                assert Objects.deepEquals(result, invocation.invoke());
+                // System.out.println(invocation + ": " + result);
+                return result;
+            }
+        } else {
+            // System.out.println("not pure: " + method);
+        }
+
+        Object result = invocation.invoke();
+        result = context.get(result);
+        if (isConstantData) {
+            constantDataInvocations.put(invocation, result);
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Invocation.java	Fri Nov 07 14:50:43 2014 +0100
@@ -0,0 +1,79 @@
+/*
+ * 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 java.util.stream.*;
+
+class Invocation {
+    final Method method;
+    final Object receiver;
+    final Object[] args;
+
+    public Invocation(Method method, Object receiver, Object[] args) {
+        this.method = method;
+        this.receiver = receiver;
+        this.args = args;
+    }
+
+    @Override
+    public String toString() {
+        String res = receiver.getClass().getSimpleName() + "@" + System.identityHashCode(receiver) + "." + method.getName();
+        if (args == null) {
+            return res + "()";
+        }
+        return res + "(" + Arrays.asList(args).stream().map(o -> String.valueOf(o)).collect(Collectors.joining(", ")) + ")";
+    }
+
+    public Object invoke() {
+        try {
+            if (args == null) {
+                return method.invoke(receiver);
+            } else {
+                return method.invoke(receiver, args);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Invocation) {
+            Invocation that = (Invocation) obj;
+            assert that.receiver == receiver;
+            return that.method.equals(this.method) && Arrays.equals(that.args, this.args);
+
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (args == null) {
+            return method.hashCode();
+        }
+        return method.hashCode() ^ args.length;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/ProxyUtil.java	Fri Nov 07 14:50:43 2014 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011, 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.util.*;
+
+public final class ProxyUtil {
+
+    public static Class<?>[] getAllInterfaces(Class<?> clazz) {
+        HashSet<Class<?>> interfaces = new HashSet<>();
+        getAllInterfaces(clazz, interfaces);
+        return interfaces.toArray(new Class<?>[interfaces.size()]);
+    }
+
+    private static void getAllInterfaces(Class<?> clazz, HashSet<Class<?>> interfaces) {
+        for (Class<?> iface : clazz.getInterfaces()) {
+            if (!interfaces.contains(iface)) {
+                interfaces.add(iface);
+                getAllInterfaces(iface, interfaces);
+            }
+        }
+        if (clazz.getSuperclass() != null) {
+            getAllInterfaces(clazz.getSuperclass(), interfaces);
+        }
+    }
+}
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Fri Nov 07 12:36:32 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Fri Nov 07 14:50:43 2014 +0100
@@ -41,7 +41,9 @@
 import com.oracle.graal.api.runtime.*;
 import com.oracle.graal.baseline.*;
 import com.oracle.graal.compiler.*;
+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.target.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
@@ -672,6 +674,34 @@
     }
 
     /**
+     * Determines if every compilation should also be attempted in a replay {@link Context}.
+     *
+     * <pre>
+     * -Dgraal.testReplay=true
+     * </pre>
+     */
+    private static final boolean TEST_REPLAY = Boolean.getBoolean("graal.testReplay");
+
+    protected CompilationResult replayCompile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) {
+
+        StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner) : graph;
+        lastCompiledGraph = graphToCompile;
+
+        CallingConvention cc = getCallingConvention(getCodeCache(), Type.JavaCallee, graphToCompile.method(), false);
+        try (Context c = new Context(); Debug.Scope s = Debug.scope("ReplayCompiling", new DebugDumpScope("REPLAY", true))) {
+            Request<CompilationResult> request = new GraalCompiler.Request<>(graphToCompile, null, cc, installedCodeOwner, getProviders(), getBackend(), getCodeCache().getTarget(), null,
+                            getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, getProfilingInfo(graphToCompile), getSpeculationLog(), getSuites(), new CompilationResult(),
+                            CompilationResultBuilderFactory.Default);
+
+            request = c.get(request);
+
+            return GraalCompiler.compile(request);
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+
+    /**
      * Compiles a given method.
      *
      * @param installedCodeOwner the method the compiled code will be associated with when installed
@@ -683,8 +713,13 @@
         StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner) : graph;
         lastCompiledGraph = graphToCompile;
         CallingConvention cc = getCallingConvention(getCodeCache(), Type.JavaCallee, graphToCompile.method(), false);
-        return GraalCompiler.compileGraph(graphToCompile, null, cc, installedCodeOwner, getProviders(), getBackend(), getCodeCache().getTarget(), null, getDefaultGraphBuilderSuite(),
+        CompilationResult res = GraalCompiler.compileGraph(graphToCompile, null, cc, installedCodeOwner, getProviders(), getBackend(), getCodeCache().getTarget(), null, getDefaultGraphBuilderSuite(),
                         OptimisticOptimizations.ALL, getProfilingInfo(graphToCompile), getSpeculationLog(), getSuites(), new CompilationResult(), CompilationResultBuilderFactory.Default);
+
+        if (TEST_REPLAY && graph == null) {
+            replayCompile(installedCodeOwner, null);
+        }
+        return res;
     }
 
     protected StructuredGraph lastCompiledGraph;