Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -353,14 +353,14 @@ public boolean isDelayed() {
return compilationBehavior == CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
}

public void setPinnedToInitialLayer() {
public void setPinnedToInitialLayer(Object reason) {
BigBang bigbang = getUniverse().getBigbang();
AnalysisError.guarantee(bigbang.getHostVM().buildingInitialLayer(), "Methods can only be pinned to the initial layer: %s", this);
boolean nonAbstractInstanceClass = !declaringClass.isArray() && declaringClass.isInstanceClass() && !declaringClass.isAbstract();
AnalysisError.guarantee(nonAbstractInstanceClass, "Only methods from non abstract instance class can be delayed: %s", this);
bigbang.forcedAddRootMethod(this, true, "Method is pinned to the initial layer");
AnalysisError.guarantee(nonAbstractInstanceClass, "Only methods from non abstract instance class can be pinned: %s", this);
bigbang.forcedAddRootMethod(this, true, "pinned to initial layer: " + reason);
if (!isStatic()) {
declaringClass.registerAsInstantiated(this + " is pinned to the initial layer");
declaringClass.registerAsInstantiated("declared method " + this.format("%H.%n(%p)") + " is pinned to initial layer: " + reason);
}
setNewCompilationBehavior(CompilationBehavior.PINNED_TO_INITIAL_LAYER);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,10 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
initializeBigBang(bb, options, featureHandler, nativeLibraries, debug, aMetaAccess, aUniverse.getSubstitutions(), loader, true,
new SubstrateClassInitializationPlugin(hostVM), this.isStubBasedPluginsSupported(), aProviders);

loader.classLoaderSupport.getClassesToIncludeUnconditionally().forEach(clazz -> bb.tryRegisterTypeForBaseImage(originalMetaAccess.lookupJavaType(clazz)));
if (ImageLayerBuildingSupport.buildingSharedLayer()) {
HostedImageLayerBuildingSupport.singleton().registerBaseLayerTypes(bb, originalMetaAccess, loader.classLoaderSupport);
}

if (loader.classLoaderSupport.isPreserveMode()) {
PreserveOptionsSupport.registerPreservedClasses(bb, originalMetaAccess, loader.classLoaderSupport);
}
Expand Down Expand Up @@ -1244,71 +1247,8 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature
HostedImageLayerBuildingSupport.singleton().getLoader().relinkNonTransformedStaticFinalFieldValues();
}

/*
* System classes and fields are necessary to tell the static analysis that certain things
* really "exist". The most common reason for that is that there are no instances and
* allocations of these classes seen during the static analysis. The heap chunks are one
* good example.
*/
try (Indent ignored = debug.logAndIndent("add initial classes/fields/methods")) {
bb.addRootClass(Object.class, false, false).registerAsInstantiated("root class");
bb.addRootField(DynamicHub.class, "vtable");
bb.addRootClass(String.class, false, false).registerAsInstantiated("root class");
bb.addRootClass(String[].class, false, false).registerAsInstantiated("root class");
bb.addRootField(String.class, "value").registerAsInstantiated("root class");
bb.addRootClass(long[].class, false, false).registerAsInstantiated("root class");
bb.addRootClass(byte[].class, false, false).registerAsInstantiated("root class");
bb.addRootClass(byte[][].class, false, false).registerAsInstantiated("root class");
bb.addRootClass(Object[].class, false, false).registerAsInstantiated("root class");
bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated("root class");
bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated("root class");

/* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */
bb.addRootClass(MethodPointer.class, false, true);
if (SubstrateOptions.useRelativeCodePointers()) {
bb.addRootClass(MethodOffset.class, false, true);
}

bb.addRootMethod(ReflectionUtil.lookupMethod(SubstrateArraycopySnippets.class, "doArraycopy", Object.class, int.class, Object.class, int.class, int.class), true,
"Runtime support, registered in " + NativeImageGenerator.class);
bb.addRootMethod(ReflectionUtil.lookupMethod(Object.class, "getClass"), true, "Runtime support, registered in " + NativeImageGenerator.class);

for (JavaKind kind : JavaKind.values()) {
if (kind.isPrimitive() && kind != JavaKind.Void) {
bb.addRootClass(kind.toJavaClass(), false, true);
bb.addRootClass(kind.toBoxedJavaClass(), false, true).registerAsInstantiated("root class");
bb.addRootField(kind.toBoxedJavaClass(), "value");
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass()), true, "Runtime support, registered in " + NativeImageGenerator.class);
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), kind.getJavaName() + "Value"), true, "Runtime support, registered in " + NativeImageGenerator.class);
/*
* Register the cache location as reachable.
* BoxingSnippets$Templates#getCacheLocation accesses the cache field.
*/
Class<?>[] innerClasses = kind.toBoxedJavaClass().getDeclaredClasses();
if (innerClasses != null && innerClasses.length > 0) {
bb.getMetaAccess().lookupJavaType(innerClasses[0]).registerAsReachable("inner class of root class");
}
}
}
/* SubstrateTemplates#toLocationIdentity accesses the Counter.value field. */
bb.getMetaAccess().lookupJavaType(JavaKind.Void.toJavaClass()).registerAsReachable("root class");
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.util.Counter.class).registerAsReachable("root class");
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.allocationprofile.AllocationCounter.class).registerAsReachable("root class");
/*
* SubstrateAllocationProfilingData is not actually present in the image since it is
* only allocated at build time, is passed to snippets as a @ConstantParameter, and it
* only contains final fields that are constant-folded. However, since the profiling
* object is only allocated during lowering it is processed by the shadow heap after
* analysis, so its type needs to be already marked reachable at this point.
*/
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.SubstrateAllocationProfilingData.class).registerAsReachable("root class");
/*
* Similarly to above, StackSlotIdentity only gets reachable during lowering, through
* build time allocated constants. It doesn't actually end up in the image heap since
* all its fields are final and are constant-folded, but the type becomes reachable,
* through the shadow heap processing, after analysis.
*/
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.stackvalue.StackValueNode.StackSlotIdentity.class).registerAsReachable("root class");
registerRootElements(bb);

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

