1 /*
   2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.hotspot;
  26 
  27 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
  28 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
  29 import static org.graalvm.compiler.core.common.GraalOptions.UseEncodedGraphs;
  30 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
  31 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
  32 
  33 import java.util.Set;
  34 
  35 import jdk.internal.vm.compiler.collections.EconomicSet;
  36 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  37 import org.graalvm.compiler.bytecode.BytecodeProvider;
  38 import org.graalvm.compiler.core.common.CompilationIdentifier;
  39 import org.graalvm.compiler.debug.DebugContext;
  40 import org.graalvm.compiler.debug.GraalError;
  41 import org.graalvm.compiler.graph.NodeSourcePosition;
  42 import org.graalvm.compiler.hotspot.meta.HotSpotWordOperationPlugin;
  43 import org.graalvm.compiler.hotspot.word.HotSpotOperation;
  44 import org.graalvm.compiler.nodes.Cancellable;
  45 import org.graalvm.compiler.nodes.Invoke;
  46 import org.graalvm.compiler.nodes.StructuredGraph;
  47 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  48 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderPlugin;
  49 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
  50 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  51 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
  52 import org.graalvm.compiler.options.OptionValues;
  53 import org.graalvm.compiler.phases.util.Providers;
  54 import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
  55 import org.graalvm.compiler.replacements.ReplacementsImpl;
  56 
  57 import jdk.vm.ci.code.TargetDescription;
  58 import jdk.vm.ci.common.NativeImageReinitialize;
  59 import jdk.vm.ci.meta.ResolvedJavaMethod;
  60 
  61 /**
  62  * Filters certain method substitutions based on whether there is underlying hardware support for
  63  * them.
  64  */
  65 public class HotSpotReplacementsImpl extends ReplacementsImpl {
  66     public HotSpotReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target) {
  67         super(new GraalDebugHandlersFactory(snippetReflection), providers, snippetReflection, bytecodeProvider, target);
  68     }
  69 
  70     HotSpotReplacementsImpl(HotSpotReplacementsImpl replacements, Providers providers) {
  71         super(new GraalDebugHandlersFactory(replacements.snippetReflection), providers, replacements.snippetReflection,
  72                         replacements.getDefaultReplacementBytecodeProvider(), replacements.target);
  73     }
  74 
  75     @Override
  76     public Class<? extends GraphBuilderPlugin> getIntrinsifyingPlugin(ResolvedJavaMethod method) {
  77         return method.getAnnotation(HotSpotOperation.class) != null ? HotSpotWordOperationPlugin.class : super.getIntrinsifyingPlugin(method);
  78     }
  79 
  80     @Override
  81     public void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
  82         if (!IS_IN_NATIVE_IMAGE) {
  83             if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
  84                 synchronized (HotSpotReplacementsImpl.class) {
  85                     if (snippetEncoder == null) {
  86                         snippetEncoder = new SymbolicSnippetEncoder(this);
  87                     }
  88                     snippetEncoder.registerMethodSubstitution(plugin, original, context, options);
  89                 }
  90             }
  91         }
  92     }
  93 
  94     @Override
  95     public StructuredGraph getIntrinsicGraph(ResolvedJavaMethod method, CompilationIdentifier compilationId, DebugContext debug, Cancellable cancellable) {
  96         boolean useEncodedGraphs = UseEncodedGraphs.getValue(debug.getOptions());
  97         if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
  98             HotSpotReplacementsImpl replacements = (HotSpotReplacementsImpl) providers.getReplacements();
  99             InvocationPlugin plugin = replacements.getGraphBuilderPlugins().getInvocationPlugins().lookupInvocation(method);
 100             if (plugin instanceof MethodSubstitutionPlugin) {
 101                 MethodSubstitutionPlugin msp = (MethodSubstitutionPlugin) plugin;
 102                 if (useEncodedGraphs) {
 103                     replacements.registerMethodSubstitution(msp, method, ROOT_COMPILATION, debug.getOptions());
 104                 }
 105                 StructuredGraph methodSubstitution = replacements.getMethodSubstitution(msp, method, ROOT_COMPILATION, StructuredGraph.AllowAssumptions.YES, cancellable, debug.getOptions());
 106                 methodSubstitution.resetDebug(debug);
 107                 return methodSubstitution;
 108             }
 109             return null;
 110         }
 111         return super.getIntrinsicGraph(method, compilationId, debug, cancellable);
 112     }
 113 
 114     @Override
 115     public StructuredGraph getSubstitution(ResolvedJavaMethod targetMethod, int invokeBci, boolean trackNodeSourcePosition, NodeSourcePosition replaceePosition, OptionValues options) {
 116         boolean useEncodedGraphs = UseEncodedGraphs.getValue(options);
 117         if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
 118             InvocationPlugin plugin = getGraphBuilderPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
 119             if (plugin instanceof MethodSubstitutionPlugin && (!plugin.inlineOnly() || invokeBci >= 0)) {
 120                 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
 121                 if (!IS_IN_NATIVE_IMAGE && useEncodedGraphs) {
 122                     registerMethodSubstitution(msPlugin, targetMethod, INLINE_AFTER_PARSING, options);
 123                 }
 124                 // This assumes the normal path creates the graph using
 125                 // GraphBuilderConfiguration.getSnippetDefault with omits exception edges
 126                 StructuredGraph subst = getMethodSubstitution(msPlugin, targetMethod, INLINE_AFTER_PARSING, StructuredGraph.AllowAssumptions.NO, null, options);
 127                 return subst;
 128             }
 129         }
 130 
 131         return super.getSubstitution(targetMethod, invokeBci, trackNodeSourcePosition, replaceePosition, options);
 132     }
 133 
 134     @Override
 135     public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
 136         if (b.parsingIntrinsic() && snippetEncoder != null) {
 137             if (getIntrinsifyingPlugin(method) != null) {
 138                 snippetEncoder.addDelayedInvocationPluginMethod(method);
 139                 return;
 140             }
 141         }
 142         super.notifyNotInlined(b, method, invoke);
 143     }
 144 
 145     // When assertions are enabled, these fields are used to ensure all snippets are
 146     // registered during Graal initialization which in turn ensures that native image
 147     // building will not miss any snippets.
 148     @NativeImageReinitialize private EconomicSet<ResolvedJavaMethod> registeredSnippets = EconomicSet.create();
 149     private boolean snippetRegistrationClosed;
 150 
 151     @Override
 152     public void registerSnippet(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean trackNodeSourcePosition, OptionValues options) {
 153         if (!IS_IN_NATIVE_IMAGE) {
 154             assert !snippetRegistrationClosed : "Cannot register snippet after registration is closed: " + method.format("%H.%n(%p)");
 155             assert registeredSnippets.add(method) : "Cannot register snippet twice: " + method.format("%H.%n(%p)");
 156             if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
 157                 synchronized (HotSpotReplacementsImpl.class) {
 158                     if (snippetEncoder == null) {
 159                         snippetEncoder = new SymbolicSnippetEncoder(this);
 160                     }
 161                     snippetEncoder.registerSnippet(method, original, receiver, trackNodeSourcePosition, options);
 162                 }
 163             }
 164         }
 165     }
 166 
 167     @Override
 168     public void closeSnippetRegistration() {
 169         snippetRegistrationClosed = true;
 170     }
 171 
 172     private static SymbolicSnippetEncoder.EncodedSnippets getEncodedSnippets() {
 173         return encodedSnippets;
 174     }
 175 
 176     public Set<ResolvedJavaMethod> getSnippetMethods() {
 177         if (snippetEncoder != null) {
 178             return snippetEncoder.getSnippetMethods();
 179         }
 180         return null;
 181     }
 182 
 183     static void setEncodedSnippets(SymbolicSnippetEncoder.EncodedSnippets encodedSnippets) {
 184         HotSpotReplacementsImpl.encodedSnippets = encodedSnippets;
 185     }
 186 
 187     public boolean encode(OptionValues options) {
 188         SymbolicSnippetEncoder encoder = HotSpotReplacementsImpl.snippetEncoder;
 189         if (encoder != null) {
 190             return encoder.encode(options);
 191         }
 192         return false;
 193     }
 194 
 195     private static volatile SymbolicSnippetEncoder.EncodedSnippets encodedSnippets;
 196 
 197     @NativeImageReinitialize private static SymbolicSnippetEncoder snippetEncoder;
 198 
 199     @Override
 200     public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args, boolean trackNodeSourcePosition, NodeSourcePosition replaceePosition,
 201                     OptionValues options) {
 202         StructuredGraph graph = getEncodedSnippet(method, args, StructuredGraph.AllowAssumptions.NO, options);
 203         if (graph != null) {
 204             return graph;
 205         }
 206 
 207         assert !IS_IN_NATIVE_IMAGE : "should be using encoded snippets";
 208         return super.getSnippet(method, recursiveEntry, args, trackNodeSourcePosition, replaceePosition, options);
 209     }
 210 
 211     @SuppressWarnings("try")
 212     private StructuredGraph getEncodedSnippet(ResolvedJavaMethod method, Object[] args, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
 213         boolean useEncodedGraphs = UseEncodedGraphs.getValue(options);
 214         if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
 215             synchronized (HotSpotReplacementsImpl.class) {
 216                 if (!IS_IN_NATIVE_IMAGE) {
 217                     snippetEncoder.encode(options);
 218                 }
 219 
 220                 if (getEncodedSnippets() == null) {
 221                     throw GraalError.shouldNotReachHere("encoded snippets not found");
 222                 }
 223                 // Snippets graphs can contain foreign object reference and
 224                 // outlive a single compilation.
 225                 try (CompilationContext scope = HotSpotGraalServices.enterGlobalCompilationContext()) {
 226                     StructuredGraph graph = getEncodedSnippets().getEncodedSnippet(method, this, args, allowAssumptions, options);
 227                     if (graph == null) {
 228                         throw GraalError.shouldNotReachHere("snippet not found: " + method.format("%H.%n(%p)"));
 229                     }
 230                     return graph;
 231                 }
 232             }
 233         } else {
 234             assert registeredSnippets == null || registeredSnippets.contains(method) : "Asking for snippet method that was never registered: " + method.format("%H.%n(%p)");
 235         }
 236         return null;
 237     }
 238 
 239     @Override
 240     public StructuredGraph getMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context,
 241                     StructuredGraph.AllowAssumptions allowAssumptions, Cancellable cancellable, OptionValues options) {
 242         boolean useEncodedGraphs = UseEncodedGraphs.getValue(options);
 243         if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
 244             if (!IS_IN_NATIVE_IMAGE) {
 245                 snippetEncoder.encode(options);
 246             }
 247 
 248             if (getEncodedSnippets() == null) {
 249                 throw GraalError.shouldNotReachHere("encoded snippets not found");
 250             }
 251             return getEncodedSnippets().getMethodSubstitutionGraph(plugin, original, this, context, allowAssumptions, cancellable, options);
 252         }
 253         return null;
 254     }
 255 
 256 }