Skip to content

Commit 6df04c9

Browse files
committed
[GR-49383] Implement base layer open-world analysis
PullRequest: graal/15778
2 parents e026ca2 + 5ab9db9 commit 6df04c9

File tree

22 files changed

+474
-62
lines changed

22 files changed

+474
-62
lines changed

substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,11 @@
3838
import java.util.ArrayList;
3939
import java.util.List;
4040

41-
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
42-
import jdk.graal.compiler.debug.DebugContext;
43-
import jdk.graal.compiler.debug.Indent;
44-
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
45-
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
46-
import jdk.graal.compiler.options.OptionValues;
47-
import jdk.graal.compiler.phases.util.Providers;
48-
import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
49-
import jdk.graal.compiler.word.WordTypes;
5041
import org.graalvm.nativeimage.hosted.Feature;
5142

5243
import com.oracle.graal.pointsto.AnalysisObjectScanningObserver;
5344
import com.oracle.graal.pointsto.AnalysisPolicy;
45+
import com.oracle.graal.pointsto.ClassInclusionPolicy;
5446
import com.oracle.graal.pointsto.api.PointstoOptions;
5547
import com.oracle.graal.pointsto.flow.context.bytecode.BytecodeSensitiveAnalysisPolicy;
5648
import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier;
@@ -79,6 +71,15 @@
7971
import com.oracle.svm.util.ModuleSupport;
8072
import com.oracle.svm.util.ReflectionUtil;
8173

74+
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
75+
import jdk.graal.compiler.debug.DebugContext;
76+
import jdk.graal.compiler.debug.Indent;
77+
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
78+
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
79+
import jdk.graal.compiler.options.OptionValues;
80+
import jdk.graal.compiler.phases.util.Providers;
81+
import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
82+
import jdk.graal.compiler.word.WordTypes;
8283
import jdk.vm.ci.amd64.AMD64Kind;
8384
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
8485
import jdk.vm.ci.meta.JavaKind;
@@ -151,8 +152,9 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options) {
151152
originalProviders.getPlatformConfigurationProvider(), aMetaAccessExtensionProvider, originalProviders.getLoopsDataProvider());
152153
standaloneHost.initializeProviders(aProviders);
153154
analysisName = getAnalysisName(mainEntryClass);
155+
ClassInclusionPolicy classInclusionPolicy = new ClassInclusionPolicy.DefaultAllInclusionPolicy("Included in the base image");
154156
bigbang = new StandalonePointsToAnalysis(options, aUniverse, standaloneHost, aMetaAccess, snippetReflection, aConstantReflection, aProviders.getWordTypes(), debugContext,
155-
new TimerCollection());
157+
new TimerCollection(), classInclusionPolicy);
156158
standaloneHost.setImageName(analysisName);
157159
aUniverse.setBigBang(bigbang);
158160
ImageHeap heap = new ImageHeap();

substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandalonePointsToAnalysis.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Set;
3030
import java.util.concurrent.ConcurrentHashMap;
3131

32+
import com.oracle.graal.pointsto.ClassInclusionPolicy;
3233
import com.oracle.graal.pointsto.PointsToAnalysis;
3334
import com.oracle.graal.pointsto.api.HostVM;
3435
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
@@ -48,8 +49,8 @@ public class StandalonePointsToAnalysis extends PointsToAnalysis {
4849
private final Set<AnalysisMethod> addedClinits = ConcurrentHashMap.newKeySet();
4950

5051
public StandalonePointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM hostVM, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflectionProvider,
51-
ConstantReflectionProvider constantReflectionProvider, WordTypes wordTypes, DebugContext debugContext, TimerCollection timerCollection) {
52-
super(options, universe, hostVM, metaAccess, snippetReflectionProvider, constantReflectionProvider, wordTypes, new UnsupportedFeatures(), debugContext, timerCollection);
52+
ConstantReflectionProvider constantReflectionProvider, WordTypes wordTypes, DebugContext debugContext, TimerCollection timerCollection, ClassInclusionPolicy classInclusionPolicy) {
53+
super(options, universe, hostVM, metaAccess, snippetReflectionProvider, constantReflectionProvider, wordTypes, new UnsupportedFeatures(), debugContext, timerCollection, classInclusionPolicy);
5354
}
5455

5556
@Override

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
package com.oracle.graal.pointsto;
2626

2727
import java.io.PrintWriter;
28+
import java.util.Arrays;
2829
import java.util.Collections;
2930
import java.util.List;
3031
import java.util.function.Function;
32+
import java.util.stream.Stream;
3133

3234
import org.graalvm.nativeimage.hosted.Feature;
3335

