Skip to content

Commit 79b423d

Browse files
committed
Run reachability handlers concurrently during analysis.
wip
1 parent eeba06f commit 79b423d

File tree

17 files changed

+705
-101
lines changed

17 files changed

+705
-101
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -232,11 +232,24 @@ interface BeforeAnalysisAccess extends FeatureAccess {
232232
void registerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Object... elements);
233233

234234
/**
235-
* Registers a callback that is invoked once {@link Feature#duringAnalysis during analysis}
236-
* for each time a method that overrides the specified {param baseMethod} is determined to
237-
* be reachable at run time. In addition the handler will also get invoked once when the
238-
* {param baseMethod} itself becomes reachable. The specific method that becomes reachable
239-
* is passed to the handler as the second parameter.
235+
* Registers a callback that is invoked once during analysis for each time a method that
236+
* overrides the specified {param baseMethod} is determined to be reachable at run time. In
237+
* addition, the handler will also get invoked once when the {param baseMethod} itself
238+
* becomes reachable. The specific method that becomes reachable is passed to the handler as
239+
* the second parameter.
240+
* <p/>
241+
* A method is considered reachable at run time if it can be executed, as determined via
242+
* static analysis.
243+
* <p/>
244+
* Therefore, if a method can be statically bound (usually, that means it is final or
245+
* private or static, but not abstract, or the declaring class is final), or it is a
246+
* constructors, and it is the target of a reachable invoke, or it is inlined, then it is
247+
* considered run time reachable.
248+
* <p/>
249+
* A virtual methods is considered run time reachable if it's declaring class or any of its
250+
* subtypes is instantiated and the method is the target of a reachable invoke, or it is
251+
* inlined. Even if the declaring type itself is not marked as instantiated the method can
252+
* still be reachable via special invokes, e.g., `super` calls.
240253
*
241254
* @since 19.3
242255
*/
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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.meta;
26+
27+
import java.lang.reflect.Executable;
28+
import java.util.Set;
29+
import java.util.concurrent.ConcurrentHashMap;
30+
import java.util.concurrent.atomic.AtomicBoolean;
31+
import java.util.function.BiConsumer;
32+
import java.util.function.Consumer;
33+
34+
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
35+
36+
import com.oracle.graal.pointsto.PointsToAnalysis;
37+
38+
public abstract class AnalysisElement {
39+
40+
/**
41+
* Contains reachability handlers that are notified when the element is marked as reachable.
42+
* Each handler is notified only once, and then it is removed from the set.
43+
*/
44+
private final Set<ElementReachableNotification> elementReachableNotifications = ConcurrentHashMap.newKeySet();
45+
46+
public void registerReachabilityNotification(ElementReachableNotification notification) {
47+
elementReachableNotifications.add(notification);
48+
}
49+
50+
protected void notifyReachabilityCallbacks(AnalysisUniverse universe) {
51+
elementReachableNotifications.forEach(c -> c.notifyCallback(universe, this));
52+
elementReachableNotifications.removeIf(ElementReachableNotification::isNotified);
53+
}
54+
55+
public abstract boolean isReachable();
56+
57+
protected abstract void onReachable();
58+
59+
/** Return true if reachability handlers should be executed for this element. */
60+
public boolean isTriggered() {
61+
return isReachable();
62+
}
63+
64+
public static final class ElementReachableNotification {
65+
66+
private final Consumer<DuringAnalysisAccess> callback;
67+
private final AtomicBoolean notified = new AtomicBoolean();
68+
69+
public ElementReachableNotification(Consumer<DuringAnalysisAccess> callback) {
70+
this.callback = callback;
71+
}
72+
73+
public boolean isNotified() {
74+
return notified.get();
75+
}
76+
77+
/**
78+
* Notify the callback exactly once. Note that this callback can be shared by multiple
79+
* triggers, the one that triggers it is passed into triggeredElement for debugging.
80+
*/
81+
public void notifyCallback(AnalysisUniverse universe, AnalysisElement triggeredElement) {
82+
assert triggeredElement.isTriggered();
83+
if (!notified.getAndSet(true)) {
84+
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess()));
85+
}
86+
}
87+
}
88+
89+
public static final class SubtypeReachableNotification {
90+
private final BiConsumer<DuringAnalysisAccess, Class<?>> callback;
91+
private final Set<AnalysisType> seenSubtypes = ConcurrentHashMap.newKeySet();
92+
93+
public SubtypeReachableNotification(BiConsumer<DuringAnalysisAccess, Class<?>> callback) {
94+
this.callback = callback;
95+
}
96+
97+
/** Notify the callback exactly once for each reachable subtype. */
98+
public void notifyCallback(AnalysisUniverse universe, AnalysisType reachableSubtype) {
99+
assert reachableSubtype.isReachable();
100+
if (seenSubtypes.add(reachableSubtype)) {
101+
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess(), reachableSubtype.getJavaClass()));
102+
}
103+
}
104+
}
105+
106+
public static final class MethodOverrideReachableNotification {
107+
private final BiConsumer<DuringAnalysisAccess, Executable> callback;
108+
private final Set<AnalysisMethod> seenOverride = ConcurrentHashMap.newKeySet();
109+
110+
public MethodOverrideReachableNotification(BiConsumer<DuringAnalysisAccess, Executable> callback) {
111+
this.callback = callback;
112+
}
113+
114+
/** Notify the callback exactly once for each reachable method override. */
115+
public void notifyCallback(AnalysisUniverse universe, AnalysisMethod reachableOverride) {
116+
assert reachableOverride.isReachable();
117+
if (seenOverride.add(reachableOverride)) {
118+
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess(), reachableOverride.getJavaMethod()));
119+
}
120+
}
121+
}
122+
123+
private static void execute(AnalysisUniverse universe, Runnable task) {
124+
/*
125+
* Post the tasks to the analysis executor. This ensures that even for elements registered
126+
* as reachable early, before the analysis is started, the reachability callbacks are run
127+
* during the analysis.
128+
*/
129+
((PointsToAnalysis) universe.getBigbang()).postTask(task);
130+
}
131+
132+
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
import org.graalvm.util.GuardedAnnotationAccess;
3838

