Skip to content

Commit da0599d

Browse files
committed
[GR-70846] Ensure core types can be treated as closed
PullRequest: graal/22484
2 parents 0143a21 + db883d8 commit da0599d

File tree

11 files changed

+214
-24
lines changed

11 files changed

+214
-24
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,11 @@ public boolean isClosed(AnalysisType type) {
175175
/* In a closed type world all subtypes known. */
176176
return true;
177177
}
178-
/* Array and leaf types are by definition closed. */
179-
return type.isArray() || type.isLeaf();
178+
/*
179+
* Array and leaf types are by definition closed. Core types are considered as closed to
180+
* allow optimizations to be applied on them.
181+
*/
182+
return type.isArray() || type.isLeaf() || hostVM.isCoreType(type);
180183
}
181184

182185
@Override

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,18 +347,13 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
347347
* 2. The receiver TypeFlow's type is a closed type, so it may be not extended in a later
348348
* layer.
349349
*
350-
* 3. The receiver TypeFlow's type is a core type, so it may be not extended in a later
351-
* layer. (GR-70846: This check condition will be merged with the previous one once core
352-
* types are fully considered as closed.)
353-
*
354-
* 4. The receiver TypeFlow is not saturated.
350+
* 3. The receiver TypeFlow is not saturated.
355351
*
356352
* Otherwise, when the receiver's analysis results are incomplete, then it is possible for
357353
* more types to be observed in subsequent layers.
358354
*/
359355
boolean receiverAnalysisResultsComplete = strengthenGraphs.isClosedTypeWorld ||
360-
(hasReceiver && (analysis.isClosed(invokeFlow.getReceiverType()) || analysis.getHostVM().isCoreType(invokeFlow.getReceiverType()) ||
361-
!methodFlow.isSaturated(analysis, invokeFlow.getReceiver())));
356+
(hasReceiver && (analysis.isClosed(invokeFlow.getReceiverType()) || !methodFlow.isSaturated(analysis, invokeFlow.getReceiver())));
362357

