Mercurial > hg > truffle
view graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsInstaller.java @ 8430:6920ffef95a8
small documentation fix
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Thu, 21 Mar 2013 13:45:27 +0100 |
parents | 1571adaf302b |
children | be5ce3e9d473 |
line wrap: on
line source
/* * 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.replacements; import static com.oracle.graal.api.meta.MetaUtil.*; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.*; import sun.misc.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.api.replacements.*; import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.replacements.Snippet.DefaultSnippetInliningPolicy; import com.oracle.graal.replacements.Snippet.SnippetInliningPolicy; import com.oracle.graal.word.phases.*; /** * Utility for managing the pre-processing and installation of replacements. Replacements are either * {@linkplain Snippets snippets}, {@linkplain MethodSubstitution method substitutions} or * {@link MacroSubstitution macro substitutions}. */ public class ReplacementsInstaller { protected final MetaAccessProvider runtime; protected final TargetDescription target; protected final Assumptions assumptions; protected final BoxingMethodPool pool; private final Thread owner; /** * A graph cache used by this installer to avoid using the compiler storage for each method * processed during snippet installation. Without this, all processed methods are to be * determined as {@linkplain InliningUtil#canIntrinsify intrinsifiable}. */ private final Map<ResolvedJavaMethod, StructuredGraph> graphCache; public ReplacementsInstaller(MetaAccessProvider runtime, Assumptions assumptions, TargetDescription target) { this.runtime = runtime; this.target = target; this.assumptions = assumptions; this.pool = new BoxingMethodPool(runtime); this.graphCache = new HashMap<>(); this.owner = Thread.currentThread(); } /** * Finds all the snippet methods in a given class, builds a graph for them and installs the * graph with the key value of {@code Snippet.class} in the * {@linkplain ResolvedJavaMethod#getCompilerStorage() compiler storage} of each method. */ public void installSnippets(Class<? extends Snippets> snippets) { for (Method method : snippets.getDeclaredMethods()) { if (method.getAnnotation(Snippet.class) != null) { int modifiers = method.getModifiers(); if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { throw new RuntimeException("Snippet must not be abstract or native"); } ResolvedJavaMethod snippet = runtime.lookupJavaMethod(method); assert snippet.getCompilerStorage().get(Snippet.class) == null : method; StructuredGraph graph = makeGraph(snippet, inliningPolicy(snippet)); // System.out.println("snippet: " + graph); snippet.getCompilerStorage().put(Snippet.class, graph); } } } /** * Finds all the methods in a given class annotated with {@link MethodSubstitution} or * {@link MacroSubstitution}. It builds graphs for the former and installs them in the * {@linkplain ResolvedJavaMethod#getCompilerStorage() compiler storage} of the original (i.e., * substituted) method with a key of {@code MethodSubstitution.class}. For the latter, the * denoted {@linkplain MacroSubstitution#macro() macro} node type is install in the compiler * storage with a key of {@code Node.class}. */ public void installSubstitutions(Class<?> substitutions) { assert owner == Thread.currentThread() : "substitution installation must be single threaded"; ClassSubstitution classSubstitution = substitutions.getAnnotation(ClassSubstitution.class); assert classSubstitution != null; assert !Snippets.class.isAssignableFrom(substitutions); for (Method substituteMethod : substitutions.getDeclaredMethods()) { MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class); MacroSubstitution macroSubstitution = substituteMethod.getAnnotation(MacroSubstitution.class); if (methodSubstitution == null && macroSubstitution == null) { continue; } int modifiers = substituteMethod.getModifiers(); if (!Modifier.isStatic(modifiers)) { throw new RuntimeException("Substitution methods must be static: " + substituteMethod); } if (methodSubstitution != null) { if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { throw new RuntimeException("Substitution method must not be abstract or native: " + substituteMethod); } String originalName = originalName(substituteMethod, methodSubstitution.value()); Class[] originalParameters = originalParameters(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic()); Member originalMethod = originalMethod(classSubstitution, originalName, originalParameters); if (originalMethod != null) { installMethodSubstitution(originalMethod, substituteMethod); } } if (macroSubstitution != null) { String originalName = originalName(substituteMethod, macroSubstitution.value()); Class[] originalParameters = originalParameters(substituteMethod, macroSubstitution.signature(), macroSubstitution.isStatic()); Member originalMethod = originalMethod(classSubstitution, originalName, originalParameters); if (originalMethod != null) { installMacroSubstitution(originalMethod, macroSubstitution.macro()); } } } } // These fields are used to detect calls from the substitute method to the original method. ResolvedJavaMethod substitute; ResolvedJavaMethod original; boolean substituteCallsOriginal; /** * Installs a method substitution. * * @param originalMethod a method or constructor being substituted * @param substituteMethod the substitute method */ protected void installMethodSubstitution(Member originalMethod, Method substituteMethod) { substitute = runtime.lookupJavaMethod(substituteMethod); if (originalMethod instanceof Method) { original = runtime.lookupJavaMethod((Method) originalMethod); } else { original = runtime.lookupJavaConstructor((Constructor) originalMethod); } try { Debug.log("substitution: " + MetaUtil.format("%H.%n(%p)", original) + " --> " + MetaUtil.format("%H.%n(%p)", substitute)); StructuredGraph graph = makeGraph(substitute, inliningPolicy(substitute)); Object oldValue = original.getCompilerStorage().put(MethodSubstitution.class, graph); assert oldValue == null; } finally { substitute = null; original = null; substituteCallsOriginal = false; } } /** * Installs a macro substitution. * * @param originalMethod a method or constructor being substituted * @param macro the substitute macro node class */ protected void installMacroSubstitution(Member originalMethod, Class<? extends FixedWithNextNode> macro) { ResolvedJavaMethod originalJavaMethod; if (originalMethod instanceof Method) { originalJavaMethod = runtime.lookupJavaMethod((Method) originalMethod); } else { originalJavaMethod = runtime.lookupJavaConstructor((Constructor) originalMethod); } Object oldValue = originalJavaMethod.getCompilerStorage().put(Node.class, macro); assert oldValue == null; } private SnippetInliningPolicy inliningPolicy(ResolvedJavaMethod method) { Class<? extends SnippetInliningPolicy> policyClass = SnippetInliningPolicy.class; Snippet snippet = method.getAnnotation(Snippet.class); if (snippet != null) { policyClass = snippet.inlining(); } if (policyClass == SnippetInliningPolicy.class) { return new DefaultSnippetInliningPolicy(runtime, pool); } try { return policyClass.getConstructor().newInstance(); } catch (Exception e) { throw new GraalInternalError(e); } } /** * Does final processing of a snippet graph. */ protected void finalizeGraph(ResolvedJavaMethod method, StructuredGraph graph) { new NodeIntrinsificationPhase(runtime, pool).apply(graph); assert SnippetTemplate.hasConstantParameter(method) || NodeIntrinsificationVerificationPhase.verify(graph); if (substitute == null) { new SnippetFrameStateCleanupPhase().apply(graph); new DeadCodeEliminationPhase().apply(graph); new InsertStateAfterPlaceholderPhase().apply(graph); } else { new DeadCodeEliminationPhase().apply(graph); } } public StructuredGraph makeGraph(final ResolvedJavaMethod method, final SnippetInliningPolicy policy) { return Debug.scope("BuildSnippetGraph", new Object[]{method}, new Callable<StructuredGraph>() { @Override public StructuredGraph call() throws Exception { StructuredGraph graph = parseGraph(method, policy); finalizeGraph(method, graph); Debug.dump(graph, "%s: Final", method.getName()); return graph; } }); } private StructuredGraph parseGraph(final ResolvedJavaMethod method, final SnippetInliningPolicy policy) { StructuredGraph graph = graphCache.get(method); if (graph == null) { graph = buildGraph(method, policy == null ? inliningPolicy(method) : policy); graphCache.put(method, graph); } return graph; } /** * Builds the initial graph for a snippet. */ protected StructuredGraph buildInitialGraph(final ResolvedJavaMethod method) { final StructuredGraph graph = new StructuredGraph(method); GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(); GraphBuilderPhase graphBuilder = new GraphBuilderPhase(runtime, config, OptimisticOptimizations.NONE); graphBuilder.apply(graph); Debug.dump(graph, "%s: %s", method.getName(), GraphBuilderPhase.class.getSimpleName()); new WordTypeVerificationPhase(runtime, target.wordKind).apply(graph); new NodeIntrinsificationPhase(runtime, pool).apply(graph); return graph; } /** * Called after a graph is inlined. * * @param caller the graph into which {@code callee} was inlined * @param callee the graph that was inlined into {@code caller} */ protected void afterInline(StructuredGraph caller, StructuredGraph callee) { if (GraalOptions.OptCanonicalizer) { new WordTypeRewriterPhase(runtime, target.wordKind).apply(caller); new CanonicalizerPhase(runtime, assumptions).apply(caller); } } /** * Called after all inlining for a given graph is complete. */ protected void afterInlining(StructuredGraph graph) { new NodeIntrinsificationPhase(runtime, pool).apply(graph); new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); new DeadCodeEliminationPhase().apply(graph); if (GraalOptions.OptCanonicalizer) { new CanonicalizerPhase(runtime, assumptions).apply(graph); } } private StructuredGraph buildGraph(final ResolvedJavaMethod method, final SnippetInliningPolicy policy) { assert !Modifier.isAbstract(method.getModifiers()) && !Modifier.isNative(method.getModifiers()) : method; final StructuredGraph graph = buildInitialGraph(method); for (Invoke invoke : graph.getInvokes()) { MethodCallTargetNode callTarget = invoke.methodCallTarget(); ResolvedJavaMethod callee = callTarget.targetMethod(); if (callee == substitute) { final StructuredGraph originalGraph = new StructuredGraph(original); new GraphBuilderPhase(runtime, GraphBuilderConfiguration.getSnippetDefault(), OptimisticOptimizations.NONE).apply(originalGraph); InliningUtil.inline(invoke, originalGraph, true); Debug.dump(graph, "after inlining %s", callee); afterInline(graph, originalGraph); substituteCallsOriginal = true; } else { if ((callTarget.invokeKind() == InvokeKind.Static || callTarget.invokeKind() == InvokeKind.Special) && policy.shouldInline(callee, method)) { StructuredGraph targetGraph = parseGraph(callee, policy); InliningUtil.inline(invoke, targetGraph, true); Debug.dump(graph, "after inlining %s", callee); afterInline(graph, targetGraph); } } } afterInlining(graph); for (LoopEndNode end : graph.getNodes(LoopEndNode.class)) { end.disableSafepoint(); } if (GraalOptions.ProbabilityAnalysis) { new DeadCodeEliminationPhase().apply(graph); new ComputeProbabilityPhase().apply(graph); } return graph; } private static String originalName(Method substituteMethod, String methodSubstitution) { if (methodSubstitution.isEmpty()) { return substituteMethod.getName(); } else { return methodSubstitution; } } /** * Resolves a name to a class. * * @param className the name of the class to resolve * @param optional if true, resolution failure returns null * @return the resolved class or null if resolution fails and {@code optional} is true */ private static Class resolveType(String className, boolean optional) { try { // Need to use launcher class path to handle classes // that are not on the boot class path ClassLoader cl = Launcher.getLauncher().getClassLoader(); return Class.forName(className, false, cl); } catch (ClassNotFoundException e) { if (optional) { return null; } throw new GraalInternalError("Could not resolve type " + className); } } private static Class resolveType(JavaType type) { JavaType base = type; int dimensions = 0; while (base.getComponentType() != null) { base = base.getComponentType(); dimensions++; } Class baseClass = base.getKind() != Kind.Object ? base.getKind().toJavaClass() : resolveType(toJavaName(base), false); return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass(); } private Class[] originalParameters(Method substituteMethod, String methodSubstitution, boolean isStatic) { Class[] parameters; if (methodSubstitution.isEmpty()) { parameters = substituteMethod.getParameterTypes(); if (!isStatic) { assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter"; parameters = Arrays.copyOfRange(parameters, 1, parameters.length); } } else { Signature signature = runtime.parseMethodDescriptor(methodSubstitution); parameters = new Class[signature.getParameterCount(false)]; for (int i = 0; i < parameters.length; i++) { parameters[i] = resolveType(signature.getParameterType(i, null)); } } return parameters; } private static Member originalMethod(ClassSubstitution classSubstitution, String name, Class[] parameters) { Class<?> originalClass = classSubstitution.value(); if (originalClass == ClassSubstitution.class) { originalClass = resolveType(classSubstitution.className(), classSubstitution.optional()); if (originalClass == null) { // optional class was not found return null; } } try { if (name.equals("<init>")) { return originalClass.getDeclaredConstructor(parameters); } else { return originalClass.getDeclaredMethod(name, parameters); } } catch (NoSuchMethodException | SecurityException e) { throw new GraalInternalError(e); } } }