Skip to content

Commit 87b0750

Browse files
committed
[GR-58701] Define SVM API surface for base layer.
PullRequest: graal/21985
2 parents a45fe3c + 47f3305 commit 87b0750

File tree

4 files changed

+150
-71
lines changed

4 files changed

+150
-71
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,14 +353,14 @@ public boolean isDelayed() {
353353
return compilationBehavior == CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
354354
}
355355

356-
public void setPinnedToInitialLayer() {
356+
public void setPinnedToInitialLayer(Object reason) {
357357
BigBang bigbang = getUniverse().getBigbang();
358358
AnalysisError.guarantee(bigbang.getHostVM().buildingInitialLayer(), "Methods can only be pinned to the initial layer: %s", this);
359359
boolean nonAbstractInstanceClass = !declaringClass.isArray() && declaringClass.isInstanceClass() && !declaringClass.isAbstract();
360-
AnalysisError.guarantee(nonAbstractInstanceClass, "Only methods from non abstract instance class can be delayed: %s", this);
361-
bigbang.forcedAddRootMethod(this, true, "Method is pinned to the initial layer");
360+
AnalysisError.guarantee(nonAbstractInstanceClass, "Only methods from non abstract instance class can be pinned: %s", this);
361+
bigbang.forcedAddRootMethod(this, true, "pinned to initial layer: " + reason);
362362
if (!isStatic()) {
363-
declaringClass.registerAsInstantiated(this + " is pinned to the initial layer");
363+
declaringClass.registerAsInstantiated("declared method " + this.format("%H.%n(%p)") + " is pinned to initial layer: " + reason);
364364
}
365365
setNewCompilationBehavior(CompilationBehavior.PINNED_TO_INITIAL_LAYER);
366366
}

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

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,10 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
11101110
initializeBigBang(bb, options, featureHandler, nativeLibraries, debug, aMetaAccess, aUniverse.getSubstitutions(), loader, true,
11111111
new SubstrateClassInitializationPlugin(hostVM), this.isStubBasedPluginsSupported(), aProviders);
11121112

1113-
loader.classLoaderSupport.getClassesToIncludeUnconditionally().forEach(clazz -> bb.tryRegisterTypeForBaseImage(originalMetaAccess.lookupJavaType(clazz)));
1113+
if (ImageLayerBuildingSupport.buildingSharedLayer()) {
1114+
HostedImageLayerBuildingSupport.singleton().registerBaseLayerTypes(bb, originalMetaAccess, loader.classLoaderSupport);
1115+
}
1116+
11141117
if (loader.classLoaderSupport.isPreserveMode()) {
11151118
PreserveOptionsSupport.registerPreservedClasses(bb, originalMetaAccess, loader.classLoaderSupport);
11161119
}
@@ -1244,71 +1247,8 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature
12441247
HostedImageLayerBuildingSupport.singleton().getLoader().relinkNonTransformedStaticFinalFieldValues();
12451248
}
12461249