3939
import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
40-
import com.oracle.graal.pointsto.api.PointstoOptions;
4140
import com.oracle.graal.pointsto.api.HostVM;
41+
import com.oracle.graal.pointsto.api.PointstoOptions;
4242
import com.oracle.graal.pointsto.flow.ContextInsensitiveFieldTypeFlow;
4343
import com.oracle.graal.pointsto.flow.FieldTypeFlow;
4444
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
@@ -52,7 +52,7 @@
5252
import jdk.vm.ci.meta.ResolvedJavaField;
5353
import jdk.vm.ci.meta.ResolvedJavaType;
5454

55-
public abstract class AnalysisField implements ResolvedJavaField, OriginalFieldProvider {
55+
public abstract class AnalysisField extends AnalysisElement implements ResolvedJavaField, OriginalFieldProvider {
5656

5757
@SuppressWarnings("rawtypes")//
5858
private static final AtomicReferenceFieldUpdater<AnalysisField, Object> OBSERVERS_UPDATER = //
@@ -242,6 +242,7 @@ public boolean registerAsAccessed() {
242242
boolean firstAttempt = AtomicUtils.atomicMark(isAccessed);
243243
notifyUpdateAccessInfo();
244244
if (firstAttempt) {
245+
onReachable();
245246
getUniverse().onFieldAccessed(this);
246247
getUniverse().getHeapScanner().onFieldRead(this);
247248
}
@@ -255,6 +256,7 @@ public boolean registerAsRead(MethodTypeFlow method) {
255256
readBy.put(method, Boolean.TRUE);
256257
}
257258
if (firstAttempt) {
259+
onReachable();
258260
getUniverse().onFieldAccessed(this);
259261
getUniverse().getHeapScanner().onFieldRead(this);
260262
}
@@ -273,15 +275,19 @@ public boolean registerAsWritten(MethodTypeFlow method) {
273275
if (writtenBy != null && method != null) {
274276
writtenBy.put(method, Boolean.TRUE);
275277
}
276-
if (firstAttempt && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object)) {
277-
getUniverse().onFieldAccessed(this);
278+
if (firstAttempt) {
279+
onReachable();
280+
if (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object) {
281+
getUniverse().onFieldAccessed(this);
282+
}
278283
}
279284
return firstAttempt;
280285
}
281286

282287
public void markFolded() {
283288
if (AtomicUtils.atomicMark(isFolded)) {
284289
getDeclaringClass().registerAsReachable();
290+
onReachable();
285291
}
286292
}
287293

@@ -385,10 +391,16 @@ public boolean isFolded() {
385391
return isFolded.get();
386392
}
387393

394+
@Override
388395
public boolean isReachable() {
389396
return isAccessed.get() || isRead.get() || isWritten.get() || isFolded.get();
390397
}
391398

399+
@Override
400+
public void onReachable() {
401+
notifyReachabilityCallbacks(declaringClass.getUniverse());
402+
}
403+
392404
public void setCanBeNull(boolean canBeNull) {
393405
this.canBeNull = canBeNull;
394406
notifyUpdateAccessInfo();

0 commit comments

Comments
 (0)