/**
* System classes and fields are necessary to tell the static analysis that certain things
* really "exist". The most common reason for that is that there are no instances and
* allocations of these classes seen during the static analysis. The heap chunks are one good
* example.
*/
private static void registerRootElements(Inflation bb) {
String rootClassReason = "system class included unconditionally";
String rootMethodReason = "system method included unconditionally";
bb.addRootClass(Object.class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootField(DynamicHub.class, "vtable");
bb.addRootClass(String.class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(String[].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootField(String.class, "value").registerAsInstantiated(rootClassReason);
bb.addRootClass(long[].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(byte[].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(byte[][].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(Object[].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated(rootClassReason);
bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated(rootClassReason);

/* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */
bb.addRootClass(MethodPointer.class, false, true);
if (SubstrateOptions.useRelativeCodePointers()) {
bb.addRootClass(MethodOffset.class, false, true);
}

bb.addRootMethod(ReflectionUtil.lookupMethod(SubstrateArraycopySnippets.class, "doArraycopy",
Object.class, int.class, Object.class, int.class, int.class), true, rootMethodReason);
bb.addRootMethod(ReflectionUtil.lookupMethod(Object.class, "getClass"), true, rootMethodReason);

for (JavaKind kind : JavaKind.values()) {
if (kind.isPrimitive() && kind != JavaKind.Void) {
bb.addRootClass(kind.toJavaClass(), false, true);
bb.addRootClass(kind.toBoxedJavaClass(), false, true).registerAsInstantiated(rootClassReason);
bb.addRootField(kind.toBoxedJavaClass(), "value");
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass()), true, rootMethodReason);
bb.addRootMethod(ReflectionUtil.lookupMethod(kind.toBoxedJavaClass(), kind.getJavaName() + "Value"), true, rootMethodReason);
/*
* Register the cache location as reachable.
* BoxingSnippets$Templates#getCacheLocation accesses the cache field.
*/
Class<?>[] innerClasses = kind.toBoxedJavaClass().getDeclaredClasses();
if (innerClasses != null && innerClasses.length > 0) {
bb.getMetaAccess().lookupJavaType(innerClasses[0]).registerAsReachable("inner class of " + rootClassReason);
}
}
}
/* SubstrateTemplates#toLocationIdentity accesses the Counter.value field. */
bb.getMetaAccess().lookupJavaType(JavaKind.Void.toJavaClass()).registerAsReachable(rootClassReason);
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.util.Counter.class).registerAsReachable(rootClassReason);
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.allocationprofile.AllocationCounter.class).registerAsReachable(rootClassReason);
/*
* SubstrateAllocationProfilingData is not actually present in the image since it is only
* allocated at build time, is passed to snippets as a @ConstantParameter, and it only
* contains final fields that are constant-folded. However, since the profiling object is
* only allocated during lowering it is processed by the shadow heap after analysis, so its
* type needs to be already marked reachable at this point.
*/
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.SubstrateAllocationProfilingData.class).registerAsReachable(rootClassReason);
/*
* Similarly to above, StackSlotIdentity only gets reachable during lowering, through build
* time allocated constants. It doesn't actually end up in the image heap since all its
* fields are final and are constant-folded, but the type becomes reachable, through the
* shadow heap processing, after analysis.
*/
bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.graal.stackvalue.StackValueNode.StackSlotIdentity.class).registerAsReachable(rootClassReason);
}

public static void performSnippetGraphAnalysis(BigBang bb, SubstrateReplacements replacements, OptionValues options, Function<Object, Object> objectTransformer) {
Collection<StructuredGraph> snippetGraphs = replacements.getSnippetGraphs(GraalOptions.TrackNodeSourcePosition.getValue(options), options, objectTransformer);
if (bb instanceof NativeImagePointsToAnalysis pointsToAnalysis) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platform.LINUX_AMD64;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.svm.core.SubstrateOptions;
Expand Down Expand Up @@ -72,6 +73,7 @@
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.options.OptionsContainer;
import jdk.vm.ci.meta.MetaAccessProvider;

public final class HostedImageLayerBuildingSupport extends ImageLayerBuildingSupport {

Expand Down Expand Up @@ -285,9 +287,9 @@ private static boolean isLayerUseOptionEnabled(OptionValues values) {
return false;
}

/** Currently layered images are only supported on {@link LINUX_AMD64}. */
private static boolean supportedPlatform(Platform platform) {
boolean supported = platform instanceof LINUX_AMD64;
return supported;
return platform instanceof LINUX_AMD64;
}

public static HostedImageLayerBuildingSupport initialize(HostedOptionValues values, ImageClassLoader imageClassLoader, Path builderTempDir) {
Expand Down Expand Up @@ -395,4 +397,8 @@ public static void setupSharedLayerLibrary(NativeLibraries nativeLibs) {
HostedDynamicLayerInfo.singleton().registerLibName(libName);
nativeLibs.addDynamicNonJniLibrary(libName);
}

public void registerBaseLayerTypes(BigBang bb, MetaAccessProvider originalMetaAccess, NativeImageClassLoaderSupport classLoaderSupport) {
classLoaderSupport.getClassesToIncludeUnconditionally().forEach(clazz -> bb.tryRegisterTypeForBaseImage(originalMetaAccess.lookupJavaType(clazz)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.hosted.imagelayer;

import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.util.ReflectionUtil;

import jdk.internal.misc.Unsafe;

@AutomaticallyRegisteredFeature
public class InitialLayerFeature implements InternalFeature {
@Override
public boolean isInConfiguration(Feature.IsInConfigurationAccess access) {
return ImageLayerBuildingSupport.buildingInitialLayer();
}

@Override
public void beforeAnalysis(BeforeAnalysisAccess a) {
BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a;

/*
* Make sure that critical VM components are included in the base layer by registering
* runtime APIs as entry points. Although the types below are part of java.base, so they
* would anyway be included in every base layer created with module=java.base, this ensures
* that the base layer is usable regardless of the class inclusion policy.
*/
String pinReason = "base layer entry point included unconditionally";
AnalysisMetaAccess metaAccess = access.getMetaAccess();
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Unsafe.class, "getUnsafe")).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Unsafe.class, "allocateInstance", Class.class)).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "getRuntime")).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "gc")).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Class.class, "getResource", String.class)).setPinnedToInitialLayer(pinReason);
}

}