Mercurial > hg > graal-compiler
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;