diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index c1b51a072e91..2f5c63c82c87 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -408,13 +408,28 @@ "workingSets": "SVM", }, + + "com.oracle.graal.reachability": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + "com.oracle.graal.pointsto", + ], + "checkstyle": "com.oracle.svm.core", + "javaCompliance": "11+", + "annotationProcessors": [ + "compiler:GRAAL_PROCESSOR", + ], + "workingSets": "SVM", + }, + "com.oracle.svm.hosted": { "subDir": "src", "sourceDirs": ["src"], "dependencies": [ "com.oracle.objectfile", "com.oracle.svm.core", - "com.oracle.graal.pointsto", + "com.oracle.graal.reachability" ], "requires" : [ "java.desktop", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java new file mode 100644 index 000000000000..8d8c63e9293c --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto; + +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.api.PointstoOptions; +import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +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.meta.HostedProviders; +import com.oracle.graal.pointsto.reports.StatisticsPrinter; +import com.oracle.graal.pointsto.util.CompletionExecutor; +import com.oracle.graal.pointsto.util.Timer; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugHandlersFactory; +import org.graalvm.compiler.nodes.spi.Replacements; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.printer.GraalDebugHandlersFactory; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ForkJoinPool; + +public abstract class AbstractAnalysisEngine implements BigBang { + + private final Boolean extendedAsserts; + private final Timer processFeaturesTimer; + private final Timer analysisTimer; + protected final Timer checkObjectsTimer; + protected final Timer reachabilityTimer; + protected final AnalysisMetaAccess metaAccess; + private final HostedProviders providers; + protected final HostVM hostVM; + protected final ForkJoinPool executorService; + private final Runnable heartbeatCallback; + protected final UnsupportedFeatures unsupportedFeatures; + protected final DebugContext debug; + protected final OptionValues options; + protected final AnalysisUniverse universe; + private final List debugHandlerFactories; + private final HeapScanningPolicy heapScanningPolicy; + private final Replacements replacements; + protected final CompletionExecutor executor; + protected final AnalysisTiming timing; + + public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, HostedProviders providers, HostVM hostVM, ForkJoinPool executorService, Runnable heartbeatCallback, + UnsupportedFeatures unsupportedFeatures) { + this.options = options; + this.universe = universe; + this.debugHandlerFactories = Collections.singletonList(new GraalDebugHandlersFactory(providers.getSnippetReflection())); + this.debug = new org.graalvm.compiler.debug.DebugContext.Builder(options, debugHandlerFactories).build(); + this.metaAccess = (AnalysisMetaAccess) providers.getMetaAccess(); + this.providers = providers; + this.hostVM = hostVM; + this.executorService = executorService; + this.executor = new CompletionExecutor(this, executorService, heartbeatCallback); + // todo specify timing via option + this.timing = null; + this.executor.init(timing); + this.heartbeatCallback = heartbeatCallback; + this.unsupportedFeatures = unsupportedFeatures; + this.replacements = providers.getReplacements(); + + String imageName = hostVM.getImageName(); + this.processFeaturesTimer = new Timer(imageName, "(features)", false); + this.checkObjectsTimer = new Timer(imageName, "(objects)", false); + this.reachabilityTimer = new Timer(imageName, "(reachability)", false); + this.analysisTimer = new Timer(imageName, "analysis", true); + + this.extendedAsserts = PointstoOptions.ExtendedAsserts.getValue(options); + + this.heapScanningPolicy = PointstoOptions.ExhaustiveHeapScan.getValue(options) + ? HeapScanningPolicy.scanAll() + : HeapScanningPolicy.skipTypes(skippedHeapTypes()); + } + + @Override + public void cleanupAfterAnalysis() { + universe.getTypes().forEach(AnalysisType::cleanupAfterAnalysis); + universe.getFields().forEach(AnalysisField::cleanupAfterAnalysis); + universe.getMethods().forEach(AnalysisMethod::cleanupAfterAnalysis); + } + + @Override + public Timer getAnalysisTimer() { + return analysisTimer; + } + + @Override + public Timer getProcessFeaturesTimer() { + return processFeaturesTimer; + } + + @Override + public void printTimers() { + reachabilityTimer.print(); + checkObjectsTimer.print(); + processFeaturesTimer.print(); + } + + @Override + public void printTimerStatistics(PrintWriter out) { + // todo print reachability here + StatisticsPrinter.print(out, "features_time_ms", processFeaturesTimer.getTotalTime()); + StatisticsPrinter.print(out, "total_analysis_time_ms", analysisTimer.getTotalTime()); + + StatisticsPrinter.printLast(out, "total_memory_bytes", analysisTimer.getTotalMemory()); + } + + @Override + public AnalysisType[] skippedHeapTypes() { + return new AnalysisType[]{metaAccess.lookupJavaType(String.class)}; + } + + @Override + public boolean extendedAsserts() { + return extendedAsserts; + } + + @Override + public OptionValues getOptions() { + return options; + } + + @Override + public Runnable getHeartbeatCallback() { + return heartbeatCallback; + } + + @Override + public DebugContext getDebug() { + return debug; + } + + @Override + public List getDebugHandlerFactories() { + return debugHandlerFactories; + } + + @Override + public AnalysisPolicy analysisPolicy() { + return universe.analysisPolicy(); + } + + @Override + public AnalysisUniverse getUniverse() { + return universe; + } + + @Override + public HostedProviders getProviders() { + return providers; + } + + @Override + public AnalysisMetaAccess getMetaAccess() { + return metaAccess; + } + + @Override + public UnsupportedFeatures getUnsupportedFeatures() { + return unsupportedFeatures; + } + + @Override + public final SnippetReflectionProvider getSnippetReflectionProvider() { + return providers.getSnippetReflection(); + } + + @Override + public final ConstantReflectionProvider getConstantReflectionProvider() { + return providers.getConstantReflection(); + } + + @Override + public HeapScanningPolicy scanningPolicy() { + return heapScanningPolicy; + } + + @Override + public HostVM getHostVM() { + return hostVM; + } + + protected void schedule(Runnable task) { + executor.execute((d) -> task.run()); + } + + @Override + public Replacements getReplacements() { + return replacements; + } + + private class AnalysisTiming extends PointsToAnalysis.BucketTiming { + + @Override + public void printHeader() { + super.printHeader(); + System.out.println(); + } + + @Override + public void print() { + super.print(); + System.out.println(); + } + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractReachabilityAnalysis.java new file mode 100644 index 000000000000..f74f23a0921e --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractReachabilityAnalysis.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto; + +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; +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.meta.HostedProviders; +import org.graalvm.compiler.options.OptionValues; + +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.concurrent.ForkJoinPool; + +import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere; + +public abstract class AbstractReachabilityAnalysis extends AbstractAnalysisEngine { + public AbstractReachabilityAnalysis(OptionValues options, AnalysisUniverse universe, HostedProviders providers, HostVM hostVM, ForkJoinPool executorService, Runnable heartbeatCallback, + UnsupportedFeatures unsupportedFeatures) { + super(options, universe, providers, hostVM, executorService, heartbeatCallback, unsupportedFeatures); + } + + @Override + public AnalysisType addRootClass(Class clazz, boolean addFields, boolean addArrayClass) { + AnalysisType type = metaAccess.lookupJavaType(clazz); + type.registerAsReachable(); + return addRootClass(type, addFields, addArrayClass); + } + + @Override + public AnalysisMethod addRootMethod(Class clazz, String methodName, Class... parameterTypes) { + try { + Method method = clazz.getDeclaredMethod(methodName, parameterTypes); + return addRootMethod(method); + } catch (NoSuchMethodException ex) { + throw shouldNotReachHere(ex); + } + } + + @Override + public AnalysisMethod addRootMethod(Executable method) { + AnalysisMethod aMethod = metaAccess.lookupJavaMethod(method); + addRootMethod(aMethod); + return aMethod; + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java index e5ce0dc53b71..a77703e3ae17 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java @@ -41,10 +41,22 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisType.UsageKind; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.util.Timer; import jdk.vm.ci.meta.ConstantReflectionProvider; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugHandlersFactory; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.compiler.nodes.spi.Replacements; +import org.graalvm.compiler.options.OptionValues; + +import java.io.PrintWriter; +import java.util.List; +import java.util.function.Function; + /** * Central static analysis interface that groups together the functionality of reachability analysis @@ -104,6 +116,10 @@ public interface BigBang extends ReachabilityAnalysis, HeapScanning { void runAnalysis(DebugContext debug, Function duringAnalysisAction) throws InterruptedException; + boolean strengthenGraalGraphs(); + + Replacements getReplacements(); + /** You can blacklist certain callees here. */ @SuppressWarnings("unused") default boolean isCallAllowed(PointsToAnalysis bb, AnalysisMethod caller, AnalysisMethod target, NodeSourcePosition srcPosition) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java index 6bab1c026d9e..a01a7bf4d26a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java @@ -207,6 +207,7 @@ public void printTimerStatistics(PrintWriter out) { StatisticsPrinter.printLast(out, "total_memory_bytes", analysisTimer.getTotalMemory()); } + @Override public boolean strengthenGraalGraphs() { return strengthenGraalGraphs; } @@ -357,6 +358,7 @@ public AnalysisMetaAccess getMetaAccess() { return metaAccess; } + @Override public Replacements getReplacements() { return replacements; } @@ -485,7 +487,8 @@ public AnalysisType addRootClass(Class clazz, boolean addFields, boolean addA } @SuppressWarnings({"try"}) - private AnalysisType addRootClass(AnalysisType type, boolean addFields, boolean addArrayClass) { + @Override + public AnalysisType addRootClass(AnalysisType type, boolean addFields, boolean addArrayClass) { try (Indent indent = debug.logAndIndent("add root class %s", type.getName())) { for (AnalysisField field : type.getInstanceFields(false)) { if (addFields) { @@ -634,6 +637,27 @@ public boolean finish() throws InterruptedException { } } + @Override + public void markTypeReachable(AnalysisType type) { + type.registerAsReachable(); + } + + @Override + public void markTypeInHeap(AnalysisType type) { + type.registerAsInHeap(); + } + + @Override + public void markFieldAccessed(AnalysisField field) { + field.registerAsAccessed(); + } + + @Override + public void markMethodImplementationInvoked(AnalysisMethod method, Object reason) { + addRootMethod(method).registerAsImplementationInvoked(); + + } + @SuppressWarnings("try") public boolean doTypeflow() throws InterruptedException { boolean didSomeWork; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java index 88eebb9c1c54..268d70096250 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java @@ -30,6 +30,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.typestate.TypeState; +import com.oracle.svm.util.UnsafePartitionKind; import java.lang.reflect.Executable; @@ -50,6 +51,8 @@ public interface ReachabilityAnalysis { */ AnalysisType addRootClass(Class clazz, boolean addFields, boolean addArrayClass); + AnalysisType addRootClass(AnalysisType type, boolean addFields, boolean addArrayClass); + /** * Marks given field as accessed. */ @@ -70,6 +73,48 @@ public interface ReachabilityAnalysis { */ AnalysisMethod addRootMethod(Class clazz, String methodName, Class... parameterTypes); + void markTypeReachable(AnalysisType type); + + void markTypeInHeap(AnalysisType type); + + void markFieldAccessed(AnalysisField field); + + default boolean markFieldUnsafeAccessed(AnalysisField field) { + if (!field.isUnsafeAccessed()) { + /* Register the field as unsafe accessed. */ + field.registerAsAccessed(); + field.registerAsUnsafeAccessed(); + /* Force the update of registered unsafe loads and stores. */ + forceUnsafeUpdate(field); + return true; + } + return false; + } + + default void registerAsFrozenUnsafeAccessed(AnalysisField field) { + field.setUnsafeFrozenTypeState(true); + } + + default void registerAsUnsafeAccessed(AnalysisField field, UnsafePartitionKind partitionKind) { + if (!field.isUnsafeAccessed()) { + /* Register the field as unsafe accessed. */ + field.registerAsAccessed(); + field.registerAsUnsafeAccessed(partitionKind); + /* Force the update of registered unsafe loads and stores. */ + forceUnsafeUpdate(field); + } + } + + default void markFieldRead(AnalysisField field) { + markFieldAccessed(field); + } + + default void markFieldWritten(AnalysisField field) { + markFieldAccessed(field); + } + + void markMethodImplementationInvoked(AnalysisMethod method, Object reason); + /** * Waits until the analysis is done. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java index 0986e8ddb718..ee030e023e36 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java @@ -81,7 +81,7 @@ public class PointstoOptions { public static final OptionKey ExtendedAsserts = new OptionKey<>(false); @Option(help = "Track the callers for methods and accessing methods for fields.")// - public static final OptionKey TrackAccessChain = new OptionKey<>(false); + public static final OptionKey TrackAccessChain = new OptionKey<>(true); @Option(help = "Track the input for type flows.")// public static final OptionKey TrackInputFlows = new OptionKey<>(false); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 9f7cce7c7e13..e027c1c70eb3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -32,6 +32,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.Collection; +import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -93,7 +94,7 @@ public abstract class AnalysisMethod implements WrappedJavaMethod, GraphProvider private static final Object GRAPH_CACHE_UNPARSED = "unparsed"; private static final Object GRAPH_CACHE_CLEARED = "cleared by cleanupAfterAnalysis"; - private StructuredGraph analyzedGraph; + private volatile StructuredGraph analyzedGraph; /** * All concrete methods that can actually be called when calling this method. This includes all @@ -101,6 +102,8 @@ public abstract class AnalysisMethod implements WrappedJavaMethod, GraphProvider */ protected AnalysisMethod[] implementations; + private Object reason; + public AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped) { this.wrapped = wrapped; this.id = universe.nextMethodId.getAndIncrement(); @@ -213,7 +216,13 @@ public void registerAsEntryPoint(Object newEntryPointData) { } public boolean registerAsInvoked() { - return AtomicUtils.atomicMark(isInvoked); + if (AtomicUtils.atomicMark(isInvoked)) { + getDeclaringClass() + .getInvokedMethods() + .add(this); + return true; + } + return false; } public boolean registerAsImplementationInvoked() { @@ -613,4 +622,18 @@ public StructuredGraph getAnalyzedGraph() { public void setAnalyzedGraph(StructuredGraph analyzedGraph) { this.analyzedGraph = analyzedGraph; } + + public void setReason(Object reason) { + this.reason = reason; + } + + public String getReason() { + if (reason == null) { + return "no reason - it just happened :O"; + } + if (reason instanceof StackTraceElement[]) { + return Arrays.toString(((StackTraceElement[]) reason)); + } + return reason.toString(); + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index b19bd086af29..cbcfe86edaca 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -107,6 +107,9 @@ public class AnalysisType implements WrappedJavaType, OriginalClassProvider, Com private final Set subTypes; AnalysisType superClass; + private final Set instantiatedSubtypes = ConcurrentHashMap.newKeySet(); + private final Set invokedMethods = ConcurrentHashMap.newKeySet(); + private final int id; private final JavaKind storageKind; @@ -419,6 +422,7 @@ public static boolean verifyAssignableTypes(BigBang bb) { public boolean registerAsInHeap() { registerAsReachable(); if (AtomicUtils.atomicMark(isInHeap)) { + registerAsInstantiated(UsageKind.InHeap); universe.onTypeInstantiated(this, UsageKind.InHeap); return true; } @@ -431,18 +435,28 @@ public boolean registerAsInHeap() { public boolean registerAsAllocated(Node node) { registerAsReachable(); if (AtomicUtils.atomicMark(isAllocated)) { + registerAsInstantiated(UsageKind.Allocated); universe.onTypeInstantiated(this, UsageKind.Allocated); return true; } return false; } + /** Register the type as instantiated with all its super types. */ + private void registerAsInstantiated(UsageKind usageKind) { + forAllSuperTypes(t -> t.instantiatedSubtypes.add(this)); + } + /** * Register the type as assignable with all its super types. This is a blocking call to ensure * that the type is registered with all its super types before it is propagated by the analysis * through type flows. */ public void registerAsAssignable(BigBang bb) { + // todo refactor + if (!(bb instanceof PointsToAnalysis)) { + return; + } TypeState typeState = TypeState.forType(((PointsToAnalysis) bb), this, true); /* * Register the assignable type with its super types. Skip this type, it can lead to a @@ -775,7 +789,21 @@ public int getModifiers() { @Override public boolean isAssignableFrom(ResolvedJavaType other) { - ResolvedJavaType subst = universe.substitutions.resolve(((AnalysisType) other).wrapped); + AnalysisType analysisOther; + if (other instanceof AnalysisType) { + analysisOther = ((AnalysisType) other); + } else if (other instanceof WrappedJavaType) { + // might be a HostedType, which is not accessible here, but implements WrappedJavaType + WrappedJavaType wrapped = (WrappedJavaType) other; + if (wrapped.getWrapped() instanceof AnalysisType) { + analysisOther = ((AnalysisType) wrapped.getWrapped()); + } else { + analysisOther = universe.lookup(other); + } + } else { + analysisOther = universe.lookup(other); + } + ResolvedJavaType subst = universe.substitutions.resolve(analysisOther.wrapped); return wrapped.isAssignableFrom(subst); } @@ -814,7 +842,8 @@ private void addSubType(AnalysisType subType) { @Override public AnalysisType findLeastCommonAncestor(ResolvedJavaType otherType) { - ResolvedJavaType subst = universe.substitutions.resolve(((AnalysisType) otherType).wrapped); + AnalysisType analysisOther = otherType instanceof AnalysisType ? (AnalysisType) otherType : universe.lookup(otherType); + ResolvedJavaType subst = universe.substitutions.resolve(analysisOther.wrapped); return universe.lookup(wrapped.findLeastCommonAncestor(subst)); } @@ -1132,4 +1161,12 @@ private ResolvedJavaField[] interceptInstanceFields(ResolvedJavaField[] fields) public interface InstanceFieldsInterceptor { ResolvedJavaField[] interceptInstanceFields(AnalysisUniverse universe, ResolvedJavaField[] fields, AnalysisType type); } + + public Set getInstantiatedSubtypes() { + return instantiatedSubtypes; + } + + public Set getInvokedMethods() { + return invokedMethods; + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 837d4ba74b12..24b76be614d2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -184,6 +184,7 @@ public AnalysisType optionalLookup(ResolvedJavaType type) { @Override public AnalysisType lookup(JavaType type) { + assert !(type instanceof AnalysisType) : "Must not a lookup a type that already is an AnalysisType: " + type; JavaType result = lookupAllowUnresolved(type); if (result == null) { return null; @@ -343,6 +344,7 @@ public JavaKind getStorageKind(ResolvedJavaType type, MetaAccessProvider metaAcc @Override public AnalysisField lookup(JavaField field) { + assert !(field instanceof AnalysisField) : "Must not a lookup a field that already is an AnalysisField: " + field; JavaField result = lookupAllowUnresolved(field); if (result == null) { return null; @@ -400,6 +402,7 @@ private AnalysisField createField(ResolvedJavaField field) { @Override public AnalysisMethod lookup(JavaMethod method) { + assert !(method instanceof AnalysisMethod) : "Must not a lookup a method that already is an AnalysisMethod: " + method; JavaMethod result = lookupAllowUnresolved(method); if (result == null) { return null; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysis.java index 5e6874405637..b7d60e8566a4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysis.java @@ -28,6 +28,7 @@ import java.util.Deque; import java.util.concurrent.ConcurrentHashMap; +import com.oracle.graal.pointsto.BigBang; import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; @@ -52,7 +53,6 @@ import org.graalvm.compiler.printer.GraalDebugHandlersFactory; import org.graalvm.compiler.replacements.PEGraphDecoder; -import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.svm.util.ClassUtil; @@ -82,7 +82,7 @@ public static class Options { } @SuppressWarnings("try") - public static StructuredGraph decodeGraph(PointsToAnalysis bb, AnalysisMethod method, AnalysisParsedGraph analysisParsedGraph) { + public static StructuredGraph decodeGraph(BigBang bb, AnalysisMethod method, AnalysisParsedGraph analysisParsedGraph) { DebugContext.Description description = new DebugContext.Description(method, ClassUtil.getUnqualifiedName(method.getClass()) + ":" + method.getId()); DebugContext debug = new DebugContext.Builder(bb.getOptions(), new GraalDebugHandlersFactory(bb.getProviders().getSnippetReflection())).description(description).build(); @@ -152,10 +152,10 @@ class InlineBeforeAnalysisMethodScope extends PEMethodScope { } } - private final PointsToAnalysis bb; + private final BigBang bb; private final InlineBeforeAnalysisPolicy policy; - InlineBeforeAnalysisGraphDecoder(PointsToAnalysis bb, InlineBeforeAnalysisPolicy policy, StructuredGraph graph) { + InlineBeforeAnalysisGraphDecoder(BigBang bb, InlineBeforeAnalysisPolicy policy, StructuredGraph graph) { super(AnalysisParsedGraph.HOST_ARCHITECTURE, graph, bb.getProviders(), null, bb.getProviders().getGraphBuilderPlugins().getInvocationPlugins(), new InlineInvokePlugin[]{new InlineBeforeAnalysisInlineInvokePlugin(policy)}, diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java index 6664431a1d04..4c9bc51017b9 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java @@ -29,7 +29,6 @@ import java.util.Map; import com.oracle.graal.pointsto.BigBang; -import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.infrastructure.Universe; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -43,7 +42,7 @@ public abstract class AbstractAnalysisResultsBuilder { - protected final PointsToAnalysis bb; + protected final BigBang bb; /** * The universe used to convert analysis metadata to hosted metadata, or {@code null} if no @@ -62,7 +61,7 @@ public abstract class AbstractAnalysisResultsBuilder { private final JavaMethodProfile[] methods1; private final Map methods; - protected AbstractAnalysisResultsBuilder(PointsToAnalysis bb, Universe converter) { + protected AbstractAnalysisResultsBuilder(BigBang bb, Universe converter) { this.bb = bb; this.converter = converter; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/DummyResultsBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/DummyResultsBuilder.java new file mode 100644 index 000000000000..e6050e899b02 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/DummyResultsBuilder.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.results; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.infrastructure.Universe; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import jdk.vm.ci.meta.JavaTypeProfile; +import jdk.vm.ci.meta.TriState; + +public class DummyResultsBuilder extends AbstractAnalysisResultsBuilder { + public DummyResultsBuilder(BigBang bb, Universe converter) { + super(bb, converter); + } + + @Override + public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { + return StaticAnalysisResults.NO_RESULTS; + } + + @Override + public JavaTypeProfile makeTypeProfile(AnalysisField field) { + return new JavaTypeProfile(TriState.UNKNOWN, 1, new JavaTypeProfile.ProfiledType[0]); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StaticAnalysisResultsBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StaticAnalysisResultsBuilder.java index 36127355abe3..3fdd48be2c14 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StaticAnalysisResultsBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StaticAnalysisResultsBuilder.java @@ -58,9 +58,13 @@ public StaticAnalysisResultsBuilder(PointsToAnalysis bb, Universe converter) { super(bb, converter); } + private PointsToAnalysis getAnalysis() { + return ((PointsToAnalysis) bb); + } + @Override public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { - + PointsToAnalysis pointsToAnalysis = getAnalysis(); MethodTypeFlow methodFlow = PointsToAnalysis.assertPointsToAnalysisMethod(method).getTypeFlow(); MethodFlowsGraph originalFlows = methodFlow.getOriginalMethodFlows(); @@ -76,12 +80,12 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { */ continue; } - if (methodFlow.isSaturated(bb, parameter)) { + if (methodFlow.isSaturated(pointsToAnalysis, parameter)) { /* The parameter type flow is saturated, it's type state doesn't matter. */ continue; } - TypeState paramTypeState = methodFlow.foldTypeFlow(bb, parameter); + TypeState paramTypeState = methodFlow.foldTypeFlow(pointsToAnalysis, parameter); JavaTypeProfile paramProfile = makeTypeProfile(paramTypeState); if (paramProfile != null) { ensureSize(paramProfiles, i); @@ -93,7 +97,7 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { parameterTypeProfiles = paramProfiles.toArray(new JavaTypeProfile[paramProfiles.size()]); } - JavaTypeProfile resultTypeProfile = makeTypeProfile(methodFlow.foldTypeFlow(bb, originalFlows.getResult())); + JavaTypeProfile resultTypeProfile = makeTypeProfile(methodFlow.foldTypeFlow(pointsToAnalysis, originalFlows.getResult())); ArrayList entries = new ArrayList<>(method.getCodeSize()); @@ -102,7 +106,7 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { int bci = (int) entry.getKey(); InstanceOfTypeFlow originalInstanceOf = entry.getValue(); - if (methodFlow.isSaturated(bb, originalInstanceOf)) { + if (methodFlow.isSaturated(pointsToAnalysis, originalInstanceOf)) { /* * If the instance flow is saturated its exact type state doesn't matter. This * instanceof cannot be optimized. @@ -111,8 +115,8 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { } /* Fold the instanceof flows. */ - TypeState instanceOfTypeState = methodFlow.foldTypeFlow(bb, originalInstanceOf); - originalInstanceOf.setState(bb, instanceOfTypeState); + TypeState instanceOfTypeState = methodFlow.foldTypeFlow(pointsToAnalysis, originalInstanceOf); + originalInstanceOf.setState(pointsToAnalysis, instanceOfTypeState); JavaTypeProfile typeProfile = makeTypeProfile(instanceOfTypeState); if (typeProfile != null) { @@ -130,17 +134,17 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { TypeState invokeTypeState = null; /* If the receiver flow is saturated its exact type state doesn't matter. */ - if (originalInvoke.getTargetMethod().hasReceiver() && !methodFlow.isSaturated(bb, originalInvoke.getReceiver())) { - invokeTypeState = methodFlow.foldTypeFlow(bb, originalInvoke.getReceiver()); - originalInvoke.setState(bb, invokeTypeState); + if (originalInvoke.getTargetMethod().hasReceiver() && !methodFlow.isSaturated(pointsToAnalysis, originalInvoke.getReceiver())) { + invokeTypeState = methodFlow.foldTypeFlow(pointsToAnalysis, originalInvoke.getReceiver()); + originalInvoke.setState(pointsToAnalysis, invokeTypeState); } TypeFlow originalReturn = originalInvoke.getActualReturn(); TypeState returnTypeState = null; /* If the return flow is saturated its exact type state doesn't matter. */ - if (originalReturn != null && !methodFlow.isSaturated(bb, originalReturn)) { - returnTypeState = methodFlow.foldTypeFlow(bb, originalReturn); - originalReturn.setState(bb, returnTypeState); + if (originalReturn != null && !methodFlow.isSaturated(pointsToAnalysis, originalReturn)) { + returnTypeState = methodFlow.foldTypeFlow(pointsToAnalysis, originalReturn); + originalReturn.setState(pointsToAnalysis, returnTypeState); } JavaTypeProfile typeProfile = makeTypeProfile(invokeTypeState); @@ -155,7 +159,7 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { } } - if (PointstoOptions.PrintSynchronizedAnalysis.getValue(bb.getOptions())) { + if (PointstoOptions.PrintSynchronizedAnalysis.getValue(pointsToAnalysis.getOptions())) { originalFlows.getMiscFlows().stream() .filter(flow -> flow instanceof MonitorEnterTypeFlow) .map(flow -> (MonitorEnterTypeFlow) flow) @@ -163,8 +167,8 @@ public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { .sorted(Comparator.comparingInt(m2 -> m2.getState().typesCount())) .forEach(monitorEnter -> { TypeState monitorEntryState = monitorEnter.getState(); - String typesString = TypeStateUtils.closeToAllInstantiated(bb, monitorEntryState) ? "close to all instantiated" - : StreamSupport.stream(monitorEntryState.types(bb).spliterator(), false).map(AnalysisType::getName).collect(Collectors.joining(", ")); + String typesString = TypeStateUtils.closeToAllInstantiated(pointsToAnalysis, monitorEntryState) ? "close to all instantiated" + : StreamSupport.stream(monitorEntryState.types(pointsToAnalysis).spliterator(), false).map(AnalysisType::getName).collect(Collectors.joining(", ")); StringBuilder strb = new StringBuilder(); strb.append("Location: "); String methodName = method.format("%h.%n(%p)"); 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 45706782fd30..3b49641fa5a5 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 @@ -113,6 +113,10 @@ public StrengthenGraphs(PointsToAnalysis bb, Universe converter) { super(bb, converter); } + private PointsToAnalysis getAnalysis() { + return ((PointsToAnalysis) bb); + } + @Override @SuppressWarnings("try") public StaticAnalysisResults makeOrApplyResults(AnalysisMethod method) { @@ -284,6 +288,7 @@ public void simplify(Node n, SimplifierTool tool) { } private void handleInvoke(Invoke invoke, SimplifierTool tool) { + PointsToAnalysis analysis = getAnalysis(); FixedNode node = invoke.asFixedNode(); MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); @@ -322,8 +327,8 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { } else { TypeState receiverTypeState = null; /* If the receiver flow is saturated, its exact type state does not matter. */ - if (invokeFlow.getTargetMethod().hasReceiver() && !methodFlow.isSaturated(bb, invokeFlow.getReceiver())) { - receiverTypeState = methodFlow.foldTypeFlow(bb, invokeFlow.getReceiver()); + if (invokeFlow.getTargetMethod().hasReceiver() && !methodFlow.isSaturated(analysis, invokeFlow.getReceiver())) { + receiverTypeState = methodFlow.foldTypeFlow(analysis, invokeFlow.getReceiver()); } JavaTypeProfile typeProfile = makeTypeProfile(receiverTypeState); @@ -416,10 +421,11 @@ private void devirtualizeInvoke(AnalysisMethod singleCallee, Invoke invoke) { } private boolean isUnreachable(Node branch) { + PointsToAnalysis analysis = getAnalysis(); TypeFlow branchFlow = originalFlows.getNodeFlows().get(branch); return branchFlow != null && - !methodFlow.isSaturated(bb, branchFlow) && - methodFlow.foldTypeFlow(bb, branchFlow).isEmpty(); + !methodFlow.isSaturated(analysis, branchFlow) && + methodFlow.foldTypeFlow(analysis, branchFlow).isEmpty(); } private void updateStampInPlace(ValueNode node, Stamp newStamp, SimplifierTool tool) { @@ -468,6 +474,7 @@ private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchor } private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, FixedWithNextNode anchorPoint, SimplifierTool tool) { + PointsToAnalysis analysis = getAnalysis(); if (node.getStackKind() != JavaKind.Object) { return null; } @@ -478,12 +485,12 @@ private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, */ return null; } - if (methodFlow.isSaturated(bb, nodeFlow)) { + if (methodFlow.isSaturated(analysis, nodeFlow)) { /* The type flow is saturated, its type state does not matter. */ return null; } - TypeState nodeTypeState = methodFlow.foldTypeFlow(bb, nodeFlow); + TypeState nodeTypeState = methodFlow.foldTypeFlow(analysis, nodeFlow); node.inferStamp(); ObjectStamp oldStamp = (ObjectStamp) node.stamp(NodeView.DEFAULT); AnalysisType oldType = (AnalysisType) oldStamp.type(); @@ -496,7 +503,7 @@ private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, * stamp is already more precise than the static analysis results. */ List typeStateTypes = new ArrayList<>(nodeTypeState.typesCount()); - for (AnalysisType typeStateType : nodeTypeState.types(bb)) { + for (AnalysisType typeStateType : nodeTypeState.types(analysis)) { if (oldType == null || (oldStamp.isExactType() ? oldType.equals(typeStateType) : oldType.isAssignableFrom(typeStateType))) { typeStateTypes.add(typeStateType); } diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummary.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummary.java new file mode 100644 index 000000000000..20f945fd26a8 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummary.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; +import jdk.vm.ci.meta.JavaConstant; +import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; +import org.graalvm.compiler.core.common.spi.ForeignCallSignature; + +import java.util.Arrays; +import java.util.List; + +public class MethodSummary { + public static final MethodSummary EMPTY = new MethodSummary(new AnalysisMethod[0], new AnalysisMethod[0], new AnalysisType[0], new AnalysisType[0], new AnalysisField[0], new AnalysisField[0], + new JavaConstant[0], new ForeignCallDescriptor[0], new ForeignCallSignature[0]); + + public final AnalysisMethod[] invokedMethods; + public final AnalysisMethod[] implementationInvokedMethods; + public final AnalysisType[] accessedTypes; + public final AnalysisType[] instantiatedTypes; + public final AnalysisField[] readFields; + public final AnalysisField[] writtenFields; + public final JavaConstant[] embeddedConstants; + public final ForeignCallDescriptor[] foreignCallDescriptors; + public final ForeignCallSignature[] foreignCallSignatures; + + public MethodSummary(AnalysisMethod[] invokedMethods, AnalysisMethod[] implementationInvokedMethods, AnalysisType[] accessedTypes, AnalysisType[] instantiatedTypes, AnalysisField[] readFields, + AnalysisField[] writtenFields, + JavaConstant[] embeddedConstants, + ForeignCallDescriptor[] foreignCallDescriptors, + ForeignCallSignature[] foreignCallSignatures) { + this.invokedMethods = invokedMethods; + this.implementationInvokedMethods = implementationInvokedMethods; + this.accessedTypes = accessedTypes; + this.instantiatedTypes = instantiatedTypes; + this.readFields = readFields; + this.writtenFields = writtenFields; + this.embeddedConstants = embeddedConstants; + this.foreignCallDescriptors = foreignCallDescriptors; + this.foreignCallSignatures = foreignCallSignatures; + } + + public MethodSummary(AnalysisMethod[] invokedMethods, AnalysisMethod[] implementationInvokedMethods, AnalysisType[] accessedTypes, AnalysisType[] instantiatedTypes, AnalysisField[] readFields, + AnalysisField[] writtenFields) { + this.invokedMethods = invokedMethods; + this.implementationInvokedMethods = implementationInvokedMethods; + this.accessedTypes = accessedTypes; + this.instantiatedTypes = instantiatedTypes; + this.readFields = readFields; + this.writtenFields = writtenFields; + this.embeddedConstants = new JavaConstant[0]; + this.foreignCallDescriptors = new ForeignCallDescriptor[0]; + this.foreignCallSignatures = new ForeignCallSignature[0]; + } + + public String textSummary() { + StringBuilder builder = new StringBuilder(); + return builder.append("invoked: ") + .append(invokedMethods.length) + .append(", impl invoked: ") + .append(implementationInvokedMethods.length) + .append(", accessed: ") + .append(accessedTypes.length) + .append(", instantiated: ") + .append(instantiatedTypes.length) + .append(", read: ") + .append(readFields.length) + .append(", written: ") + .append(writtenFields.length) + .append(", embedded: ") + .append(embeddedConstants.length) + .append(", desc: ") + .append(foreignCallDescriptors.length) + .append(", sign: ") + .append(foreignCallSignatures.length) + .toString(); + } + + public static MethodSummary accessed(List accessedTypes) { + return new MethodSummary(new AnalysisMethod[0], new AnalysisMethod[0], accessedTypes.toArray(new AnalysisType[0]), new AnalysisType[0], new AnalysisField[0], new AnalysisField[0]); + } + + @Override + public String toString() { + return "MethodSummary{" + + "invokedMethods=" + Arrays.toString(invokedMethods) + + ", implementationInvokedMethods=" + Arrays.toString(implementationInvokedMethods) + + ", accessedTypes=" + Arrays.toString(accessedTypes) + + ", instantiatedTypes=" + Arrays.toString(instantiatedTypes) + + ", readFields=" + Arrays.toString(readFields) + + ", writtenFields=" + Arrays.toString(writtenFields) + + ", embeddedConstants=" + Arrays.toString(embeddedConstants) + + '}'; + } + + public MethodSummary withoutMethods() { + return new MethodSummary(new AnalysisMethod[0], new AnalysisMethod[0], accessedTypes, instantiatedTypes, readFields, writtenFields, embeddedConstants, foreignCallDescriptors, + foreignCallSignatures); + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummaryProvider.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummaryProvider.java new file mode 100644 index 000000000000..b7d0170d80ee --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/MethodSummaryProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import org.graalvm.compiler.nodes.StructuredGraph; + +public interface MethodSummaryProvider { + MethodSummary getSummary(BigBang bigBang, AnalysisMethod method); + + MethodSummary getSummary(BigBang bigBang, StructuredGraph graph); +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysis.java new file mode 100644 index 000000000000..3f0d5cebbf63 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysis.java @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.pointsto.AbstractReachabilityAnalysis; +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.api.PointstoOptions; +import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; +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.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.pointsto.typestate.TypeState; +import com.oracle.graal.pointsto.util.AnalysisError; +import com.oracle.graal.pointsto.util.Timer; +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; +import org.graalvm.compiler.core.common.spi.ForeignCallSignature; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.extended.ForeignCall; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.replacements.nodes.BinaryMathIntrinsicNode; +import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.function.Function; + +import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere; + +public abstract class ReachabilityAnalysis extends AbstractReachabilityAnalysis { + + private final MethodSummaryProvider methodSummaryProvider; + private final AnalysisType objectType; + private final Timer summaryTimer; + + public ReachabilityAnalysis(OptionValues options, AnalysisUniverse universe, HostedProviders providers, HostVM hostVM, ForkJoinPool executorService, Runnable heartbeatCallback, + UnsupportedFeatures unsupportedFeatures, MethodSummaryProvider methodSummaryProvider) { + super(options, universe, providers, hostVM, executorService, heartbeatCallback, unsupportedFeatures); + this.methodSummaryProvider = methodSummaryProvider; + this.objectType = metaAccess.lookupJavaType(Object.class); + this.summaryTimer = new Timer(hostVM.getImageName(), "((summaries))", false); + } + + @SuppressWarnings("try") + @Override + public AnalysisType addRootClass(AnalysisType type, boolean addFields, boolean addArrayClass) { + try (Indent indent = debug.logAndIndent("add root class %s", type.getName())) { + for (AnalysisField field : type.getInstanceFields(false)) { + if (addFields) { + field.registerAsAccessed(); + } + } + + markTypeReachable(type); + + if (type.getSuperclass() != null) { + addRootClass(type.getSuperclass(), addFields, addArrayClass); + } + if (addArrayClass) { + addRootClass(type.getArrayClass(), false, false); + } + } + return type; + } + + @SuppressWarnings("try") + @Override + public AnalysisType addRootField(Class clazz, String fieldName) { + AnalysisType type = addRootClass(clazz, false, false); + for (AnalysisField field : type.getInstanceFields(true)) { + if (field.getName().equals(fieldName)) { + try (Indent indent = debug.logAndIndent("add root field %s in class %s", fieldName, clazz.getName())) { + field.registerAsAccessed(); + } + return field.getType(); + } + } + throw shouldNotReachHere("field not found: " + fieldName); + } + + @Override + public AnalysisMethod addRootMethod(AnalysisMethod method) { + if (!method.registerAsRootMethod()) { + return method; + } + if (!method.isStatic()) { + markTypeInstantiated(method.getDeclaringClass()); + } + method.registerAsInvoked(); + markMethodImplementationInvoked(method, new RuntimeException().getStackTrace()); + return method; + } + + @Override + public void markMethodImplementationInvoked(AnalysisMethod method, Object reason) { + if (method == null) { + System.err.println("Null method received"); + System.out.println("reson: " + reason); + new RuntimeException().printStackTrace(); + return; + } + if (!method.registerAsImplementationInvoked()) { + return; + } + schedule(() -> onMethodImplementationInvoked(method)); + } + + public final Map summaries = new ConcurrentHashMap<>(); + + @SuppressWarnings("try") + private void onMethodImplementationInvoked(AnalysisMethod method) { + try { + MethodSummary summary; + try (Timer.StopTimer t = summaryTimer.start()) { + summary = methodSummaryProvider.getSummary(this, method); + } + processSummary(method, summary); + summaries.put(method, summary); + } catch (Throwable ex) { + System.err.println("Failed to provide a summary for " + method.format("%H.%n(%p)")); + System.err.println(ex + " " + ex.getMessage()); + System.err.println("Parsing reason: " + method.getReason()); + ex.printStackTrace(); + } + } + + private void processSummary(AnalysisMethod method, MethodSummary summary) { + for (AnalysisMethod invokedMethod : summary.invokedMethods) { + markMethodInvoked(invokedMethod); + } + for (AnalysisMethod invokedMethod : summary.implementationInvokedMethods) { + markMethodInvoked(invokedMethod); + markMethodImplementationInvoked(invokedMethod, method); + } + for (AnalysisType type : summary.accessedTypes) { + markTypeReachable(type); + } + for (AnalysisType type : summary.instantiatedTypes) { + markTypeInstantiated(type); + } + for (AnalysisField field : summary.readFields) { + markFieldRead(field); + } + for (AnalysisField field : summary.writtenFields) { + markFieldWritten(field); + } + for (JavaConstant constant : summary.embeddedConstants) { + if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { + // todo heap initiate scanning + // track the constant + if (this.scanningPolicy().trackConstant(this, constant)) { + BytecodePosition position = new BytecodePosition(null, method, 0); + getUniverse().registerEmbeddedRoot(constant, position); + + Object obj = getSnippetReflectionProvider().asObject(Object.class, constant); + AnalysisType type = getMetaAccess().lookupJavaType(obj.getClass()); + markTypeInHeap(type); + } + } + } + for (ForeignCallDescriptor descriptor : summary.foreignCallDescriptors) { + registerForeignCall(descriptor); + } + for (ForeignCallSignature signature : summary.foreignCallSignatures) { + registerForeignCall(getProviders().getForeignCalls().getDescriptor(signature)); + } + } + + @Override + public void markFieldAccessed(AnalysisField field) { + field.registerAsAccessed(); + } + + @Override + public void markFieldRead(AnalysisField field) { + field.registerAsRead(null); + } + + @Override + public void markFieldWritten(AnalysisField field) { + field.registerAsWritten(null); + } + + @Override + public void markTypeReachable(AnalysisType type) { + // todo double check whether all necessary logic is in + type.registerAsReachable(); + } + + @Override + public void markTypeInHeap(AnalysisType type) { + markTypeInstantiated(type); + type.registerAsInHeap(); + } + + public void markTypeInstantiated(AnalysisType type) { + if (!type.registerAsAllocated(null)) { + return; + } + schedule(() -> onTypeInstantiated(type)); + } + + private void onTypeInstantiated(AnalysisType type) { + type.forAllSuperTypes(current -> { + Set invokedMethods = current.getInvokedMethods(); + for (AnalysisMethod method : invokedMethods) { + if (method.isStatic()) { + continue; + } + AnalysisMethod implementationInvokedMethod = type.resolveConcreteMethod(method, current); + if (implementationInvokedMethod == null) { +// System.out.println("onMethodInvoked: method " + method + " on type " + current + " is null"); + continue; + } + markMethodImplementationInvoked(implementationInvokedMethod, type); // todo better + // reason + } + }); + } + + private void markMethodInvoked(AnalysisMethod method) { + if (!method.registerAsInvoked()) { + return; + } + schedule(() -> onMethodInvoked(method)); + } + + private void onMethodInvoked(AnalysisMethod method) { + AnalysisType clazz = method.getDeclaringClass(); + Set instantiatedSubtypes = clazz.getInstantiatedSubtypes(); + if (method.isStatic()) { + // todo better reason + markMethodImplementationInvoked(method, null); + return; + } + for (AnalysisType subtype : instantiatedSubtypes) { + AnalysisMethod resolvedMethod = subtype.resolveConcreteMethod(method, clazz); + if (resolvedMethod == null) { +// System.out.println("onMethodInvoked: method " + method + " on type " + subtype + " is null"); + continue; + } + markMethodImplementationInvoked(resolvedMethod, method); // todo better reason + } + } + + @Override + public boolean finish() throws InterruptedException { + universe.setAnalysisDataValid(false); + + int numTypes; + do { + runReachability(); + + assert executor.getPostedOperations() == 0; + numTypes = universe.getTypes().size(); + + checkObjectGraph(); + + } while (executor.getPostedOperations() != 0 || numTypes != universe.getTypes().size()); + + universe.setAnalysisDataValid(true); + + return true; + } + + @SuppressWarnings("try") + @Override + public void runAnalysis(DebugContext debugContext, Function analysisEndCondition) throws InterruptedException { + // todo this is ugly copy paste from points-to + int numIterations = 0; + while (true) { + try (Indent indent2 = debugContext.logAndIndent("new analysis iteration")) { + /* + * Do the analysis (which itself is done in a similar iterative process) + */ + boolean analysisChanged = finish(); + + numIterations++; + if (numIterations > 1000) { + /* + * Usually there are < 10 iterations. If we have so many iterations, we probably + * have an endless loop (but at least we have a performance problem because we + * re-start the analysis so often). + */ + throw AnalysisError.shouldNotReachHere(String.format("Static analysis did not reach a fix point after %d iterations because a Feature keeps requesting new analysis iterations. " + + "The analysis itself %s find a change in type states in the last iteration.", + numIterations, analysisChanged ? "DID" : "DID NOT")); + } + + /* + * Allow features to change the universe. + */ + try (Timer.StopTimer t2 = getProcessFeaturesTimer().start()) { + int numTypes = universe.getTypes().size(); + int numMethods = universe.getMethods().size(); + int numFields = universe.getFields().size(); + if (analysisEndCondition.apply(universe)) { + if (numTypes != universe.getTypes().size() || numMethods != universe.getMethods().size() || numFields != universe.getFields().size()) { + throw AnalysisError.shouldNotReachHere( + "When a feature makes more types, methods, or fields reachable, it must require another analysis iteration via DuringAnalysisAccess.requireAnalysisIteration()"); + } + return; + } + } + } + } + } + + @SuppressWarnings("try") + private void runReachability() throws InterruptedException { + try (Timer.StopTimer t = reachabilityTimer.start()) { + if (!executor.isStarted()) { + executor.start(); + } + executor.complete(); + executor.shutdown(); + executor.init(timing); + } + } + + private final ObjectScanner.ReusableSet scannedObjects = new ObjectScanner.ReusableSet(); + + @SuppressWarnings("try") + private void checkObjectGraph() throws InterruptedException { + try (Timer.StopTimer t = checkObjectsTimer.start()) { + scannedObjects.reset(); + // scan constants + boolean isParallel = PointstoOptions.ScanObjectsParallel.getValue(options); + ObjectScanner objectScanner = new ObjectScanner(this, isParallel ? executor : null, scannedObjects, new ReachabilityObjectScanner(this, metaAccess)) { + }; + checkObjectGraph(objectScanner); + if (isParallel) { + executor.start(); + objectScanner.scanBootImageHeapRoots(null, null); + executor.complete(); + executor.shutdown(); + executor.init(timing); + } else { + objectScanner.scanBootImageHeapRoots(null, null); + } + } + } + + protected abstract void checkObjectGraph(ObjectScanner objectScanner); + + @Override + public void cleanupAfterAnalysis() { + super.cleanupAfterAnalysis(); + } + + @Override + public void forceUnsafeUpdate(AnalysisField field) { + // todo what to do? + } + + @Override + public void registerAsJNIAccessed(AnalysisField field, boolean writable) { + // todo what to do? + } + + @Override + public TypeState getAllSynchronizedTypeState() { + // todo don't overapproximate so much + return objectType.getTypeFlow(this, true).getState(); + } + + @SuppressWarnings("try") + public void processGraph(StructuredGraph graph) { + MethodSummary summary; + try (Timer.StopTimer t = summaryTimer.start()) { + summary = methodSummaryProvider.getSummary(this, graph); + } + AnalysisMethod method = analysisMethod(graph.method()); + method.registerAsInvoked(); + method.registerAsImplementationInvoked(); + processSummary(method, summary.withoutMethods()); + + registerForeignCalls(graph); + } + + private void registerForeignCalls(StructuredGraph graph) { + for (Node n : graph.getNodes()) { + if (n instanceof ForeignCall) { + ForeignCall node = (ForeignCall) n; + registerForeignCall(node.getDescriptor()); + } else if (n instanceof UnaryMathIntrinsicNode) { + UnaryMathIntrinsicNode node = (UnaryMathIntrinsicNode) n; + registerForeignCall(getProviders().getForeignCalls().getDescriptor(node.getOperation().foreignCallSignature)); + } else if (n instanceof BinaryMathIntrinsicNode) { + BinaryMathIntrinsicNode node = (BinaryMathIntrinsicNode) n; + registerForeignCall(getProviders().getForeignCalls().getDescriptor(node.getOperation().foreignCallSignature)); + } else if (n instanceof FrameState) { + FrameState node = (FrameState) n; + AnalysisMethod method = (AnalysisMethod) node.getMethod(); + if (method != null) { + markTypeReachable(method.getDeclaringClass()); + } + } + } + } + + private void registerForeignCall(ForeignCallDescriptor descriptor) { + Optional targetMethod = getHostVM().handleForeignCall(descriptor, getProviders().getForeignCalls()); + targetMethod.ifPresent(this::addRootMethod); + } + + private AnalysisMethod analysisMethod(ResolvedJavaMethod method) { + return method instanceof AnalysisMethod ? ((AnalysisMethod) method) : universe.lookup(method); + } + + @Override + public void printTimers() { + summaryTimer.print(); + super.printTimers(); + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java new file mode 100644 index 000000000000..39a2ec300603 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityObjectScanner.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.ObjectScanningObserver; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisType; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; + +// todo resolve return values +public class ReachabilityObjectScanner implements ObjectScanningObserver { + + private final ReachabilityAnalysis bb; + private final AnalysisMetaAccess access; + + public ReachabilityObjectScanner(BigBang bb, AnalysisMetaAccess access) { + this.bb = ((ReachabilityAnalysis) bb); + this.access = access; + } + + @Override + public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ObjectScanner.ScanReason reason) { + field.registerAsAccessed(); + if (fieldValue.isNonNull() && fieldValue.getJavaKind() == JavaKind.Object) { + // todo mark as instantiated +// bb.markTypeInstantiated(constantType(bb, fieldValue)); + } + return true; + } + + @Override + public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ObjectScanner.ScanReason reason) { + if (receiver != null) { + bb.markTypeReachable(constantType(receiver)); + } + bb.markTypeReachable(field.getType()); +// System.out.println("Scanning field " + field); + return true; + } + + @Override + public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ObjectScanner.ScanReason reason) { + if (receiver != null) { + bb.markTypeReachable(constantType(receiver)); + } + bb.markTypeReachable(field.getType()); +// System.out.println("Scanning field " + field); + return true; + } + + @Override + public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ObjectScanner.ScanReason reason) { + bb.markTypeReachable(arrayType); + return true; + } + + @Override + public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ObjectScanner.ScanReason reason) { + bb.markTypeReachable(arrayType); + bb.markTypeInstantiated(elementType); + return true; + } + + @Override + public void forEmbeddedRoot(JavaConstant root, ObjectScanner.ScanReason reason) { + bb.markTypeReachable(constantType(root)); + bb.markTypeInstantiated(constantType(root)); + } + + @Override + public void forScannedConstant(JavaConstant scannedValue, ObjectScanner.ScanReason reason) { + AnalysisType type = constantType(scannedValue); +// System.out.println("Scanning constant of type " + type); + bb.markTypeInstantiated(type); + type.registerAsInHeap(); + } + + private AnalysisType constantType(JavaConstant constant) { + return access.lookupJavaType(constantAsObject(constant).getClass()); + } + + private Object constantAsObject(JavaConstant constant) { + return bb.getSnippetReflectionProvider().asObject(Object.class, constant); + } + +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SerializableMethodSummary.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SerializableMethodSummary.java new file mode 100644 index 000000000000..6d160df85876 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SerializableMethodSummary.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.reachability.summaries.MethodHash; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Objects; + +public class SerializableMethodSummary implements Serializable { + + private static final long serialVersionUID = 1L; + + public SerializableMethodSummary(MethodHash hash, MethodId[] invokedMethods, MethodId[] implementationInvokedMethods, ClassId[] accessedTypes, ClassId[] instantiatedTypes, FieldId[] readFields, + FieldId[] writtenFields) { + this.hash = hash; + this.invokedMethods = invokedMethods; + this.implementationInvokedMethods = implementationInvokedMethods; + this.accessedTypes = accessedTypes; + this.instantiatedTypes = instantiatedTypes; + this.readFields = readFields; + this.writtenFields = writtenFields; + } + + public static class ClassId implements Serializable { + + private static final long serialVersionUID = 1L; + + public final String className; + + public ClassId(String className) { + this.className = className; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClassId classId = (ClassId) o; + return Objects.equals(className, classId.className); + } + + @Override + public int hashCode() { + return Objects.hash(className); + } + + @Override + public String toString() { + return "ClassId{" + + "className='" + className + '\'' + + '}'; + } + } + + public static class MethodId implements Serializable { + + private static final long serialVersionUID = 1L; + + public final ClassId classId; + public final String methodName; + + public MethodId(ClassId classId, String methodName) { + this.classId = classId; + this.methodName = methodName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MethodId methodId = (MethodId) o; + return Objects.equals(classId, methodId.classId) && Objects.equals(methodName, methodId.methodName); + } + + @Override + public int hashCode() { + return Objects.hash(classId, methodName); + } + + @Override + public String toString() { + return "MethodId{" + + "classId=" + classId + + ", methodName='" + methodName + '\'' + + '}'; + } + } + + public static class FieldId implements Serializable { + private static final long serialVersionUID = 1L; + + public final ClassId classId; + public final String fieldName; + + public FieldId(ClassId classId, String fieldName) { + this.classId = classId; + this.fieldName = fieldName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FieldId fieldId = (FieldId) o; + return Objects.equals(classId, fieldId.classId) && Objects.equals(fieldName, fieldId.fieldName); + } + + @Override + public int hashCode() { + return Objects.hash(classId, fieldName); + } + + @Override + public String toString() { + return "FieldId{" + + "classId=" + classId + + ", fieldName='" + fieldName + '\'' + + '}'; + } + } + + public final MethodHash hash; + public final MethodId[] invokedMethods; + public final MethodId[] implementationInvokedMethods; + public final ClassId[] accessedTypes; + public final ClassId[] instantiatedTypes; + public final FieldId[] readFields; + public final FieldId[] writtenFields; + + @Override + public String toString() { + return "SerializableMethodSummary{" + + "hash=" + hash + + ", invokedMethods=" + Arrays.toString(invokedMethods) + + ", implementationInvokedMethods=" + Arrays.toString(implementationInvokedMethods) + + ", accessedTypes=" + Arrays.toString(accessedTypes) + + ", instantiatedTypes=" + Arrays.toString(instantiatedTypes) + + ", readFields=" + Arrays.toString(readFields) + + ", writtenFields=" + Arrays.toString(writtenFields) + + '}'; + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SimpleInMemoryMethodSummaryProvider.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SimpleInMemoryMethodSummaryProvider.java new file mode 100644 index 000000000000..4656064cd2eb --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/SimpleInMemoryMethodSummaryProvider.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +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.phases.InlineBeforeAnalysis; +import com.oracle.graal.pointsto.util.AnalysisError; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; +import org.graalvm.compiler.core.common.spi.ForeignCallSignature; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.CallTargetNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.extended.ForeignCall; +import org.graalvm.compiler.nodes.java.AccessFieldNode; +import org.graalvm.compiler.nodes.java.InstanceOfNode; +import org.graalvm.compiler.nodes.java.LoadFieldNode; +import org.graalvm.compiler.nodes.java.NewArrayNode; +import org.graalvm.compiler.nodes.java.NewInstanceNode; +import org.graalvm.compiler.nodes.java.NewMultiArrayNode; +import org.graalvm.compiler.nodes.java.StoreFieldNode; +import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; +import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; +import org.graalvm.compiler.replacements.nodes.BinaryMathIntrinsicNode; +import org.graalvm.compiler.replacements.nodes.MacroInvokable; +import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class SimpleInMemoryMethodSummaryProvider implements MethodSummaryProvider { + + protected final AnalysisUniverse universe; + protected final AnalysisMetaAccess metaAccess; + + public SimpleInMemoryMethodSummaryProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess) { + this.universe = universe; + this.metaAccess = metaAccess; + } + + @Override + public MethodSummary getSummary(BigBang bb, AnalysisMethod method) { + AnalysisParsedGraph analysisParsedGraph = method.ensureGraphParsed(bb); + if (analysisParsedGraph.getEncodedGraph() == null) { + System.err.println("Encoded empty for " + method); + List accessedTypes = new ArrayList<>(); + try { + accessedTypes = Arrays.stream(method.getParameters()).map(param -> analysisType(((ResolvedJavaType) param.getType()))).collect(Collectors.toList()); + } catch (UnsupportedOperationException ex) { + ex.printStackTrace(); + } + accessedTypes.add(analysisType((ResolvedJavaType) method.getSignature().getReturnType(null))); + return MethodSummary.accessed(accessedTypes); + } + + StructuredGraph decoded = InlineBeforeAnalysis.decodeGraph(bb, method, analysisParsedGraph); + + if (decoded == null) { + throw AnalysisError.shouldNotReachHere("Failed to decode a graph for " + method.format("%H.%n(%p)")); + } + + // to preserve the graphs for compilation + method.setAnalyzedGraph(decoded); + + return new Instance().createSummaryFromGraph(decoded); + } + + @Override + public MethodSummary getSummary(BigBang bigBang, StructuredGraph graph) { + return new Instance().createSummaryFromGraph(graph); + } + + @SuppressWarnings("unused") + protected void delegateNodeProcessing(Instance instance, Node node) { + } + + private AnalysisType analysisType(ResolvedJavaType type) { + return type instanceof AnalysisType ? ((AnalysisType) type) : universe.lookup(type); + } + + private AnalysisMethod analysisMethod(ResolvedJavaMethod method) { + return method instanceof AnalysisMethod ? ((AnalysisMethod) method) : universe.lookup(method); + } + + private AnalysisField analysisField(ResolvedJavaField field) { + return field instanceof AnalysisField ? ((AnalysisField) field) : universe.lookup(field); + } + + protected class Instance { + public final List accessedTypes = new ArrayList<>(); + public final List instantiatedTypes = new ArrayList<>(); + public final List readFields = new ArrayList<>(); + public final List writtenFields = new ArrayList<>(); + public final List invokedMethods = new ArrayList<>(); + public final List implementationInvokedMethods = new ArrayList<>(); + public final List embeddedConstants = new ArrayList<>(); + public final List foreignCallDescriptors = new ArrayList<>(); + public final List foreignCallSignatures = new ArrayList<>(); + + private MethodSummary createSummaryFromGraph(StructuredGraph graph) { + for (Node n : graph.getNodes()) { + if (n instanceof NewInstanceNode) { + NewInstanceNode node = (NewInstanceNode) n; + instantiatedTypes.add(analysisType(node.instanceClass())); + } else if (n instanceof NewArrayNode) { + NewArrayNode node = (NewArrayNode) n; + instantiatedTypes.add(analysisType(node.elementType()).getArrayClass()); + } else if (n instanceof NewMultiArrayNode) { + NewMultiArrayNode node = (NewMultiArrayNode) n; + ResolvedJavaType type = node.type(); + for (int i = 0; i < node.dimensionCount(); i++) { + instantiatedTypes.add(analysisType(type)); + type = type.getComponentType(); + } + } else if (n instanceof VirtualInstanceNode) { + VirtualInstanceNode node = (VirtualInstanceNode) n; + instantiatedTypes.add(analysisType(node.type())); +// for (ResolvedJavaField field : node.getFields()) { +// readFields.add(analysisField(field)); +// writtenFields.add(analysisField(field)); +// } + } else if (n instanceof VirtualArrayNode) { + VirtualArrayNode node = (VirtualArrayNode) n; + instantiatedTypes.add(analysisType(node.componentType()).getArrayClass()); + } 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; + } + embeddedConstants.add(((JavaConstant) node.getValue())); + } else if (n instanceof InstanceOfNode) { + InstanceOfNode node = (InstanceOfNode) n; + accessedTypes.add(analysisType(node.type().getType())); + } else if (n instanceof AccessFieldNode) { + if (n instanceof LoadFieldNode) { + LoadFieldNode node = (LoadFieldNode) n; + readFields.add(analysisField(node.field())); + } else if (n instanceof StoreFieldNode) { + StoreFieldNode node = (StoreFieldNode) n; + writtenFields.add(analysisField(node.field())); + } else { + throw AnalysisError.shouldNotReachHere("Unhalded AccessFieldNode Type"); + } + } else if (n instanceof Invoke) { + Invoke node = (Invoke) n; + CallTargetNode.InvokeKind kind = node.getInvokeKind(); + AnalysisMethod targetMethod = analysisMethod(node.getTargetMethod()); + if (targetMethod == null) { + continue; + } + if (kind.isDirect()) { + implementationInvokedMethods.add(targetMethod); + } else { + invokedMethods.add(targetMethod); + } + } else if (n instanceof FrameState) { + FrameState node = (FrameState) n; + ResolvedJavaMethod method = node.getMethod(); + if (method != null) { + AnalysisMethod analysisMethod = analysisMethod(method); + accessedTypes.add(analysisMethod.getDeclaringClass()); + } + } else if (n instanceof MacroInvokable) { + MacroInvokable node = (MacroInvokable) n; + AnalysisMethod targetMethod = analysisMethod(node.getTargetMethod()); + if (node.getInvokeKind().isDirect()) { + implementationInvokedMethods.add(targetMethod); + } else { + invokedMethods.add(targetMethod); + } + } else if (n instanceof ForeignCall) { + foreignCallDescriptors.add(((ForeignCall) n).getDescriptor()); + } else if (n instanceof UnaryMathIntrinsicNode) { + foreignCallSignatures.add(((UnaryMathIntrinsicNode) n).getOperation().foreignCallSignature); + } else if (n instanceof BinaryMathIntrinsicNode) { + foreignCallSignatures.add(((BinaryMathIntrinsicNode) n).getOperation().foreignCallSignature); + } + delegateNodeProcessing(this, n); + } + return new MethodSummary(invokedMethods.toArray(new AnalysisMethod[0]), implementationInvokedMethods.toArray(new AnalysisMethod[0]), + accessedTypes.toArray(new AnalysisType[0]), + instantiatedTypes.toArray(new AnalysisType[0]), readFields.toArray(new AnalysisField[0]), writtenFields.toArray(new AnalysisField[0]), + embeddedConstants.toArray(new JavaConstant[0]), foreignCallDescriptors.toArray(new ForeignCallDescriptor[0]), foreignCallSignatures.toArray(new ForeignCallSignature[0])); + } + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/DummyHashingStrategy.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/DummyHashingStrategy.java new file mode 100644 index 000000000000..e151aab5da46 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/DummyHashingStrategy.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.reachability.MethodSummary; +import com.oracle.graal.reachability.SerializableMethodSummary; + +import java.util.Arrays; + +public class DummyHashingStrategy implements HashingStrategy { + @Override + public PersistedSummary prepare(AnalysisMethod method, MethodSummary summary) { + long hash = getHash(method); + return new PersistedSummary(summary, new MethodHash(hash)); + } + + @Override + public boolean isValid(AnalysisMethod method, SerializableMethodSummary summary) { + return summary.hash.getValue() == getHash(method); + } + + private static long getHash(AnalysisMethod method) { + return Arrays.hashCode(method.getCode()); + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/HashingStrategy.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/HashingStrategy.java new file mode 100644 index 000000000000..32fb4bf45587 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/HashingStrategy.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.reachability.MethodSummary; +import com.oracle.graal.reachability.SerializableMethodSummary; + +public interface HashingStrategy { + + PersistedSummary prepare(AnalysisMethod method, MethodSummary summary); + + boolean isValid(AnalysisMethod method, SerializableMethodSummary summary); +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodHash.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodHash.java new file mode 100644 index 000000000000..9d34022e7869 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodHash.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +import java.io.Serializable; + +public final class MethodHash implements Serializable { + private static final long serialVersionUID = 1L; + + private final long value; + + MethodHash(long value) { + this.value = value; + } + + public long getValue() { + return value; + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodSummaryStorage.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodSummaryStorage.java new file mode 100644 index 000000000000..639bc3ef872c --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/MethodSummaryStorage.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +import com.oracle.graal.pointsto.BigBang; +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.reachability.MethodSummary; +import com.oracle.graal.reachability.MethodSummaryProvider; +import com.oracle.graal.reachability.SerializableMethodSummary; +import com.oracle.graal.reachability.SimpleInMemoryMethodSummaryProvider; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.options.OptionValues; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class MethodSummaryStorage implements MethodSummaryProvider { + + public MethodSummaryStorage(ResolutionStrategy resolutionStrategy, SimpleInMemoryMethodSummaryProvider simpleInMemoryMethodSummaryProvider, OptionValues options) { + this.resolutionStrategy = resolutionStrategy; + this.simpleInMemoryMethodSummaryProvider = simpleInMemoryMethodSummaryProvider; + this.options = options; + } + + public static class Options { + @Option(help = "Summary storage file", type = OptionType.User)// + public static final OptionKey SummaryFile = new OptionKey<>(""); + } + + private final Map storage = new ConcurrentHashMap<>(); + + private final HashingStrategy hashingStrategy = new DummyHashingStrategy(); + + private final ResolutionStrategy resolutionStrategy; + private final SimpleInMemoryMethodSummaryProvider simpleInMemoryMethodSummaryProvider; + private final OptionValues options; + + @Override + public MethodSummary getSummary(BigBang bb, AnalysisMethod method) { + PersistedSummary persistedSummary = storage.get(method); + if (persistedSummary != null) { + return persistedSummary.getSummary(); + } + MethodSummary summary = simpleInMemoryMethodSummaryProvider.getSummary(bb, method); + PersistedSummary preparedSummary = hashingStrategy.prepare(method, summary); + storage.put(method, preparedSummary); + return summary; + } + + @Override + public MethodSummary getSummary(BigBang bigBang, StructuredGraph graph) { + return simpleInMemoryMethodSummaryProvider.getSummary(bigBang, graph); + } + + public void loadData() { + String summaryFileName = Options.SummaryFile.getValue(options); + if (summaryFileName.isEmpty()) { + return; + } + try (ObjectInputStream stream = new ObjectInputStream(new FileInputStream(summaryFileName))) { + @SuppressWarnings("unchecked") + Map summaries = (Map) stream.readObject(); + processSummaryFile(summaries); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } + + private void processSummaryFile(Map summaries) { + for (Map.Entry methodEntry : summaries.entrySet()) { + System.out.println("!! " + methodEntry.getKey()); + AnalysisMethod analysisMethod = resolutionStrategy.resolveMethod(methodEntry.getKey()); + if (analysisMethod == null) { + err("Could not resolve method " + methodEntry.getKey()); + continue; + } + try { + if (hashingStrategy.isValid(analysisMethod, methodEntry.getValue())) { + MethodSummary resolvedSummary = resolveSummary(methodEntry.getValue()); + storage.put(analysisMethod, hashingStrategy.prepare(analysisMethod, resolvedSummary)); + } else { + err("Method summary for " + analysisMethod + " is not valid."); + } + } catch (RuntimeException ex) { + err("Cannot resolve summary for " + analysisMethod + ": " + ex.getMessage()); + continue; + } + } + } + + private MethodSummary resolveSummary(SerializableMethodSummary value) { + AnalysisMethod[] invokedMethods = resolveHelper(value.invokedMethods, resolutionStrategy::resolveMethod, AnalysisMethod.class); + AnalysisMethod[] implementationInvokedMethods = resolveHelper(value.implementationInvokedMethods, resolutionStrategy::resolveMethod, AnalysisMethod.class); + AnalysisType[] accessedTypes = resolveHelper(value.accessedTypes, resolutionStrategy::resolveClass, AnalysisType.class); + AnalysisType[] instantiatedTypes = resolveHelper(value.instantiatedTypes, resolutionStrategy::resolveClass, AnalysisType.class); + AnalysisField[] readFields = resolveHelper(value.readFields, resolutionStrategy::resolveField, AnalysisField.class); + AnalysisField[] writtenFields = resolveHelper(value.writtenFields, resolutionStrategy::resolveField, AnalysisField.class); + return new MethodSummary(invokedMethods, implementationInvokedMethods, accessedTypes, instantiatedTypes, readFields, writtenFields); + } + + public Type[] resolveHelper(SerializedType[] inputArray, Function resolver, Class typeTag) { + @SuppressWarnings("unchecked") + Type[] res = (Type[]) Array.newInstance(typeTag, inputArray.length); + for (int i = 0; i < inputArray.length; i++) { + SerializedType elem = inputArray[i]; + Type resolved = resolver.apply(elem); + if (resolved == null) { + throw new RuntimeException("Failed to resolve " + elem); + } + res[i] = resolved; + } + return res; + } + + public void persistData() { + String summaryFileName = Options.SummaryFile.getValue(options); + if (summaryFileName.isEmpty()) { + return; + } + try (ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(summaryFileName))) { + stream.writeObject(serializeStorage()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private Map serializeStorage() { + Map serializableMap = new HashMap<>(); + for (Map.Entry entry : storage.entrySet()) { + if (!canSerialize(entry.getValue())) { + err("Can't serialize a summary"); + return null; + } + SerializableMethodSummary summary = serializeSummary(entry.getValue()); + serializableMap.put(resolutionStrategy.getId(entry.getKey()), summary); + } + return serializableMap; + } + + private SerializableMethodSummary serializeSummary(PersistedSummary persistedSummary) { + MethodSummary summary = persistedSummary.getSummary(); + + SerializableMethodSummary.MethodId[] invokedMethods = Arrays.stream(summary.invokedMethods).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.MethodId[]::new); + SerializableMethodSummary.MethodId[] implInvokedMethods = Arrays.stream(summary.implementationInvokedMethods).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.MethodId[]::new); + SerializableMethodSummary.ClassId[] accessedTypes = Arrays.stream(summary.accessedTypes).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.ClassId[]::new); + SerializableMethodSummary.ClassId[] instantiatedTypes = Arrays.stream(summary.instantiatedTypes).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.ClassId[]::new); + SerializableMethodSummary.FieldId[] readFields = Arrays.stream(summary.readFields).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.FieldId[]::new); + SerializableMethodSummary.FieldId[] writtenFields = Arrays.stream(summary.writtenFields).map(resolutionStrategy::getId).toArray(SerializableMethodSummary.FieldId[]::new); + return new SerializableMethodSummary(persistedSummary.getHash(), invokedMethods, implInvokedMethods, accessedTypes, instantiatedTypes, readFields, writtenFields); + } + + private static boolean canSerialize(PersistedSummary persistedSummary) { + MethodSummary summary = persistedSummary.getSummary(); + // todo implement + return true; + } + + public void err(String msg) { + System.err.println(msg); + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/PersistedSummary.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/PersistedSummary.java new file mode 100644 index 000000000000..f9813096492e --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/PersistedSummary.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +import com.oracle.graal.reachability.MethodSummary; + +public class PersistedSummary { + + private final MethodSummary summary; + private final MethodHash hash; + + public PersistedSummary(MethodSummary summary, MethodHash hash) { + this.summary = summary; + this.hash = hash; + } + + public MethodSummary getSummary() { + return summary; + } + + public MethodHash getHash() { + return hash; + } +} diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/ResolutionStrategy.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/ResolutionStrategy.java new file mode 100644 index 000000000000..512240e50802 --- /dev/null +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/summaries/ResolutionStrategy.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.reachability.summaries; + +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.reachability.SerializableMethodSummary; + +public interface ResolutionStrategy { + + AnalysisType resolveClass(SerializableMethodSummary.ClassId classId); + + AnalysisMethod resolveMethod(AnalysisType clazz, SerializableMethodSummary.MethodId methodId); + + AnalysisMethod resolveMethod(SerializableMethodSummary.MethodId methodId); + + AnalysisField resolveField(AnalysisType clazz, SerializableMethodSummary.FieldId fieldId); + + AnalysisField resolveField(SerializableMethodSummary.FieldId fieldId); + + SerializableMethodSummary.ClassId getId(AnalysisType type); + + SerializableMethodSummary.MethodId getId(AnalysisMethod method); + + SerializableMethodSummary.FieldId getId(AnalysisField type); +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/RuntimeCodeCacheWalker.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/RuntimeCodeCacheWalker.java index 2614fc375a2d..bc0942c65a34 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/RuntimeCodeCacheWalker.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/RuntimeCodeCacheWalker.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.genscavenge; +import com.oracle.svm.core.annotate.Uninterruptible; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -57,6 +58,7 @@ final class RuntimeCodeCacheWalker implements CodeInfoVisitor { } @Override + @Uninterruptible(reason = "Called from uninterruptible code.") @DuplicatedInNativeCode public boolean visitCode(T codeInfo) { if (RuntimeCodeInfoAccess.areAllObjectsOnImageHeap(codeInfo)) { @@ -111,10 +113,12 @@ public boolean visitCode(T codeInfo) { return true; } + @Uninterruptible(reason = "Called from uninterruptible code.") private static boolean isReachable(Object possiblyForwardedObject) { return RuntimeCodeCacheReachabilityAnalyzer.isReachable(Word.objectToUntrackedPointer(possiblyForwardedObject)); } + @Uninterruptible(reason = "Called from uninterruptible code.") private boolean hasWeakReferenceToUnreachableObject(CodeInfo codeInfo) { checkForUnreachableObjectsVisitor.initialize(); RuntimeCodeInfoAccess.walkWeakReferences(codeInfo, checkForUnreachableObjectsVisitor); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java index 593c0307eb04..f32d569e45fd 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java @@ -34,8 +34,10 @@ public interface UContextRegisterDumper extends RegisterDumper { void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations); + @Uninterruptible(reason = "Called from uninterruptible code.") PointerBase getHeapBase(ucontext_t uContext); + @Uninterruptible(reason = "Called from uninterruptible code.") PointerBase getThreadPointer(ucontext_t uContext); PointerBase getSP(ucontext_t uContext); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64LinuxUContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64LinuxUContextRegisterDumper.java index ca6a660c6bb9..bebcf0e24b6d 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64LinuxUContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64LinuxUContextRegisterDumper.java @@ -82,14 +82,14 @@ public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInf } @Override - @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true, calleeMustBe = false) + @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true) public PointerBase getHeapBase(ucontext_t uContext) { GregsPointer gregs = uContext.uc_mcontext_linux_amd64_gregs(); return WordFactory.pointer(gregs.read(GregEnumLinuxAMD64.REG_R14())); } @Override - @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true, calleeMustBe = false) + @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true) public PointerBase getThreadPointer(ucontext_t uContext) { GregsPointer gregs = uContext.uc_mcontext_linux_amd64_gregs(); return WordFactory.pointer(gregs.read(GregEnumLinuxAMD64.REG_R15())); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java index c9774ee878da..36a780f80ae2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java @@ -55,8 +55,10 @@ interface Context extends PointerBase { void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations); + @Uninterruptible(reason = "Called from uninterruptible code.") PointerBase getHeapBase(Context context); + @Uninterruptible(reason = "Called from uninterruptible code.") PointerBase getThreadPointer(Context context); PointerBase getSP(Context context); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 497f13a1d3c0..1cc0dcbacd10 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -279,7 +279,7 @@ public Boolean getValue(OptionValues values) { * Build output options. */ @Option(help = "Use new build output style", type = OptionType.User)// - public static final HostedOptionKey BuildOutputUseNewStyle = new HostedOptionKey<>(true); + public static final HostedOptionKey BuildOutputUseNewStyle = new HostedOptionKey<>(false); @Option(help = "Prefix build output with ':'", type = OptionType.User)// public static final HostedOptionKey BuildOutputPrefix = new HostedOptionKey<>(false); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 0dd70333fd63..b991967ede62 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -226,6 +226,7 @@ private static void invalidateCodeAtSafepoint(CodeInfo info) { } @RestrictHeapAccess(access = Access.NO_ALLOCATION, reason = "Called by the GC") + @Uninterruptible(reason = "Called from uninterruptible code.") public static void invalidateNonStackCodeAtSafepoint(CodeInfo info) { VMOperation.guaranteeGCInProgress("Must only be called during a GC."); RuntimeCodeCache codeCache = getRuntimeCodeCache(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java index 7d0aeb8fdc17..f3cc011bd77c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java @@ -333,6 +333,7 @@ public interface CodeInfoVisitor { * continue, else false. */ @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while visiting code.") + @Uninterruptible(reason = "Called from uninterruptible code.") boolean visitCode(T codeInfo); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java index 895a4e7efe89..87623286597f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java @@ -146,6 +146,7 @@ public static Object[] prepareHeapObjectData(CodeInfoTether tether, String name, return objectFields; } + @Uninterruptible(reason = "Called from uninterruptible code.") public static boolean areAllObjectsOnImageHeap(CodeInfo info) { return cast(info).getAllObjectsAreInImageHeap(); } @@ -153,6 +154,7 @@ public static boolean areAllObjectsOnImageHeap(CodeInfo info) { /** * Walks all strong references in a {@link CodeInfo} object. */ + @Uninterruptible(reason = "Called from uninterruptible code.") public static boolean walkStrongReferences(CodeInfo info, ObjectReferenceVisitor visitor) { return NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor, CodeInfoImpl.FIRST_STRONGLY_REFERENCED_OBJFIELD, CodeInfoImpl.STRONGLY_REFERENCED_OBJFIELD_COUNT); } @@ -161,6 +163,7 @@ public static boolean walkStrongReferences(CodeInfo info, ObjectReferenceVisitor * Walks all weak references in a {@link CodeInfo} object. */ @DuplicatedInNativeCode + @Uninterruptible(reason = "Called from uninterruptible code.") public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor visitor) { CodeInfoImpl impl = cast(info); boolean continueVisiting = true; @@ -181,6 +184,7 @@ public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor v * This method only visits a very specific subset of all the references, so you typically want * to use {@link #walkStrongReferences} and/or {@link #walkWeakReferences} instead. */ + @Uninterruptible(reason = "Called from uninterruptible code.") public static boolean walkObjectFields(CodeInfo info, ObjectReferenceVisitor visitor) { return NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index 776e25bd3f54..e642c8399cf8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -656,7 +656,9 @@ public void lower(ValidateNewInstanceClassNode node, LoweringTool tool) { */ public static DynamicHub ensureMarkedAsInstantiated(DynamicHub hub) { if (!hub.isInstantiated()) { - throw VMError.shouldNotReachHere("Cannot allocate type that is not marked as instantiated: " + hub.getName()); + System.err.println("Cannot allocate type that is not marked as instantiated: " + hub.getName()); +// throw VMError.shouldNotReachHere("Cannot allocate type that is not marked as instantiated: " + +// hub.getName()); } return hub; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java index 747476cecd70..03e155b5abcc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.heap; +import com.oracle.svm.core.annotate.Uninterruptible; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -62,6 +63,7 @@ public RuntimeCodeCacheCleaner() { } @Override + @Uninterruptible(reason = "Called from uninterruptible code.") public boolean visitCode(T codeInfo) { if (RuntimeCodeInfoAccess.areAllObjectsOnImageHeap(codeInfo)) { return true; @@ -79,6 +81,7 @@ public boolean visitCode(T codeInfo) { return true; } + @Uninterruptible(reason = "Called from uninterruptible code.") private static void freeMemory(CodeInfo codeInfo) { boolean removed = RuntimeCodeInfoMemory.singleton().removeDuringGC(codeInfo); assert removed : "must have been present"; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrUnlockedChunkWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrUnlockedChunkWriter.java index e31b42626f88..b49bc178a969 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrUnlockedChunkWriter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrUnlockedChunkWriter.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.jfr; +import com.oracle.svm.core.annotate.Uninterruptible; + /** * An interface that collects all {@link JfrChunkWriter} methods that may be called without holding * a lock. @@ -44,5 +46,6 @@ public interface JfrUnlockedChunkWriter { * It is valid to call this method without locking but be aware that the result will be racy in * that case. */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) boolean hasOpenFile(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 26642bc4400d..0a374dbe949a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -337,7 +337,8 @@ public void registerAsUsed(Class clazz) { } public void registerAsUsed(AnalysisType aType) { - aType.registerAsReachable(); + bb.markTypeReachable(aType); +// aType.registerAsReachable(); } @Override @@ -346,17 +347,20 @@ public void registerAsInHeap(Class clazz) { } public void registerAsInHeap(AnalysisType aType) { - aType.registerAsInHeap(); + bb.markTypeInHeap(aType); +// aType.registerAsInHeap(); } @Override public void registerAsAccessed(Field field) { - getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); + registerAsUsed(getMetaAccess().lookupJavaType(field.getDeclaringClass())); +// getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); registerAsAccessed(getMetaAccess().lookupJavaField(field)); } public void registerAsAccessed(AnalysisField aField) { - aField.registerAsAccessed(); + bb.markFieldAccessed(aField); +// aField.registerAsAccessed(); } public void registerAsRead(Field field) { @@ -365,34 +369,39 @@ public void registerAsRead(Field field) { } public void registerAsRead(AnalysisField aField) { - aField.registerAsRead(null); + bb.markFieldRead(aField); +// aField.registerAsRead(null); } @Override public void registerAsUnsafeAccessed(Field field) { - getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); + registerAsUsed(getMetaAccess().lookupJavaType(field.getDeclaringClass())); +// getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); registerAsUnsafeAccessed(getMetaAccess().lookupJavaField(field)); } public boolean registerAsUnsafeAccessed(AnalysisField aField) { - if (!aField.isUnsafeAccessed()) { - /* Register the field as unsafe accessed. */ - aField.registerAsAccessed(); - aField.registerAsUnsafeAccessed(); - /* Force the update of registered unsafe loads and stores. */ - bb.forceUnsafeUpdate(aField); - return true; - } - return false; + return bb.markFieldUnsafeAccessed(aField); +// if (!field.isUnsafeAccessed()) { +// /* Register the field as unsafe accessed. */ +// field.registerAsAccessed(); +// field.registerAsUnsafeAccessed(bb.getUniverse()); +// /* Force the update of registered unsafe loads and stores. */ +// bb.forceUnsafeUpdate(field); +// return true; +// } + } public void registerAsFrozenUnsafeAccessed(Field field) { - getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); + registerAsUsed(getMetaAccess().lookupJavaType(field.getDeclaringClass())); +// getMetaAccess().lookupJavaType(field.getDeclaringClass()).registerAsReachable(); registerAsFrozenUnsafeAccessed(getMetaAccess().lookupJavaField(field)); } public void registerAsFrozenUnsafeAccessed(AnalysisField aField) { - aField.setUnsafeFrozenTypeState(true); + bb.registerAsFrozenUnsafeAccessed(aField); +// aField.setUnsafeFrozenTypeState(true); registerAsUnsafeAccessed(aField); } @@ -408,6 +417,14 @@ public void registerAsUnsafeAccessed(AnalysisField aField, UnsafePartitionKind p /* Force the update of registered unsafe loads and stores. */ bb.forceUnsafeUpdate(aField); } + bb.registerAsUnsafeAccessed(aField, partitionKind); +// if (!aField.isUnsafeAccessed()) { +// /* Register the field as unsafe accessed. */ +// aField.registerAsAccessed(); +// aField.registerAsUnsafeAccessed(bb.getUniverse(), partitionKind); +// /* Force the update of registered unsafe loads and stores. */ +// bb.forceUnsafeUpdate(aField); +// } } public void registerAsInvoked(Executable method) { @@ -415,7 +432,9 @@ public void registerAsInvoked(Executable method) { } public void registerAsInvoked(AnalysisMethod aMethod) { - bb.addRootMethod(aMethod).registerAsImplementationInvoked(); +// bb.addRootMethod(aMethod).registerAsImplementationInvoked(null); + bb.addRootMethod(aMethod); + bb.markMethodImplementationInvoked(aMethod, null); } public void registerAsCompiled(Executable method) { @@ -428,6 +447,7 @@ public void registerAsCompiled(AnalysisMethod aMethod) { } public void registerUnsafeFieldsRecomputed(Class clazz) { + // todo intercept this as well? getMetaAccess().lookupJavaType(clazz).registerUnsafeFieldsRecomputed(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index 9ecdd04255b7..5266c3f55a45 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -31,6 +31,13 @@ import java.util.Set; import java.util.concurrent.ForkJoinPool; +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.results.DummyResultsBuilder; +import com.oracle.graal.reachability.MethodSummaryProvider; +import com.oracle.graal.reachability.SimpleInMemoryMethodSummaryProvider; +import com.oracle.svm.hosted.analysis.Inflation; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.debug.DebugContext; @@ -39,7 +46,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; -import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.MethodTypeFlow; import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; @@ -53,8 +59,6 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.monitor.MultiThreadedMonitorSupport; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.analysis.Inflation; import com.oracle.svm.hosted.analysis.flow.SVMMethodTypeFlowBuilder; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.code.CompileQueue; @@ -151,6 +155,10 @@ public MethodTypeFlowBuilder createMethodTypeFlowBuilder(PointsToAnalysis bb, St return new SVMMethodTypeFlowBuilder(bb, graph); } + public MethodSummaryProvider createMethodSummaryProvider(AnalysisUniverse universe, AnalysisMetaAccess aMetaAccess) { + return new SimpleInMemoryMethodSummaryProvider(universe, aMetaAccess); + } + public void findAllFieldsForLayout(HostedUniverse universe, @SuppressWarnings("unused") HostedMetaAccess metaAccess, @SuppressWarnings("unused") Map universeFields, ArrayList rawFields, @@ -183,7 +191,8 @@ public AbstractAnalysisResultsBuilder createStaticAnalysisResultsBuilder(Inflati } } else { /*- A custom result builder for Reachability analysis will probably have to be created */ - throw VMError.shouldNotReachHere("Unsupported analysis type: " + bb.getClass()); + // todo better profiles? + return new DummyResultsBuilder(bb, universe); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 2a1146463534..a0a8340e1a8f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -30,7 +30,9 @@ import static org.graalvm.compiler.hotspot.JVMCIVersionCheck.JVMCI8_RELEASES_URL; import static org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.registerInvocationPlugins; +import java.io.FileWriter; import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.Reference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -46,6 +48,7 @@ import java.util.Comparator; import java.util.EnumMap; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -54,8 +57,18 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BooleanSupplier; +import java.util.function.Consumer; import java.util.stream.Collectors; +import com.oracle.graal.pointsto.PointsToAnalysis; +import com.oracle.graal.pointsto.meta.PointsToAnalysisFactory; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +import com.oracle.graal.pointsto.reports.ReportUtils; +import com.oracle.graal.reachability.MethodSummary; +import com.oracle.graal.reachability.SimpleInMemoryMethodSummaryProvider; +import com.oracle.graal.reachability.summaries.MethodSummaryStorage; +import com.oracle.svm.core.code.ImageCodeInfo; +import com.oracle.svm.hosted.analysis.NativeImageReachabilityAnalysis; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.Fold; @@ -316,6 +329,8 @@ public class NativeImageGenerator { private final Map> buildArtifacts = new EnumMap<>(ArtifactType.class); + private MethodSummaryStorage methodSummaryStorage; + public NativeImageGenerator(ImageClassLoader loader, HostedOptionProvider optionProvider, Pair mainEntryPoint, ProgressReporter reporter) { this.loader = loader; this.mainEntryPoint = mainEntryPoint; @@ -544,6 +559,10 @@ private void doRun(Map entryPoints, return; } + dump(imageName, aUniverse); + +// dumpAnalysisStats(); + NativeImageHeap heap; HostedMetaAccess hMetaAccess; SharedRuntimeConfigurationBuilder runtime; @@ -615,6 +634,9 @@ private void doRun(Map entryPoints, compileQueue = HostedConfiguration.instance().createCompileQueue(debug, featureHandler, hUniverse, runtime, DeoptTester.enabled(), bb.getProviders().getSnippetReflection(), compilationExecutor); compileQueue.finish(debug); + System.out.println("Number of parsed methods: " + CompileQueue.parsedMethods.size()); + System.out.println("Number of compiled methods: " + CompileQueue.compiledMethods.size()); +// dumpParseTree(compileQueue); /* release memory taken by graphs for the image writing */ hUniverse.getMethods().forEach(HostedMethod::clear); @@ -690,9 +712,178 @@ private void doRun(Map entryPoints, if (SubstrateOptions.BuildOutputBreakdowns.getValue()) { ProgressReporter.singleton().printBreakdowns(compileQueue.getCompilationTasks(), image.getHeap().getObjects()); } + + if (methodSummaryStorage != null) { + methodSummaryStorage.persistData(); + } + } + } + + private void dump(String bigbang, AnalysisUniverse aUniverse) { + long types = aUniverse.getTypes().stream().filter(AnalysisType::isReachable).count(); + long instantiatedTypes = aUniverse.getTypes().stream().filter(AnalysisType::isInstantiated).count(); + long invokedMethods = aUniverse.getMethods().stream().filter(AnalysisMethod::isInvoked).count(); + long implInvokedMethods = aUniverse.getMethods().stream().filter(AnalysisMethod::isImplementationInvoked).count(); + System.out.println("!!!!!" + bigbang + " types:" + instantiatedTypes + " / " + types + ", methods: " + invokedMethods + " / " + implInvokedMethods); + } + + private static final String DUMP_FOLDER = "/Users/dkozak/tmp/hello-dir/stats/"; + private static final String METHOD_FORMAT = "%H.%n(%P)"; + + private void dumpAnalysisStats() { + AnalysisUniverse universe = getBigbang().getUniverse(); + List reachableTypes = universe.getTypes().stream().filter(AnalysisType::isReachable).map(AnalysisType::getName).sorted().collect(Collectors.toList()); + List invokedMethods = universe.getMethods().stream().filter(AnalysisMethod::isInvoked).map(it -> it.format("%H.%n(%P)")).sorted().collect(Collectors.toList()); + List implInvokedMethods = universe.getMethods().stream().filter(AnalysisMethod::isImplementationInvoked).map(it -> it.format("%H.%n(%P)")).sorted().collect(Collectors.toList()); + System.out.println("Reachable types " + reachableTypes.size()); + System.out.println("Invoked methods " + invokedMethods.size()); + System.out.println("Implementation invoked methods " + implInvokedMethods.size()); + + String prefix = analysisPrefix(); + + List, String>> pairs = Arrays.asList(Pair.create(reachableTypes, "types"), Pair.create(invokedMethods, "invokedMethods"), + Pair.create(implInvokedMethods, "implInvokedMethods")); + + for (Pair, String> pair : pairs) { + try (FileWriter writer = new FileWriter(DUMP_FOLDER + prefix + pair.getRight())) { + for (String line : pair.getLeft()) { + writer.write(line); + writer.write('\n'); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + try (FileWriter writer = new FileWriter(DUMP_FOLDER + prefix + "invokeStats")) { + List implInvoked = universe.getMethods().stream().filter(AnalysisMethod::isImplementationInvoked).collect(Collectors.toList()); + for (AnalysisMethod method : implInvoked) { + writer.write(method.format(METHOD_FORMAT)); + writer.write(','); + List callees = getCallees(method); + writer.write(Integer.toString(callees.size())); + writer.write('\n'); + } + } catch (IOException e) { + e.printStackTrace(); } } + private static String analysisPrefix() { + return NativeImageOptions.UseExperimentalReachabilityAnalysis.getValue() ? "reachability_" : "points-to_"; + } + + @SuppressWarnings("unused") + private static void dumpParseTree(CompileQueue compileQueue) { + String prefix = analysisPrefix(); + List>> entries = CompileQueue.parseTree.entrySet().stream().sorted(Comparator.comparing(entry -> entry.getKey().format(METHOD_FORMAT))) + .collect(Collectors.toList()); + try (FileWriter writer = new FileWriter(DUMP_FOLDER + prefix + "parse_tree")) { + for (Map.Entry> entry : entries) { + writer.write(entry.getKey().format(METHOD_FORMAT)); + writer.write(','); + writer.write(Integer.toString(entry.getValue().size())); + writer.write('\n'); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private List getCallees(AnalysisMethod method) { + if (bb instanceof NativeImageReachabilityAnalysis) { + return getCalleesR(((NativeImageReachabilityAnalysis) bb), method); + } else if (bb instanceof NativeImagePointsToAnalysis) { + return getCalleesP((NativeImagePointsToAnalysis) bb, method); + } + throw VMError.shouldNotReachHere(); + } + + @SuppressWarnings("unused") + private static List getCalleesP(NativeImagePointsToAnalysis bb, AnalysisMethod method) { + return ((PointsToAnalysisMethod) method).getTypeFlow().getInvokes().stream().flatMap(it -> it.getCallees().stream()).collect(Collectors.toList()); + } + + private static List getCalleesR(NativeImageReachabilityAnalysis bb, AnalysisMethod method) { + MethodSummary summary = bb.summaries.get(method); + if (summary == null) { +// System.err.println("Don't have a summary for " + method); + return Collections.emptyList(); + } + List callees = new ArrayList<>(); + Collections.addAll(callees, summary.implementationInvokedMethods); + for (AnalysisMethod invokedMethod : summary.invokedMethods) { + AnalysisType clazz = invokedMethod.getDeclaringClass(); + for (AnalysisType subtype : clazz.getInstantiatedSubtypes()) { + AnalysisMethod resolved = subtype.resolveConcreteMethod(invokedMethod, clazz); + if (resolved != null) { + callees.add(resolved); + } + } + } + return callees; + } + + @SuppressWarnings("unused") + private static String dumpChain(AnalysisMethod method) { + ArrayList methods = new ArrayList<>(); + Set seen = new HashSet<>(); + + boolean success = dfs(method, methods, seen); + StringBuilder builder = new StringBuilder(); + if (!success) { + builder.append("Path is incomplete :X \n"); + } + return serializePath(methods, builder); + } + + private static String serializePath(ArrayList methods, StringBuilder builder) { + for (int i = methods.size() - 1; i >= 0; i--) { + builder.append(methods.get(i)); + if (i != 0) { + builder.append("->"); + } + } + return builder.toString(); + } + + private static boolean dfs(AnalysisMethod method, ArrayList methods, Set seen) { + String name = method.format("%H.%n(%P)"); + if (!seen.add(name)) { + return false; + } + methods.add(name); + if (method.isRootMethod()) { + return true; + } + Set callers = method.getCallers(); + for (AnalysisMethod caller : callers) { + if (dfs(caller, methods, seen)) { + return true; + } + } + if (callers.isEmpty()) { + System.out.println(serializePath(methods, new StringBuilder())); + } + methods.remove(methods.size() - 1); + return false; + } + + void reportBuildArtifacts(String imageName) { + Path buildDir = generatedFiles(HostedOptionValues.singleton()); + Consumer writerConsumer = writer -> buildArtifacts.forEach((artifactType, paths) -> { + writer.println("[" + artifactType + "]"); + if (artifactType == BuildArtifacts.ArtifactType.JDK_LIB_SHIM) { + writer.println("# Note that shim JDK libraries depend on this"); + writer.println("# particular native image (including its name)"); + writer.println("# and therefore cannot be used with others."); + } + paths.stream().map(Path::toAbsolutePath).map(buildDir::relativize).forEach(writer::println); + writer.println(); + }); + ReportUtils.report("build artifacts", buildDir.resolve(imageName + ".build_artifacts.txt"), writerConsumer); + } + @SuppressWarnings("try") private boolean runPointsToAnalysis(String imageName, OptionValues options, DebugContext debug) { try (Indent ignored = debug.logAndIndent("run analysis")) { @@ -970,6 +1161,8 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInHeap(); bb.addRootClass(PointerBase[].class, false, false).registerAsInHeap(); + bb.addRootField(ImageCodeInfo.class, "codeStart"); + try { bb.addRootMethod(SubstrateArraycopySnippets.class.getDeclaredMethod("doArraycopy", Object.class, int.class, Object.class, int.class, int.class)); bb.addRootMethod(Object.class.getDeclaredMethod("getClass")); @@ -1002,13 +1195,24 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature bb.getAnnotationSubstitutionProcessor(), classInitializationPlugin, bb.getHostVM().getClassInitializationSupport(), ConfigurationValues.getTarget()); registerReplacements(debug, featureHandler, null, aProviders, true, initForeignCalls); - for (StructuredGraph graph : aReplacements.getSnippetGraphs(GraalOptions.TrackNodeSourcePosition.getValue(options), options)) { - HostedConfiguration.instance().createMethodTypeFlowBuilder(((NativeImagePointsToAnalysis) bb), graph).registerUsedElements(false); + Collection snippetGraphs = aReplacements.getSnippetGraphs(GraalOptions.TrackNodeSourcePosition.getValue(options), options); + // todo refactor + if (bb instanceof NativeImagePointsToAnalysis) { + for (StructuredGraph graph : snippetGraphs) { + HostedConfiguration.instance().createMethodTypeFlowBuilder(((NativeImagePointsToAnalysis) bb), graph).registerUsedElements(false); + } + } else if (bb instanceof NativeImageReachabilityAnalysis) { + NativeImageReachabilityAnalysis reachabilityAnalysis = (NativeImageReachabilityAnalysis) bb; + for (StructuredGraph graph : snippetGraphs) { + reachabilityAnalysis.processGraph(graph); + } + } else { + throw VMError.shouldNotReachHere("Unknown analysis type - please specify how to handle snippets"); } } } - public static Inflation createBigBang(OptionValues options, TargetDescription target, AnalysisUniverse aUniverse, ForkJoinPool analysisExecutor, + public Inflation createBigBang(OptionValues options, TargetDescription target, AnalysisUniverse aUniverse, ForkJoinPool analysisExecutor, Runnable heartbeatCallback, AnalysisMetaAccess aMetaAccess, AnalysisConstantReflectionProvider aConstantReflection, WordTypes aWordTypes, SnippetReflectionProvider aSnippetReflection, AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ForeignCallsProvider aForeignCalls, ClassInitializationSupport classInitializationSupport, Providers originalProviders) { @@ -1032,6 +1236,14 @@ public static Inflation createBigBang(OptionValues options, TargetDescription ta aProviders = new HostedProviders(aMetaAccess, null, aConstantReflection, aConstantFieldProvider, aForeignCalls, aLoweringProvider, aReplacments, aStampProvider, aSnippetReflection, aWordTypes, platformConfig, aMetaAccessExtensionProvider, originalProviders.getLoopsDataProvider()); + if (NativeImageOptions.UseExperimentalReachabilityAnalysis.getValue()) { + SimpleInMemoryMethodSummaryProvider simpleInMemoryMethodSummaryProvider = ((SimpleInMemoryMethodSummaryProvider) HostedConfiguration.instance().createMethodSummaryProvider(aUniverse, + aMetaAccess)); + methodSummaryStorage = new MethodSummaryStorage(new ResolutionStrategyImpl(loader, aMetaAccess), simpleInMemoryMethodSummaryProvider, options); + methodSummaryStorage.loadData(); + return new NativeImageReachabilityAnalysis(options, aUniverse, aProviders, annotationSubstitutionProcessor, analysisExecutor, heartbeatCallback, + methodSummaryStorage); + } return new NativeImagePointsToAnalysis(options, aUniverse, aProviders, annotationSubstitutionProcessor, analysisExecutor, heartbeatCallback, new SubstrateUnsupportedFeatures()); } @@ -1518,7 +1730,8 @@ private void checkName(String name, AnalysisMethod method) { * they are JDK internal types. */ String lname = name.toLowerCase(); - if (lname.contains("hosted")) { + // todo resolve (d-kozak) + if (lname.contains("hosted") && !lname.startsWith("com.oracle.svm.core.graal.snippets.deopthostedsnippets")) { bb.getUnsupportedFeatures().addMessage(name, method, "Hosted element used at run time: " + name); } else if (SubstrateUtil.isBuildingLibgraal() && (!name.startsWith("jdk.internal")) && (lname.contains("hotspot"))) { bb.getUnsupportedFeatures().addMessage(name, method, "HotSpot element used at run time: " + name); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index 50a255d37c4a..beef95d881f8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -59,6 +59,9 @@ public class NativeImageOptions { @Option(help = "Overrides CPUFeatures and uses the native architecture, i.e., the architecture of a machine that builds an image. NativeArchitecture takes precedence over CPUFeatures", type = User)// public static final HostedOptionKey NativeArchitecture = new HostedOptionKey<>(false); + @Option(help = "Print information about classes, methods, and fields that are present in the native image")// + public static final HostedOptionKey UseExperimentalReachabilityAnalysis = new HostedOptionKey<>(true); + @Option(help = "Print information about classes, methods, and fields that are present in the native image")// public static final HostedOptionKey PrintUniverse = new HostedOptionKey<>(false); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResolutionStrategyImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResolutionStrategyImpl.java new file mode 100644 index 000000000000..c8a64f8d2344 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResolutionStrategyImpl.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted; + +import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.reachability.SerializableMethodSummary; +import com.oracle.graal.reachability.summaries.ResolutionStrategy; + +public class ResolutionStrategyImpl implements ResolutionStrategy { + private final ImageClassLoader loader; + private final AnalysisMetaAccess metaAccess; + + public ResolutionStrategyImpl(ImageClassLoader loader, AnalysisMetaAccess metaAccess) { + this.loader = loader; + this.metaAccess = metaAccess; + } + + @Override + public AnalysisType resolveClass(SerializableMethodSummary.ClassId classId) { + try { + Class clazz = loader.forName(classId.className); + return metaAccess.lookupJavaType(clazz); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } catch (UnsupportedFeatureException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public AnalysisMethod resolveMethod(AnalysisType clazz, SerializableMethodSummary.MethodId methodId) { + for (AnalysisMethod method : clazz.getDeclaredMethods()) { + if (getId(method).equals(methodId)) { + return method; + } + } + for (AnalysisMethod ctor : clazz.getDeclaredConstructors()) { + if (getId(ctor).equals(methodId)) { + return ctor; + } + } + return null; + } + + @Override + public AnalysisMethod resolveMethod(SerializableMethodSummary.MethodId methodId) { + AnalysisType clazz = resolveClass(methodId.classId); + if (clazz == null) { + return null; + } + return resolveMethod(clazz, methodId); + } + + @Override + public AnalysisField resolveField(AnalysisType clazz, SerializableMethodSummary.FieldId fieldId) { + for (AnalysisField instanceField : clazz.getInstanceFields(false)) { + if (getId(instanceField).equals(fieldId)) { + return instanceField; + } + } + for (AnalysisField staticField : clazz.getStaticFields()) { + if (getId(staticField).equals(fieldId)) { + return staticField; + } + } + return null; + } + + @Override + public AnalysisField resolveField(SerializableMethodSummary.FieldId fieldId) { + AnalysisType clazz = resolveClass(fieldId.classId); + if (clazz == null) { + return null; + } + return resolveField(clazz, fieldId); + } + + @Override + public SerializableMethodSummary.ClassId getId(AnalysisType type) { + return new SerializableMethodSummary.ClassId(type.getJavaClass().getName()); + } + + @Override + public SerializableMethodSummary.MethodId getId(AnalysisMethod method) { + return new SerializableMethodSummary.MethodId(getId(method.getDeclaringClass()), method.getQualifiedName()); + } + + @Override + public SerializableMethodSummary.FieldId getId(AnalysisField field) { + return new SerializableMethodSummary.FieldId(getId(field.getDeclaringClass()), field.getName()); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index a5b0c98f8537..baacc6302df5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -98,7 +98,11 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie throw VMError.shouldNotReachHere("Cannot read instance field of a class that is initialized at run time: " + field.format("%H.%n")); } } else { - value = universe.lookup(ReadableJavaField.readFieldValue(suppliedMetaAccess, originalConstantReflection, field.wrapped, universe.toHosted(receiver))); + ResolvedJavaField current = field; + while (current instanceof AnalysisField) { + current = ((AnalysisField) current).wrapped; + } + value = universe.lookup(ReadableJavaField.readFieldValue(suppliedMetaAccess, originalConstantReflection, current, universe.toHosted(receiver))); } return interceptValue(field, value); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/Inflation.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/Inflation.java index a0120b86dd3e..629da27f5478 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/Inflation.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/Inflation.java @@ -44,6 +44,7 @@ public interface Inflation extends BigBang { AnnotationSubstitutionProcessor getAnnotationSubstitutionProcessor(); + @Override SubstrateReplacements getReplacements(); @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysis.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysis.java new file mode 100644 index 000000000000..be6adac48e57 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysis.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.analysis; + +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.reachability.MethodSummaryProvider; +import com.oracle.graal.reachability.ReachabilityAnalysis; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.UnknownPrimitiveField; +import com.oracle.svm.core.graal.meta.SubstrateReplacements; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.word.WordBase; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; + +import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere; + +public class NativeImageReachabilityAnalysis extends ReachabilityAnalysis implements Inflation { + + private Set handledUnknownValueFields = ConcurrentHashMap.newKeySet(); + private final AnnotationSubstitutionProcessor annotationSubstitutionProcessor; + private final DynamicHubInitializer dynamicHubInitializer; + private final boolean strengthenGraalGraphs; + + public NativeImageReachabilityAnalysis(OptionValues options, AnalysisUniverse universe, HostedProviders providers, AnnotationSubstitutionProcessor annotationSubstitutionProcessor, + ForkJoinPool executor, + Runnable heartbeatCallback, MethodSummaryProvider methodSummaryProvider) { + super(options, universe, providers, universe.hostVM(), executor, heartbeatCallback, new SubstrateUnsupportedFeatures(), methodSummaryProvider); + this.annotationSubstitutionProcessor = annotationSubstitutionProcessor; + this.strengthenGraalGraphs = SubstrateOptions.parseOnce(); + this.dynamicHubInitializer = new DynamicHubInitializer(metaAccess, unsupportedFeatures, providers.getConstantReflection()); + } + + @Override + public boolean strengthenGraalGraphs() { + return strengthenGraalGraphs; + } + + @Override + public AnnotationSubstitutionProcessor getAnnotationSubstitutionProcessor() { + return annotationSubstitutionProcessor; + } + + @Override + protected void checkObjectGraph(ObjectScanner objectScanner) { + universe.getFields().forEach(this::handleUnknownValueField); + universe.getTypes().stream().filter(AnalysisType::isReachable).forEach(dynamicHubInitializer::initializeMetaData); + + /* Scan hubs of all types that end up in the native image. */ + universe.getTypes().stream().filter(AnalysisType::isReachable).forEach(type -> scanHub(objectScanner, type)); + } + + @Override + public void cleanupAfterAnalysis() { + super.cleanupAfterAnalysis(); + handledUnknownValueFields = null; + } + + @Override + public SubstrateReplacements getReplacements() { + return (SubstrateReplacements) super.getReplacements(); + } + + @Override + public SVMHost getHostVM() { + return (SVMHost) hostVM; + } + + @Override + public void checkUserLimitations() { + // todo + } + + private void scanHub(ObjectScanner objectScanner, AnalysisType type) { + SVMHost svmHost = (SVMHost) hostVM; + JavaConstant hubConstant = SubstrateObjectConstant.forObject(svmHost.dynamicHub(type)); + objectScanner.scanConstant(hubConstant, ObjectScanner.OtherReason.HUB); + } + + private void handleUnknownValueField(AnalysisField field) { + if (handledUnknownValueFields.contains(field)) { + return; + } + if (!field.isAccessed()) { + /* + * Field is not reachable yet, so do no process it. In particular, we must not register + * types listed in the @UnknownObjectField annotation as allocated when the field is not + * yet reachable + */ + return; + } + + UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class); + UnknownPrimitiveField unknownPrimitiveField = field.getAnnotation(UnknownPrimitiveField.class); + if (unknownObjectField != null) { + assert !Modifier.isFinal(field.getModifiers()) : "@UnknownObjectField annotated field " + field.format("%H.%n") + " cannot be final"; + assert field.getJavaKind() == JavaKind.Object; + + field.setCanBeNull(unknownObjectField.canBeNull()); + + List aAnnotationTypes = extractAnnotationTypes(field, unknownObjectField); + + for (AnalysisType type : aAnnotationTypes) { + type.registerAsAllocated(null); + } + + /* + * Use the annotation types, instead of the declared type, in the UnknownObjectField + * annotated fields initialization. + */ + handleUnknownObjectField(field, aAnnotationTypes.toArray(new AnalysisType[0])); + + } else if (unknownPrimitiveField != null) { + assert !Modifier.isFinal(field.getModifiers()) : "@UnknownPrimitiveField annotated field " + field.format("%H.%n") + " cannot be final"; + /* + * Register a primitive field as containing unknown values(s), i.e., is usually written + * only in hosted code. + */ + + field.registerAsWritten(null); + } + + handledUnknownValueFields.add(field); + } + + private List extractAnnotationTypes(AnalysisField field, UnknownObjectField unknownObjectField) { + List> annotationTypes = new ArrayList<>(Arrays.asList(unknownObjectField.types())); + for (String annotationTypeName : unknownObjectField.fullyQualifiedTypes()) { + try { + Class annotationType = Class.forName(annotationTypeName); + annotationTypes.add(annotationType); + } catch (ClassNotFoundException e) { + throw shouldNotReachHere("Annotation type not found " + annotationTypeName); + } + } + + List aAnnotationTypes = new ArrayList<>(); + AnalysisType declaredType = field.getType(); + + for (Class annotationType : annotationTypes) { + AnalysisType aAnnotationType = metaAccess.lookupJavaType(annotationType); + + assert !WordBase.class.isAssignableFrom(annotationType) : "Annotation type must not be a subtype of WordBase: field: " + field + " | declared type: " + declaredType + + " | annotation type: " + annotationType; + assert declaredType.isAssignableFrom(aAnnotationType) : "Annotation type must be a subtype of the declared type: field: " + field + " | declared type: " + declaredType + + " | annotation type: " + annotationType; + assert aAnnotationType.isArray() || (aAnnotationType.isInstanceClass() && !Modifier.isAbstract(aAnnotationType.getModifiers())) : "Annotation type failure: field: " + field + + " | annotation type " + aAnnotationType; + + aAnnotationTypes.add(aAnnotationType); + } + return aAnnotationTypes; + } + + /** + * Register a field as containing unknown object(s), i.e., is usually written only in hosted + * code. It can have multiple declared types provided via annotation. + */ + private void handleUnknownObjectField(AnalysisField aField, AnalysisType... declaredTypes) { + assert aField.getJavaKind() == JavaKind.Object; + + aField.registerAsWritten(null); + + /* Link the field with all declared types. */ + for (AnalysisType fieldDeclaredType : declaredTypes) { + markTypeReachable(fieldDeclaredType); + } + } + +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/TypeInitializerGraph.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/TypeInitializerGraph.java index ea361d318812..bb8bc9624333 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/TypeInitializerGraph.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/TypeInitializerGraph.java @@ -118,7 +118,9 @@ private Safety initialTypeInitializerSafety(AnalysisType t) { } boolean isUnsafe(AnalysisType type) { - return types.get(type) == Safety.UNSAFE; + // todo fix + return true; + // return types.get(type) == Safety.UNSAFE; } public void setUnsafe(AnalysisType t) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 26a7220db38c..b5e7d66ac62f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; @@ -36,11 +37,14 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; +import com.oracle.svm.hosted.phases.StrengthenStampsPhase; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -168,7 +172,6 @@ import com.oracle.svm.hosted.phases.HostedGraphBuilderPhase; import com.oracle.svm.hosted.phases.ImageBuildStatisticsCounterPhase; import com.oracle.svm.hosted.phases.ImplicitAssertionsPhase; -import com.oracle.svm.hosted.phases.StrengthenStampsPhase; import com.oracle.svm.hosted.substitute.DeletedMethod; import com.oracle.svm.util.ImageBuildStatistics; @@ -377,7 +380,6 @@ public CompileQueue(DebugContext debug, FeatureHandler featureHandler, HostedUni this.executor = new CompletionExecutor(universe.getBigBang(), executorService, universe.getBigBang().getHeartbeatCallback()); this.featureHandler = featureHandler; this.snippetReflection = snippetReflection; - callForReplacements(debug, runtimeConfig); } @@ -397,11 +399,12 @@ public void finish(DebugContext debug) { try (ProgressReporter.ReporterClosable ac = reporter.printParsing(new Timer(imageName, "(parse)"))) { parseAll(); } + // todo re-enable // Checking @Uninterruptible annotations does not take long enough to justify a timer. - new UninterruptibleAnnotationChecker(universe.getMethods()).check(); +// new UninterruptibleAnnotationChecker(universe.getMethods()).check(); // Checking @RestrictHeapAccess annotations does not take long enough to justify a // timer. - RestrictHeapAccessAnnotationChecker.check(debug, universe, universe.getMethods()); +// RestrictHeapAccessAnnotationChecker.check(debug, universe, universe.getMethods()); /* * The graph in the analysis universe is no longer necessary. This clears the graph for @@ -456,7 +459,9 @@ protected PhaseSuite afterParseCanonicalization() { phaseSuite.appendPhase(new DeadStoreRemovalPhase()); phaseSuite.appendPhase(new DevirtualizeCallsPhase()); phaseSuite.appendPhase(CanonicalizerPhase.create()); - phaseSuite.appendPhase(new StrengthenStampsPhase()); + if (!NativeImageOptions.UseExperimentalReachabilityAnalysis.getValue()) { + phaseSuite.appendPhase(new StrengthenStampsPhase()); + } phaseSuite.appendPhase(CanonicalizerPhase.create()); phaseSuite.appendPhase(new OptimizeExceptionPathsPhase()); if (ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(universe.hostVM().options())) { @@ -553,14 +558,19 @@ protected void parseAll() throws InterruptedException { * parsed methods. */ private void parseAheadOfTimeCompiledMethods() { - universe.getMethods().stream() + List entryPoints = universe.getMethods().stream() .filter(method -> method.isEntryPoint() || CompilationInfoSupport.singleton().isForcedCompilation(method)) - .forEach(method -> ensureParsed(method, null, new EntryPointReason())); + .collect(Collectors.toList()); + System.out.println("Entry points: " + entryPoints.size()); + entryPoints.forEach(method -> ensureParsed(method, null, new EntryPointReason())); SubstrateForeignCallsProvider foreignCallsProvider = (SubstrateForeignCallsProvider) runtimeConfig.getProviders().getForeignCalls(); - foreignCallsProvider.getForeignCalls().values().stream() + List foreignCallEntryPoints = foreignCallsProvider.getForeignCalls().values().stream() .map(linkage -> (HostedMethod) linkage.getDescriptor().findMethod(runtimeConfig.getProviders().getMetaAccess())) .filter(method -> method.wrapped.isRootMethod()) + .collect(Collectors.toList()); + System.out.println("Foreign call entry points: " + foreignCallEntryPoints.size()); + foreignCallEntryPoints .forEach(method -> ensureParsed(method, null, new EntryPointReason())); } @@ -789,14 +799,20 @@ private StructuredGraph transplantGraph(DebugContext debug, HostedMethod hMethod AnalysisMethod aMethod = hMethod.getWrapped(); StructuredGraph aGraph = aMethod.getAnalyzedGraph(); if (aGraph == null) { - throw VMError.shouldNotReachHere("Method not parsed during static analysis: " + aMethod.format("%r %H.%n(%p)") + ". Reachable from: " + reason); + // todo (d-kozak) how to handle this properly? (spring boot case, where method.getCode() + // is null) + HostedProviders providers = universe.getBigBang().getProviders(); + String msg = "Method not parsed during static analysis: " + aMethod.format("%r %H.%n(%p)") + ". Reachable from: " + reason; + System.err.println(msg); + aGraph = DeletedMethod.buildGraph(debug, hMethod, providers, msg); +// throw VMError.shouldNotReachHere(msg); } /* * The graph in the analysis universe is no longer necessary once it is transplanted into * the hosted universe. */ - aMethod.setAnalyzedGraph(null); + // aMethod.setAnalyzedGraph(null); OptionValues options = getCustomizedOptions(debug); /* @@ -1018,6 +1034,8 @@ private void transplantVirtualObjectState(VirtualObjectNode virtualObject, List< private final boolean parseOnce = SubstrateOptions.parseOnce(); + public static final Set parsedMethods = ConcurrentHashMap.newKeySet(); + @SuppressWarnings("try") private void defaultParseFunction(DebugContext debug, HostedMethod method, CompileReason reason, RuntimeConfiguration config) { if (method.getAnnotation(NodeIntrinsic.class) != null) { @@ -1026,6 +1044,12 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi ". Make sure you have used Graal annotation processors on the parent-project of the method's declaring class."); } + if (method.getQualifiedName().startsWith("org.springframework.transaction.reactive.TransactionalOperator.create(")) { + // todo(d-kozak) get rid of this + System.err.println("!!!!!! Skipping org.springframework.transaction.reactive.TransactionalOperator.create("); + return; + } + HostedProviders providers = (HostedProviders) config.lookupBackend(method).getProviders(); boolean needParsing = false; @@ -1053,6 +1077,7 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi graph = new StructuredGraph.Builder(getCustomizedOptions(debug), debug).method(method).build(); } } + method.compilationInfo.graph = graph; try (DebugContext.Scope s = debug.scope("Parsing", graph, method, this)) { try { @@ -1091,6 +1116,8 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi } } + parsedMethods.add(method); + } catch (Throwable ex) { GraalError error = ex instanceof GraalError ? (GraalError) ex : new GraalError(ex); error.addContext("method: " + method.format("%r %H.%n(%p)")); @@ -1102,9 +1129,18 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi } } + public static final Map> parseTree = new ConcurrentHashMap<>(); + + private static List getEntry(HostedMethod method) { + return parseTree.computeIfAbsent(method, key -> Collections.synchronizedList(new ArrayList<>())); + } + private void ensureParsed(HostedMethod method, CompileReason reason, CallTargetNode targetNode, HostedMethod invokeTarget, boolean isIndirect) { if (isIndirect) { - for (HostedMethod invokeImplementation : invokeTarget.getImplementations()) { + HostedMethod[] implementations = invokeTarget.getImplementations(); + List targets = getEntry(method); + Collections.addAll(targets, implementations); + for (HostedMethod invokeImplementation : implementations) { handleSpecialization(method, targetNode, invokeTarget, invokeImplementation); ensureParsed(invokeImplementation, method, new VirtualCallReason(method, invokeImplementation, reason)); } @@ -1120,6 +1156,7 @@ private void ensureParsed(HostedMethod method, CompileReason reason, CallTargetN * implementation invoked status. */ if (invokeTarget.wrapped.isSimplyImplementationInvoked()) { + getEntry(method).add(invokeTarget); handleSpecialization(method, targetNode, invokeTarget, invokeTarget); ensureParsed(invokeTarget, method, new DirectCallReason(method, reason)); } @@ -1298,6 +1335,8 @@ protected CompilationResult doCompile(DebugContext debug, final HostedMethod met return fun.compile(debug, method, compilationIdentifier, reason, runtimeConfig); } + public static final Set compiledMethods = ConcurrentHashMap.newKeySet(); + @SuppressWarnings("try") private CompilationResult defaultCompileFunction(DebugContext debug, HostedMethod method, CompilationIdentifier compilationIdentifier, CompileReason reason, RuntimeConfiguration config) { if (NativeImageOptions.PrintAOTCompilation.getValue()) { @@ -1351,6 +1390,8 @@ private CompilationResult defaultCompileFunction(DebugContext debug, HostedMetho result.setTargetCode(Arrays.copyOf(result.getTargetCode(), result.getTargetCodeSize()), result.getTargetCodeSize()); } + compiledMethods.add(method); + return result; } } catch (Throwable ex) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 3c079e52d2fd..ec89a1afc9b9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -328,7 +328,11 @@ public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod m, ResolvedJa * Check that the SharedType implementation, which is used for JIT compilation, gives the * same result as the hosted implementation. */ - assert hResult == null || isWordType() || hResult.equals(SharedType.super.resolveConcreteMethod(method, callerType)); + boolean condition = hResult == null || isWordType() || hResult.equals(SharedType.super.resolveConcreteMethod(method, callerType)); + if (!condition) { + System.err.println("condition failed for " + m); + } + assert condition; return hResult; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java index 2551b7cba9b4..7695ac8ca576 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java @@ -137,6 +137,11 @@ public HostedType optionalLookup(JavaType type) { @Override public HostedField lookup(JavaField field) { + if (field == null) { + System.err.println("Trying to lookup a field, which is null"); + new RuntimeException().printStackTrace(); + return null; + } JavaField result = lookupAllowUnresolved(field); if (result instanceof ResolvedJavaField) { return (HostedField) result; @@ -159,6 +164,10 @@ public HostedField optionalLookup(JavaField field) { @Override public HostedMethod lookup(JavaMethod method) { + if (method == null) { + System.err.println("Trying to lookup a method, which is null"); + return null; + } JavaMethod result = lookupAllowUnresolved(method); if (result instanceof ResolvedJavaMethod) { return (HostedMethod) result; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java index bbc213d7e1af..064c4e054a2d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java @@ -417,9 +417,13 @@ public void buildTypeInformation(HostedUniverse hUniverse) { } boolean isInstantiated = type.getWrapped().isInstantiated(); - assert !isInstantiated || + boolean condition = !isInstantiated || ((type.isInstanceClass() && !Modifier.isAbstract(type.getModifiers())) || type.isArray()); + if (!condition) { + System.err.println("condition broken for " + type); + } + assert condition; if (subtypeStampType == null) { /* Type has no instantiated subtypes. */