363358
if (callTarget.invokeKind().isDirect()) {
364359
/*

substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/layeredimage/LayeredCompilationBehavior.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* Used to specify how a method needs to be compiled when building layered images.
3434
*/
3535
@Retention(RetentionPolicy.RUNTIME)
36-
@Target({ElementType.METHOD})
36+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
3737
public @interface LayeredCompilationBehavior {
3838
/**
3939
* This state represents how a method should be compiled in layered images. The state of a

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.graalvm.nativeimage.c.function.CodePointer;
3636
import org.graalvm.word.Pointer;
3737

38+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
39+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior;
3840
import com.oracle.svm.core.graal.code.StubCallingConvention;
3941
import com.oracle.svm.core.heap.RestrictHeapAccess;
4042
import com.oracle.svm.core.log.Log;
@@ -79,6 +81,7 @@ private static void invalidCodeAddressHandler() {
7981
@StubCallingConvention
8082
@NeverInline("We need a separate frame that stores all registers")
8183
@Uninterruptible(reason = "Precaution.")
84+
@LayeredCompilationBehavior(Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
8285
private static void invalidVTableEntryHandler() {
8386
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
8487
CodePointer callerIP = KnownIntrinsics.readReturnAddress();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.graalvm.word.WordBase;
5757

5858
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
59+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior;
5960
import com.oracle.svm.core.c.CGlobalData;
6061
import com.oracle.svm.core.c.CGlobalDataFactory;
6162
import com.oracle.svm.core.c.function.CEntryPointActions;
@@ -166,7 +167,7 @@ public List<String> getInputArguments() {
166167
* For layered images this method is delayed until the application layer. This is necessary so
167168
* that the method handle can be inlined before analysis.
168169
*/
169-
@LayeredCompilationBehavior(LayeredCompilationBehavior.Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
170+
@LayeredCompilationBehavior(Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
170171
public static void invokeMain(String[] args) throws Throwable {
171172
String[] mainArgs = args;
172173
if (ImageSingletons.contains(PreMainSupport.class)) {
@@ -276,9 +277,11 @@ private static void runShutdown0() {
276277
}
277278
}
278279

280+
/** The entry point of the image needs to be in the application layer. */
279281
@Uninterruptible(reason = "Thread state not set up yet.")
280282
@CEntryPoint(include = CEntryPoint.NotIncludedAutomatically.class)
281283
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class)
284+
@LayeredCompilationBehavior(Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
282285
public static int run(int argc, CCharPointerPointer argv) {
283286
if (SubstrateOptions.RunMainInNewThread.getValue()) {
284287
return doRunInNewThread(argc, argv);
@@ -289,7 +292,7 @@ public static int run(int argc, CCharPointerPointer argv) {
289292

290293
/** SVM start-up logic should be pinned to the initial layer. */
291294
@Uninterruptible(reason = "Thread state not setup yet.")
292-
@LayeredCompilationBehavior(LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER)
295+
@LayeredCompilationBehavior(Behavior.PINNED_TO_INITIAL_LAYER)
293296
private static int doRun(int argc, CCharPointerPointer argv) {
294297
try {
295298
CPUFeatureAccess cpuFeatureAccess = ImageSingletons.lookup(CPUFeatureAccess.class);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
import org.graalvm.nativeimage.Platforms;
5454
import org.graalvm.nativeimage.impl.InternalPlatform;
5555

56+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
57+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior;
5658
import com.oracle.svm.core.GCRelatedMXBeans;
5759
import com.oracle.svm.core.SubstrateUtil;
5860
import com.oracle.svm.core.Uninterruptible;
@@ -136,6 +138,7 @@ public static ManagementSupport getSingleton() {
136138
return ImageSingletons.lookup(ManagementSupport.class);
137139
}
138140

141+
@LayeredCompilationBehavior(Behavior.PINNED_TO_INITIAL_LAYER)
139142
public <T extends PlatformManagedObject> T getPlatformMXBean(Class<T> clazz) {
140143
Object result = getPlatformMXBeans0(clazz);
141144
if (result == null) {
@@ -152,6 +155,7 @@ public PlatformManagedObject getPlatformMXBeanRaw(Class<? extends PlatformManage
152155
}
153156

154157
@SuppressWarnings("unchecked")
158+
@LayeredCompilationBehavior(Behavior.PINNED_TO_INITIAL_LAYER)
155159
public <T extends PlatformManagedObject> List<T> getPlatformMXBeans(Class<T> clazz) {
156160
Object result = getPlatformMXBeans0(clazz);
157161
if (result == null) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@
4040
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
4141

4242
import com.oracle.svm.core.BuildPhaseProvider;
43+
import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation;
4344
import com.oracle.svm.core.SubstrateUtil;
4445
import com.oracle.svm.core.configure.RuntimeDynamicAccessMetadata;
46+
import com.oracle.svm.core.heap.UnknownObjectField;
4547
import com.oracle.svm.core.hub.DynamicHub;
4648
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
4749
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
@@ -209,12 +211,14 @@ public String getClassLoaderSerializationLookupKey(ClassLoader classLoader) {
209211
* Temporary key for maps ideally indexed by their {@link Class} or {@link DynamicHub}. At
210212
* runtime, these maps should be indexed by {@link DynamicHub#getTypeID}
211213
*/
214+
@Platforms(Platform.HOSTED_ONLY.class)
212215
public record DynamicHubKey(DynamicHub hub) {
213216
public int getTypeID() {
214217
return hub.getTypeID();
215218
}
216219
}
217220

221+
@UnknownObjectField(fullyQualifiedTypes = "org.graalvm.collections.EconomicMapImpl", availability = AfterCompilation.class) //
218222
private final EconomicMap<Object /* DynamicHubKey or DynamicHub.typeID */, RuntimeDynamicAccessMetadata> classes = EconomicMap.create();
219223
private final EconomicMap<String, RuntimeDynamicAccessMetadata> lambdaCapturingClasses = EconomicMap.create();
220224

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ExtensionLayerImageFeature.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,41 @@
2424
*/
2525
package com.oracle.svm.hosted;
2626

27+
import java.util.Arrays;
28+
import java.util.Objects;
29+
import java.util.function.Predicate;
30+
31+
import org.graalvm.collections.EconomicMap;
32+
33+
import com.oracle.graal.pointsto.api.HostVM;
34+
import com.oracle.graal.pointsto.meta.AnalysisType;
35+
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
36+
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
2737
import com.oracle.svm.core.c.BoxedRelocatedPointer;
2838
import com.oracle.svm.core.code.ImageCodeInfo;
2939
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
3040
import com.oracle.svm.core.feature.InternalFeature;
3141
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
42+
import com.oracle.svm.core.traits.BuiltinTraits.BuildtimeAccessOnly;
43+
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
44+
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent;
45+
import com.oracle.svm.core.traits.SingletonTraits;
46+
import com.oracle.svm.core.util.VMError;
3247
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
48+
import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl;
49+
import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport;
50+
import com.oracle.svm.hosted.imagelayer.InitialLayerFeature;
51+
import com.oracle.svm.hosted.imagelayer.SVMImageLayerLoader;
3352
import com.oracle.svm.util.ReflectionUtil;
3453

3554
/**
3655
* This feature contains some configs currently necessary to build an extension layer. We'll need
3756
* better mechanisms to avoid these workarounds.
3857
*/
3958
@AutomaticallyRegisteredFeature
59+
@SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, layeredInstallationKind = Independent.class)
4060
final class ExtensionLayerImageFeature implements InternalFeature {
61+
private static final Object NULL_SUPER_CORE_TYPE = new Object();
4162

4263
@Override
4364
public boolean isInConfiguration(IsInConfigurationAccess access) {
@@ -72,4 +93,83 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
7293
*/
7394
access.registerAsInHeap(ReflectionUtil.lookupClass(false, "java.util.concurrent.ConcurrentHashMap$CounterCell"));
7495
}
96+
97+
@Override
98+
public void beforeCompilation(BeforeCompilationAccess a) {
99+
BeforeCompilationAccessImpl access = (BeforeCompilationAccessImpl) a;
100+
AnalysisUniverse universe = access.aUniverse;
101+
HostVM hostVM = universe.hostVM();
102+
SVMImageLayerLoader imageLayerLoader = HostedImageLayerBuildingSupport.singleton().getLoader();
103+
EconomicMap<AnalysisType, Object> superCoreTypes = EconomicMap.create();
104+
105+
for (var type : universe.getTypes()) {
106+
/*
107+
* These checks allow to ensure that core types can be treated as closed (see
108+
* PointsToAnalysis.isClosed(AnalysisType)).
109+
*/
110+
boolean coreType = hostVM.isCoreType(type);
111+
if (coreType) {
112+
superCoreTypes.put(type, type);
113+
}
114+
AnalysisType superCoreType = getSuperCoreType(type, hostVM, superCoreTypes);
115+
boolean extendsCoreType = superCoreType != null && !coreType;
116+
if (coreType || extendsCoreType) {
117+
/*
118+
* This checks that all core types and types that extend or implement a core type
119+
* were not marked as reachable or instantiated only in an extension layer, showing
120+
* that the analysis of the core was complete.
121+
*/
122+
checkCondition(type.isReachable(), imageLayerLoader::isReachableInPreviousLayer, type, extendsCoreType, superCoreType, "reachable");
123+
checkCondition(type.isInstantiated(), imageLayerLoader::isInstantiatedInPreviousLayer, type, extendsCoreType, superCoreType, "instantiated");
124+
}
125+
}
126+
}
127+
128+
public AnalysisType getSuperCoreType(AnalysisType type, HostVM hostVM, EconomicMap<AnalysisType, Object> superCoreTypes) {
129+
/* Check the cache to see if the super core type was already computed. */
130+
if (superCoreTypes.containsKey(type)) {
131+
Object result = superCoreTypes.get(type);
132+
return result == NULL_SUPER_CORE_TYPE ? null : (AnalysisType) result;
133+
}
134+
135+
AnalysisType result = null;
136+
137+
/* Go through the super types to check if one of them is a core type. */
138+
AnalysisType superType = type.getSuperclass();
139+
if (superType != null) {
140+
result = hostVM.isCoreType(superType) ? superType : getSuperCoreType(superType, hostVM, superCoreTypes);
141+
}
142+
143+
if (result == null) {
144+
/*
145+
* If no result was found, iterate through all interfaces to see if one of them is a
146+
* core type.
147+
*/
148+
AnalysisType[] interfaces = type.getInterfaces();
149+
var coreInterface = Arrays.stream(interfaces).map(i -> getSuperCoreType(i, hostVM, superCoreTypes)).filter(Objects::nonNull).findFirst();
150+
if (coreInterface.isPresent()) {
151+
result = coreInterface.get();
152+
}
153+
}
154+
155+
/* Cache the result. */
156+
superCoreTypes.put(type, result == null ? NULL_SUPER_CORE_TYPE : result);
157+
158+
return result;
159+
}
160+
161+
private static void checkCondition(boolean condition, Predicate<AnalysisType> test, AnalysisType type, boolean extendsCoreType, AnalysisType superCoreType, String kind) {
162+
if (condition) {
163+
String hint = "Please make sure that all core types are seen by the initial layer analysis by either registering more entry points using " +
164+
LayeredCompilationBehavior.class + " with the " + LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER +
165+
" value or by explicitly registering the entry point or type reachability in " + InitialLayerFeature.class;
166+
if (extendsCoreType) {
167+
VMError.guarantee(test.test(type), "The type %s is extending the core type %s which was not seen as %s the initial layer. " +
168+
"It is illegal to extend core types in subsequent layers. %s", type, superCoreType, kind, hint);
169+
} else {
170+
VMError.guarantee(test.test(type), "The core type %s was not seen as %s the initial layer. " +
171+
"It is illegal for core types to become reachable in subsequent layers. %s", type, kind, hint);
172+
}
173+
}
174+
}
75175
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/InitialLayerFeature.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,21 @@
2626

2727
import static com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER;
2828

29+
import java.lang.reflect.Proxy;
30+
2931
import org.graalvm.nativeimage.hosted.Feature;
3032

33+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
3134
import com.oracle.svm.common.hosted.layeredimage.LayeredCompilationSupport;
35+
import com.oracle.svm.core.Uninterruptible;
36+
import com.oracle.svm.core.bootstrap.BootstrapMethodInfo;
3237
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
3338
import com.oracle.svm.core.feature.InternalFeature;
3439
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
40+
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
3541
import com.oracle.svm.util.ReflectionUtil;
3642

43+
import jdk.internal.loader.ClassLoaders;
3744
import jdk.internal.misc.Unsafe;
3845

3946
@AutomaticallyRegisteredFeature
@@ -43,8 +50,10 @@ public boolean isInConfiguration(Feature.IsInConfigurationAccess access) {
4350
return ImageLayerBuildingSupport.buildingInitialLayer();
4451
}
4552

53+
@SuppressWarnings("deprecation")
4654
@Override
47-
public void duringSetup(DuringSetupAccess access) {
55+
public void duringSetup(DuringSetupAccess a) {
56+
DuringSetupAccessImpl access = (DuringSetupAccessImpl) a;
4857
/*
4958
* Make sure that critical VM components are included in the base layer by registering
5059
* runtime APIs as entry points. Although the types below are part of java.base, so they
@@ -57,6 +66,12 @@ public void duringSetup(DuringSetupAccess access) {
5766
compilationSupport.registerCompilationBehavior(ReflectionUtil.lookupMethod(Runtime.class, "getRuntime"), PINNED_TO_INITIAL_LAYER);
5867
compilationSupport.registerCompilationBehavior(ReflectionUtil.lookupMethod(Runtime.class, "gc"), PINNED_TO_INITIAL_LAYER);
5968
compilationSupport.registerCompilationBehavior(ReflectionUtil.lookupMethod(Class.class, "getResource", String.class), PINNED_TO_INITIAL_LAYER);
69+
70+
AnalysisMetaAccess metaAccess = access.getMetaAccess();
71+
metaAccess.lookupJavaType(Uninterruptible.class).registerAsReachable("Core type");
72+
metaAccess.lookupJavaType(Proxy.getProxyClass(ClassLoaders.appClassLoader(), Uninterruptible.class)).registerAsInstantiated("Core type");
73+
metaAccess.lookupJavaType(BootstrapMethodInfo.class).registerAsInstantiated("Core type");
74+
metaAccess.lookupJavaType(BootstrapMethodInfo.ExceptionWrapper.class).registerAsInstantiated("Core type");
6075
}
6176

6277
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisMethod.WrappedMethod;
110110
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisMethod.WrappedMethod.WrappedMember;
111111
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisType;
112+
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisType.Reader;
112113
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisType.WrappedType;
113114
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisType.WrappedType.SerializationGenerated;
114115
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnnotationElement;
@@ -1858,6 +1859,36 @@ private void scanCompanionField(DynamicHub hub) {
18581859
instance.readFieldValue(metaAccess.lookupJavaField(dynamicHubCompanionField));
18591860
}
18601861

1862+
public boolean isReachableInPreviousLayer(AnalysisType type) {
1863+
return getPropertyInPreviousLayer(type, Reader::getIsReachable);
1864+
}
1865+
1866+
public boolean isInstantiatedInPreviousLayer(AnalysisType type) {
1867+
return getPropertyInPreviousLayer(type, Reader::getIsInstantiated);
1868+
}
1869+
1870+
private boolean getPropertyInPreviousLayer(AnalysisType type, Function<PersistedAnalysisType.Reader, Boolean> propertyGetter) {
1871+
Integer typeId;
1872+
if (type.getWrapped() instanceof BaseLayerType baseLayerType) {
1873+
typeId = baseLayerType.getBaseLayerId();
1874+
} else {
1875+
/*
1876+
* Types that cannot be loaded manually can be duplicated and can get a new type id even
1877+
* if they were in a shared layer. In this case, using the type identifier can still
1878+
* retrieve the id from the shared layer.
1879+
*/
1880+
String typeDescriptor = imageLayerSnapshotUtil.getTypeDescriptor(type);
1881+
typeId = typeDescriptorToBaseLayerId.get(typeDescriptor);
1882+
}
1883+
if (typeId != null) {
1884+
var typeInfo = findType(typeId);
1885+
if (typeInfo != null) {
1886+
return propertyGetter.apply(typeInfo);
1887+
}
1888+
}
1889+
return false;
1890+
}
1891+
18611892
public record LayeredSimulationResult(boolean successful, EconomicMap<AnalysisField, JavaConstant> staticFieldValues) {
18621893
}
18631894

0 commit comments

Comments
 (0)