Skip to content

Commit b781892

Browse files
committed
[GR-31713] Implement a prototype of Reachability Analysis for Native Image
PullRequest: graal/9774
2 parents add3a99 + 0338665 commit b781892

File tree

46 files changed

+2088
-179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2088
-179
lines changed

java-benchmarks/mx.java-benchmarks/mx_java_benchmarks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ def extra_image_build_argument(self, benchmark, args):
376376
'--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.localization=ALL-UNNAMED',
377377
'--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED',
378378
'--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.threadlocal=ALL-UNNAMED',
379+
'--initialize-at-run-time=io.netty.internal.tcnative.SSL,io.netty.handler.codec.compression.ZstdOptions',
379380
'-H:+StackTrace'] + super(BaseQuarkusBenchmarkSuite, self).extra_image_build_argument(benchmark, args)
380381

381382

@@ -2356,7 +2357,7 @@ def createCommandLineArgs(self, benchmarks, bmSuiteArgs):
23562357
mx.abort("Must specify exactly one benchmark to run.")
23572358
elif benchmarks[0] not in self.benchmarkList(bmSuiteArgs):
23582359
mx.abort("The specified benchmark doesn't exist. Possible values are: " + ", ".join(self.benchmarkList(bmSuiteArgs)))
2359-
vmArgs = self.runArgs(bmSuiteArgs)
2360+
vmArgs = self.vmArgs(bmSuiteArgs)
23602361
runArgs = self.runArgs(bmSuiteArgs)
23612362
appArgs = self.appArgs(benchmarks[0])
23622363
return vmArgs + self.classpathAndMainClass(benchmarks[0]) + runArgs + appArgs

substratevm/mx.substratevm/suite.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,13 +407,28 @@
407407
"workingSets": "SVM",
408408
},
409409