36+
import com.oracle.graal.pointsto.ClassInclusionPolicy.LayeredBaseImageInclusionPolicy;
3437
import com.oracle.graal.pointsto.api.HostVM;
3538
import com.oracle.graal.pointsto.api.PointstoOptions;
3639
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
@@ -97,11 +100,12 @@ public abstract class AbstractAnalysisEngine implements BigBang {
97100
protected final Timer processFeaturesTimer;
98101
protected final Timer analysisTimer;
99102
protected final Timer verifyHeapTimer;
103+
protected final ClassInclusionPolicy classInclusionPolicy;
100104

101105
@SuppressWarnings("this-escape")
102106
public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, HostVM hostVM, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflectionProvider,
103107
ConstantReflectionProvider constantReflectionProvider, WordTypes wordTypes, UnsupportedFeatures unsupportedFeatures, DebugContext debugContext,
104-
TimerCollection timerCollection) {
108+
TimerCollection timerCollection, ClassInclusionPolicy classInclusionPolicy) {
105109
this.options = options;
106110
this.universe = universe;
107111
this.debugHandlerFactories = Collections.singletonList(new GraalDebugHandlersFactory(snippetReflectionProvider));
@@ -123,6 +127,8 @@ public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, H
123127
this.snippetReflectionProvider = snippetReflectionProvider;
124128
this.constantReflectionProvider = constantReflectionProvider;
125129
this.wordTypes = wordTypes;
130+
classInclusionPolicy.setBigBang(this);
131+
this.classInclusionPolicy = classInclusionPolicy;
126132
}
127133

