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
4 changes: 4 additions & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,10 @@
org.graalvm.nativeimage.foreign,
org.graalvm.truffle.runtime.svm,
com.oracle.truffle.enterprise.svm""",
"""com.oracle.svm.common.hosted.layeredimage to org.graalvm.nativeimage.pointsto,
org.graalvm.nativeimage.builder""",
"""com.oracle.svm.common.layeredimage to org.graalvm.nativeimage.pointsto,
org.graalvm.nativeimage.builder""",
],
},
"noMavenJavadoc": True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.AtomicUtils;
import com.oracle.graal.pointsto.util.ConcurrentLightHashSet;
import com.oracle.svm.common.hosted.layeredimage.LayeredCompilationSupport;
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior;
import com.oracle.svm.common.meta.MultiMethod;

import jdk.graal.compiler.debug.DebugContext;
Expand Down Expand Up @@ -198,7 +201,7 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
*/
private boolean hasOpaqueReturn;

private CompilationBehavior compilationBehavior = CompilationBehavior.DEFAULT;
private LayeredCompilationBehavior.Behavior compilationBehavior;

@SuppressWarnings({"this-escape", "unchecked"})
protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey, Map<MultiMethodKey, MultiMethod> multiMethodMap) {
Expand Down Expand Up @@ -280,6 +283,19 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped,
parsingContextMaxDepth = universe.analysisPolicy().parsingContextMaxDepth();

this.enableReachableInCurrentLayer = universe.hostVM.enableReachableInCurrentLayer();
compilationBehavior = LayeredCompilationBehavior.Behavior.DEFAULT;
if (universe.hostVM.buildingImageLayer()) {
var annotationExtractor = universe.getAnnotationExtractor();
if (annotationExtractor.hasAnnotation(wrapped, LayeredCompilationBehavior.class)) {
LayeredCompilationBehavior behavior = annotationExtractor.extractAnnotation(wrapped, LayeredCompilationBehavior.class, true);
compilationBehavior = behavior.value();
if (compilationBehavior == LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER && universe.hostVM.buildingExtensionLayer() && !isInBaseLayer) {
var errorMessage = String.format("User methods with layered compilation behavior %s must be registered via %s in the initial layer",
LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER, LayeredCompilationSupport.class);
throw AnalysisError.userError(errorMessage);
}
}
}
}

@SuppressWarnings("this-escape")
Expand Down Expand Up @@ -314,21 +330,21 @@ protected AnalysisMethod(AnalysisMethod original, MultiMethodKey multiMethodKey)
}

/**
* This method should not be used directly, except to set the {@link CompilationBehavior} from a
* previous layer. To set a new {@link CompilationBehavior}, please use the associated setter.
* This method should not be used directly, except to set the {@link Behavior} from a previous
* layer. To set a new {@link Behavior}, please use the associated setter.
*/
public void setCompilationBehavior(CompilationBehavior compilationBehavior) {
public void setCompilationBehavior(LayeredCompilationBehavior.Behavior compilationBehavior) {
assert getUniverse().getBigbang().getHostVM().buildingImageLayer() : "The method compilation behavior can only be set in layered images";
this.compilationBehavior = compilationBehavior;
}

private void setNewCompilationBehavior(CompilationBehavior compilationBehavior) {
assert (!isInBaseLayer && this.compilationBehavior == CompilationBehavior.DEFAULT) || this.compilationBehavior == compilationBehavior : "The method was already assigned " +
private void setNewCompilationBehavior(LayeredCompilationBehavior.Behavior compilationBehavior) {
assert (!isInBaseLayer && this.compilationBehavior == LayeredCompilationBehavior.Behavior.DEFAULT) || this.compilationBehavior == compilationBehavior : "The method was already assigned " +
this.compilationBehavior + ", but trying to assign " + compilationBehavior;
setCompilationBehavior(compilationBehavior);
}

public CompilationBehavior getCompilationBehavior() {
public LayeredCompilationBehavior.Behavior getCompilationBehavior() {
return compilationBehavior;
}

Expand All @@ -342,35 +358,35 @@ public void setFullyDelayedToApplicationLayer() {
AnalysisError.guarantee(parsedGraphCacheState.get() == GraphCacheEntry.UNPARSED, "The method %s was marked as delayed to the application layer but was already parsed", this);
AnalysisError.guarantee(!hostVM.hasAlwaysInlineDirective(this), "Method %s with an always inline directive cannot be delayed to the application layer as such methods cannot be inlined", this);
AnalysisError.guarantee(isConcrete(), "Method %s is not concrete and cannot be delayed to the application layer", this);
setNewCompilationBehavior(CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER);
setNewCompilationBehavior(LayeredCompilationBehavior.Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER);
}

/**
* Returns true if this method is marked as delayed to the application layer and the current
* layer is a shared layer.
*/
public boolean isDelayed() {
return compilationBehavior == CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
return compilationBehavior == LayeredCompilationBehavior.Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
}

/**
* Ensures this method is compiled in the initial layer. See
* {@link CompilationBehavior#PINNED_TO_INITIAL_LAYER} for more details.
* {@link Behavior#PINNED_TO_INITIAL_LAYER} for more details.
*/
public void setPinnedToInitialLayer(Object reason) {
public void setPinnedToInitialLayer() {
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 pinned: %s", this);
bigbang.forcedAddRootMethod(this, true, "pinned to initial layer: " + reason);
bigbang.forcedAddRootMethod(this, true, "pinned to initial layer");
if (!isStatic()) {
declaringClass.registerAsInstantiated("declared method " + this.format("%H.%n(%p)") + " is pinned to initial layer: " + reason);
declaringClass.registerAsInstantiated("declared method " + this.format("%H.%n(%p)") + " is pinned to initial layer");
}
setNewCompilationBehavior(CompilationBehavior.PINNED_TO_INITIAL_LAYER);
setNewCompilationBehavior(LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER);
}

public boolean isPinnedToInitialLayer() {
return compilationBehavior == CompilationBehavior.PINNED_TO_INITIAL_LAYER;
return compilationBehavior == LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER;
}

private static String createName(ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey) {
Expand Down Expand Up @@ -1432,40 +1448,4 @@ public boolean hasOpaqueReturn() {
}

protected abstract AnalysisMethod createMultiMethod(AnalysisMethod analysisMethod, MultiMethodKey newMultiMethodKey);

/**
* This state represents how a method should be compiled in layered images. The state of a
* method can only be decided in the first layer if it is marked as tracked across layers. The
* state has to stay the same across all the extension layers. If not specified, the state of a
* method will be {@link CompilationBehavior#DEFAULT}.
*/
public enum CompilationBehavior {

/**
* Method remains unanalyzed until the application layer and any inlining in a shared layer
* is prevented. A call to the method in a shared layer will be replaced by an indirect
* call. The compilation of those methods is then forced in the application layer and the
* corresponding symbol is declared as global.
*
* A delayed method that is not referenced in any shared layer is treated as a
* {@link CompilationBehavior#DEFAULT} method in the application layer and does not have to
* be compiled. If it is only referenced in the application layer, it might be inlined and
* not compiled at all.
*/
FULLY_DELAYED_TO_APPLICATION_LAYER,

/**
* Method can be inlined into other methods, both before analysis and during compilation,
* and will be compiled as a distinct compilation unit as stipulated by the normal native
* image generation process (i.e., the method is installed as a root and/or a reference to
* the method exists via a call and/or an explicit MethodReference).
*/
DEFAULT,

/**
* Method is pinned to the initial layer, meaning it has to be analyzed and compiled in this
* specific layer.
*/
PINNED_TO_INITIAL_LAYER,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.common.hosted.layeredimage;

import java.lang.reflect.Executable;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior.Behavior;

/**
* Used to programmatically associate {@link LayeredCompilationBehavior} to a method.
*/
@Platforms(Platform.HOSTED_ONLY.class)
public abstract class LayeredCompilationSupport {

public static LayeredCompilationSupport singleton() {
return ImageSingletons.lookup(LayeredCompilationSupport.class);
}

/**
* Registers an explicit {@link LayeredCompilationBehavior} to a method. Note it is illegal to
* pass the compilation behavior {@link Behavior#DEFAULT} via this method. In addition, this
* method should be exclusively called by {@link Feature}s in {@link Feature#duringSetup}.
*/
public abstract void registerCompilationBehavior(Executable method, LayeredCompilationBehavior.Behavior behavior);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.common.layeredimage;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Used to specify how a method needs to be compiled when building layered images.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LayeredCompilationBehavior {
/**
* This state represents how a method should be compiled in layered images. The state of a
* method can only be decided in the first layer if it is marked as tracked across layers. The
* state has to stay the same across all the extension layers. If not specified, the state of a
* method will be {@link Behavior#DEFAULT}.
*/
enum Behavior {

/**
* Method remains unanalyzed until the application layer and any inlining in a shared layer
* is prevented. A call to the method in a shared layer will be replaced by an indirect
* call. The compilation of those methods is then forced in the application layer and the
* corresponding symbol is declared as global.
*
* A delayed method that is not referenced in any shared layer is treated as a
* {@link Behavior#DEFAULT} method in the application layer and does not have to be
* compiled. If it is only referenced in the application layer, it might be inlined and not
* compiled at all.
*/
FULLY_DELAYED_TO_APPLICATION_LAYER,

/**
* Method can be inlined into other methods, both before analysis and during compilation,
* and will be compiled as a distinct compilation unit as stipulated by the normal native
* image generation process (i.e., the method is installed as a root and/or a reference to
* the method exists via a call and/or an explicit MethodReference).
*/
DEFAULT,

/**
* Method is pinned to the initial layer, meaning it has to be analyzed and compiled in this
* specific layer.
*/
PINNED_TO_INITIAL_LAYER,
}

Behavior value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordBase;

import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.c.function.CEntryPointActions;
Expand Down Expand Up @@ -165,6 +166,7 @@ public List<String> getInputArguments() {
* For layered images this method is delayed until the application layer. This is necessary so
* that the method handle can be inlined before analysis.
*/
@LayeredCompilationBehavior(LayeredCompilationBehavior.Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
public static void invokeMain(String[] args) throws Throwable {
String[] mainArgs = args;
if (ImageSingletons.contains(PreMainSupport.class)) {
Expand Down Expand Up @@ -285,7 +287,9 @@ public static int run(int argc, CCharPointerPointer argv) {
}
}

/** SVM start-up logic should be pinned to the initial layer. */
@Uninterruptible(reason = "Thread state not setup yet.")
@LayeredCompilationBehavior(LayeredCompilationBehavior.Behavior.PINNED_TO_INITIAL_LAYER)
private static int doRun(int argc, CCharPointerPointer argv) {
try {
CPUFeatureAccess cpuFeatureAccess = ImageSingletons.lookup(CPUFeatureAccess.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.impl.InternalPlatform.WINDOWS_BASE;

import com.oracle.svm.common.layeredimage.LayeredCompilationBehavior;
import com.oracle.svm.core.heap.dump.HeapDumping;
import com.oracle.svm.core.jdk.management.ManagementAgentModule;
import com.oracle.svm.core.option.APIOption;
Expand Down Expand Up @@ -173,6 +174,7 @@ public static boolean hasHeapDumpSupport() {
* the app layer. Otherwise {@link SubstrateOptions#Name} will refer to the initial layer's
* name.
*/
@LayeredCompilationBehavior(LayeredCompilationBehavior.Behavior.FULLY_DELAYED_TO_APPLICATION_LAYER)
static String determineHeapDumpPath() {
return HeapDumping.getHeapDumpPath(SubstrateOptions.Name.getValue() + ".hprof");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
import com.oracle.svm.core.traits.SingletonTraits;
import com.oracle.svm.core.util.ByteArrayReader;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.FeatureImpl.AfterCompilationAccessImpl;
import com.oracle.svm.util.ReflectionUtil;

Expand Down Expand Up @@ -116,10 +115,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
if (VMInspectionOptions.hasHeapDumpSupport()) {
RuntimeSupport.getRuntimeSupport().addStartupHook(new HeapDumpStartupHook());
RuntimeSupport.getRuntimeSupport().addShutdownHook(new HeapDumpShutdownHook());
if (ImageLayerBuildingSupport.buildingImageLayer()) {
var method = ReflectionUtil.lookupMethod(VMInspectionOptions.class, "determineHeapDumpPath");
((FeatureImpl.BeforeAnalysisAccessImpl) access).getMetaAccess().lookupJavaMethod(method).setFullyDelayedToApplicationLayer();
}
}
}

Expand Down
Loading