410+
411+
"com.oracle.graal.reachability": {
412+
"subDir": "src",
413+
"sourceDirs": ["src"],
414+
"dependencies": [
415+
"com.oracle.graal.pointsto",
416+
],
417+
"checkstyle": "com.oracle.svm.core",
418+
"javaCompliance": "11+",
419+
"annotationProcessors": [
420+
"compiler:GRAAL_PROCESSOR",
421+
],
422+
"workingSets": "SVM",
423+
},
424+
410425
"com.oracle.svm.hosted": {
411426
"subDir": "src",
412427
"sourceDirs": ["src"],
413428
"dependencies": [
414429
"com.oracle.objectfile",
415430
"com.oracle.svm.core",
416-
"com.oracle.graal.pointsto",
431+
"com.oracle.graal.reachability"
417432
],
418433
"requires" : [
419434
"java.desktop",
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
/*
2+
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.graal.pointsto;
26+
27+
import com.oracle.graal.pointsto.api.HostVM;
28+
import com.oracle.graal.pointsto.api.PointstoOptions;
29+
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
30+
import com.oracle.graal.pointsto.meta.AnalysisField;
31+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
32+
import com.oracle.graal.pointsto.meta.AnalysisMethod;
33+
import com.oracle.graal.pointsto.meta.AnalysisType;
34+
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
35+
import com.oracle.graal.pointsto.meta.HostedProviders;
36+
import com.oracle.graal.pointsto.reports.StatisticsPrinter;
37+
import com.oracle.graal.pointsto.util.AnalysisError;
38+
import com.oracle.graal.pointsto.util.CompletionExecutor;
39+
import com.oracle.graal.pointsto.util.Timer;
40+
import com.oracle.graal.pointsto.util.TimerCollection;
41+
import jdk.vm.ci.meta.ConstantReflectionProvider;
42+
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
43+
import org.graalvm.compiler.debug.DebugContext;
44+
import org.graalvm.compiler.debug.DebugHandlersFactory;
45+
import org.graalvm.compiler.debug.Indent;
46+
import org.graalvm.compiler.nodes.spi.Replacements;
47+
import org.graalvm.compiler.options.OptionValues;
48+
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
49+
50+
import java.io.PrintWriter;
51+
import java.util.Collections;
52+
import java.util.List;
53+
import java.util.concurrent.ForkJoinPool;
54+
import java.util.function.Function;
55+
56+
/**
57+
* This abstract class will be shared between Reachability and Points-to. It contains generic
58+
* methods needed by both types of analysis and getters.
59+
*/
60+
public abstract class AbstractAnalysisEngine implements BigBang {
61+
62+
private final Boolean extendedAsserts;
63+
private final Timer processFeaturesTimer;
64+
private final Timer analysisTimer;
65+
protected final Timer verifyHeapTimer;
66+
protected final Timer reachabilityTimer;
67+
protected final AnalysisMetaAccess metaAccess;
68+
private final HostedProviders providers;
69+
protected final HostVM hostVM;
70+
protected final ForkJoinPool executorService;
71+
private final Runnable heartbeatCallback;
72+
protected final UnsupportedFeatures unsupportedFeatures;
73+
protected final DebugContext debug;
74+
protected final OptionValues options;
75+
protected final AnalysisUniverse universe;
76+
private final List<DebugHandlersFactory> debugHandlerFactories;
77+
private final HeapScanningPolicy heapScanningPolicy;
78+
private final Replacements replacements;
79+
protected final CompletionExecutor executor;
80+
protected final AnalysisTiming timing;
81+
82+
public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, HostedProviders providers, HostVM hostVM, ForkJoinPool executorService, Runnable heartbeatCallback,
83+
UnsupportedFeatures unsupportedFeatures, TimerCollection timerCollection) {
84+
this.options = options;
85+
this.universe = universe;
86+
this.debugHandlerFactories = Collections.singletonList(new GraalDebugHandlersFactory(providers.getSnippetReflection()));
87+
this.debug = new org.graalvm.compiler.debug.DebugContext.Builder(options, debugHandlerFactories).build();
88+
this.metaAccess = (AnalysisMetaAccess) providers.getMetaAccess();
89+
this.providers = providers;
90+
this.hostVM = hostVM;
91+
this.executorService = executorService;
92+
this.executor = new CompletionExecutor(this, executorService, heartbeatCallback);
93+
this.timing = PointstoOptions.ProfileAnalysisOperations.getValue(options) ? new AbstractAnalysisEngine.AnalysisTiming() : null;
94+
this.executor.init(timing);
95+
this.heartbeatCallback = heartbeatCallback;
96+
this.unsupportedFeatures = unsupportedFeatures;
97+
this.replacements = providers.getReplacements();
98+
99+
this.processFeaturesTimer = timerCollection.get(TimerCollection.Registry.FEATURES);
100+
this.verifyHeapTimer = timerCollection.get(TimerCollection.Registry.VERIFY_HEAP);
101+
this.reachabilityTimer = timerCollection.createTimer("(reachability)", false);
102+
this.analysisTimer = timerCollection.get(TimerCollection.Registry.ANALYSIS);
103+
104+
this.extendedAsserts = PointstoOptions.ExtendedAsserts.getValue(options);
105+
106+
this.heapScanningPolicy = PointstoOptions.ExhaustiveHeapScan.getValue(options)
107+
? HeapScanningPolicy.scanAll()
108+
: HeapScanningPolicy.skipTypes(skippedHeapTypes());
109+
}
110+
111+
@SuppressWarnings("try")
112+
@Override
113+
public void runAnalysis(DebugContext debugContext, Function<AnalysisUniverse, Boolean> analysisEndCondition) throws InterruptedException {
114+
int numIterations = 0;
115+
while (true) {
116+
try (Indent indent2 = debugContext.logAndIndent("new analysis iteration")) {
117+
/*
118+
* Do the analysis (which itself is done in a similar iterative process)
119+
*/
120+
boolean analysisChanged = finish();
121+
122+
numIterations++;
123+
if (numIterations > 1000) {
124+
/*
125+
* Usually there are < 10 iterations. If we have so many iterations, we probably
126+
* have an endless loop (but at least we have a performance problem because we
127+
* re-start the analysis so often).
128+
*/
129+
throw AnalysisError.shouldNotReachHere(String.format("Static analysis did not reach a fix point after %d iterations because a Feature keeps requesting new analysis iterations. " +
130+
"The analysis itself %s find a change in type states in the last iteration.",
131+
numIterations, analysisChanged ? "DID" : "DID NOT"));
132+
}
133+
/*
134+
* Allow features to change the universe.
135+
*/
136+
int numTypes = universe.getTypes().size();
137+
int numMethods = universe.getMethods().size();
138+
int numFields = universe.getFields().size();
139+
if (analysisEndCondition.apply(universe)) {
140+
if (numTypes != universe.getTypes().size() || numMethods != universe.getMethods().size() || numFields != universe.getFields().size()) {
141+
throw AnalysisError.shouldNotReachHere(
142+
"When a feature makes more types, methods, or fields reachable, it must require another analysis iteration via DuringAnalysisAccess.requireAnalysisIteration()");
143+
}
144+
/*
145+
* Manual rescanning doesn't explicitly require analysis iterations, but it can
146+
* insert some pending operations.
147+
*/
148+
boolean pendingOperations = executor.getPostedOperations() > 0;
149+
if (pendingOperations) {
150+
System.out.println("Found pending operations, continuing analysis.");
151+
continue;
152+
}
153+
/* Outer analysis loop is done. Check if heap verification modifies analysis. */
154+
if (!analysisModified()) {
155+
return;
156+
}
157+
}
158+
}
159+
}
160+
}
161+
162+
@SuppressWarnings("try")
163+
private boolean analysisModified() throws InterruptedException {
164+
boolean analysisModified;
165+
try (Timer.StopTimer ignored = verifyHeapTimer.start()) {
166+
analysisModified = universe.getHeapVerifier().requireAnalysisIteration(executor);
167+
}
168+
/* Initialize for the next iteration. */
169+
executor.init(timing);
170+
return analysisModified;
171+
}
172+
173+
@Override
174+
public void cleanupAfterAnalysis() {
175+
universe.getTypes().forEach(AnalysisType::cleanupAfterAnalysis);
176+
universe.getFields().forEach(AnalysisField::cleanupAfterAnalysis);
177+
universe.getMethods().forEach(AnalysisMethod::cleanupAfterAnalysis);
178+
179+
PointsToAnalysis.ConstantObjectsProfiler.constantTypes.clear();
180+
181+
universe.getHeapScanner().cleanupAfterAnalysis();
182+
universe.getHeapVerifier().cleanupAfterAnalysis();
183+
}
184+
185+
@Override
186+
public void printTimers() {
187+
reachabilityTimer.print();
188+
verifyHeapTimer.print();
189+
processFeaturesTimer.print();
190+
}
191+
192+
@Override
193+
public void printTimerStatistics(PrintWriter out) {
194+
// todo print reachability here
195+
StatisticsPrinter.print(out, "features_time_ms", processFeaturesTimer.getTotalTime());
196+
StatisticsPrinter.print(out, "total_analysis_time_ms", analysisTimer.getTotalTime());
197+
198+
StatisticsPrinter.printLast(out, "total_memory_bytes", analysisTimer.getTotalMemory());
199+
}
200+
201+
@Override
202+
public AnalysisType[] skippedHeapTypes() {
203+
return new AnalysisType[]{metaAccess.lookupJavaType(String.class)};
204+
}
205+
206+
@Override
207+
public boolean extendedAsserts() {
208+
return extendedAsserts;
209+
}
210+
211+
@Override
212+
public OptionValues getOptions() {
213+
return options;
214+
}
215+
216+
@Override
217+
public Runnable getHeartbeatCallback() {
218+
return heartbeatCallback;
219+
}
220+
221+
@Override
222+
public DebugContext getDebug() {
223+
return debug;
224+
}
225+
226+
@Override
227+
public List<DebugHandlersFactory> getDebugHandlerFactories() {
228+
return debugHandlerFactories;
229+
}
230+
231+
@Override
232+
public AnalysisPolicy analysisPolicy() {
233+
return universe.analysisPolicy();
234+
}
235+
236+
@Override
237+
public AnalysisUniverse getUniverse() {
238+
return universe;
239+
}
240+
241+
@Override
242+
public HostedProviders getProviders() {
243+
return providers;
244+
}
245+
246+
@Override
247+
public AnalysisMetaAccess getMetaAccess() {
248+
return metaAccess;
249+
}
250+
251+
@Override
252+
public UnsupportedFeatures getUnsupportedFeatures() {
253+
return unsupportedFeatures;
254+
}
255+
256+
@Override
257+
public final SnippetReflectionProvider getSnippetReflectionProvider() {
258+
return providers.getSnippetReflection();
259+
}
260+
261+
@Override
262+
public final ConstantReflectionProvider getConstantReflectionProvider() {
263+
return providers.getConstantReflection();
264+
}
265+
266+
@Override
267+
public HeapScanningPolicy scanningPolicy() {
268+
return heapScanningPolicy;
269+
}
270+
271+
@Override
272+
public HostVM getHostVM() {
273+
return hostVM;
274+
}
275+
276+
protected void schedule(Runnable task) {
277+
executor.execute((d) -> task.run());
278+
}
279+
280+
@Override
281+
public Replacements getReplacements() {
282+
return replacements;
283+
}
284+
285+
private class AnalysisTiming extends PointsToAnalysis.BucketTiming {
286+
287+
@Override
288+
public void printHeader() {
289+
super.printHeader();
290+
System.out.println();
291+
}
292+
293+
@Override
294+
public void print() {
295+
super.print();
296+
System.out.println();
297+
}
298+
}
299+
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.List;
2929
import java.util.function.Function;
3030

31+
import com.oracle.graal.pointsto.util.CompletionExecutor;
3132
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
3233
import org.graalvm.compiler.debug.DebugContext;
3334
import org.graalvm.compiler.debug.DebugHandlersFactory;
@@ -44,6 +45,7 @@
4445

4546
import jdk.vm.ci.code.BytecodePosition;
4647
import jdk.vm.ci.meta.ConstantReflectionProvider;
48+
import org.graalvm.compiler.nodes.spi.Replacements;
4749

4850
/**
4951
* Central static analysis interface that groups together the functionality of reachability analysis
@@ -93,6 +95,10 @@ public interface BigBang extends ReachabilityAnalysis, HeapScanning {
9395

9496
void runAnalysis(DebugContext debug, Function<AnalysisUniverse, Boolean> duringAnalysisAction) throws InterruptedException;
9597

98+
boolean strengthenGraalGraphs();
99+
100+
Replacements getReplacements();
101+
96102
/** You can blacklist certain callees here. */
97103
@SuppressWarnings("unused")
98104
default boolean isCallAllowed(PointsToAnalysis bb, AnalysisMethod caller, AnalysisMethod target, BytecodePosition srcPosition) {
@@ -114,4 +120,16 @@ default void onTypeInstantiated(AnalysisType type, UsageKind usageKind) {
114120
@SuppressWarnings("unused")
115121
default void onTypeInitialized(AnalysisType type) {
116122
}
123+
124+
void postTask(CompletionExecutor.DebugContextRunnable task);
125+
126+
void initializeMetaData(AnalysisType type);
127+
128+
/**
129+
* Callback executed after the analysis finished. The cleanupAfterAnalysis is executed after the
130+
* universe builder, which can be too late for some tasks.
131+
*/
132+
default void afterAnalysis() {
133+
134+
}
117135
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ public final void scanConstant(JavaConstant value, ScanReason reason) {
227227
return;
228228
}
229229
if (!bb.scanningPolicy().scanConstant(bb, value)) {
230-
analysisType(bb, valueObj).registerAsInHeap();
230+
bb.markTypeInHeap(analysisType(bb, valueObj));
231231
return;
232232
}
233233
if (scannedObjects.putAndAcquire(valueObj) == null) {

0 commit comments

Comments
 (0)