128134
/**
@@ -257,6 +263,10 @@ public void profileConstantObject(AnalysisType type) {
257263
}
258264
}
259265

266+
public boolean isBaseLayerAnalysisEnabled() {
267+
return classInclusionPolicy instanceof LayeredBaseImageInclusionPolicy;
268+
}
269+
260270
@Override
261271
public OptionValues getOptions() {
262272
return options;
@@ -346,6 +356,19 @@ public final boolean executorIsStarted() {
346356
return executor.isStarted();
347357
}
348358

359+
@Override
360+
public void registerTypeForBaseImage(Class<?> cls) {
361+
if (classInclusionPolicy.isClassIncluded(cls)) {
362+
classInclusionPolicy.includeClass(cls);
363+
Stream.concat(Arrays.stream(cls.getDeclaredConstructors()), Arrays.stream(cls.getDeclaredMethods()))
364+
.filter(classInclusionPolicy::isMethodIncluded)
365+
.forEach(classInclusionPolicy::includeMethod);
366+
Arrays.stream(cls.getDeclaredFields())
367+
.filter(classInclusionPolicy::isFieldIncluded)
368+
.forEach(classInclusionPolicy::includeField);
369+
}
370+
}
371+
349372
/**
350373
* Provide a non-null position. Some flows like newInstance and invoke require a non-null
351374
* position, for others is just better. The constructed position is best-effort, i.e., it

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,8 @@ default void afterAnalysis() {
136136
default AnalysisMethod fallbackResolveConcreteMethod(AnalysisType resolvingType, AnalysisMethod method) {
137137
return null;
138138
}
139+
140+
default void registerTypeForBaseImage(Class<?> cls) {
141+
142+
}
139143
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright (c) 2023, 2023, 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 java.lang.reflect.Executable;
28+
import java.lang.reflect.Field;
29+
import java.lang.reflect.Member;
30+
import java.lang.reflect.Modifier;
31+
32+
import org.graalvm.nativeimage.AnnotationAccess;
33+
34+
import com.oracle.graal.pointsto.meta.AnalysisMethod;
35+
36+
import jdk.graal.compiler.api.replacements.Fold;
37+
38+
/**
39+
* Policy used to determine which classes, methods and fields need to be included in the image when
40+
* the {@code IncludeAllFromPath} and/or {@code IncludeAllFromModule} options are specified
41+
* depending on the configuration.
42+
*/
43+
public abstract class ClassInclusionPolicy {
44+
protected BigBang bb;
45+
protected final Object reason;
46+
47+
public ClassInclusionPolicy(Object reason) {
48+
this.reason = reason;
49+
}
50+
51+
public void setBigBang(BigBang bb) {
52+
this.bb = bb;
53+
}
54+
55+
/**
56+
* Determine if the given class needs to be included in the image according to the policy.
57+
*/
58+
public abstract boolean isClassIncluded(Class<?> cls);
59+
60+
/**
61+
* Determine if the given method needs to be included in the image according to the policy.
62+
*/
63+
public boolean isMethodIncluded(Executable method) {
64+
/*
65+
* Methods annotated with @Fold should not be included in the base image as they must be
66+
* inlined. An extension image would inline the method as well and would not use the method
67+
* from the base image.
68+
*/
69+
return !AnnotationAccess.isAnnotationPresent(bb.getMetaAccess().lookupJavaMethod(method), Fold.class);
70+
}
71+
72+
/**
73+
* Determine if the given field needs to be included in the image according to the policy.
74+
*/
75+
public boolean isFieldIncluded(Field field) {
76+
if (!bb.getHostVM().platformSupported(field)) {
77+
return false;
78+
}
79+
return bb.getHostVM().isFieldIncluded(bb, field);
80+
}
81+
82+
/**
83+
* Includes the given class in the image.
84+
*/
85+
public void includeClass(Class<?> cls) {
86+
/*
87+
* Those classes cannot be registered as allocated as they cannot be instantiated. They are
88+
* instead registered as reachable as they can still have methods or fields that could be
89+
* used by an extension image.
90+
*/
91+
if (Modifier.isAbstract(cls.getModifiers()) || cls.isInterface() || cls.isPrimitive()) {
92+
bb.getMetaAccess().lookupJavaType(cls).registerAsReachable(reason);
93+
} else {
94+
bb.getMetaAccess().lookupJavaType(cls).registerAsAllocated(reason);
95+
}
96+
}
97+
98+
/**
99+
* Includes the given method in the image.
100+
*/
101+
public abstract void includeMethod(Executable method);
102+
103+
/**
104+
* Includes the given field in the image.
105+
*/
106+
public void includeField(Field field) {
107+
bb.postTask(debug -> bb.addRootField(field));
108+
}
109+
110+
/**
111+
* The analysis for the base layer of a layered image assumes that any method that is reachable
112+
* using the base java access rules can be an entry point. An upper layer does not have access
113+
* to the packages from a lower layer. Thus, only the public classes with their public and
114+
* protected inner classes and methods can be accessed by an upper layer.
115+
* <p>
116+
* Protected elements from a final or sealed class cannot be accessed as an upper layer cannot
117+
* create a new class that extends the final or sealed class.
118+
* <p>
119+
* All the fields are included disregarding access rules as a missing field would cause issues
120+
* in the object layout.
121+
*/
122+
public static class LayeredBaseImageInclusionPolicy extends ClassInclusionPolicy {
123+
public LayeredBaseImageInclusionPolicy(Object reason) {
124+
super(reason);
125+
}
126+
127+
@Override
128+
public boolean isClassIncluded(Class<?> cls) {
129+
Class<?> enclosingClass = cls.getEnclosingClass();
130+
int classModifiers = cls.getModifiers();
131+
if (enclosingClass != null) {
132+
return isAccessible(enclosingClass, classModifiers) && isClassIncluded(enclosingClass);
133+
} else {
134+
return Modifier.isPublic(classModifiers);
135+
}
136+
}
137+
138+
@Override
139+
public boolean isMethodIncluded(Executable method) {
140+
return !Modifier.isAbstract(method.getModifiers()) && isAccessible(method) && super.isMethodIncluded(method);
141+
}
142+
143+
@Override
144+
public void includeMethod(Executable method) {
145+
bb.postTask(debug -> {
146+
/*
147+
* Non-abstract methods from an abstract class or default methods from an interface
148+
* are not registered as implementation invoked by the analysis because their
149+
* declaring class cannot be marked as instantiated and AnalysisType.getTypeFlow
150+
* only includes instantiated types (see TypeFlow.addObserver). For now, to ensure
151+
* those methods are included in the image, they are manually registered as
152+
* implementation invoked.
153+
*/
154+
Class<?> declaringClass = method.getDeclaringClass();
155+
if (!Modifier.isAbstract(method.getModifiers()) && (declaringClass.isInterface() || Modifier.isAbstract(declaringClass.getModifiers()))) {
156+
AnalysisMethod analysisMethod = bb.getMetaAccess().lookupJavaMethod(method);
157+
analysisMethod.registerAsDirectRootMethod(reason);
158+
analysisMethod.registerAsImplementationInvoked(reason);
159+
}
160+
bb.forcedAddRootMethod(method, false, reason);
161+
});
162+
}
163+
}
164+
165+
/**
166+
* The default inclusion policy. Includes all classes and methods. Including all fields causes
167+
* issues at the moment, so the same rules as for the {@link LayeredBaseImageInclusionPolicy}
168+
* are used.
169+
*/
170+
public static class DefaultAllInclusionPolicy extends ClassInclusionPolicy {
171+
public DefaultAllInclusionPolicy(Object reason) {
172+
super(reason);
173+
}
174+
175+
@Override
176+
public boolean isClassIncluded(Class<?> cls) {
177+
return true;
178+
}
179+
180+
@Override
181+
public void includeMethod(Executable method) {
182+
bb.postTask(debug -> bb.addRootMethod(method, false, reason));
183+
}
184+
}
185+
186+
protected boolean isAccessible(Member member) {
187+
Class<?> cls = member.getDeclaringClass();
188+
int modifiers = member.getModifiers();
189+
return isAccessible(cls, modifiers);
190+
}
191+
192+
protected boolean isAccessible(Class<?> cls, int modifiers) {
193+
return Modifier.isPublic(modifiers) || (!Modifier.isFinal(cls.getModifiers()) && !cls.isSealed() && Modifier.isProtected(modifiers));
194+
}
195+
}

0 commit comments

Comments
 (0)