1247-
/*
1248-
* System classes and fields are necessary to tell the static analysis that certain things
1249-
* really "exist". The most common reason for that is that there are no instances and
1250-
* allocations of these classes seen during the static analysis. The heap chunks are one
1251-
* good example.
1252-
*/
12531250
try (Indent ignored = debug.logAndIndent("add initial classes/fields/methods")) {
1254-
bb.addRootClass(Object.class, false, false).registerAsInstantiated("root class");
1255-
bb.addRootField(DynamicHub.class, "vtable");
1256-
bb.addRootClass(String.class, false, false).registerAsInstantiated("root class");
1257-
bb.addRootClass(String[].class, false, false).registerAsInstantiated("root class");
1258-
bb.addRootField(String.class, "value").registerAsInstantiated("root class");
1259-
bb.addRootClass(long[].class, false, false).registerAsInstantiated("root class");
1260-
bb.addRootClass(byte[].class, false, false).registerAsInstantiated("root class");
1261-
bb.addRootClass(byte[][].class, false, false).registerAsInstantiated("root class");
1262-
bb.addRootClass(Object[].class, false, false).registerAsInstantiated("root class");
1263-
bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated("root class");
1264-
bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated("root class");
1265-
1266-
/* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */
1267-
bb.addRootClass(MethodPointer.class, false, true);
1268-
if (SubstrateOptions.useRelativeCodePointers()) {
1269-
bb.addRootClass(MethodOffset.class, false, true);
1270-
}
1271-
1272-
bb.addRootMethod(ReflectionUtil.lookupMethod(SubstrateArraycopySnippets.class, "doArraycopy", Object.class, int.class, Object.class, int.class, int.class), true,
1273-
"Runtime support, registered in " + NativeImageGenerator.class);
1274-
bb.addRootMethod(ReflectionUtil.lookupMethod(Object.class, "getClass"), true, "Runtime support, registered in " + NativeImageGenerator.class);
1275-
1276-
for (JavaKind kind : JavaKind.values()) {
1277-
if (kind.isPrimitive() && kind != JavaKind.Void) {
1278-
bb.addRootClass(kind.toJavaClass(), false, true);
1279-
bb.addRootClass(kind.toBoxedJavaClass(), false, true).registerAsInstantiated("root class");
1280-
bb.addRootField(kind.toBoxedJavaClass(), "value");
1281-
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass()), true, "Runtime support, registered in " + NativeImageGenerator.class);
1282-
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), kind.getJavaName() + "Value"), true, "Runtime support, registered in " + NativeImageGenerator.class);
1283-
/*
1284-
* Register the cache location as reachable.
1285-
* BoxingSnippets$Templates#getCacheLocation accesses the cache field.
1286-
*/
1287-
Class<?>[] innerClasses = kind.toBoxedJavaClass().getDeclaredClasses();
1288-
if (innerClasses != null && innerClasses.length > 0) {
1289-
bb.getMetaAccess().lookupJavaType(innerClasses[0]).registerAsReachable("inner class of root class");
1290-
}
1291-
}
1292-
}
1293-
/* SubstrateTemplates#toLocationIdentity accesses the Counter.value field. */
1294-
bb.getMetaAccess().lookupJavaType(JavaKind.Void.toJavaClass()).registerAsReachable("root class");
1295-
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.util.Counter.class).registerAsReachable("root class");
1296-
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.allocationprofile.AllocationCounter.class).registerAsReachable("root class");
1297-
/*
1298-
* SubstrateAllocationProfilingData is not actually present in the image since it is
1299-
* only allocated at build time, is passed to snippets as a @ConstantParameter, and it
1300-
* only contains final fields that are constant-folded. However, since the profiling
1301-
* object is only allocated during lowering it is processed by the shadow heap after
1302-
* analysis, so its type needs to be already marked reachable at this point.
1303-
*/
1304-
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.SubstrateAllocationProfilingData.class).registerAsReachable("root class");
1305-
/*
1306-
* Similarly to above, StackSlotIdentity only gets reachable during lowering, through
1307-
* build time allocated constants. It doesn't actually end up in the image heap since
1308-
* all its fields are final and are constant-folded, but the type becomes reachable,
1309-
* through the shadow heap processing, after analysis.
1310-
*/
1311-
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.stackvalue.StackValueNode.StackSlotIdentity.class).registerAsReachable("root class");
1251+
registerRootElements(bb);
13121252

13131253
NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, null, aProviders, aMetaAccess, aUniverse, nativeLibraries, loader, ParsingReason.PointsToAnalysis,
13141254
bb.getAnnotationSubstitutionProcessor(), classInitializationPlugin, ConfigurationValues.getTarget(), supportsStubBasedPlugins);
@@ -1318,6 +1258,75 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature
13181258
}
13191259
}
13201260

