From 8fb445563ed9844d78eb3e741599d02968cafe4e Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Wed, 13 Mar 2024 15:37:18 +0100 Subject: [PATCH] Remove IntrinsifyMethodHandlesInvocationPlugin. --- .../com/oracle/svm/core/SubstrateOptions.java | 2 +- .../RuntimeCompilationFeature.java | 2 +- .../svm/hosted/NativeImageGenerator.java | 14 +- .../methodhandles/MethodHandleFeature.java | 13 +- ...trinsifyMethodHandlesInvocationPlugin.java | 999 ------------------ 5 files changed, 9 insertions(+), 1021 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index d09453167ded..103b774ad5dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1054,7 +1054,7 @@ public enum ReportingMode { @Option(help = "Enable and disable normal processing of flags relating to experimental options.", type = OptionType.Expert, stability = OptionStability.EXPERIMENTAL) // public static final HostedOptionKey UnlockExperimentalVMOptions = new HostedOptionKey<>(false); - @Option(help = "Force using legacy method handle intrinsics.", type = Expert, deprecated = true, deprecationMessage = "This option was introduced to simplify migration to GraalVM 23.1 and will be removed in a future release") // + @Option(help = "Deprecated, option no longer has any effect.", deprecated = true, deprecationMessage = "It no longer has any effect, and no replacement is available")// public static final HostedOptionKey UseOldMethodHandleIntrinsics = new HostedOptionKey<>(false); @Option(help = "Include all classes, methods, and fields from given modules", type = OptionType.Debug) // diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java index 560cbcb9eb9e..eb1d990c167c 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java @@ -396,7 +396,7 @@ private void installRuntimeConfig(BeforeAnalysisAccessImpl config) { FeatureHandler featureHandler = config.getFeatureHandler(); final boolean supportsStubBasedPlugins = !SubstrateOptions.useLLVMBackend(); - NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, runtimeConfig, hostedProviders, config.getMetaAccess(), config.getUniverse(), null, config.getNativeLibraries(), + NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, runtimeConfig, hostedProviders, config.getMetaAccess(), config.getUniverse(), config.getNativeLibraries(), config.getImageClassLoader(), ParsingReason.JITCompilation, ((Inflation) config.getBigBang()).getAnnotationSubstitutionProcessor(), new SubstrateClassInitializationPlugin(config.getHostVM()), ConfigurationValues.getTarget(), supportsStubBasedPlugins); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 089ab5a2c3d6..1477e5374118 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -248,7 +248,6 @@ import com.oracle.svm.hosted.phases.EarlyConstantFoldLoadFieldPlugin; import com.oracle.svm.hosted.phases.ImageBuildStatisticsCounterPhase; import com.oracle.svm.hosted.phases.InjectedAccessorsPlugin; -import com.oracle.svm.hosted.phases.IntrinsifyMethodHandlesInvocationPlugin; import com.oracle.svm.hosted.phases.SubstrateClassInitializationPlugin; import com.oracle.svm.hosted.phases.VerifyDeoptLIRFrameStatesPhase; import com.oracle.svm.hosted.phases.VerifyNoGuardsPhase; @@ -599,7 +598,6 @@ protected void doRun(Map entryPoints, JavaMainSupport j bb.getSnippetReflectionProvider()).build(); registerGraphBuilderPlugins(featureHandler, runtimeConfiguration, (HostedProviders) runtimeConfiguration.getProviders(), bb.getMetaAccess(), aUniverse, - hUniverse, nativeLibraries, loader, ParsingReason.AOTCompilation, bb.getAnnotationSubstitutionProcessor(), new SubstrateClassInitializationPlugin((SVMHost) aUniverse.hostVM()), ConfigurationValues.getTarget(), this.isStubBasedPluginsSupported()); @@ -1145,7 +1143,7 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature */ bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.stackvalue.StackValueNode.StackSlotIdentity.class).registerAsReachable("root class"); - NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, null, aProviders, aMetaAccess, aUniverse, null, nativeLibraries, loader, ParsingReason.PointsToAnalysis, + NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, null, aProviders, aMetaAccess, aUniverse, nativeLibraries, loader, ParsingReason.PointsToAnalysis, bb.getAnnotationSubstitutionProcessor(), classInitializationPlugin, ConfigurationValues.getTarget(), supportsStubBasedPlugins); registerReplacements(debug, featureHandler, null, aProviders, true, initForeignCalls, new GraphEncoder(ConfigurationValues.getTarget().arch)); @@ -1353,7 +1351,7 @@ public void notifyNoPlugin(ResolvedJavaMethod targetMethod, OptionValues options } public static void registerGraphBuilderPlugins(FeatureHandler featureHandler, RuntimeConfiguration runtimeConfig, HostedProviders providers, AnalysisMetaAccess aMetaAccess, - AnalysisUniverse aUniverse, HostedUniverse hUniverse, NativeLibraries nativeLibs, ImageClassLoader loader, ParsingReason reason, + AnalysisUniverse aUniverse, NativeLibraries nativeLibs, ImageClassLoader loader, ParsingReason reason, AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ClassInitializationPlugin classInitializationPlugin, TargetDescription target, boolean supportsStubBasedPlugins) { GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new SubstitutionInvocationPlugins(annotationSubstitutionProcessor)); @@ -1367,12 +1365,8 @@ public static void registerGraphBuilderPlugins(FeatureHandler featureHandler, Ru SubstrateReplacements replacements = (SubstrateReplacements) providers.getReplacements(); plugins.appendInlineInvokePlugin(replacements); - if (!SubstrateOptions.UseOldMethodHandleIntrinsics.getValue()) { - if (reason.duringAnalysis()) { - plugins.appendNodePlugin(new MethodHandleWithExceptionPlugin(providers.getConstantReflection().getMethodHandleAccess(), false)); - } - } else { - plugins.appendNodePlugin(new IntrinsifyMethodHandlesInvocationPlugin(hostedSnippetReflection, providers, aUniverse, hUniverse)); + if (reason.duringAnalysis()) { + plugins.appendNodePlugin(new MethodHandleWithExceptionPlugin(providers.getConstantReflection().getMethodHandleAccess(), false)); } plugins.appendNodePlugin(new DeletedFieldsPlugin()); plugins.appendNodePlugin(new InjectedAccessorsPlugin()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java index d6edb8e48684..98de19614458 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java @@ -43,7 +43,6 @@ import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; @@ -146,15 +145,9 @@ public void duringSetup(DuringSetupAccess access) { referencedKeySetAdd = ReflectionUtil.lookupMethod(concurrentWeakInternSetClass, "add", Object.class); } - if (!SubstrateOptions.UseOldMethodHandleIntrinsics.getValue()) { - /* - * Renaming is not crucial with old method handle intrinsics, so if those are requested - * explicitly, disable renaming to offer a fallback in case it causes problems. - */ - var accessImpl = (DuringSetupAccessImpl) access; - substitutionProcessor = new MethodHandleInvokerRenamingSubstitutionProcessor(accessImpl.getBigBang()); - accessImpl.registerSubstitutionProcessor(substitutionProcessor); - } + var accessImpl = (DuringSetupAccessImpl) access; + substitutionProcessor = new MethodHandleInvokerRenamingSubstitutionProcessor(accessImpl.getBigBang()); + accessImpl.registerSubstitutionProcessor(substitutionProcessor); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java deleted file mode 100644 index b9aafee79722..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Copyright (c) 2014, 2023, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.svm.hosted.phases; - -import static jdk.graal.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createStandardInlineInfo; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; -import java.lang.invoke.VarHandle; -import java.lang.invoke.WrongMethodTypeException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.StreamSupport; - -import org.graalvm.collections.Pair; -import org.graalvm.collections.UnmodifiableEconomicMap; - -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; -import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.graal.pointsto.phases.NoClassInitializationPlugin; -import com.oracle.graal.pointsto.util.GraalAccess; -import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.graal.word.SubstrateWordTypes; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.SVMHost; -import com.oracle.svm.hosted.jdk.VarHandleFeature; -import com.oracle.svm.hosted.meta.HostedMethod; -import com.oracle.svm.hosted.meta.HostedSnippetReflectionProvider; -import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.hosted.meta.HostedUniverse; - -import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; -import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; -import jdk.graal.compiler.core.common.type.ObjectStamp; -import jdk.graal.compiler.core.common.type.PrimitiveStamp; -import jdk.graal.compiler.core.common.type.Stamp; -import jdk.graal.compiler.core.common.type.StampFactory; -import jdk.graal.compiler.core.common.type.StampPair; -import jdk.graal.compiler.core.common.type.TypeReference; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.graph.Node; -import jdk.graal.compiler.graph.NodeClass; -import jdk.graal.compiler.graph.NodeMap; -import jdk.graal.compiler.hotspot.HotSpotGraphBuilderInstance; -import jdk.graal.compiler.java.BytecodeParser; -import jdk.graal.compiler.java.GraphBuilderPhase; -import jdk.graal.compiler.nodeinfo.NodeCycles; -import jdk.graal.compiler.nodeinfo.NodeInfo; -import jdk.graal.compiler.nodeinfo.NodeSize; -import jdk.graal.compiler.nodes.AbstractBeginNode; -import jdk.graal.compiler.nodes.ArithmeticOperation; -import jdk.graal.compiler.nodes.BeginNode; -import jdk.graal.compiler.nodes.CallTargetNode.InvokeKind; -import jdk.graal.compiler.nodes.ConstantNode; -import jdk.graal.compiler.nodes.FixedGuardNode; -import jdk.graal.compiler.nodes.FixedNode; -import jdk.graal.compiler.nodes.FixedWithNextNode; -import jdk.graal.compiler.nodes.InvokeNode; -import jdk.graal.compiler.nodes.LogicNode; -import jdk.graal.compiler.nodes.NodeView; -import jdk.graal.compiler.nodes.ParameterNode; -import jdk.graal.compiler.nodes.PhiNode; -import jdk.graal.compiler.nodes.PiNode; -import jdk.graal.compiler.nodes.ReturnNode; -import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.graal.compiler.nodes.calc.FloatingNode; -import jdk.graal.compiler.nodes.calc.IsNullNode; -import jdk.graal.compiler.nodes.extended.AnchoringNode; -import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode.BytecodeExceptionKind; -import jdk.graal.compiler.nodes.graphbuilderconf.ClassInitializationPlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderTool; -import jdk.graal.compiler.nodes.graphbuilderconf.InlineInvokePlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin.OptionalInvocationPlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin.RequiredInvocationPlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; -import jdk.graal.compiler.nodes.graphbuilderconf.NodePlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.ParameterPlugin; -import jdk.graal.compiler.nodes.java.FinalFieldBarrierNode; -import jdk.graal.compiler.nodes.java.InstanceOfNode; -import jdk.graal.compiler.nodes.java.LoadFieldNode; -import jdk.graal.compiler.nodes.java.MethodCallTargetNode; -import jdk.graal.compiler.nodes.java.NewArrayNode; -import jdk.graal.compiler.nodes.java.NewInstanceNode; -import jdk.graal.compiler.nodes.java.StoreFieldNode; -import jdk.graal.compiler.nodes.spi.Replacements; -import jdk.graal.compiler.nodes.type.StampTool; -import jdk.graal.compiler.phases.OptimisticOptimizations; -import jdk.graal.compiler.phases.common.CanonicalizerPhase; -import jdk.graal.compiler.phases.util.Providers; -import jdk.graal.compiler.replacements.InlineDuringParsingPlugin; -import jdk.graal.compiler.replacements.MethodHandlePlugin; -import jdk.graal.compiler.word.WordOperationPlugin; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.DeoptimizationReason; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MethodHandleAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -/** - * Legacy code which will be replaced by the more general method handle intrinsification and - * inlining in {@link com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder}. - * - * Support for method handles that can be reduced to a plain invocation. This is enough to support - * the method handles used for Java 8 Lambda expressions. Support for arbitrary method handles is - * not possible in the Substrate VM, for the same reasons that we cannot support arbitrary - * reflection. - *

- * Design decisions for this phase: To support static analysis, the method handle invocation needs - * to be replaced with a regular invocation before static analysis runs. This requires inlining of - * all method that are involved in the method handle dispatch. We introduce the restriction that one - * method handle invocation can be reduced by exactly one regular invocation, so that we can inherit - * the bci of that invocation, and there are no nested frame states created for the inlining. The - * static analysis results are therefore registered using this bci, and the bci is still unique and - * belongs to the original method. - *

- * Implementation: The starting point is an invoke that has a {@link Constant} {@link MethodHandle} - * as an argument. The method handle is the appendix of the invokedynamic bytecode generated by - * javac for lambda expressions. We inline recursively all methods that get the method handle - * parameter, with special treatment of the invocation methods of the method handle chain provided - * by {@link MethodHandleAccessProvider}. After all inlining (which is done during parsing, - * configured by the {@link MethodHandlesInlineInvokePlugin}), we require to have just a single - * invocation left. This invocation replaces the original invoke that was our starting point. If we - * have more than a single invocation left, we fail and report it as an unsupported feature of - * Substrate VM. - *

- * The parsing is done using the original universe and providers of the HotSpot VM. This has a - * couple of advantages: Our analysis universe is not polluted with types and methods just used for - * method handles; we can use the constant folding and graph builder plugins of the HotSpot VM; and - * we can use the {@link MethodHandlePlugin} of the HotSpot VM without the need for any wrappers. - * The downside is that we have to convert types, methods, and constants between our world and the - * HotSpot world. - */ -public class IntrinsifyMethodHandlesInvocationPlugin implements NodePlugin { - - private final Providers parsingProviders; - private final HostedProviders universeProviders; - private final AnalysisUniverse aUniverse; - private final HostedUniverse hUniverse; - private final HostedSnippetReflectionProvider hostedSnippetReflection; - - private final ClassInitializationPlugin classInitializationPlugin; - - private final ResolvedJavaType methodHandleType; - private final ResolvedJavaType varHandleType; - - public IntrinsifyMethodHandlesInvocationPlugin(HostedSnippetReflectionProvider hostedSnippetReflection, HostedProviders providers, AnalysisUniverse aUniverse, - HostedUniverse hUniverse) { - this.aUniverse = aUniverse; - this.hUniverse = hUniverse; - this.universeProviders = providers; - this.hostedSnippetReflection = hostedSnippetReflection; - - Providers originalProviders = GraalAccess.getOriginalProviders(); - this.parsingProviders = new Providers(originalProviders).copyWith(new MethodHandlesMetaAccessExtensionProvider()); - - this.classInitializationPlugin = new SubstrateClassInitializationPlugin((SVMHost) aUniverse.hostVM()); - - methodHandleType = universeProviders.getMetaAccess().lookupJavaType(MethodHandle.class); - varHandleType = universeProviders.getMetaAccess().lookupJavaType(VarHandle.class); - } - - @Override - public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] originalArgs) { - ValueNode receiverForNullCheck = null; - ValueNode[] args = originalArgs; - if ((!method.isStatic() || isVarHandleGuards(method)) && args.length > 0 && args[0] instanceof PhiNode) { - PhiNode phi = (PhiNode) args[0]; - /* - * For loop phis, not all inputs are available yet since we have not parsed the loop - * body yet. - */ - if (!phi.isLoopPhi()) { - ValueNode filteredReceiver = filterNullPhiInputs(phi); - if (filteredReceiver != phi && filteredReceiver.isJavaConstant()) { - receiverForNullCheck = phi; - args = Arrays.copyOf(args, args.length); - args[0] = filteredReceiver; - } - } - } - - /* - * We want to process invokes that have a constant MethodHandle parameter. And we need a - * direct call, otherwise we do not have a single target method. - */ - if (b.getInvokeKind().isDirect() && (hasMethodHandleArgument(args) || isVarHandleMethod(method, args)) && !ignoreMethod(method)) { - if (b.bciCanBeDuplicated()) { - /* - * If we capture duplication of the bci, we don't process invoke. - */ - return false; - } else { - if (receiverForNullCheck != null) { - b.nullCheckedValue(receiverForNullCheck); - } - return processInvokeWithMethodHandle(b, universeProviders.getReplacements(), method, args); - } - - } else if (methodHandleType.equals(method.getDeclaringClass())) { - /* - * The native methods defined in the class MethodHandle are currently not implemented at - * all. Normally, we would mark them as @Delete to give the user a good error message. - * Unfortunately, that does not work for the MethodHandle methods because they are - * signature polymorphic, i.e., they exist in every possible signature. Therefore, we - * must only look at the declaring class and the method name here. - */ - return false; - - } else { - return false; - } - } - - private static ValueNode filterNullPhiInputs(PhiNode phi) { - ValueNode notAlwaysNullPhiInput = null; - for (ValueNode phiInput : phi.values()) { - if (StampTool.isPointerAlwaysNull(phiInput)) { - /* Ignore always null phi inputs. */ - } else if (notAlwaysNullPhiInput != null) { - /* More than one not-always-null phi inputs. Nothing more to optimize. */ - return phi; - } else { - /* First not-always-null phi input. */ - notAlwaysNullPhiInput = phiInput; - } - } - return notAlwaysNullPhiInput; - } - - private boolean hasMethodHandleArgument(ValueNode[] args) { - for (ValueNode argument : args) { - if (argument.isConstant() && argument.getStackKind() == JavaKind.Object && - (((UniverseMetaAccess) universeProviders.getMetaAccess()).isInstanceOf(argument.asJavaConstant(), methodHandleType))) { - return true; - } - } - return false; - } - - /** - * Checks if the method is the intrinsification root for a VarHandle. In the current VarHandle - * implementation, all guards are in the automatically generated class VarHandleGuards. All - * methods do have a VarHandle argument, and we expect it to be a compile-time constant. - * - * See the documentation in {@link VarHandleFeature} for more information on the overall - * VarHandle support. - */ - private boolean isVarHandleMethod(ResolvedJavaMethod method, ValueNode[] args) { - /* - * We do the check by class name because then we 1) do not need an explicit Java version - * check (VarHandle was introduced with JDK 9), 2) VarHandleGuards is a non-public class - * that we cannot reference by class literal, and 3) we do not need to worry about analysis - * vs. hosted types. If the VarHandle implementation changes, we need to update our whole - * handling anyway. - */ - if (isVarHandleGuards(method)) { - if (args.length < 1 || !args[0].isJavaConstant() || !isVarHandle(args[0])) { - return false; - } - VarHandleFeature.eagerlyInitializeVarHandle(hostedSnippetReflection.asObject(VarHandle.class, args[0].asJavaConstant())); - return true; - } else { - return false; - } - } - - private static boolean isVarHandleGuards(ResolvedJavaMethod method) { - return method.getDeclaringClass().toJavaName(true).equals("java.lang.invoke.VarHandleGuards"); - } - - private boolean isVarHandle(ValueNode arg) { - return varHandleType.isAssignableFrom(universeProviders.getMetaAccess().lookupJavaType(arg.asJavaConstant())); - } - - private static final List>> IGNORE_FILTER = Arrays.asList( - Pair.create("java.lang.invoke.MethodHandle", Collections.singletonList("bindTo")), // Class.cast() - Pair.create("java.lang.invoke.MethodHandles", Arrays.asList("dropArguments", "filterReturnValue", "foldArguments", "insertArguments")), - Pair.create("java.lang.invoke.Invokers", Collections.singletonList("spreadInvoker"))); - - private static boolean ignoreMethod(ResolvedJavaMethod method) { - String className = method.getDeclaringClass().toJavaName(true); - String methodName = method.getName(); - for (Pair> ignored : IGNORE_FILTER) { - if (ignored.getLeft().equals(className) && ignored.getRight().contains(methodName)) { - return true; - } - } - return false; - } - - class MethodHandlesParameterPlugin implements ParameterPlugin { - private final ValueNode[] methodHandleArguments; - - MethodHandlesParameterPlugin(ValueNode[] methodHandleArguments) { - this.methodHandleArguments = methodHandleArguments; - } - - @Override - public FloatingNode interceptParameter(GraphBuilderTool b, int index, StampPair stamp) { - if (methodHandleArguments[index].isConstant()) { - /* Convert the constant from our world to the HotSpot world. */ - return ConstantNode.forConstant(toOriginal(methodHandleArguments[index].asJavaConstant()), parsingProviders.getMetaAccess()); - } else { - /* - * Propagate the parameter type from our world to the HotSpot world. We have more - * precise type information from the arguments than the parameters of the method - * would be. - */ - Stamp argStamp = methodHandleArguments[index].stamp(NodeView.DEFAULT); - ResolvedJavaType argType = StampTool.typeOrNull(argStamp); - if (argType != null) { - // TODO For trustInterfaces = false, we cannot be more specific here - // (i.e. we cannot use TypeReference.createExactTrusted here) - TypeReference typeref = TypeReference.createWithoutAssumptions(OriginalClassProvider.getOriginalType(argType)); - argStamp = StampTool.isPointerNonNull(argStamp) ? StampFactory.objectNonNull(typeref) : StampFactory.object(typeref); - } - return new ParameterNode(index, StampPair.createSingle(argStamp)); - } - } - } - - class MethodHandlesInlineInvokePlugin implements InlineInvokePlugin { - @Override - public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - /* Avoid infinite recursion and excessive graphs with (more or less random) limits. */ - if (b.getDepth() > 20 || b.getGraph().getNodeCount() > 1000) { - return null; - } - - String className = method.getDeclaringClass().toJavaName(true); - if (className.startsWith("java.lang.invoke.VarHandle") && (!className.equals("java.lang.invoke.VarHandle") || method.getName().equals("getMethodHandleUncached"))) { - /* - * Do not inline implementation methods of various VarHandle implementation classes. - * They are too complex and cannot be reduced to a single invoke or field access. - * There is also no need to inline them, because they are not related to any - * MethodHandle mechanism. - * - * Methods defined in VarHandle itself are fine and not covered by this rule, apart - * from well-known methods that are never useful to be inlined. If these methods are - * reached, intrinsification will not be possible in any case. - */ - return null; - } else if (className.startsWith("java.lang.invoke") && !className.contains("InvokerBytecodeGenerator")) { - /* - * Inline all helper methods used by method handles. We do not know exactly which - * ones they are, but they are all be from the same package. - */ - return createStandardInlineInfo(method); - } else if (className.equals("sun.invoke.util.ValueConversions")) { - /* - * Inline trivial helper methods for value conversion. - */ - return new InlineDuringParsingPlugin().shouldInlineInvoke(b, method, args); - } - return null; - } - } - - class MethodHandlesMetaAccessExtensionProvider implements MetaAccessExtensionProvider { - @Override - public JavaKind getStorageKind(JavaType type) { - throw VMError.shouldNotReachHere("storage kind information is only needed for optimization phases not used by the method handle intrinsification"); - } - - @Override - public boolean canConstantFoldDynamicAllocation(ResolvedJavaType type) { - if (hUniverse == null) { - /* - * During static analysis, every type can be constant folded and the static analysis - * will see the real allocation. - */ - return true; - } else { - ResolvedJavaType convertedType = optionalLookup(type); - return convertedType != null && ((HostedType) convertedType).isInstantiated(); - } - } - - @Override - public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect) { - throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport - } - - @Override - public boolean canVirtualize(ResolvedJavaType instanceType) { - return true; - } - } - - private static ResolvedJavaField findField(ResolvedJavaType type, String name) { - for (ResolvedJavaField field : type.getInstanceFields(false)) { - if (field.getName().equals(name)) { - return field; - } - } - throw GraalError.shouldNotReachHere("Required field " + name + " not found in " + type); // ExcludeFromJacocoGeneratedReport - } - - private static void registerInvocationPlugins(InvocationPlugins plugins, Replacements replacements) { - Registration r = new Registration(plugins, "java.lang.invoke.DirectMethodHandle", replacements); - r.register(new RequiredInvocationPlugin("ensureInitialized", Receiver.class) { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { - /* - * Method handles for static methods have a guard that initializes the class (if the - * class was not yet initialized when the method handle was created). We emit the - * class initialization check manually later on when appending nodes to the target - * graph. - */ - JavaConstant receiverConstant = receiver.get(true).asJavaConstant(); - if (receiverConstant != null && receiverConstant.isNonNull()) { - ResolvedJavaField memberField = findField(targetMethod.getDeclaringClass(), "member"); - JavaConstant member = b.getConstantReflection().readFieldValue(memberField, receiverConstant); - ResolvedJavaField clazzField = findField(memberField.getType().resolve(memberField.getDeclaringClass()), "clazz"); - JavaConstant clazz = b.getConstantReflection().readFieldValue(clazzField, member); - b.add(new DirectMethodHandleEnsureInitializedNode(b.getConstantReflection().asJavaType(clazz))); - return true; - } - return false; - } - }); - - r = new Registration(plugins, "java.lang.invoke.Invokers", replacements); - r.register(new OptionalInvocationPlugin("maybeCustomize", MethodHandle.class) { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode mh) { - /* - * JDK 8 update 60 added an additional customization possibility for method handles. - * For all use cases that we care about, that seems to be unnecessary, so we can - * just do nothing. - */ - return true; - } - }); - - r = new Registration(plugins, Objects.class, replacements); - r.register(new RequiredInvocationPlugin("requireNonNull", Object.class) { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode object) { - /* - * Instead of inlining the method, intrinsify it to a pattern that we can easily - * detect when looking at the parsed graph. - */ - b.push(JavaKind.Object, b.addNonNullCast(object)); - return true; - } - }); - r = new Registration(plugins, MethodHandle.class, replacements); - r.register(new RequiredInvocationPlugin("asType", Receiver.class, MethodType.class) { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode newTypeNode) { - ValueNode methodHandleNode = receiver.get(false); - if (methodHandleNode.isJavaConstant() && newTypeNode.isJavaConstant()) { - /* - * If both, the MethodHandle and the MethodType are constant, we can evaluate - * asType eagerly and embed the result as a constant in the graph. - */ - SnippetReflectionProvider snippetReflection = GraalAccess.getOriginalSnippetReflection(); - MethodHandle mh = snippetReflection.asObject(MethodHandle.class, methodHandleNode.asJavaConstant()); - MethodType mt = snippetReflection.asObject(MethodType.class, newTypeNode.asJavaConstant()); - if (mh == null || mt == null) { - return false; - } - final MethodHandle asType; - try { - asType = mh.asType(mt); - } catch (WrongMethodTypeException t) { - return false; - } - JavaConstant asTypeConstant = snippetReflection.forObject(asType); - ConstantNode asTypeNode = ConstantNode.forConstant(asTypeConstant, b.getMetaAccess(), b.getGraph()); - b.push(JavaKind.Object, asTypeNode); - return true; - } - return false; - } - }); - } - - @SuppressWarnings("try") - private boolean processInvokeWithMethodHandle(GraphBuilderContext b, Replacements replacements, ResolvedJavaMethod methodHandleMethod, ValueNode[] methodHandleArguments) { - Plugins graphBuilderPlugins = new Plugins(parsingProviders.getReplacements().getGraphBuilderPlugins()); - - registerInvocationPlugins(graphBuilderPlugins.getInvocationPlugins(), replacements); - - graphBuilderPlugins.prependParameterPlugin(new MethodHandlesParameterPlugin(methodHandleArguments)); - graphBuilderPlugins.clearInlineInvokePlugins(); - graphBuilderPlugins.prependInlineInvokePlugin(new MethodHandlesInlineInvokePlugin()); - graphBuilderPlugins.prependNodePlugin(new MethodHandlePlugin(parsingProviders.getConstantReflection().getMethodHandleAccess(), false)); - - /* We do all the word type rewriting because parameters to the lambda can be word types. */ - SnippetReflectionProvider originalSnippetReflection = GraalAccess.getOriginalSnippetReflection(); - ConstantReflectionProvider originalConstantReflection = GraalAccess.getOriginalProviders().getConstantReflection(); - WordOperationPlugin wordOperationPlugin = new WordOperationPlugin(originalSnippetReflection, originalConstantReflection, - new SubstrateWordTypes(parsingProviders.getMetaAccess(), ConfigurationValues.getWordKind()), - parsingProviders.getPlatformConfigurationProvider().getBarrierSet()); - graphBuilderPlugins.appendInlineInvokePlugin(wordOperationPlugin); - graphBuilderPlugins.appendTypePlugin(wordOperationPlugin); - graphBuilderPlugins.appendNodePlugin(wordOperationPlugin); - graphBuilderPlugins.setClassInitializationPlugin(new NoClassInitializationPlugin()); - - GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getSnippetDefault(graphBuilderPlugins); - GraphBuilderPhase.Instance graphBuilder = new HotSpotGraphBuilderInstance(parsingProviders, graphBuilderConfig, OptimisticOptimizations.NONE, null); - - DebugContext debug = b.getDebug(); - StructuredGraph graph = new StructuredGraph.Builder(b.getOptions(), debug) // - .method(toOriginal(methodHandleMethod)) // - .recordInlinedMethods(false).build(); - try (DebugContext.Scope s = debug.scope("IntrinsifyMethodHandles", graph)) { - graphBuilder.apply(graph); - /* - * The canonicalizer converts unsafe field accesses for get/set method handles back to - * high-level field load and store nodes. - */ - CanonicalizerPhase.create().apply(graph, parsingProviders); - - debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Intrinisfication graph before transplant"); - - NodeMap transplanted = new NodeMap<>(graph); - for (ParameterNode oParam : graph.getNodes(ParameterNode.TYPE)) { - transplanted.put(oParam, methodHandleArguments[oParam.index()]); - } - - Transplanter transplanter = new Transplanter(b, transplanted); - try { - transplanter.graph(graph); - return true; - } catch (AbortTransplantException ex) { - /* - * The method handle cannot be intrinsified. If non-constant method handles are not - * supported, the code that throws an error at runtime was already appended, so - * nothing more to do. If non-constant method handles are supported, we return false - * so that the bytecode parser emit a regular invoke bytecode, i.e., the constant - * method handle is treated as if it were non-constant. - */ - return ex.handled; - } - } catch (Throwable ex) { - throw debug.handle(ex); - } - } - - /** - * Transplants the graph parsed in the HotSpot universe into the currently parsed method. This - * requires conversion of all types, methods, fields, and constants. - * - * Currently, only linear control flow in the original graph is supported. Nodes in the original - * graph that have implicit exception edges ({@link InvokeNode}, {@link FixedGuardNode} are - * converted to nodes with explicit exception edges. So the transplanted graph has a limited - * amount of control flow for exception handling, but still no control flow merges. - * - * All necessary frame states use the same bci from the caller, i.e., all transplanted method - * calls, field stores, exceptions, ... look as if they are coming from the original invocation - * site of the method handle. This means the static analysis is not storing any analysis results - * for these calls, because lookup of analysis results requires a unique bci. - * - * During this process, values are not pushed and popped from the frame state as usual. Instead, - * at most one value is temporarily pushed onto the frame state's stack. During the generation - * process {@link #tempFrameStackValue} is used to represent the value currently temporarily - * pushed onto the stack. - */ - class Transplanter { - private final BytecodeParser b; - private final NodeMap transplanted; - private JavaKind tempFrameStackValue; - - Transplanter(GraphBuilderContext b, NodeMap transplanted) { - this.b = (BytecodeParser) b; - this.transplanted = transplanted; - this.tempFrameStackValue = null; - } - - void graph(StructuredGraph graph) throws AbortTransplantException { - JavaKind returnResultKind = b.getInvokeReturnType().getJavaKind().getStackKind(); - FixedNode oNode = graph.start().next(); - while (true) { - if (fixedWithNextNode(oNode)) { - oNode = ((FixedWithNextNode) oNode).next(); - - } else if (oNode instanceof ReturnNode) { - ReturnNode oReturn = (ReturnNode) oNode; - /* Push the returned result. */ - if (returnResultKind != JavaKind.Void) { - b.push(returnResultKind, node(oReturn.result())); - } - /* We are done. */ - return; - - } else { - throw bailout(); - } - } - } - - /** - * @return whether the current frame has enough space for a new value of the given kind to - * be pushed to the stack. - */ - private boolean frameStackHasSpaceForKind(JavaKind javaKind) { - return b.getFrameStateBuilder().stackSize() + (javaKind.needsTwoSlots() ? 2 : 1) <= b.getMethod().getMaxStackSize(); - } - - /** - * If space is available, temporarily push {@code value} onto frame's stack. - */ - private void pushToFrameStack(ValueNode value) { - JavaKind kind = value.getStackKind(); - /* Pushing new value if there is space. */ - if (frameStackHasSpaceForKind(kind)) { - b.push(kind, value); - tempFrameStackValue = kind; - } - } - - /* - * Remove temp value, if present, from stack. - */ - private void popTempFrameStackValue() { - if (tempFrameStackValue != null) { - b.pop(tempFrameStackValue); - tempFrameStackValue = null; - } - } - - private boolean fixedWithNextNode(FixedNode oNode) throws AbortTransplantException { - if (oNode.getClass() == InvokeNode.class) { - InvokeNode oInvoke = (InvokeNode) oNode; - MethodCallTargetNode oCallTarget = (MethodCallTargetNode) oInvoke.callTarget(); - transplantInvoke(oInvoke, lookup(oCallTarget.targetMethod()), oCallTarget.invokeKind(), nodes(oCallTarget.arguments()), oCallTarget.returnKind()); - return true; - - } else if (oNode.getClass() == FixedGuardNode.class) { - FixedGuardNode oGuard = (FixedGuardNode) oNode; - - if (b.needsExplicitException()) { - BytecodeExceptionKind tExceptionKind; - ValueNode[] tExceptionArguments; - if (oGuard.getReason() == DeoptimizationReason.NullCheckException) { - tExceptionKind = BytecodeExceptionKind.NULL_POINTER; - tExceptionArguments = ValueNode.EMPTY_ARRAY; - } else if (oGuard.getReason() == DeoptimizationReason.ClassCastException && oGuard.condition().getClass() == InstanceOfNode.class) { - /* - * Throwing the ClassCastException requires the checked object and the - * expected type as arguments, which we can get for the InstanceOfNode. - */ - InstanceOfNode oCondition = (InstanceOfNode) oGuard.condition(); - tExceptionKind = BytecodeExceptionKind.CLASS_CAST; - tExceptionArguments = new ValueNode[]{ - node(oCondition.getValue()), - ConstantNode.forConstant(b.getConstantReflection().asJavaClass(lookup(oCondition.type().getType())), b.getMetaAccess(), b.getGraph())}; - } else { - /* - * Several other deoptimization reasons could be supported easily, but for - * now there is no need for them. - */ - return false; - } - - AbstractBeginNode tPassingSuccessor = b.emitBytecodeExceptionCheck((LogicNode) node(oGuard.condition()), !oGuard.isNegated(), tExceptionKind, tExceptionArguments); - /* - * Anchor-usages of the guard are redirected to the BeginNode after the explicit - * exception check. If the check was eliminated, we add a new temporary - * BeginNode. - */ - transplanted.put(oGuard, tPassingSuccessor != null ? tPassingSuccessor : b.add(new BeginNode())); - return true; - - } else { - /* - * When explicit exceptions are unneeded, then directly convert the guard node. - */ - LogicNode condition = (LogicNode) node(oGuard.condition().asNode()); - FixedGuardNode tGuard = b.add(new FixedGuardNode(condition, oGuard.getReason(), oGuard.getAction(), oGuard.isNegated())); - transplanted.put(oGuard, tGuard); - return true; - } - - } else if (oNode.getClass() == LoadFieldNode.class) { - LoadFieldNode oLoad = (LoadFieldNode) oNode; - ResolvedJavaField tTarget = lookup(oLoad.field()); - maybeEmitClassInitialization(b, tTarget.isStatic(), tTarget.getDeclaringClass()); - ValueNode tLoad = b.add(LoadFieldNode.create(null, node(oLoad.object()), tTarget)); - transplanted.put(oLoad, tLoad); - return true; - - } else if (oNode.getClass() == StoreFieldNode.class) { - StoreFieldNode oStore = (StoreFieldNode) oNode; - ResolvedJavaField tTarget = lookup(oStore.field()); - maybeEmitClassInitialization(b, tTarget.isStatic(), tTarget.getDeclaringClass()); - b.add(new StoreFieldNode(node(oStore.object()), tTarget, node(oStore.value()))); - return true; - - } else if (oNode.getClass() == NewInstanceNode.class) { - NewInstanceNode oNew = (NewInstanceNode) oNode; - ResolvedJavaType tInstanceClass = lookup(oNew.instanceClass()); - maybeEmitClassInitialization(b, true, tInstanceClass); - NewInstanceNode tNew = b.add(new NewInstanceNode(tInstanceClass, oNew.fillContents())); - transplanted.put(oNew, tNew); - return true; - - } else if (oNode.getClass() == NewArrayNode.class) { - NewArrayNode oNew = (NewArrayNode) oNode; - NewArrayNode tNew = b.add(new NewArrayNode(lookup(oNew.elementType()), b.maybeEmitExplicitNegativeArraySizeCheck(node(oNew.length())), oNew.fillContents())); - transplanted.put(oNew, tNew); - return true; - - } else if (oNode.getClass() == FinalFieldBarrierNode.class) { - FinalFieldBarrierNode oNew = (FinalFieldBarrierNode) oNode; - FinalFieldBarrierNode tNew = b.add(new FinalFieldBarrierNode(node(oNew.getValue()))); - transplanted.put(oNew, tNew); - return true; - - } else if (oNode.getClass() == DirectMethodHandleEnsureInitializedNode.class) { - DirectMethodHandleEnsureInitializedNode oInit = (DirectMethodHandleEnsureInitializedNode) oNode; - ResolvedJavaType tInstanceClass = lookup(oInit.instanceClass()); - maybeEmitClassInitialization(b, true, tInstanceClass); - return true; - } else { - return false; - } - } - - private ValueNode[] nodes(List oNodes) throws AbortTransplantException { - ValueNode[] tNodes = new ValueNode[oNodes.size()]; - for (int i = 0; i < tNodes.length; i++) { - tNodes[i] = node(oNodes.get(i)); - } - return tNodes; - } - - private ValueNode node(Node oNode) throws AbortTransplantException { - if (oNode == null) { - return null; - } - Node tNode = transplanted.get(oNode); - if (tNode != null) { - return (ValueNode) tNode; - } - - if (oNode.getClass() == ConstantNode.class) { - ConstantNode oConstant = (ConstantNode) oNode; - tNode = ConstantNode.forConstant(constant(oConstant.getValue()), universeProviders.getMetaAccess()); - - } else if (oNode.getClass() == DirectMethodHandleEnsureInitializedNode.class) { - return null; - } else if (oNode.getClass() == PiNode.class) { - PiNode oPi = (PiNode) oNode; - tNode = new PiNode(node(oPi.object()), stamp(oPi.piStamp()), node(oPi.getGuard().asNode())); - - } else if (oNode.getClass() == InstanceOfNode.class) { - InstanceOfNode oInstanceOf = (InstanceOfNode) oNode; - tNode = InstanceOfNode.createHelper(stamp(oInstanceOf.getCheckedStamp()), - node(oInstanceOf.getValue()), - oInstanceOf.profile(), - (AnchoringNode) node((ValueNode) oInstanceOf.getAnchor())); - - } else if (oNode.getClass() == IsNullNode.class) { - IsNullNode oIsNull = (IsNullNode) oNode; - tNode = IsNullNode.create(node(oIsNull.getValue())); - - } else if (oNode instanceof ArithmeticOperation) { - /* - * We consider all arithmetic operations as safe for transplant, since they do not - * have side effects and also do not reference any types or other elements that we - * would need to modify. - */ - for (Node input : oNode.inputs()) { - /* - * Make sure all input nodes are transplanted first, and registered in the - * transplanted map. - */ - node(input); - } - List oNodes = Collections.singletonList(oNode); - UnmodifiableEconomicMap tNodes = b.getGraph().addDuplicates(oNodes, oNode.graph(), 1, transplanted); - /* - * The following assertion looks strange, but NodeMap.size() is not implemented so - * we need to iterate the map to get the size. - */ - assert StreamSupport.stream(tNodes.getKeys().spliterator(), false).count() == 1; - tNode = tNodes.get(oNode); - - } else { - throw bailout(); - } - - tNode = b.add((ValueNode) tNode); - assert tNode.verify(); - transplanted.put(oNode, tNode); - return (ValueNode) tNode; - } - - private void transplantInvoke(InvokeNode oNode, ResolvedJavaMethod tTargetMethod, InvokeKind invokeKind, ValueNode[] arguments, JavaKind invokeResultKind) { - maybeEmitClassInitialization(b, invokeKind == InvokeKind.Static, tTargetMethod.getDeclaringClass()); - - if (invokeResultKind == JavaKind.Void) { - /* - * Invokedynamics can be parsed into a NewInstanceNode & InvokeNode combo. In this - * situation, it is necessary to push the NewInstanceNode onto the stack so that it - * is included in the stateDuring FrameState of the InvokeNode. - */ - Node pred = oNode; - do { - pred = pred.predecessor(); - /* - * The prior NewInstanceNode may be guarded by a fixed guard (e.g., for an - * instanceof check). In this case, look at fixed guard's predecessor. - */ - } while (pred.getClass() == FixedGuardNode.class); - if (pred.getClass() == NewInstanceNode.class && transplanted.containsKey(pred)) { - Node tNew = transplanted.get(pred); - pushToFrameStack((ValueNode) tNew); - } - } - - b.handleReplacedInvoke(invokeKind, tTargetMethod, arguments, false, lookup(oNode.callTarget().referencedType())); - - if (invokeResultKind != JavaKind.Void) { - /* - * The invoke was pushed by handleReplacedInvoke, pop it again. Note that the popped - * value is not necessarily an Invoke, because inlining during parsing and - * intrinsification can happen. - */ - transplanted.put(oNode, b.pop(invokeResultKind)); - } else { - popTempFrameStackValue(); - } - } - - @SuppressWarnings("unchecked") - private T stamp(T oStamp) throws AbortTransplantException { - Stamp result; - if (oStamp.getClass() == ObjectStamp.class) { - ObjectStamp oObjectStamp = (ObjectStamp) oStamp; - result = new ObjectStamp(lookup(oObjectStamp.type()), oObjectStamp.isExactType(), oObjectStamp.nonNull(), oObjectStamp.alwaysNull(), oObjectStamp.isAlwaysArray()); - } else if (oStamp instanceof PrimitiveStamp) { - result = oStamp; - } else { - throw bailout(); - } - assert oStamp.getClass() == result.getClass(); - return (T) result; - } - - private JavaConstant constant(Constant oConstant) throws AbortTransplantException { - JavaConstant tConstant; - if (oConstant == JavaConstant.NULL_POINTER) { - return JavaConstant.NULL_POINTER; - } else if (oConstant instanceof JavaConstant) { - tConstant = lookup((JavaConstant) oConstant); - } else { - throw bailout(); - } - return tConstant; - } - - private RuntimeException bailout() throws AbortTransplantException { - boolean handled = false; - /* - * We need to get out of recursive transplant methods. Easier to use an exception than - * to explicitly check every method invocation for a possible abort. - */ - throw new AbortTransplantException(handled); - } - } - - @SuppressWarnings("serial") - static class AbortTransplantException extends Exception { - private final boolean handled; - - AbortTransplantException(boolean handled) { - this.handled = handled; - } - } - - private void maybeEmitClassInitialization(GraphBuilderContext b, boolean isStatic, ResolvedJavaType declaringClass) { - if (isStatic) { - /* - * We know that this code only runs during bytecode parsing, so the casts to - * BytecodeParser are safe. We want to avoid putting additional rarely used methods into - * GraphBuilderContext. - */ - classInitializationPlugin.apply(b, declaringClass, () -> ((BytecodeParser) b).getFrameStateBuilder().create(b.bci(), (BytecodeParser) b.getNonIntrinsicAncestor(), false, null, null)); - } - } - - private ResolvedJavaMethod lookup(ResolvedJavaMethod method) { - ResolvedJavaMethod result = aUniverse.lookup(method); - if (hUniverse != null) { - result = hUniverse.lookup(result); - } - return result; - } - - private ResolvedJavaField lookup(ResolvedJavaField field) { - ResolvedJavaField result = aUniverse.lookup(field); - if (hUniverse != null) { - result = hUniverse.lookup(result); - } - return result; - } - - private ResolvedJavaType lookup(ResolvedJavaType type) { - ResolvedJavaType result = aUniverse.lookup(type); - if (hUniverse != null) { - result = hUniverse.lookup(result); - } - return result; - } - - private ResolvedJavaType optionalLookup(ResolvedJavaType type) { - ResolvedJavaType result = aUniverse.optionalLookup(type); - if (result != null && hUniverse != null) { - result = hUniverse.optionalLookup(result); - } - return result; - } - - private JavaConstant lookup(JavaConstant constant) { - /* Redirect constant lookup through the shadow heap. */ - return aUniverse.lookup(constant); - } - - private JavaConstant toOriginal(JavaConstant constant) { - if (constant == null) { - return null; - } else if (constant.getJavaKind().isObject() && !constant.isNull()) { - return GraalAccess.getOriginalSnippetReflection().forObject(hostedSnippetReflection.asObject(Object.class, constant)); - } else { - return constant; - } - } - - private static ResolvedJavaMethod toOriginal(ResolvedJavaMethod method) { - if (method instanceof HostedMethod) { - return ((HostedMethod) method).wrapped.wrapped; - } else if (method instanceof AnalysisMethod) { - return ((AnalysisMethod) method).wrapped; - } else { - return method; - } - } -} - -@NodeInfo(size = NodeSize.SIZE_16, cycles = NodeCycles.CYCLES_2, cyclesRationale = "Class initialization only runs at most once at run time, so the amortized cost is only the is-initialized check") -final class DirectMethodHandleEnsureInitializedNode extends FixedWithNextNode { - - public static final NodeClass TYPE = NodeClass.create(DirectMethodHandleEnsureInitializedNode.class); - - private final ResolvedJavaType clazz; - - protected DirectMethodHandleEnsureInitializedNode(ResolvedJavaType clazz) { - super(TYPE, StampFactory.forVoid()); - this.clazz = clazz; - } - - public ResolvedJavaType instanceClass() { - return clazz; - } -}