diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/AbstractForeignCallStub.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/AbstractForeignCallStub.java index a3d04d541a58..54e3e431954a 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/AbstractForeignCallStub.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/AbstractForeignCallStub.java @@ -240,7 +240,7 @@ protected final StructuredGraph getGraph(DebugContext debug, CompilationIdentifi ResolvedJavaMethod getAndClearObjectResult = foreignCallSnippets.getAndClearObjectResult.getMethod(); ResolvedJavaMethod verifyObject = foreignCallSnippets.verifyObject.getMethod(); ResolvedJavaMethod thisMethod = getGraphMethod(); - GraphKit kit = new GraphKit(debug, thisMethod, providers, wordTypes, providers.getGraphBuilderPlugins(), compilationId, toString(), false, true); + HotSpotGraphKit kit = new HotSpotGraphKit(debug, thisMethod, providers, wordTypes, providers.getGraphBuilderPlugins(), compilationId, toString(), false, true); StructuredGraph graph = kit.getGraph(); graph.getGraphState().forceDisableFrameStateVerification(); ReadRegisterNode thread = kit.append(new ReadRegisterNode(providers.getRegisters().getThreadRegister(), wordTypes.getWordKind(), true, false)); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/HotSpotGraphKit.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/HotSpotGraphKit.java new file mode 100644 index 000000000000..375796c23b7c --- /dev/null +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/HotSpotGraphKit.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022, 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 org.graalvm.compiler.hotspot.stubs; + +import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; +import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; + +import org.graalvm.compiler.core.common.CompilationIdentifier; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.java.GraphBuilderPhase; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.InvokeNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; +import org.graalvm.compiler.phases.common.inlining.InliningUtil; +import org.graalvm.compiler.phases.util.Providers; +import org.graalvm.compiler.replacements.GraphKit; +import org.graalvm.compiler.word.WordTypes; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * HotSpot-specific extensions for manually building a graph. + * + * The support for inlining calls is here and not in the base class because the + * {@link GraphBuilderPhase} is VM specific, i.e., for other VMs a subclass of + * {@link GraphBuilderPhase} might be necessary for correct parsing. + */ +public class HotSpotGraphKit extends GraphKit { + + public HotSpotGraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, Plugins graphBuilderPlugins, CompilationIdentifier compilationId, String name, + boolean trackNodeSourcePosition, boolean recordInlinedMethods) { + super(debug, stubMethod, providers, wordTypes, graphBuilderPlugins, compilationId, name, trackNodeSourcePosition, recordInlinedMethods); + } + + /** + * Recursively {@linkplain #inlineAsIntrinsic inlines} all invocations currently in the graph. + * The graph of the inlined method is processed in the same manner as for snippets and method + * substitutions (e.g. intrinsics). + */ + public void inlineInvokesAsIntrinsics(String reason, String phase) { + while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { + for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { + inlineAsIntrinsic(invoke, reason, phase); + } + } + + // Clean up all code that is now dead after inlining. + new DeadCodeEliminationPhase().apply(graph); + } + + /** + * Inlines a given invocation to a method. The graph of the inlined method is processed in the + * same manner as for snippets and method substitutions (e.g. intrinsics). + */ + public void inlineAsIntrinsic(Invoke invoke, String reason, String phase) { + assert invoke instanceof Node; + Node invokeNode = (Node) invoke; + ResolvedJavaMethod method = invoke.callTarget().targetMethod(); + + Plugins plugins = new Plugins(graphBuilderPlugins); + GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); + + StructuredGraph calleeGraph; + if (IS_IN_NATIVE_IMAGE) { + calleeGraph = getReplacements().getSnippet(method, null, null, null, false, null, invokeNode.getOptions()); + } else { + calleeGraph = new StructuredGraph.Builder(invokeNode.getOptions(), invokeNode.getDebug()).method(method).trackNodeSourcePosition( + invokeNode.graph().trackNodeSourcePosition()).setIsSubstitution(true).build(); + IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING); + GraphBuilderPhase.Instance instance = createGraphBuilderInstance(config, OptimisticOptimizations.NONE, initialReplacementContext); + instance.apply(calleeGraph); + } + new DeadCodeEliminationPhase().apply(calleeGraph); + + InliningUtil.inline(invoke, calleeGraph, false, method, reason, phase); + } + + protected GraphBuilderPhase.Instance createGraphBuilderInstance(GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, + IntrinsicContext initialIntrinsicContext) { + /* There is no HotSpot-specific subclass of GraphBuilderPhase yet. */ + return new GraphBuilderPhase.Instance(getProviders(), graphBuilderConfig, optimisticOpts, initialIntrinsicContext); + } +} diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java index e3e416619181..b9b7253b5581 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java @@ -27,7 +27,6 @@ import static jdk.vm.ci.code.BytecodeFrame.UNKNOWN_BCI; import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; import static org.graalvm.compiler.nodes.CallTargetNode.InvokeKind.Static; -import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -42,7 +41,6 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Graph; -import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.Node.ValueNumberable; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.java.FrameStateBuilder; @@ -56,7 +54,6 @@ import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.IfNode; -import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeNode; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.LogicNode; @@ -72,14 +69,10 @@ import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool; -import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.spi.CoreProvidersDelegate; import org.graalvm.compiler.nodes.type.StampTool; -import org.graalvm.compiler.phases.OptimisticOptimizations; -import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; -import org.graalvm.compiler.phases.common.inlining.InliningUtil; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.word.WordTypes; @@ -95,7 +88,7 @@ * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase * bytecode parsing} based graph creation). */ -public class GraphKit extends CoreProvidersDelegate implements GraphBuilderTool { +public abstract class GraphKit extends CoreProvidersDelegate implements GraphBuilderTool { protected final StructuredGraph graph; protected final WordTypes wordTypes; @@ -350,74 +343,6 @@ public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) { return true; } - /** - * Recursively {@linkplain #inlineAsIntrinsic inlines} all invocations currently in the graph. - * The graph of the inlined method is processed in the same manner as for snippets and method - * substitutions (e.g. intrinsics). - */ - public void inlineInvokesAsIntrinsics(String reason, String phase) { - while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { - for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { - inlineAsIntrinsic(invoke, reason, phase); - } - } - - // Clean up all code that is now dead after inlining. - new DeadCodeEliminationPhase().apply(graph); - } - - /** - * Inlines a given invocation to a method. The graph of the inlined method is processed in the - * same manner as for snippets and method substitutions (e.g. intrinsics). - */ - public void inlineAsIntrinsic(Invoke invoke, String reason, String phase) { - assert invoke instanceof Node; - Node invokeNode = (Node) invoke; - ResolvedJavaMethod method = invoke.callTarget().targetMethod(); - - Plugins plugins = new Plugins(graphBuilderPlugins); - GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); - - StructuredGraph calleeGraph; - if (IS_IN_NATIVE_IMAGE) { - calleeGraph = getReplacements().getSnippet(method, null, null, null, false, null, invokeNode.getOptions()); - } else { - calleeGraph = new StructuredGraph.Builder(invokeNode.getOptions(), invokeNode.getDebug()).method(method).trackNodeSourcePosition( - invokeNode.graph().trackNodeSourcePosition()).setIsSubstitution(true).build(); - IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING); - GraphBuilderPhase.Instance instance = createGraphBuilderInstance(config, OptimisticOptimizations.NONE, initialReplacementContext); - instance.apply(calleeGraph); - } - new DeadCodeEliminationPhase().apply(calleeGraph); - - InliningUtil.inline(invoke, calleeGraph, false, method, reason, phase); - } - - public void inline(Invoke invoke, String reason, String phase) { - assert invoke instanceof Node; - Node invokeNode = (Node) invoke; - ResolvedJavaMethod methodToInline = invoke.callTarget().targetMethod(); - Plugins plugins = new Plugins(graphBuilderPlugins); - GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins); - StructuredGraph calleeGraph = new StructuredGraph.Builder(invokeNode.getOptions(), invokeNode.getDebug()).method(methodToInline).trackNodeSourcePosition( - invokeNode.graph().trackNodeSourcePosition()).setIsSubstitution(false).build(); - /* - * Using null as the intrinsic context makes the ByteCodeParser inline invokes using - * InliningScope instead of IntrinsicScope. This allows exceptions to be a part of the - * inlined method. - */ - GraphBuilderPhase.Instance instance = createGraphBuilderInstance(config, OptimisticOptimizations.NONE, null); - instance.apply(calleeGraph); - - new DeadCodeEliminationPhase().apply(calleeGraph); - InliningUtil.inline(invoke, calleeGraph, false, methodToInline, reason, phase); - } - - protected GraphBuilderPhase.Instance createGraphBuilderInstance(GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, - IntrinsicContext initialIntrinsicContext) { - return new GraphBuilderPhase.Instance(getProviders(), graphBuilderConfig, optimisticOpts, initialIntrinsicContext); - } - protected void pushStructure(Structure structure) { structures.add(structure); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java index d6addaafbe95..fff16555d125 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java @@ -36,6 +36,8 @@ import com.oracle.graal.pointsto.typestate.SingleTypeState; import com.oracle.graal.pointsto.typestate.TypeState; +import jdk.vm.ci.meta.JavaConstant; + public class ContextSensitiveSingleTypeState extends SingleTypeState { /** The objects of this type state. */ protected final AnalysisObject[] objects; @@ -145,8 +147,16 @@ public boolean isAllocation() { } @Override - public boolean isConstant() { - return objects[0].isConstantContextSensitiveObject(); + public JavaConstant asConstant() { + JavaConstant result = null; + for (AnalysisObject object : objects) { + JavaConstant objectConstant = object.asConstant(); + if (objectConstant == null || (result != null && !result.equals(objectConstant))) { + return null; + } + result = objectConstant; + } + return result; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java index 0d97c8456785..3caef40f1e5b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java @@ -179,12 +179,8 @@ public final boolean isAllocationContextSensitiveObject() { return this.kind == AnalysisObjectKind.AllocationContextSensitive; } - public final boolean isConstantContextSensitiveObject() { - return this.kind == AnalysisObjectKind.ConstantContextSensitive; - } - - public final boolean isConstantObject() { - return this.kind == AnalysisObjectKind.ConstantObject; + public JavaConstant asConstant() { + return null; } public ArrayElementsTypeStore getArrayElementsTypeStore() { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java index d4a5e93abc2c..89f6b8d9e820 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java @@ -79,7 +79,8 @@ public ConstantContextSensitiveObject(PointsToAnalysis bb, AnalysisType type, Ja bb.profileConstantObject(type); } - public JavaConstant getConstant() { + @Override + public JavaConstant asConstant() { return constant; } @@ -128,7 +129,7 @@ public boolean isEmptyObjectArrayConstant(PointsToAnalysis bb) { return false; } - return AnalysisObject.isEmptyObjectArrayConstant(bb, getConstant()); + return AnalysisObject.isEmptyObjectArrayConstant(bb, asConstant()); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index 315daf8a4ef4..50fb336c9cf0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -83,6 +83,7 @@ import com.oracle.graal.pointsto.flow.MethodFlowsGraph; import com.oracle.graal.pointsto.flow.MethodTypeFlow; import com.oracle.graal.pointsto.flow.TypeFlow; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.infrastructure.Universe; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -91,6 +92,7 @@ import com.oracle.graal.pointsto.typestate.TypeState; import com.oracle.svm.util.ImageBuildStatistics; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaMethodProfile; import jdk.vm.ci.meta.JavaTypeProfile; @@ -105,7 +107,7 @@ * processing the graph. * * From the single-method view that the compiler has when later compiling the graph, static analysis - * results appear "out of thin air": At some some random point in the graph, we suddenly have a more + * results appear "out of thin air": At some random point in the graph, we suddenly have a more * precise type (= stamp) for a value. Since many nodes are floating, and even currently fixed nodes * might float later, we need to be careful that all information coming from the type flow graph * remains properly anchored to the point where the static analysis actually proved the information. @@ -246,12 +248,12 @@ public void simplify(Node n, SimplifierTool tool) { if (n instanceof ParameterNode) { ParameterNode node = (ParameterNode) n; StartNode anchorPoint = graph.start(); - Stamp newStamp = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool); - updateStampUsingPiNode(node, newStamp, anchorPoint, tool); + Object newStampOrConstant = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool); + updateStampUsingPiNode(node, newStampOrConstant, anchorPoint, tool); } else if (n instanceof LoadFieldNode || n instanceof LoadIndexedNode) { FixedWithNextNode node = (FixedWithNextNode) n; - Stamp newStamp = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool); + Object newStampOrConstant = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool); /* * Even though the memory load will be a floating node later, we can update the * stamp directly because the type information maintained by the static analysis @@ -259,7 +261,13 @@ public void simplify(Node n, SimplifierTool tool) { * context-sensitive analysis, we will need to change this. But for now, we are * fine. */ - updateStampInPlace(node, newStamp, tool); + if (newStampOrConstant instanceof JavaConstant) { + ConstantNode replacement = ConstantNode.forConstant((JavaConstant) newStampOrConstant, bb.getMetaAccess(), graph); + graph.replaceFixedWithFloating(node, replacement); + tool.addToWorkList(replacement); + } else { + updateStampInPlace(node, (Stamp) newStampOrConstant, tool); + } } else if (n instanceof Invoke) { Invoke invoke = (Invoke) n; @@ -385,13 +393,13 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { NodeInputList arguments = callTarget.arguments(); for (int i = 0; i < arguments.size(); i++) { ValueNode argument = arguments.get(i); - Stamp newStamp = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool); + Object newStampOrConstant = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool); if (node.isDeleted()) { /* Parameter stamp was empty, so invoke is unreachable. */ return; - } else if (newStamp != null) { - PiNode pi = insertPi(argument, newStamp, beforeInvoke); - if (pi != null) { + } else if (newStampOrConstant != null) { + ValueNode pi = insertPi(argument, newStampOrConstant, beforeInvoke); + if (pi != null && pi != argument) { callTarget.replaceAllInputs(argument, pi); } } @@ -423,8 +431,8 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { optimizeReturnedParameter(callees, arguments, node, tool); FixedWithNextNode anchorPointAfterInvoke = (FixedWithNextNode) (invoke instanceof InvokeWithExceptionNode ? invoke.next() : invoke); - Stamp newStamp = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool); - updateStampUsingPiNode(node, newStamp, anchorPointAfterInvoke, tool); + Object newStampOrConstant = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool); + updateStampUsingPiNode(node, newStampOrConstant, anchorPointAfterInvoke, tool); } /** @@ -519,9 +527,9 @@ private void updateStampInPlace(ValueNode node, Stamp newStamp, SimplifierTool t } } - private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNextNode anchorPoint, SimplifierTool tool) { - if (newStamp != null && node.hasUsages() && !createdPiNodes.isMarked(node)) { - PiNode pi = insertPi(node, newStamp, anchorPoint); + private void updateStampUsingPiNode(ValueNode node, Object newStampOrConstant, FixedWithNextNode anchorPoint, SimplifierTool tool) { + if (newStampOrConstant != null && node.hasUsages() && !createdPiNodes.isMarked(node)) { + ValueNode pi = insertPi(node, newStampOrConstant, anchorPoint); if (pi != null) { /* * The Canonicalizer that drives all of our node processing is iterative. We @@ -529,9 +537,12 @@ private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNex */ createdPiNodes.mark(node); - FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter(); - node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState); - + if (pi.isConstant()) { + node.replaceAtUsages(pi); + } else { + FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter(); + node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState); + } tool.addToWorkList(pi.usages()); } } @@ -540,7 +551,17 @@ private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNex /* * See comment on {@link StrengthenGraphs} on why anchoring is necessary. */ - private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchorPoint) { + private ValueNode insertPi(ValueNode input, Object newStampOrConstant, FixedWithNextNode anchorPoint) { + if (newStampOrConstant instanceof JavaConstant) { + JavaConstant constant = (JavaConstant) newStampOrConstant; + if (input.isConstant()) { + assert input.asConstant().equals(constant); + return null; + } + return ConstantNode.forConstant(constant, bb.getMetaAccess(), graph); + } + + Stamp piStamp = (Stamp) newStampOrConstant; Stamp oldStamp = input.stamp(NodeView.DEFAULT); Stamp computedStamp = oldStamp.improveWith(piStamp); if (oldStamp.equals(computedStamp)) { @@ -553,7 +574,7 @@ private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchor return graph.unique(new PiNode(input, piStamp, anchor)); } - private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, FixedWithNextNode anchorPoint, SimplifierTool tool) { + private Object strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, FixedWithNextNode anchorPoint, SimplifierTool tool) { PointsToAnalysis pta = getAnalysis(); if (node.getStackKind() != JavaKind.Object) { return null; @@ -571,6 +592,23 @@ private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, } TypeState nodeTypeState = methodFlow.foldTypeFlow(pta, nodeFlow); + + if (!nodeTypeState.canBeNull()) { + JavaConstant constantValue = nodeTypeState.asConstant(); + if (constantValue instanceof ImageHeapConstant) { + /* + * GR-42996: until the AOT compilation can properly constant fold also + * ImageHeapConstant, we unwrap the ImageHeapConstant to the hosted object. This + * also means we do not constant fold yet when the constant does not wrap a + * hosted object. + */ + constantValue = ((ImageHeapConstant) constantValue).getHostedObject(); + } + if (constantValue != null) { + return constantValue; + } + } + node.inferStamp(); ObjectStamp oldStamp = (ObjectStamp) node.stamp(NodeView.DEFAULT); AnalysisType oldType = (AnalysisType) oldStamp.type(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java index ec634493bc31..953820221765 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java @@ -100,8 +100,8 @@ public void noteMerge(PointsToAnalysis bb) { } @Override - public boolean isConstant() { - return true; + public JavaConstant asConstant() { + return constant; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java index 8c0cda1e01d8..bcb3bfa195cf 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java @@ -632,7 +632,7 @@ public static String asString(TypeState s) { return ""; } - String sKind = s.isAllocation() ? "Alloc" : s.isConstant() ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : ""; + String sKind = s.isAllocation() ? "Alloc" : s.asConstant() != null ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : ""; String sSizeOrType = s instanceof MultiTypeState ? s.typesCount() + "" : s.exactType().toJavaName(false); int objectsNumber = s.objectsCount(); String canBeNull = s.canBeNull() ? "null" : "!null"; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java index fb89b57f405a..1358b96cab1b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java @@ -115,8 +115,16 @@ public boolean isAllocation() { return false; } - public boolean isConstant() { - return false; + /** + * Returns a non-null value when this type state represents a single constant value, or null if + * this type state is not a single constant. + * + * Note that the {@link #canBeNull()} flag still applies when a constant is returned. A type + * state that is a "constant or null" both returns a non-null result for {@link #asConstant()}} + * and true for {@link #canBeNull()}. + */ + public JavaConstant asConstant() { + return null; } public boolean isEmpty() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 2b8ab540ea27..a8f9a69d3a4b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -46,8 +46,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiPredicate; -import com.oracle.svm.core.jdk.InternalVMMethod; -import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; @@ -105,6 +103,8 @@ import com.oracle.svm.core.hub.HubType; import com.oracle.svm.core.hub.ReferenceType; import com.oracle.svm.core.jdk.ClassLoaderSupport; +import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; import com.oracle.svm.core.jdk.RecordSupport; import com.oracle.svm.core.jdk.SealedClassSupport; import com.oracle.svm.core.option.HostedOptionKey; @@ -116,6 +116,7 @@ import com.oracle.svm.hosted.code.InliningUtilities; import com.oracle.svm.hosted.code.UninterruptibleAnnotationChecker; import com.oracle.svm.hosted.heap.PodSupport; +import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase; @@ -157,6 +158,8 @@ public class SVMHost extends HostVM { private final ConcurrentMap> initializedClasses = new ConcurrentHashMap<>(); private final ConcurrentMap analysisTrivialMethods = new ConcurrentHashMap<>(); + private final Set finalFieldsInitializedOutsideOfConstructor = ConcurrentHashMap.newKeySet(); + public SVMHost(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport, UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform, SnippetReflectionProvider originalSnippetReflection) { super(options, classLoader); @@ -219,7 +222,7 @@ public void checkForbidden(AnalysisType type, AnalysisType.UsageKind kind) { @Override public Instance createGraphBuilderPhase(HostedProviders providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { - return new AnalysisGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, providers.getWordTypes()); + return new AnalysisGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, providers.getWordTypes(), this); } @Override @@ -774,4 +777,29 @@ public Comparator getTypeComparator() { return HostedUniverse.TYPE_COMPARATOR.compare((HostedType) o1, (HostedType) o2); }; } + + /** + * According to the Java VM specification, final instance fields are only allowed to be written + * in a constructor. But some compilers violate the spec, notably the Scala compiler. This means + * final fields of image heap objects can be modified at image run time, and constant folding + * such fields at image build time would fold in the wrong value. As a workaround, we record + * which final instance fields are written outside of a constructor, and disable constant + * folding for those fields. + * + * Note that there can be races: A constant folding can happen during bytecode parsing before + * the store was recorded. Currently, we do not encounter that problem because constant folding + * only happens after static analysis. But there would not be a good solution other than + * aborting the image build, because a constant folding cannot be reversed. + */ + public void recordFieldStore(ResolvedJavaField field, ResolvedJavaMethod method) { + if (!field.isStatic() && field.isFinal() && (!method.isConstructor() || !field.getDeclaringClass().equals(method.getDeclaringClass()))) { + AnalysisField aField = field instanceof HostedField ? ((HostedField) field).getWrapped() : (AnalysisField) field; + finalFieldsInitializedOutsideOfConstructor.add(aField); + } + } + + public boolean preventConstantFolding(ResolvedJavaField field) { + AnalysisField aField = field instanceof HostedField ? ((HostedField) field).getWrapped() : (AnalysisField) field; + return finalFieldsInitializedOutsideOfConstructor.contains(aField); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java index 51a16e38e792..0e932aa27df8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java @@ -46,7 +46,7 @@ public class AnalysisConstantFieldProvider extends SharedConstantFieldProvider { public AnalysisConstantFieldProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, AnalysisConstantReflectionProvider constantReflection, ClassInitializationSupport classInitializationSupport) { - super(metaAccess, classInitializationSupport); + super(metaAccess, classInitializationSupport, (SVMHost) universe.hostVM()); this.universe = universe; this.constantReflection = constantReflection; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/HostedRuntimeConfigurationBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/HostedRuntimeConfigurationBuilder.java index 660aaeb3aa03..348a729714a7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/HostedRuntimeConfigurationBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/HostedRuntimeConfigurationBuilder.java @@ -106,7 +106,7 @@ protected CodeCacheProvider createCodeCacheProvider(RegisterConfig registerConfi @Override protected ConstantFieldProvider createConstantFieldProvider(Providers p) { - return new HostedConstantFieldProvider(p.getMetaAccess(), classInitializationSupport); + return new HostedConstantFieldProvider(p.getMetaAccess(), classInitializationSupport, universe.hostVM()); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantFieldProvider.java index a53d7ab551bf..48d568fac2c2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantFieldProvider.java @@ -29,6 +29,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.meta.ReadableJavaField; +import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; @@ -38,8 +39,8 @@ @Platforms(Platform.HOSTED_ONLY.class) public class HostedConstantFieldProvider extends SharedConstantFieldProvider { - public HostedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) { - super(metaAccess, classInitializationSupport); + public HostedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport, SVMHost hostVM) { + super(metaAccess, classInitializationSupport, hostVM); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java index 2147e7acaac1..e8497aa48283 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java @@ -30,6 +30,7 @@ import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import jdk.vm.ci.meta.JavaConstant; @@ -42,11 +43,13 @@ public abstract class SharedConstantFieldProvider extends JavaConstantFieldProvi protected final ClassInitializationSupport classInitializationSupport; protected final UniverseMetaAccess metaAccess; + protected final SVMHost hostVM; - public SharedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) { + public SharedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport, SVMHost hostVM) { super(metaAccess); this.classInitializationSupport = classInitializationSupport; this.metaAccess = (UniverseMetaAccess) metaAccess; + this.hostVM = hostVM; } @Override @@ -54,6 +57,10 @@ public boolean isFinalField(ResolvedJavaField field, ConstantFieldTool tool) if (classInitializationSupport.shouldInitializeAtRuntime(field.getDeclaringClass())) { return false; } + if (hostVM.preventConstantFolding(field)) { + return false; + } + return super.isFinalField(field, tool); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/AnalysisGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/AnalysisGraphBuilderPhase.java index f18c16a77fdc..7bdb4f4046e0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/AnalysisGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/AnalysisGraphBuilderPhase.java @@ -42,27 +42,36 @@ import com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; public class AnalysisGraphBuilderPhase extends SharedGraphBuilderPhase { + private final SVMHost hostVM; + public AnalysisGraphBuilderPhase(CoreProviders providers, - GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes) { + GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, SVMHost hostVM) { super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); + this.hostVM = hostVM; } @Override protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) { - return new AnalysisBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext); + return new AnalysisBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext, hostVM); } public static class AnalysisBytecodeParser extends SharedBytecodeParser { + + private final SVMHost hostVM; + protected AnalysisBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, - IntrinsicContext intrinsicContext) { + IntrinsicContext intrinsicContext, SVMHost hostVM) { super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true); + this.hostVM = hostVM; } @Override @@ -129,5 +138,11 @@ protected void genInvokeDynamic(int cpi, int opcode) { } super.genInvokeDynamic(cpi, opcode); } + + @Override + protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) { + hostVM.recordFieldStore(field, method); + super.genStoreField(receiver, field, value); + } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java index 6e0e19c86d19..a53a7b93b946 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java @@ -27,7 +27,6 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.java.GraphBuilderPhase.Instance; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.ConstantNode; @@ -43,14 +42,10 @@ import org.graalvm.compiler.nodes.extended.BranchProbabilityNode; import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode; import org.graalvm.compiler.nodes.extended.GuardingNode; -import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; -import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; import org.graalvm.compiler.nodes.java.LoadFieldNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.type.StampTool; -import org.graalvm.compiler.phases.OptimisticOptimizations; -import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.results.StaticAnalysisResults; import com.oracle.svm.core.c.BoxedRelocatedPointer; @@ -89,20 +84,6 @@ protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, Res } } - @Override - protected Instance createGraphBuilderInstance(GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, - IntrinsicContext initialIntrinsicContext) { - - ResolvedJavaMethod method = graph.method(); - if (method instanceof AnalysisMethod) { - return new AnalysisGraphBuilderPhase(getProviders(), graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); - } else if (method instanceof HostedMethod) { - return new HostedGraphBuilderPhase(getProviders(), graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); - } else { - throw VMError.shouldNotReachHere(); - } - } - public void emitEnsureInitializedCall(ResolvedJavaType type) { if (EnsureClassInitializedNode.needsRuntimeInitialization(graph.method().getDeclaringClass(), type)) { ValueNode hub = createConstant(getConstantReflection().asJavaClass(type), JavaKind.Object);