1261+
/**
1262+
* System classes and fields are necessary to tell the static analysis that certain things
1263+
* really "exist". The most common reason for that is that there are no instances and
1264+
* allocations of these classes seen during the static analysis. The heap chunks are one good
1265+
* example.
1266+
*/
1267+
private static void registerRootElements(Inflation bb) {
1268+
String rootClassReason = "system class included unconditionally";
1269+
String rootMethodReason = "system method included unconditionally";
1270+
bb.addRootClass(Object.class, false, false).registerAsInstantiated(rootClassReason);
1271+
bb.addRootField(DynamicHub.class, "vtable");
1272+
bb.addRootClass(String.class, false, false).registerAsInstantiated(rootClassReason);
1273+
bb.addRootClass(String[].class, false, false).registerAsInstantiated(rootClassReason);
1274+
bb.addRootField(String.class, "value").registerAsInstantiated(rootClassReason);
1275+
bb.addRootClass(long[].class, false, false).registerAsInstantiated(rootClassReason);
1276+
bb.addRootClass(byte[].class, false, false).registerAsInstantiated(rootClassReason);
1277+
bb.addRootClass(byte[][].class, false, false).registerAsInstantiated(rootClassReason);
1278+
bb.addRootClass(Object[].class, false, false).registerAsInstantiated(rootClassReason);
1279+
bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated(rootClassReason);
1280+
bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated(rootClassReason);
1281+
1282+
/* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */
1283+
bb.addRootClass(MethodPointer.class, false, true);
1284+
if (SubstrateOptions.useRelativeCodePointers()) {
1285+
bb.addRootClass(MethodOffset.class, false, true);
1286+
}
1287+
1288+
bb.addRootMethod(ReflectionUtil.lookupMethod(SubstrateArraycopySnippets.class, "doArraycopy",
1289+
Object.class, int.class, Object.class, int.class, int.class), true, rootMethodReason);
1290+
bb.addRootMethod(ReflectionUtil.lookupMethod(Object.class, "getClass"), true, rootMethodReason);
1291+
1292+
for (JavaKind kind : JavaKind.values()) {
1293+
if (kind.isPrimitive() && kind != JavaKind.Void) {
1294+
bb.addRootClass(kind.toJavaClass(), false, true);
1295+
bb.addRootClass(kind.toBoxedJavaClass(), false, true).registerAsInstantiated(rootClassReason);
1296+
bb.addRootField(kind.toBoxedJavaClass(), "value");
1297+
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass()), true, rootMethodReason);
1298+
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), kind.getJavaName() + "Value"), true, rootMethodReason);
1299+
/*
1300+
* Register the cache location as reachable.
1301+
* BoxingSnippets$Templates#getCacheLocation accesses the cache field.
1302+
*/
1303+
Class<?>[] innerClasses = kind.toBoxedJavaClass().getDeclaredClasses();
1304+
if (innerClasses != null && innerClasses.length > 0) {
1305+
bb.getMetaAccess().lookupJavaType(innerClasses[0]).registerAsReachable("inner class of " + rootClassReason);
1306+
}
1307+
}
1308+
}
1309+
/* SubstrateTemplates#toLocationIdentity accesses the Counter.value field. */
1310+
bb.getMetaAccess().lookupJavaType(JavaKind.Void.toJavaClass()).registerAsReachable(rootClassReason);
1311+
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.util.Counter.class).registerAsReachable(rootClassReason);
1312+
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.allocationprofile.AllocationCounter.class).registerAsReachable(rootClassReason);
1313+
/*
1314+
* SubstrateAllocationProfilingData is not actually present in the image since it is only
1315+
* allocated at build time, is passed to snippets as a @ConstantParameter, and it only
1316+
* contains final fields that are constant-folded. However, since the profiling object is
1317+
* only allocated during lowering it is processed by the shadow heap after analysis, so its
1318+
* type needs to be already marked reachable at this point.
1319+
*/
1320+
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.SubstrateAllocationProfilingData.class).registerAsReachable(rootClassReason);
1321+
/*
1322+
* Similarly to above, StackSlotIdentity only gets reachable during lowering, through build
1323+
* time allocated constants. It doesn't actually end up in the image heap since all its
1324+
* fields are final and are constant-folded, but the type becomes reachable, through the
1325+
* shadow heap processing, after analysis.
1326+
*/
1327+
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.stackvalue.StackValueNode.StackSlotIdentity.class).registerAsReachable(rootClassReason);
1328+
}
1329+
13211330
public static void performSnippetGraphAnalysis(BigBang bb, SubstrateReplacements replacements, OptionValues options, Function<Object, Object> objectTransformer) {
13221331
Collection<StructuredGraph> snippetGraphs = replacements.getSnippetGraphs(GraalOptions.TrackNodeSourcePosition.getValue(options), options, objectTransformer);
13231332
if (bb instanceof NativeImagePointsToAnalysis pointsToAnalysis) {

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.graalvm.nativeimage.Platform;
4040
import org.graalvm.nativeimage.Platform.LINUX_AMD64;
4141

42+
import com.oracle.graal.pointsto.BigBang;
4243
import com.oracle.graal.pointsto.api.PointstoOptions;
4344
import com.oracle.graal.pointsto.util.AnalysisError;
4445
import com.oracle.svm.core.SubstrateOptions;
@@ -72,6 +73,7 @@
7273
import jdk.graal.compiler.options.OptionKey;
7374
import jdk.graal.compiler.options.OptionValues;
7475
import jdk.graal.compiler.options.OptionsContainer;
76+
import jdk.vm.ci.meta.MetaAccessProvider;
7577

7678
public final class HostedImageLayerBuildingSupport extends ImageLayerBuildingSupport {
7779

@@ -285,9 +287,9 @@ private static boolean isLayerUseOptionEnabled(OptionValues values) {
285287
return false;
286288
}
287289

290+
/** Currently layered images are only supported on {@link LINUX_AMD64}. */
288291
private static boolean supportedPlatform(Platform platform) {
289-
boolean supported = platform instanceof LINUX_AMD64;
290-
return supported;
292+
return platform instanceof LINUX_AMD64;
291293
}
292294

293295
public static HostedImageLayerBuildingSupport initialize(HostedOptionValues values, ImageClassLoader imageClassLoader, Path builderTempDir) {
@@ -395,4 +397,8 @@ public static void setupSharedLayerLibrary(NativeLibraries nativeLibs) {
395397
HostedDynamicLayerInfo.singleton().registerLibName(libName);
396398
nativeLibs.addDynamicNonJniLibrary(libName);
397399
}
400+
401+
public void registerBaseLayerTypes(BigBang bb, MetaAccessProvider originalMetaAccess, NativeImageClassLoaderSupport classLoaderSupport) {
402+
classLoaderSupport.getClassesToIncludeUnconditionally().forEach(clazz -> bb.tryRegisterTypeForBaseImage(originalMetaAccess.lookupJavaType(clazz)));
403+
}
398404
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2025, 2025, 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.svm.hosted.imagelayer;
26+
27+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
28+
import org.graalvm.nativeimage.hosted.Feature;
29+
30+
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
31+
import com.oracle.svm.core.feature.InternalFeature;
32+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
33+
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
34+
import com.oracle.svm.util.ReflectionUtil;
35+
36+
import jdk.internal.misc.Unsafe;
37+
38+
@AutomaticallyRegisteredFeature
39+
public class InitialLayerFeature implements InternalFeature {
40+
@Override
41+
public boolean isInConfiguration(Feature.IsInConfigurationAccess access) {
42+
return ImageLayerBuildingSupport.buildingInitialLayer();
43+
}
44+
45+
@Override
46+
public void beforeAnalysis(BeforeAnalysisAccess a) {
47+
BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a;
48+
49+
/*
50+
* Make sure that critical VM components are included in the base layer by registering
51+
* runtime APIs as entry points. Although the types below are part of java.base, so they
52+
* would anyway be included in every base layer created with module=java.base, this ensures
53+
* that the base layer is usable regardless of the class inclusion policy.
54+
*/
55+
String pinReason = "base layer entry point included unconditionally";
56+
AnalysisMetaAccess metaAccess = access.getMetaAccess();
57+
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Unsafe.class, "getUnsafe")).setPinnedToInitialLayer(pinReason);
58+
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Unsafe.class, "allocateInstance", Class.class)).setPinnedToInitialLayer(pinReason);
59+
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "getRuntime")).setPinnedToInitialLayer(pinReason);
60+
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "gc")).setPinnedToInitialLayer(pinReason);
61+
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Class.class, "getResource", String.class)).setPinnedToInitialLayer(pinReason);
62+
}
63+
64+
}

0 commit comments

Comments
 (0)