From 3f1a26b444f2c40467a31463fc6c9524130f24d8 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Thu, 15 May 2025 17:22:38 +0200 Subject: [PATCH] Fix reachability analysis Reuse the registrations and optimizations done before analysis as much as possible --- .../pointsto/flow/MethodTypeFlowBuilder.java | 67 +++--- .../pointsto/results/StrengthenGraphs.java | 5 +- .../DirectMethodProcessingHandler.java | 211 ++++++------------ .../ReachabilityAnalysisEngine.java | 27 +-- .../ReachabilityAnalysisMethod.java | 22 +- .../ReachabilityAnalysisType.java | 3 +- 6 files changed, 141 insertions(+), 194 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index 9028ccedcf91..eb169807dd57 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -184,7 +184,7 @@ public class MethodTypeFlowBuilder { protected StructuredGraph graph; private NodeBitMap processedNodes; private Map> loopPhiFlows; - private final MethodFlowsGraph.GraphKind graphKind; + private final GraphKind graphKind; private boolean processed = false; private final boolean newFlowsGraph; @@ -240,28 +240,7 @@ private boolean parse(Object reason, boolean forceReparse) { graph = InlineBeforeAnalysis.decodeGraph(bb, method, analysisParsedGraph); try (DebugContext.Scope s = graph.getDebug().scope("MethodTypeFlowBuilder", graph)) { - CanonicalizerPhase canonicalizerPhase = CanonicalizerPhase.create(); - canonicalizerPhase.apply(graph, bb.getProviders(method)); - if (PointstoOptions.ConditionalEliminationBeforeAnalysis.getValue(bb.getOptions())) { - /* - * Removing unnecessary conditions before the static analysis runs reduces the size - * of the type flow graph. For example, this removes redundant null checks: the - * bytecode parser emits explicit null checks before e.g., all method calls, field - * access, array accesses; many of those dominate each other. - */ - new IterativeConditionalEliminationPhase(canonicalizerPhase, false).apply(graph, bb.getProviders(method)); - } - if (PointstoOptions.EscapeAnalysisBeforeAnalysis.getValue(bb.getOptions())) { - if (method.isOriginalMethod()) { - /* - * Deoptimization Targets cannot have virtual objects in frame states. - * - * Also, more work is needed to enable PEA in Runtime Compiled Methods. - */ - new BoxNodeIdentityPhase().apply(graph, bb.getProviders(method)); - new PartialEscapePhase(false, canonicalizerPhase, bb.getOptions()).apply(graph, bb.getProviders(method)); - } - } + optimizeGraphBeforeAnalysis(bb, method, graph); if (!bb.getUniverse().hostVM().validateGraph(bb, graph)) { graph = null; @@ -277,8 +256,33 @@ private boolean parse(Object reason, boolean forceReparse) { } } - public static void registerUsedElements(PointsToAnalysis bb, StructuredGraph graph, boolean usePredicates) { - PointsToAnalysisMethod method = (PointsToAnalysisMethod) graph.method(); + public static void optimizeGraphBeforeAnalysis(AbstractAnalysisEngine bb, AnalysisMethod method, StructuredGraph graph) { + CanonicalizerPhase canonicalizerPhase = CanonicalizerPhase.create(); + canonicalizerPhase.apply(graph, bb.getProviders(method)); + if (PointstoOptions.ConditionalEliminationBeforeAnalysis.getValue(bb.getOptions())) { + /* + * Removing unnecessary conditions before the static analysis runs reduces the size of + * the type flow graph. For example, this removes redundant null checks: the bytecode + * parser emits explicit null checks before e.g., all method calls, field access, array + * accesses; many of those dominate each other. + */ + new IterativeConditionalEliminationPhase(canonicalizerPhase, false).apply(graph, bb.getProviders(method)); + } + if (PointstoOptions.EscapeAnalysisBeforeAnalysis.getValue(bb.getOptions())) { + if (method.isOriginalMethod()) { + /* + * Deoptimization Targets cannot have virtual objects in frame states. + * + * Also, more work is needed to enable PEA in Runtime Compiled Methods. + */ + new BoxNodeIdentityPhase().apply(graph, bb.getProviders(method)); + new PartialEscapePhase(false, canonicalizerPhase, bb.getOptions()).apply(graph, bb.getProviders(method)); + } + } + } + + public static void registerUsedElements(AbstractAnalysisEngine bb, StructuredGraph graph, boolean usePredicates) { + var method = (AnalysisMethod) graph.method(); HostedProviders providers = bb.getProviders(method); for (Node n : graph.getNodes()) { if (n instanceof InstanceOfNode) { @@ -295,7 +299,8 @@ public static void registerUsedElements(PointsToAnalysis bb, StructuredGraph gra type.registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); for (var f : type.getInstanceFields(true)) { var field = (AnalysisField) f; - field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(bb, field.getStorageKind())); + PointsToAnalysis pta = (PointsToAnalysis) bb; + field.getInitialFlow().addState(pta, TypeState.defaultValueForKind(pta, field.getStorageKind())); } } @@ -416,7 +421,7 @@ public static void registerUsedElements(PointsToAnalysis bb, StructuredGraph gra * {@link FrameState} are only used for debugging. We do not want to have larger images just so * that users can see a constant value in the debugger. */ - protected static boolean ignoreConstant(PointsToAnalysis bb, ConstantNode node) { + protected static boolean ignoreConstant(AbstractAnalysisEngine bb, ConstantNode node) { for (var u : node.usages()) { if (u instanceof ClassIsAssignableFromNode usage) { if (!bb.getHostVM().isClosedTypeWorld() || usage.getOtherClass() == node || usage.getThisClass() != node) { @@ -472,7 +477,7 @@ protected static boolean needsUnsafeRegistration(FieldOffsetProvider node) { * later. Therefore, we must mark the instanceof checked type as reachable. Moreover, stamp * strengthening based on reachability status of types must be disabled. */ - protected static boolean ignoreInstanceOfType(PointsToAnalysis bb, AnalysisType type) { + protected static boolean ignoreInstanceOfType(AbstractAnalysisEngine bb, AnalysisType type) { if (bb.getHostVM().ignoreInstanceOfTypeDisallowed()) { return false; } @@ -493,11 +498,11 @@ protected static boolean ignoreInstanceOfType(PointsToAnalysis bb, AnalysisType return true; } - private static void registerEmbeddedRoot(PointsToAnalysis bb, ConstantNode cn) { + private static void registerEmbeddedRoot(AbstractAnalysisEngine bb, ConstantNode cn) { bb.getUniverse().registerEmbeddedRoot(cn.asJavaConstant(), AbstractAnalysisEngine.sourcePosition(cn)); } - private static void registerForeignCall(PointsToAnalysis bb, ForeignCallsProvider foreignCallsProvider, ForeignCallDescriptor foreignCallDescriptor, ResolvedJavaMethod from) { + private static void registerForeignCall(AbstractAnalysisEngine bb, ForeignCallsProvider foreignCallsProvider, ForeignCallDescriptor foreignCallDescriptor, ResolvedJavaMethod from) { Optional targetMethod = bb.getHostVM().handleForeignCall(foreignCallDescriptor, foreignCallsProvider); targetMethod.ifPresent(analysisMethod -> bb.addRootMethod(analysisMethod, true, from)); } @@ -725,7 +730,7 @@ protected void apply(boolean forceReparse, Object reason) { } boolean insertPlaceholderFlows = bb.getHostVM().getMultiMethodAnalysisPolicy().insertPlaceholderParamAndReturnFlows(method.getMultiMethodKey()); - if (graphKind == MethodFlowsGraph.GraphKind.STUB) { + if (graphKind == GraphKind.STUB) { AnalysisError.guarantee(insertPlaceholderFlows, "placeholder flows must be enabled for STUB graphkinds."); insertPlaceholderParamAndReturnFlows(); return; 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 04ee7c817262..ca214f4442a3 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 @@ -52,6 +52,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.PointsToAnalysisField; import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.typestate.PrimitiveConstantTypeState; import com.oracle.graal.pointsto.typestate.TypeState; @@ -437,7 +438,7 @@ public void simplify(Node n, SimplifierTool tool) { Object newStampOrConstant = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool); updateStampUsingPiNode(node, newStampOrConstant, anchorPoint, tool); - } else if (n instanceof LoadFieldNode node) { + } else if (n instanceof LoadFieldNode node && node.field() instanceof PointsToAnalysisField field) { /* * First step: it is beneficial to strengthen the stamp of the LoadFieldNode because * then there is no artificial anchor after which the more precise type is @@ -445,7 +446,7 @@ public void simplify(Node n, SimplifierTool tool) { * update the stamp directly to the stamp that is correct for the whole method and * all inlined methods. */ - Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, ((AnalysisField) node.field()).getSinkFlow(), node, tool); + Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, field.getSinkFlow(), node, tool); if (fieldNewStampOrConstant instanceof JavaConstant) { ConstantNode replacement = ConstantNode.forConstant((JavaConstant) fieldNewStampOrConstant, bb.getMetaAccess(), graph); graph.replaceFixedWithFloating(node, replacement); diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/DirectMethodProcessingHandler.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/DirectMethodProcessingHandler.java index d0d699e05707..e356c9269c30 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/DirectMethodProcessingHandler.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/DirectMethodProcessingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -25,42 +25,37 @@ package com.oracle.graal.reachability; import java.lang.reflect.Modifier; -import java.util.Optional; import org.graalvm.nativeimage.AnnotationAccess; import com.oracle.graal.pointsto.AbstractAnalysisEngine; -import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.svm.common.meta.MultiMethod; +import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; -import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; -import jdk.graal.compiler.core.common.spi.ForeignCallSignature; -import jdk.graal.compiler.core.common.spi.ForeignCallsProvider; import jdk.graal.compiler.graph.Node; import jdk.graal.compiler.nodes.CallTargetNode; -import jdk.graal.compiler.nodes.ConstantNode; -import jdk.graal.compiler.nodes.FrameState; import jdk.graal.compiler.nodes.Invoke; import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.extended.ForeignCall; -import jdk.graal.compiler.nodes.java.InstanceOfNode; -import jdk.graal.compiler.nodes.java.LoadFieldNode; -import jdk.graal.compiler.nodes.java.NewArrayNode; -import jdk.graal.compiler.nodes.java.NewArrayWithExceptionNode; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.ValueNodeInterface; +import jdk.graal.compiler.nodes.extended.FieldOffsetProvider; +import jdk.graal.compiler.nodes.extended.GetClassNode; +import jdk.graal.compiler.nodes.extended.RawLoadNode; +import jdk.graal.compiler.nodes.extended.RawStoreNode; +import jdk.graal.compiler.nodes.java.AtomicReadAndAddNode; +import jdk.graal.compiler.nodes.java.AtomicReadAndWriteNode; +import jdk.graal.compiler.nodes.java.DynamicNewInstanceNode; import jdk.graal.compiler.nodes.java.NewInstanceNode; -import jdk.graal.compiler.nodes.java.NewInstanceWithExceptionNode; -import jdk.graal.compiler.nodes.java.NewMultiArrayNode; -import jdk.graal.compiler.nodes.java.NewMultiArrayWithExceptionNode; -import jdk.graal.compiler.nodes.java.StoreFieldNode; +import jdk.graal.compiler.nodes.java.UnsafeCompareAndExchangeNode; +import jdk.graal.compiler.nodes.java.UnsafeCompareAndSwapNode; +import jdk.graal.compiler.nodes.type.StampTool; +import jdk.graal.compiler.nodes.virtual.AllocatedObjectNode; +import jdk.graal.compiler.nodes.virtual.CommitAllocationNode; import jdk.graal.compiler.nodes.virtual.VirtualArrayNode; import jdk.graal.compiler.nodes.virtual.VirtualInstanceNode; -import jdk.graal.compiler.replacements.nodes.BinaryMathIntrinsicNode; import jdk.graal.compiler.replacements.nodes.MacroInvokable; -import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode; import jdk.vm.ci.code.BytecodePosition; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; /** * This handler walks the structured graphs of methods and directly calls back into the @@ -80,6 +75,10 @@ public void processGraph(ReachabilityAnalysisEngine bb, StructuredGraph graph) { } private static void analyzeStructuredGraph(ReachabilityAnalysisEngine bb, ReachabilityAnalysisMethod method, StructuredGraph graph) { + /* First, reuse all the registrations done before the type flow graph creation. */ + MethodTypeFlowBuilder.registerUsedElements(bb, graph, true); + + /* Then, perform extra registrations that happen in PTA during the analysis. */ if (method != null) { boolean isStatic = Modifier.isStatic(method.getModifiers()); int parameterCount = method.getSignature().getParameterCount(!isStatic); @@ -93,124 +92,62 @@ private static void analyzeStructuredGraph(ReachabilityAnalysisEngine bb, Reacha } for (Node n : graph.getNodes()) { - if (n instanceof NewInstanceNode) { - NewInstanceNode node = (NewInstanceNode) n; - ((ReachabilityAnalysisType) node.instanceClass()).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof NewInstanceWithExceptionNode) { - NewInstanceWithExceptionNode node = (NewInstanceWithExceptionNode) n; - ((ReachabilityAnalysisType) node.instanceClass()).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof NewArrayNode) { - NewArrayNode node = (NewArrayNode) n; - ((ReachabilityAnalysisType) node.elementType()).getArrayClass().registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof NewArrayWithExceptionNode) { - NewArrayWithExceptionNode node = (NewArrayWithExceptionNode) n; - ((ReachabilityAnalysisType) node.elementType()).getArrayClass().registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof NewMultiArrayNode) { - NewMultiArrayNode node = (NewMultiArrayNode) n; - ResolvedJavaType type = node.type(); - for (int i = 0; i < node.dimensionCount(); i++) { - ((ReachabilityAnalysisType) type).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - type = type.getComponentType(); - } - } else if (n instanceof NewMultiArrayWithExceptionNode) { - NewMultiArrayWithExceptionNode node = (NewMultiArrayWithExceptionNode) n; - ResolvedJavaType type = node.type(); - for (int i = 0; i < node.dimensionCount(); i++) { - ((ReachabilityAnalysisType) type).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - type = type.getComponentType(); - } - } else if (n instanceof VirtualInstanceNode) { - VirtualInstanceNode node = (VirtualInstanceNode) n; - ((ReachabilityAnalysisType) node.type()).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof VirtualArrayNode) { - VirtualArrayNode node = (VirtualArrayNode) n; - ((ReachabilityAnalysisType) node.componentType()).getArrayClass().registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof ConstantNode) { - ConstantNode node = (ConstantNode) n; - if (!(node.getValue() instanceof JavaConstant)) { - /* - * The bytecode parser sometimes embeds low-level VM constants for types into - * the high-level graph. Since these constants are the result of type lookups, - * these types are already marked as reachable. Eventually, the bytecode parser - * should be changed to only use JavaConstant. - */ - continue; - } - JavaConstant constant = (JavaConstant) node.getValue(); - bb.handleEmbeddedConstant(method, constant, AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof InstanceOfNode) { - InstanceOfNode node = (InstanceOfNode) n; - ((ReachabilityAnalysisType) node.type().getType()).registerAsReachable(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof LoadFieldNode) { - LoadFieldNode node = (LoadFieldNode) n; - ((ReachabilityAnalysisField) node.field()).registerAsRead(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof StoreFieldNode) { - StoreFieldNode node = (StoreFieldNode) n; - ((ReachabilityAnalysisField) node.field()).registerAsWritten(AbstractAnalysisEngine.sourcePosition(node)); - } else if (n instanceof Invoke) { - Invoke node = (Invoke) n; - CallTargetNode.InvokeKind kind = node.getInvokeKind(); - ReachabilityAnalysisMethod targetMethod = (ReachabilityAnalysisMethod) node.getTargetMethod(); - if (targetMethod == null || AnnotationAccess.isAnnotationPresent(targetMethod, Node.NodeIntrinsic.class)) { - continue; - } - BytecodePosition reason = AbstractAnalysisEngine.sourcePosition(node.asNode()); - if (method != null) { - method.addInvoke(new ReachabilityInvokeInfo(targetMethod, reason, kind.isDirect())); - } - if (kind == CallTargetNode.InvokeKind.Static) { - bb.markMethodImplementationInvoked(targetMethod, reason); - } else if (kind == CallTargetNode.InvokeKind.Special) { - bb.markMethodSpecialInvoked(targetMethod, reason); - } else { - bb.markMethodInvoked(targetMethod, reason); - } - } else if (n instanceof FrameState) { - FrameState node = (FrameState) n; - ResolvedJavaMethod frameMethod = node.getMethod(); - if (frameMethod != null) { - /* - * All types referenced in (possibly inlined) frame states must be reachable, - * because these classes will be reachable from stack walking metadata. This - * metadata is only constructed after AOT compilation, so the image heap - * scanning during static analysis does not see these classes. - */ - ReachabilityAnalysisMethod analysisMethod = (ReachabilityAnalysisMethod) frameMethod; - analysisMethod.getDeclaringClass().registerAsReachable(AbstractAnalysisEngine.syntheticSourcePosition(node, method)); - } - } else if (n instanceof MacroInvokable) { - MacroInvokable node = (MacroInvokable) n; - ReachabilityAnalysisMethod targetMethod = (ReachabilityAnalysisMethod) node.getTargetMethod(); - BytecodePosition reason = AbstractAnalysisEngine.syntheticSourcePosition(node.asNode(), method); - CallTargetNode.InvokeKind kind = node.getInvokeKind(); - if (kind == CallTargetNode.InvokeKind.Static) { - bb.markMethodImplementationInvoked(targetMethod, reason); - } else if (kind == CallTargetNode.InvokeKind.Special) { - bb.markMethodSpecialInvoked(targetMethod, reason); - } else { - bb.markMethodInvoked(targetMethod, reason); + if (n instanceof NewInstanceNode node) { + ((AnalysisType) node.instanceClass()).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); + } else if (n instanceof VirtualInstanceNode node) { + ((AnalysisType) node.type()).registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); + } else if (n instanceof VirtualArrayNode node) { + ((AnalysisType) node.componentType()).getArrayClass().registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); + } else if (n instanceof CommitAllocationNode node) { + for (AllocatedObjectNode allocatedObjectNode : node.usages().filter(AllocatedObjectNode.class)) { + var type = ((AnalysisType) allocatedObjectNode.getVirtualObject().type()); + type.registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(allocatedObjectNode)); } - } else if (n instanceof ForeignCall) { - MultiMethod.MultiMethodKey key = method == null ? MultiMethod.ORIGINAL_METHOD : method.getMultiMethodKey(); - ForeignCallsProvider foreignCallsProvider = bb.getProviders(key).getForeignCalls(); - handleForeignCall(bb, ((ForeignCall) n).getDescriptor(), foreignCallsProvider, graph.method()); - } else if (n instanceof UnaryMathIntrinsicNode) { - ForeignCallSignature signature = ((UnaryMathIntrinsicNode) n).getOperation().foreignCallSignature; - MultiMethod.MultiMethodKey key = method == null ? MultiMethod.ORIGINAL_METHOD : method.getMultiMethodKey(); - ForeignCallsProvider foreignCallsProvider = bb.getProviders(key).getForeignCalls(); - handleForeignCall(bb, foreignCallsProvider.getDescriptor(signature), foreignCallsProvider, graph.method()); - } else if (n instanceof BinaryMathIntrinsicNode) { - ForeignCallSignature signature = ((BinaryMathIntrinsicNode) n).getOperation().foreignCallSignature; - MultiMethod.MultiMethodKey key = method == null ? MultiMethod.ORIGINAL_METHOD : method.getMultiMethodKey(); - ForeignCallsProvider foreignCallsProvider = bb.getProviders(key).getForeignCalls(); - handleForeignCall(bb, foreignCallsProvider.getDescriptor(signature), foreignCallsProvider, graph.method()); - + } else if (n instanceof DynamicNewInstanceNode node && node.getInstanceType() instanceof GetClassNode getClassNode) { + var receiverType = (AnalysisType) StampTool.typeOrNull(getClassNode.getObject(), bb.getMetaAccess()); + receiverType.registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); + } else if (n instanceof RawLoadNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof RawStoreNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof UnsafeCompareAndSwapNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof UnsafeCompareAndExchangeNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof AtomicReadAndWriteNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof AtomicReadAndAddNode node) { + processUnsafeField(node, node.offset()); + } else if (n instanceof Invoke node) { + processInvoke(bb, method, ((ReachabilityAnalysisMethod) node.getTargetMethod()), node.getInvokeKind(), node); + } else if (n instanceof MacroInvokable node) { + processInvoke(bb, method, ((ReachabilityAnalysisMethod) node.getTargetMethod()), node.getInvokeKind(), node); } } } - private static void handleForeignCall(ReachabilityAnalysisEngine bb, ForeignCallDescriptor descriptor, ForeignCallsProvider foreignCallsProvider, ResolvedJavaMethod from) { - Optional targetMethod = bb.getHostVM().handleForeignCall(descriptor, foreignCallsProvider); - targetMethod.ifPresent(method -> bb.addRootMethod(method, false, from)); + private static void processInvoke(ReachabilityAnalysisEngine bb, ReachabilityAnalysisMethod method, ReachabilityAnalysisMethod targetMethod, CallTargetNode.InvokeKind kind, + ValueNodeInterface node) { + if (targetMethod == null || AnnotationAccess.isAnnotationPresent(targetMethod, Node.NodeIntrinsic.class)) { + return; + } + BytecodePosition reason = AbstractAnalysisEngine.sourcePosition(node.asNode()); + if (method != null) { + method.addInvoke(new ReachabilityInvokeInfo(targetMethod, reason, kind.isDirect())); + } + if (kind == CallTargetNode.InvokeKind.Static) { + bb.markMethodImplementationInvoked(targetMethod, reason); + } else if (kind == CallTargetNode.InvokeKind.Special) { + bb.markMethodSpecialInvoked(targetMethod, reason); + } else { + bb.markMethodInvoked(targetMethod, reason); + } + } + + private static void processUnsafeField(ValueNode node, ValueNode offset) { + if (offset instanceof FieldOffsetProvider provider) { + var field = ((AnalysisField) provider.getField()); + field.registerAsUnsafeAccessed(AbstractAnalysisEngine.sourcePosition(node)); + } } } diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java index 5e04932691ef..93d48c0162b9 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, 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 @@ -166,24 +166,19 @@ public AnalysisMethod addRootMethod(AnalysisMethod m, boolean invokeSpecial, Obj assert otherRoots.length == 0 : otherRoots; ReachabilityAnalysisMethod method = (ReachabilityAnalysisMethod) m; if (m.isStatic()) { - postTask(() -> { - if (method.registerAsDirectRootMethod(reason)) { - markMethodImplementationInvoked(method, reason); - } - }); + if (method.registerAsDirectRootMethod(reason)) { + postTask(() -> markMethodImplementationInvoked(method, reason)); + } + } else if (invokeSpecial) { AnalysisError.guarantee(!method.isAbstract(), "Abstract methods cannot be registered as special invoke entry point."); - postTask(() -> { - if (method.registerAsDirectRootMethod(reason)) { - markMethodImplementationInvoked(method, reason); - } - }); + if (method.registerAsDirectRootMethod(reason)) { + postTask(() -> markMethodImplementationInvoked(method, reason)); + } } else { - postTask(() -> { - if (method.registerAsVirtualRootMethod(reason)) { - markMethodInvoked(method, reason); - } - }); + if (method.registerAsVirtualRootMethod(reason)) { + postTask(() -> markMethodInvoked(method, reason)); + } } return method; } diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisMethod.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisMethod.java index 7e99471ceee9..c3f8bd72fa71 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -30,6 +30,7 @@ import java.util.List; import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; +import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.InvokeInfo; @@ -37,6 +38,7 @@ import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.common.meta.MultiMethod; +import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.nodes.GraphEncoder; import jdk.graal.compiler.nodes.Invoke; @@ -132,22 +134,28 @@ public ReachabilityAnalysisType getDeclaringClass() { * Utility method which contains all the steps that have to be taken when parsing methods for * the analysis. */ + @SuppressWarnings("try") public static StructuredGraph getDecodedGraph(ReachabilityAnalysisEngine bb, ReachabilityAnalysisMethod method) { AnalysisParsedGraph analysisParsedGraph = method.ensureGraphParsed(bb); if (analysisParsedGraph.isIntrinsic()) { method.registerAsIntrinsicMethod("reachability analysis engine"); } AnalysisError.guarantee(analysisParsedGraph.getEncodedGraph() != null, "Cannot provide a summary for %s.", method.getQualifiedName()); + StructuredGraph graph = InlineBeforeAnalysis.decodeGraph(bb, method, analysisParsedGraph); + try (DebugContext.Scope s = graph.getDebug().scope("ReachabilityAnalysisMethod.getDecodedGraph", graph)) { + /* Make sure the same set of optimizations is done before the analysis. */ + MethodTypeFlowBuilder.optimizeGraphBeforeAnalysis(bb, method, graph); + } catch (Throwable ex) { + throw graph.getDebug().handle(ex); + } - StructuredGraph decoded = InlineBeforeAnalysis.decodeGraph(bb, method, analysisParsedGraph); - AnalysisError.guarantee(decoded != null, "Failed to decode a graph for %s.", method.getQualifiedName()); + bb.getHostVM().methodBeforeTypeFlowCreationHook(bb, method, graph); - bb.getHostVM().methodBeforeTypeFlowCreationHook(bb, method, decoded); + /* To preserve the graphs for compilation. */ + method.setAnalyzedGraph(GraphEncoder.encodeSingleGraph(graph, AnalysisParsedGraph.HOST_ARCHITECTURE)); - // to preserve the graphs for compilation - method.setAnalyzedGraph(GraphEncoder.encodeSingleGraph(decoded, AnalysisParsedGraph.HOST_ARCHITECTURE)); + return graph; - return decoded; } /** diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisType.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisType.java index 5975cebfa558..27f6744f3ad3 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisType.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -59,6 +59,7 @@ public ReachabilityAnalysisType(AnalysisUniverse universe, ResolvedJavaType java @Override protected void onInstantiated() { forAllSuperTypes(t -> ((ReachabilityAnalysisType) t).instantiatedSubtypes.add(this)); + super.onInstantiated(); } public Set getInstantiatedSubtypes() {