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
@@ -0,0 +1,77 @@
/*
* 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.core;

import java.util.EnumSet;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.c.BoxedRelocatedPointer;
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.graal.code.CGlobalDataBasePointer;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;

/**
* This singleton contains the {@link CGlobalDataBasePointer} of the current layer. In layered
* images, there is one {@link com.oracle.svm.core.c.CGlobalData} memory space for each layer, so
* when reading a {@link com.oracle.svm.core.c.CGlobalData}, the corresponding base needs to be
* used.
*/
@AutomaticallyRegisteredImageSingleton
public class CGlobalDataPointerSingleton implements MultiLayeredImageSingleton {

/**
* Image heap object storing the base address of CGlobalData memory using a relocation. Before
* the image heap is set up, CGlobalData must be accessed via relocations in the code instead.
*/
private final BoxedRelocatedPointer cGlobalDataRuntimeBaseAddress = new BoxedRelocatedPointer(CGlobalDataBasePointer.INSTANCE);

@Platforms(Platform.HOSTED_ONLY.class)
public static CGlobalDataPointerSingleton currentLayer() {
return LayeredImageSingletonSupport.singleton().lookup(CGlobalDataPointerSingleton.class, false, true);
}

public static CGlobalDataPointerSingleton[] allLayers() {
return MultiLayeredImageSingleton.getAllLayers(CGlobalDataPointerSingleton.class);
}

public BoxedRelocatedPointer getRuntimeBaseAddress() {
return cGlobalDataRuntimeBaseAddress;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
}

@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
return PersistFlags.NOTHING;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import com.oracle.svm.core.SubstrateDiagnostics.DumpDeoptStubPointer;
import com.oracle.svm.core.SubstrateDiagnostics.DumpRecentDeoptimizations;
import com.oracle.svm.core.SubstrateDiagnostics.DumpRuntimeCodeInfoMemory;
import com.oracle.svm.core.c.BoxedRelocatedPointer;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.code.CodeInfo;
import com.oracle.svm.core.code.CodeInfoAccess;
Expand All @@ -67,7 +68,6 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.graal.RuntimeCompilation;
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.RestrictHeapAccess;
Expand Down Expand Up @@ -912,7 +912,15 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev
if (SubstrateOptions.useRelativeCodePointers()) {
log.string("Code base: ").zhex(KnownIntrinsics.codeBase()).newline();
}
log.string("CGlobalData base: ").zhex(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS.getPointer()).newline();
CGlobalDataPointerSingleton[] layeredSingletons = CGlobalDataPointerSingleton.allLayers();
if (ImageLayerBuildingSupport.buildingImageLayer()) {
for (int i = 0; i < layeredSingletons.length; ++i) {
log.string("CGlobalData base for layer ").unsigned(i).string(": ").zhex(layeredSingletons[i].getRuntimeBaseAddress().getPointer()).newline();
}
} else {
BoxedRelocatedPointer baseAddress = layeredSingletons[0].getRuntimeBaseAddress();
log.string("CGlobalData base: ").zhex(baseAddress.getPointer()).newline();
}
log.string("Containerized: ").bool(Container.singleton().isContainerized()).newline();

if (Container.singleton().isContainerized()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import com.oracle.svm.core.BuildPhaseProvider.AfterHeapLayout;
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
import com.oracle.svm.core.c.BoxedRelocatedPointer;
import com.oracle.svm.core.c.CGlobalDataImpl;
import com.oracle.svm.core.heap.UnknownPrimitiveField;
import com.oracle.svm.core.util.VMError;
Expand All @@ -43,12 +42,6 @@
* {@link CGlobalDataImpl} within static fields.
*/
public final class CGlobalDataInfo {
/**
* Image heap object storing the base address of CGlobalData memory using a relocation. Before
* the image heap is set up, CGlobalData must be accessed via relocations in the code instead.
*/
public static final BoxedRelocatedPointer CGLOBALDATA_RUNTIME_BASE_ADDRESS = new BoxedRelocatedPointer(CGlobalDataBasePointer.INSTANCE);

private final CGlobalDataImpl<?> data;
private final boolean isSymbolReference;

Expand All @@ -68,13 +61,16 @@ public final class CGlobalDataInfo {
/** Cache until writing the image in case the {@link Supplier} is costly or has side-effects. */
@Platforms(HOSTED_ONLY.class) private byte[] bytes;

private final int layerNum;

@Platforms(Platform.HOSTED_ONLY.class)
public CGlobalDataInfo(CGlobalDataImpl<?> data, boolean definedAsGlobalInPriorLayer) {
public CGlobalDataInfo(CGlobalDataImpl<?> data, boolean definedAsGlobalInPriorLayer, int layerNum) {
assert data != null;
this.data = data;
this.isSymbolReference = data.isSymbolReference();
assert !this.isSymbolReference || data.symbolName != null;
this.definedAsGlobalInPriorLayer = definedAsGlobalInPriorLayer;
this.layerNum = layerNum;
}

public CGlobalDataImpl<?> getData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,12 @@ public void remove() {
public boolean advance() {
boolean advance = current.advance();
if (!advance) {
if (cursors.hasNext()) {
while (cursors.hasNext()) {
current = cursors.next();
if (keys.add(current.getKey())) {
return true;
while (current.advance()) {
if (keys.add(current.getKey())) {
return true;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.graalvm.nativeimage.ImageSingletons;

import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.svm.core.CGlobalDataPointerSingleton;
import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.c.BoxedRelocatedPointer;
import com.oracle.svm.core.c.CGlobalData;
Expand All @@ -50,14 +51,17 @@
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
import com.oracle.svm.core.graal.nodes.CGlobalDataLoadAddressNode;
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.traits.BuiltinTraits.BuildtimeAccessOnly;
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent;
import com.oracle.svm.core.traits.SingletonTraits;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.image.RelocatableBuffer;
import com.oracle.svm.hosted.imagelayer.CodeLocation;
import com.oracle.svm.hosted.imagelayer.LoadImageSingletonFeature;
import com.oracle.svm.hosted.meta.HostedSnippetReflectionProvider;
import com.oracle.svm.util.ReflectionUtil;

Expand Down Expand Up @@ -90,10 +94,12 @@
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin.RequiredInvocationPlugin;
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import jdk.graal.compiler.nodes.java.LoadFieldNode;
import jdk.graal.compiler.nodes.java.LoadIndexedNode;
import jdk.graal.compiler.nodes.memory.ReadNode;
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
import jdk.graal.compiler.phases.util.Providers;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
Expand All @@ -103,6 +109,8 @@
public class CGlobalDataFeature implements InternalFeature {

private final Method getCGlobalDataInfoMethod = ReflectionUtil.lookupMethod(CGlobalDataNonConstantRegistry.class, "getCGlobalDataInfo", CGlobalDataImpl.class);
private final Field layerNumField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "layerNum");
private final Field cGlobalDataRuntimeBaseAddressField = ReflectionUtil.lookupField(CGlobalDataPointerSingleton.class, "cGlobalDataRuntimeBaseAddress");
private final Field offsetField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "offset");
private final Field isSymbolReferenceField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "isSymbolReference");
private final Field baseHolderPointerField = ReflectionUtil.lookupField(BoxedRelocatedPointer.class, "pointer");
Expand Down Expand Up @@ -132,6 +140,17 @@ public void duringSetup(DuringSetupAccess a) {
a.registerObjectReplacer(this::replaceObject);
}

@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
if (ImageLayerBuildingSupport.buildingImageLayer()) {
/*
* The non-constant registry needs to be rescanned in layered images to make sure it is
* always reachable in every layer and that all the data is properly tracked.
*/
((FeatureImpl.BeforeAnalysisAccessImpl) access).rescanObject(nonConstantRegistry);
}
}

@Override
public void afterHeapLayout(AfterHeapLayoutAccess access) {
layout();
Expand Down Expand Up @@ -180,8 +199,39 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
}
}

JavaConstant baseHolderConstant = providers.getSnippetReflection().forObject(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS);
ConstantNode baseHolder = ConstantNode.forConstant(baseHolderConstant, b.getMetaAccess(), b.getGraph());
ValueNode baseHolder;
if (ImageLayerBuildingSupport.buildingImageLayer()) {
/*
* When building layered images, each layer has its own CGlobalData base
* pointer, meaning the one associated with this specific CGlobalDataInfo
* needs to be used.
*/
JavaConstant cGlobalDataPointerSingletonClass = providers.getSnippetReflection().forObject(CGlobalDataPointerSingleton.class);
ConstantNode classConstant = ConstantNode.forConstant(cGlobalDataPointerSingletonClass, b.getMetaAccess(), b.getGraph());

/* Load the array containing all the singletons. */
ValueNode layers = b.add(ImageSingletons.lookup(LoadImageSingletonFeature.class).loadMultiLayeredImageSingleton(b, classConstant));

/*
* Get the layer number of the CGlobalDataInfo to get the index to use in
* the singleton array.
*/
ValueNode layerNum = b.add(LoadFieldNode.create(b.getAssumptions(), info, b.getMetaAccess().lookupJavaField(layerNumField)));

/* Use the layer number to get the corresponding singleton. */
ValueNode singleton = b.add(LoadIndexedNode.create(b.getAssumptions(), layers, layerNum, null, JavaKind.Object, b.getMetaAccess(), b.getConstantReflection()));

/* Get the CGlobalData base pointer from the singleton. */
baseHolder = b.add(LoadFieldNode.create(b.getAssumptions(), singleton, b.getMetaAccess().lookupJavaField(cGlobalDataRuntimeBaseAddressField)));
} else {
/*
* In standalone image, there is only one CGlobalData base pointer, so there
* is no need to have a custom access.
*/
JavaConstant baseHolderConstant = providers.getSnippetReflection().forObject(CGlobalDataPointerSingleton.currentLayer().getRuntimeBaseAddress());
baseHolder = ConstantNode.forConstant(baseHolderConstant, b.getMetaAccess(), b.getGraph());
}

ResolvedJavaField holderPointerField = providers.getMetaAccess().lookupJavaField(baseHolderPointerField);
StampPair pointerStamp = StampPair.createSingle(providers.getWordTypes().getWordStamp((ResolvedJavaType) holderPointerField.getType()));
LoadFieldNode baseAddress = b.add(LoadFieldNode.createOverrideStamp(pointerStamp, baseHolder, holderPointerField));
Expand Down Expand Up @@ -270,7 +320,7 @@ CGlobalDataInfo createCGlobalDataInfo(CGlobalDataImpl<?> data, boolean definedAs
"We currently do not allow CGlobalData code locations to be in a hidden class. Please adapt the code accordingly. Location: %s",
data.codeLocation);
}
CGlobalDataInfo cGlobalDataInfo = new CGlobalDataInfo(data, definedAsGlobalInPriorLayer);
CGlobalDataInfo cGlobalDataInfo = new CGlobalDataInfo(data, definedAsGlobalInPriorLayer, DynamicImageLayerInfo.getCurrentLayerNumber());
if (data.nonConstant) {
nonConstantRegistry.registerNonConstantSymbol(cGlobalDataInfo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import org.graalvm.nativeimage.AnnotationAccess;
Expand Down Expand Up @@ -128,29 +127,13 @@ public boolean isInConfiguration(IsInConfigurationAccess access) {

@Override
public void registerInvocationPlugins(Providers providers, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) {
BiFunction<GraphBuilderContext, ValueNode, ValueNode> loadMultiLayeredImageSingleton = (b, classNode) -> {
Class<?> key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant());

if (ImageLayerBuildingSupport.buildingSharedLayer()) {
/*
* Load reference to the proper slot within the cross-layer singleton table.
*/
return LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess());
} else {
/*
* Can directly load the array of all objects
*/
JavaConstant multiLayerArray = getMultiLayerConstant(key, b.getMetaAccess(), b.getSnippetReflection());
return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess());
}
};

InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins.getInvocationPlugins(), MultiLayeredImageSingleton.class);
r.register(new InvocationPlugin.RequiredInvocationPlugin("getAllLayers", Class.class) {

@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode) {
b.addPush(JavaKind.Object, loadMultiLayeredImageSingleton.apply(b, classNode));
b.addPush(JavaKind.Object, loadMultiLayeredImageSingleton(b, classNode));
return true;
}
});
Expand All @@ -159,7 +142,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode, ValueNode indexNode) {
try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) {
ValueNode layerArray = b.add(loadMultiLayeredImageSingleton.apply(b, classNode));
ValueNode layerArray = b.add(loadMultiLayeredImageSingleton(b, classNode));

helper.intrinsicArrayRangeCheck(layerArray, indexNode, ConstantNode.forInt(1));
var arrayElem = LoadIndexedNode.create(null, layerArray, indexNode, null, JavaKind.Object, b.getMetaAccess(), b.getConstantReflection());
Expand All @@ -181,6 +164,23 @@ static void checkAllowNullEntries(Class<?> key) {
"This MultiLayeredSingleton requires an entry to be installed in every layer. Please see the javadoc within MultiLayeredAllowNullEntries for more details.");
}

public ValueNode loadMultiLayeredImageSingleton(GraphBuilderContext b, ValueNode classNode) {
Class<?> key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant());

if (ImageLayerBuildingSupport.buildingSharedLayer()) {
/*
* Load reference to the proper slot within the cross-layer singleton table.
*/
return LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess());
} else {
/*
* Can directly load the array of all objects
*/
JavaConstant multiLayerArray = getMultiLayerConstant(key, b.getMetaAccess(), b.getSnippetReflection());
return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess());
}
}

/**
* This method needs to be called after all image singletons are registered. Currently, some
* singletons are registered in
Expand Down