diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 1b05f5ce2d67..24157d91d156 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -2421,6 +2421,7 @@ "name" : "org.graalvm.nativeimage.pointsto.standalone", "exports" : [ "com.oracle.graal.pointsto.standalone", + "com.oracle.graal.pointsto.standalone.plugins", ], "requires": [ "java.management", diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone.test/src/com/oracle/graal/pointsto/standalone/test/StandaloneAnalysisReportTest.java b/substratevm/src/com.oracle.graal.pointsto.standalone.test/src/com/oracle/graal/pointsto/standalone/test/StandaloneAnalysisReportTest.java index 822c05077c60..01958b0a56bb 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone.test/src/com/oracle/graal/pointsto/standalone/test/StandaloneAnalysisReportTest.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone.test/src/com/oracle/graal/pointsto/standalone/test/StandaloneAnalysisReportTest.java @@ -26,13 +26,14 @@ package com.oracle.graal.pointsto.standalone.test; -import org.junit.Test; +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import static org.junit.Assert.assertTrue; +import org.junit.Ignore; +import org.junit.Test; public class StandaloneAnalysisReportTest { // Take an arbitrary case for this test @@ -58,6 +59,8 @@ public void testPrintAnalysisCallTree() throws IOException { } @Test + @Ignore // Since there is no class initialization, printing the object tree is not a meaningful + // operation at the moment. public void testPrintAnalysisObjectTree() throws IOException { PointstoAnalyzerTester tester = new PointstoAnalyzerTester(TEST_CLASS); Path testTmpDir = tester.createTestTmpDir(); diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java index b83d7f274e7f..df1c49eccfb1 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java @@ -120,7 +120,8 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options, ClassLoade SnippetReflectionProvider snippetReflection = originalProviders.getSnippetReflection(); MetaAccessProvider originalMetaAccess = originalProviders.getMetaAccess(); debugContext = new DebugContext.Builder(options, new GraalDebugHandlersFactory(snippetReflection)).build(); - StandaloneHost standaloneHost = new StandaloneHost(options); + analysisName = getAnalysisName(mainEntryClass); + StandaloneHost standaloneHost = new StandaloneHost(options, analysisName, false, true); AnalysisPolicy analysisPolicy = PointstoOptions.AllocationSiteSensitiveHeap.getValue(options) ? new BytecodeSensitiveAnalysisPolicy(options) : new DefaultAnalysisPolicy(options); @@ -128,8 +129,9 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options, ClassLoade AnalysisUniverse aUniverse = new AnalysisUniverse(standaloneHost, wordKind, analysisPolicy, SubstitutionProcessor.IDENTITY, originalMetaAccess, new PointsToAnalysisFactory(), new StandaloneAnnotationExtractor()); AnalysisMetaAccess aMetaAccess = new StandaloneAnalysisMetaAccess(aUniverse, originalMetaAccess); - StandaloneConstantReflectionProvider aConstantReflection = new StandaloneConstantReflectionProvider(aUniverse, originalProviders.getConstantReflection()); - StandaloneConstantFieldProvider aConstantFieldProvider = new StandaloneConstantFieldProvider(aMetaAccess); + StandaloneConstantReflectionProvider aConstantReflection = new StandaloneConstantReflectionProvider(aMetaAccess, aUniverse, originalProviders.getConstantReflection(), + originalProviders.getSnippetReflection()); + StandaloneConstantFieldProvider aConstantFieldProvider = new StandaloneConstantFieldProvider(aMetaAccess, originalProviders.getConstantFieldProvider(), false); AnalysisMetaAccessExtensionProvider aMetaAccessExtensionProvider = new AnalysisMetaAccessExtensionProvider(aUniverse); HostedProviders aProviders = new HostedProviders(aMetaAccess, null, aConstantReflection, aConstantFieldProvider, originalProviders.getForeignCalls(), originalProviders.getLowerer(), null, @@ -138,11 +140,9 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options, ClassLoade Replacements replacements = new StandaloneReplacementsImpl(aProviders, new ResolvedJavaMethodBytecodeProvider(), originalProviders.getCodeCache().getTarget()); aProviders = aProviders.copyWith(replacements); standaloneHost.initializeProviders(aProviders); - analysisName = getAnalysisName(mainEntryClass); ClassInclusionPolicy classInclusionPolicy = new ClassInclusionPolicy.DefaultAllInclusionPolicy("Included in the base image"); bigbang = new StandalonePointsToAnalysis(options, aUniverse, standaloneHost, aMetaAccess, snippetReflection, aConstantReflection, aProviders.getWordTypes(), debugContext, new TimerCollection(), classInclusionPolicy); - standaloneHost.setImageName(analysisName); aUniverse.setBigBang(bigbang); ImageHeap heap = new ImageHeap(); HostedValuesProvider hostedValuesProvider = new HostedValuesProvider(aMetaAccess, aUniverse); @@ -323,11 +323,10 @@ public int run() { registerEntryMethods(); registerFeatures(); int exitCode = 0; - Feature.BeforeAnalysisAccess beforeAnalysisAccess = new StandaloneAnalysisFeatureImpl.BeforeAnalysisAccessImpl(standaloneAnalysisFeatureManager, classLoaderAccess, bigbang, debugContext); + Feature.BeforeAnalysisAccess beforeAnalysisAccess = new StandaloneAnalysisFeatureImpl.BeforeAnalysisAccessImpl(bigbang); standaloneAnalysisFeatureManager.forEachFeature(feature -> feature.beforeAnalysis(beforeAnalysisAccess)); try (Timer t = new Timer("analysis", analysisName)) { - StandaloneAnalysisFeatureImpl.DuringAnalysisAccessImpl config = new StandaloneAnalysisFeatureImpl.DuringAnalysisAccessImpl(standaloneAnalysisFeatureManager, classLoaderAccess, bigbang, - debugContext); + StandaloneAnalysisFeatureImpl.DuringAnalysisAccessImpl config = new StandaloneAnalysisFeatureImpl.DuringAnalysisAccessImpl(bigbang); bigbang.getUniverse().setConcurrentAnalysisAccess(config); bigbang.runAnalysis(debugContext, (analysisUniverse) -> { bigbang.getHostVM().notifyClassReachabilityListener(analysisUniverse, config); @@ -338,7 +337,7 @@ public int run() { reportException(e); exitCode = 1; } - onAnalysisExitAccess = new StandaloneAnalysisFeatureImpl.OnAnalysisExitAccessImpl(standaloneAnalysisFeatureManager, classLoaderAccess, bigbang, debugContext); + onAnalysisExitAccess = new StandaloneAnalysisFeatureImpl.OnAnalysisExitAccessImpl(bigbang); standaloneAnalysisFeatureManager.forEachFeature(feature -> feature.onAnalysisExit(onAnalysisExitAccess)); AnalysisReporter.printAnalysisReports("pointsto_" + analysisName, options, StandaloneOptions.reportsPath(options, "reports").toString(), bigbang); bigbang.getUnsupportedFeatures().report(bigbang); diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java index 8f2b9a7f1a76..64bc6daa3faa 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java @@ -27,15 +27,19 @@ package com.oracle.graal.pointsto.standalone; import java.util.Comparator; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.standalone.plugins.StandaloneGraphBuilderPhase; import com.oracle.graal.pointsto.util.AnalysisError; +import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; +import jdk.graal.compiler.core.common.spi.ForeignCallsProvider; import jdk.graal.compiler.java.GraphBuilderPhase; import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext; @@ -46,10 +50,21 @@ public class StandaloneHost extends HostVM { private final ConcurrentHashMap> typeToClass = new ConcurrentHashMap<>(); private final ConcurrentHashMap, AnalysisType> classToType = new ConcurrentHashMap<>(); - private String imageName; + private final String imageName; + /* + * By default, there is no eager class initialization nor delayed class initialization in + * standalone analysis, so we don't need do any actual class initialization work here. Setting + * this field to true changes that behavior and allows class initialization. + */ + private final boolean initializeClasses; - public StandaloneHost(OptionValues options) { + private final boolean isClosedTypeWorld; + + public StandaloneHost(OptionValues options, String imageName, boolean initializeClasses, boolean isClosedTypeWorld) { super(options, /*- ClassLoader not supported. */ null); + this.imageName = imageName; + this.initializeClasses = initializeClasses; + this.isClosedTypeWorld = isClosedTypeWorld; } @Override @@ -79,13 +94,10 @@ public boolean isInitialized(AnalysisType type) { @Override public void onTypeReachable(BigBang bb, AnalysisType type) { - if (!type.isReachable()) { - AnalysisError.shouldNotReachHere("Registering and initializing a type that was not yet marked as reachable: " + type); + AnalysisError.guarantee(type.isReachable(), "Registering and initializing a type that was not yet marked as reachable: %s", type.toJavaName()); + if (initializeClasses) { + type.getWrapped().initialize(); } - /* - * There is no eager class initialization nor delayed class initialization in standalone - * analysis, so we don't need do any actual class initialization work here. - */ } @Override @@ -94,10 +106,6 @@ public GraphBuilderPhase.Instance createGraphBuilderPhase(HostedProviders builde return new StandaloneGraphBuilderPhase.Instance(builderProviders, graphBuilderConfig, optimisticOpts, initialIntrinsicContext); } - public void setImageName(String name) { - imageName = name; - } - @Override public String getImageName() { return imageName; @@ -105,6 +113,16 @@ public String getImageName() { @Override public Comparator getTypeComparator() { - return null; + throw new UnsupportedOperationException(); + } + + @Override + public Optional handleForeignCall(ForeignCallDescriptor foreignCallDescriptor, ForeignCallsProvider foreignCallsProvider) { + throw AnalysisError.shouldNotReachHere("StandaloneHost.handleForeignCall"); + } + + @Override + public boolean isClosedTypeWorld() { + return isClosedTypeWorld; } } diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/features/StandaloneAnalysisFeatureImpl.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/features/StandaloneAnalysisFeatureImpl.java index 05c0117f5389..308ce60d71a2 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/features/StandaloneAnalysisFeatureImpl.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/features/StandaloneAnalysisFeatureImpl.java @@ -48,23 +48,13 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; -import com.oracle.graal.pointsto.standalone.PointsToAnalyzer; import com.oracle.graal.pointsto.standalone.StandaloneHost; import com.oracle.graal.pointsto.util.AnalysisError; -import jdk.graal.compiler.debug.DebugContext; - public class StandaloneAnalysisFeatureImpl { public abstract static class FeatureAccessImpl implements Feature.FeatureAccess { - protected final StandaloneAnalysisFeatureManager featureManager; - protected final PointsToAnalyzer.ClassLoaderAccess classLoaderAccess; - protected final DebugContext debugContext; - - FeatureAccessImpl(StandaloneAnalysisFeatureManager featureManager, PointsToAnalyzer.ClassLoaderAccess classLoaderAccess, DebugContext debugContext) { - this.featureManager = featureManager; - this.classLoaderAccess = classLoaderAccess; - this.debugContext = debugContext; + FeatureAccessImpl() { } @Deprecated @@ -94,8 +84,8 @@ abstract static class AnalysisAccessBase extends FeatureAccessImpl { protected final BigBang bb; - AnalysisAccessBase(StandaloneAnalysisFeatureManager featureManager, PointsToAnalyzer.ClassLoaderAccess classLoaderAccess, BigBang bb, DebugContext debugContext) { - super(featureManager, classLoaderAccess, debugContext); + AnalysisAccessBase(BigBang bb) { + super(); this.bb = bb; } @@ -161,8 +151,8 @@ Set reachableMethodOverrides(AnalysisMethod baseMethod) { public static class BeforeAnalysisAccessImpl extends AnalysisAccessBase implements Feature.BeforeAnalysisAccess { - public BeforeAnalysisAccessImpl(StandaloneAnalysisFeatureManager featureManager, PointsToAnalyzer.ClassLoaderAccess classLoaderAccess, BigBang bb, DebugContext debugContext) { - super(featureManager, classLoaderAccess, bb, debugContext); + public BeforeAnalysisAccessImpl(BigBang bb) { + super(bb); } @Override @@ -263,8 +253,8 @@ public static class DuringAnalysisAccessImpl extends BeforeAnalysisAccessImpl im private boolean requireAnalysisIteration; - public DuringAnalysisAccessImpl(StandaloneAnalysisFeatureManager featureManager, PointsToAnalyzer.ClassLoaderAccess classLoaderAccess, BigBang bb, DebugContext debugContext) { - super(featureManager, classLoaderAccess, bb, debugContext); + public DuringAnalysisAccessImpl(BigBang bb) { + super(bb); } @Override @@ -283,8 +273,8 @@ public static class OnAnalysisExitAccessImpl extends AnalysisAccessBase implemen private final Map, Object> analysisResults = new HashMap<>(); - public OnAnalysisExitAccessImpl(StandaloneAnalysisFeatureManager featureManager, PointsToAnalyzer.ClassLoaderAccess classLoaderAccess, BigBang bb, DebugContext debugContext) { - super(featureManager, classLoaderAccess, bb, debugContext); + public OnAnalysisExitAccessImpl(BigBang bb) { + super(bb); } public void setAnalysisResult(Class feature, Object result) { diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantFieldProvider.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantFieldProvider.java index a2730dc001a6..95e96e37ad71 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantFieldProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantFieldProvider.java @@ -26,23 +26,56 @@ package com.oracle.graal.pointsto.standalone.meta; +import com.oracle.graal.pointsto.meta.AnalysisField; + +import jdk.graal.compiler.core.common.spi.ConstantFieldProvider; +import jdk.graal.compiler.core.common.spi.JavaConstantFieldProvider; +import jdk.graal.compiler.nodes.spi.CanonicalizerTool; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.graal.compiler.core.common.spi.JavaConstantFieldProvider; public class StandaloneConstantFieldProvider extends JavaConstantFieldProvider { - public StandaloneConstantFieldProvider(MetaAccessProvider metaAccess) { + private final ConstantFieldProvider originalConstantFieldProvider; + /** + * In the default standalone mode configuration, this field is set to false and all classes are + * runtime initialized. We take all fields as not final, so that it doesn't read field value + * from current environment. Test com.oracle.graal.pointsto.test.ConstantFieldTest verifies this + * method. + **/ + private final boolean foldFinalFields; + + public StandaloneConstantFieldProvider(MetaAccessProvider metaAccess, ConstantFieldProvider originalConstantFieldProvider, boolean foldFinalFields) { super(metaAccess); + this.originalConstantFieldProvider = originalConstantFieldProvider; + this.foldFinalFields = foldFinalFields; + } + + @Override + public T readConstantField(ResolvedJavaField f, ConstantFieldTool tool) { + AnalysisField field = (AnalysisField) f; + + /* + * Ideally, we would mark the type as reachable only when the field is final or stable, + * i.e., there is an actual chance of constant folding it. But for the purpose of the + * analysis tests, it does not matter if we mark a few types too many as reachable. + */ + field.getDeclaringClass().registerAsReachable(field); + + T foldedValue = originalConstantFieldProvider.readConstantField(field.wrapped, tool); + if (foldedValue != null) { + field.registerAsFolded(tool.getReason()); + } + return foldedValue; + } + + @Override + public boolean isTrustedFinal(CanonicalizerTool tool, ResolvedJavaField field) { + return foldFinalFields && originalConstantFieldProvider.isTrustedFinal(tool, field); } - /** - * In standalone mode, all classes are runtime initialized. We take all fields as not final, so - * that it doesn't read field value from current environment. Test - * com.oracle.graal.pointsto.test.ConstantFieldTest verifies this method. - **/ @Override public boolean isFinalField(ResolvedJavaField field, ConstantFieldTool tool) { - return false; + return foldFinalFields && super.isFinalField(field, tool); } } diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantReflectionProvider.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantReflectionProvider.java index 7913a482facb..7ebbe3a2d695 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/meta/StandaloneConstantReflectionProvider.java @@ -26,25 +26,40 @@ package com.oracle.graal.pointsto.standalone.meta; +import java.util.Objects; +import java.util.concurrent.ForkJoinPool; + +import com.oracle.graal.pointsto.heap.ImageHeapArray; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; +import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MemoryAccessProvider; import jdk.vm.ci.meta.MethodHandleAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; -public class StandaloneConstantReflectionProvider implements ConstantReflectionProvider { +public class StandaloneConstantReflectionProvider implements ConstantReflectionProvider, IdentityHashCodeProvider { private final AnalysisUniverse universe; private final ConstantReflectionProvider original; + private final AnalysisField commonPoolField; + private final JavaConstant commonPoolSubstitution; - public StandaloneConstantReflectionProvider(AnalysisUniverse universe, ConstantReflectionProvider original) { + public StandaloneConstantReflectionProvider(AnalysisMetaAccess aMetaAccess, AnalysisUniverse universe, ConstantReflectionProvider original, + SnippetReflectionProvider originalSnippetReflection) { this.universe = universe; this.original = original; + commonPoolField = aMetaAccess.lookupJavaField(ReflectionUtil.lookupField(ForkJoinPool.class, "common")); + commonPoolSubstitution = originalSnippetReflection.forObject(new ForkJoinPool()); } @Override @@ -54,25 +69,55 @@ public Boolean constantEquals(Constant x, Constant y) { @Override public Integer readArrayLength(JavaConstant array) { + if (array instanceof ImageHeapConstant) { + if (array instanceof ImageHeapArray) { + return ((ImageHeapArray) array).getLength(); + } + return null; + } + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return null; + } return original.readArrayLength(array); } + @Override + public Integer identityHashCode(JavaConstant constant) { + if (constant == null || constant.getJavaKind() != JavaKind.Object) { + return null; + } else if (constant.isNull()) { + /* System.identityHashCode is specified to return 0 when passed null. */ + return 0; + } + + ImageHeapConstant imageHeapConstant = (ImageHeapConstant) constant; + if (imageHeapConstant.hasIdentityHashCode()) { + return imageHeapConstant.getIdentityHashCode(); + } + Object hostedObject = Objects.requireNonNull(universe.getSnippetReflection().asObject(Object.class, constant)); + return System.identityHashCode(hostedObject); + } + @Override public JavaConstant readArrayElement(JavaConstant array, int index) { return universe.getHostedValuesProvider().interceptHosted(original.readArrayElement(array, index)); } @Override - public final JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) { - ResolvedJavaField wrappedField = ((AnalysisField) field).getWrapped(); - JavaConstant ret = universe.getHostedValuesProvider().interceptHosted(original.readFieldValue(wrappedField, receiver)); - if (ret == null) { - ret = wrappedField.getConstantValue(); - if (ret == null) { - ret = JavaConstant.defaultForKind(wrappedField.getJavaKind()); - } + public final JavaConstant readFieldValue(ResolvedJavaField f, JavaConstant receiver) { + AnalysisField field = (AnalysisField) f; + + field.beforeFieldValueAccess(); + + if (field.equals(commonPoolField)) { + /* + * Replace the common pool value from the hosted heap with a new object. The common pool + * is used during analysis, so it can expose analysis metadata to the analysis itself, + * i.e., the analysis engine analyzing itself. + */ + return commonPoolSubstitution; } - return ret; + return universe.getHostedValuesProvider().interceptHosted(original.readFieldValue(field.wrapped, receiver)); } @Override