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 @@ -172,7 +172,7 @@ protected final void scanStaticFieldRoot(AnalysisField field) {
protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason prevReason) {
ScanReason reason = new FieldScan(field, receiver, prevReason);
try {
if (!bb.getUniverse().getHeapScanner().isValueAvailable(field)) {
if (!bb.getUniverse().getHeapScanner().isValueAvailable(field, receiver)) {
/* The value is not available yet. */
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private void verifyStaticFieldValue(TypeData typeData, AnalysisField field, Java
}

private void verifyInstanceFieldValue(AnalysisField field, JavaConstant receiver, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) {
if (fieldSnapshot instanceof ImageHeapConstant ihc && ihc.isInBaseLayer() && ihc.getHostedObject() == null) {
if (fieldSnapshot instanceof ImageHeapConstant ihc && ihc.isInBaseLayer() && ihc.getHostedObject() == null && !(ihc instanceof ImageHeapRelocatableConstant)) {
/*
* We cannot verify a base layer constant which doesn't have a backing hosted
* object. Since the hosted object is missing the constant would be replaced with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ private void onStaticFieldRead(AnalysisField field) {
} else if (bb.trackPrimitiveValues() && field.getStorageKind().isPrimitive()) {
((PointsToAnalysisField) field).saturatePrimitiveField();
}
} else if (isValueAvailable(field)) {
} else if (isValueAvailable(field, null)) {
JavaConstant fieldValue = readStaticFieldValue(field);
if (fieldValue instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) {
AnalysisError.guarantee(imageHeapConstant.getOrigin() != null, "The origin of the constant %s should have been registered before", imageHeapConstant);
Expand Down Expand Up @@ -362,6 +362,10 @@ private ImageHeapArray createImageHeapObjectArray(JavaConstant constant, Analysi
}

public void registerBaseLayerValue(ImageHeapConstant constant, Object reason) {
if (constant instanceof ImageHeapRelocatableConstant) {
// relocatable constants have no backing hosted object
return;
}
JavaConstant hostedValue = constant.getHostedObject();
AnalysisError.guarantee(hostedValue.isNonNull(), "A relinked constant cannot have a NULL_CONSTANT hosted value.");
Object existingSnapshot = imageHeap.getSnapshot(hostedValue);
Expand Down Expand Up @@ -646,14 +650,15 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason
}

private void updateInstanceField(AnalysisField field, ImageHeapInstance imageHeapInstance, ScanReason reason, Consumer<ScanReason> onAnalysisModified) {
if (isValueAvailable(field)) {
if (isValueAvailable(field, imageHeapInstance)) {
JavaConstant fieldValue = imageHeapInstance.readFieldValue(field);
markReachable(fieldValue, reason, onAnalysisModified);
notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified);
}
}

public boolean isValueAvailable(@SuppressWarnings("unused") AnalysisField field) {
@SuppressWarnings("unused")
public boolean isValueAvailable(AnalysisField field, JavaConstant receiver) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ public static void patchLayeredImageHeap() {
offset = applyLayerCodePointerPatches(data, offset, layerHeapRelocs, negativeCodeBase);

/* Patch references in the image heap. */
applyLayerImageHeapRefPatches(data.add(offset), initialLayerImageHeap);
offset = applyLayerImageHeapRefPatches(data, offset, initialLayerImageHeap);

applyLayerImageHeapFieldUpdatePatches(data, offset, initialLayerImageHeap);

layerSection = layerSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
}
Expand Down Expand Up @@ -287,12 +289,17 @@ private static int applyLayerCodePointerPatches(Pointer data, int startOffset, P
return offset;
}

/**
* See {@code CrossLayerConstantRegistryFeature#generateRelocationPatchArray} for more details
* about the layout.
*/
@Uninterruptible(reason = "Thread state not yet set up.")
private static void applyLayerImageHeapRefPatches(Pointer patches, Pointer layerImageHeap) {
private static int applyLayerImageHeapRefPatches(Pointer patches, int startOffset, Pointer layerImageHeap) {
int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize();
long countAsLong = patches.readLong(0);
int offset = startOffset;
long countAsLong = patches.readLong(offset);
int count = UninterruptibleUtils.NumUtil.safeToInt(countAsLong);
int offset = Long.BYTES;
offset += Long.BYTES;
int endOffset = offset + count * Integer.BYTES;
while (offset < endOffset) {
int heapOffset = patches.readInt(offset);
Expand All @@ -304,6 +311,62 @@ private static void applyLayerImageHeapRefPatches(Pointer patches, Pointer layer
layerImageHeap.writeLong(heapOffset, referenceEncoding);
}
}
return endOffset;
}

/**
* See {@code CrossLayerUpdaterFeature#generateUpdatePatchArray} for more details about the
* layout.
*/
@Uninterruptible(reason = "Thread state not yet set up.")
private static int applyLayerImageHeapFieldUpdatePatches(Pointer patches, int startOffset, Pointer layerImageHeap) {
long countAsLong = patches.readLong(startOffset);
if (countAsLong == 0) {
// empty - nothing to do
return startOffset + Long.BYTES;
}

int headerSize = patches.readInt(startOffset + Long.BYTES);

int headerOffset = startOffset + Long.BYTES + Integer.BYTES;
int headerEndOffset = headerOffset + headerSize;

// calculate entry offset start
int entryOffset = headerEndOffset;

/* Now update values. */
while (headerOffset < headerEndOffset) {
// read appropriate slot of header
int valueSize = patches.readInt(headerOffset);
headerOffset += Integer.BYTES;
int numEntries = patches.readInt(headerOffset);
headerOffset += Integer.BYTES;
for (int j = 0; j < numEntries; j++) {
int heapOffset = patches.readInt(entryOffset);
entryOffset += Integer.BYTES;
switch (valueSize) {
case 1 -> {
byte value = patches.readByte(entryOffset);
layerImageHeap.writeByte(heapOffset, value);
entryOffset += Byte.BYTES;
}
case 4 -> {
int value = patches.readInt(entryOffset);
layerImageHeap.writeInt(heapOffset, value);
entryOffset += Integer.BYTES;
}
case 8 -> {
long value = patches.readLong(entryOffset);
layerImageHeap.writeLong(heapOffset, value);
entryOffset += Long.BYTES;
}
default -> throw VMError.shouldNotReachHereAtRuntime();
}
}
}

VMError.guarantee((startOffset + Long.BYTES + Integer.BYTES + countAsLong) == entryOffset);
return entryOffset;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.fieldvaluetransformer;

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

import com.oracle.svm.core.util.VMError;

import jdk.vm.ci.meta.JavaConstant;

/**
* Similar to {@link FieldValueTransformerWithAvailability}, except now also the value of the
* receiver is taken into consideration when deciding whether a value is available or not. This
* allows availability to be decided at a finer granularity (i.e. for a given field, only some
* values may be available at a given moment). For static fields, the receiver is always null.
*/
@Platforms(Platform.HOSTED_ONLY.class)
public abstract class FieldValueTransformerWithReceiverBasedAvailability implements FieldValueTransformerWithAvailability {

@Override
public final boolean isAvailable() {
throw VMError.intentionallyUnimplemented();
}

/**
* Returns true when the value for this custom computation is available.
*/
public abstract boolean isAvailable(JavaConstant receiver);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.heap.UnknownPrimitiveField;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layered.LayeredFieldValue;
import com.oracle.svm.core.layered.LayeredFieldValueTransformer;
import com.oracle.svm.core.meta.SharedType;

import jdk.internal.vm.annotation.Stable;
Expand Down Expand Up @@ -90,8 +93,10 @@ public final class DynamicHubCompanion {

/**
* The hub for an array of this type, or null if the array type has been determined as
* uninstantiated by the static analysis.
* uninstantiated by the static analysis. In layered builds, it is possible for this value to be
* initially set to null and then updated in a subsequent layer.
*/
@LayeredFieldValue(transformer = ArrayHubTransformer.class) //
@Stable DynamicHub arrayHub;

/**
Expand Down Expand Up @@ -182,4 +187,45 @@ public void setHubMetadata(RuntimeDynamicHubMetadata hubMetadata) {
public void setReflectionMetadata(RuntimeReflectionMetadata reflectionMetadata) {
this.reflectionMetadata = reflectionMetadata;
}

/**
* In layered builds it is possible for a {@link DynamicHubCompanion#arrayHub} to become
* reachable in a later layer than the layer in which the companion is installed in. When this
* happens we must update the companion's field to point to the newly installed value.
*/
static class ArrayHubTransformer extends LayeredFieldValueTransformer<DynamicHubCompanion> {
boolean appLayer = ImageLayerBuildingSupport.buildingApplicationLayer();

@Override
public boolean isValueAvailable(DynamicHubCompanion receiver) {
/*
* As soon as we have a non-null value we set the field. Otherwise, once the ready for
* compilation phase has been reached no new dynamic hubs will be added and the value
* can set.
*/
return receiver.arrayHub != null || BuildPhaseProvider.isReadyForCompilation();
}

@Override
public Result transform(DynamicHubCompanion receiver) {
/*
* When there is a concrete array hub value, then no update is needed in later layers.
* However, when both the value is null and we are not in the application layer, then we
* may update the value in a later layer.
*/
return new Result(receiver.arrayHub, receiver.arrayHub == null && !appLayer);
}

@Override
public boolean isUpdateAvailable(DynamicHubCompanion receiver) {
/* A concrete new value has been found. */
return receiver.arrayHub != null;
}

@Override
public Result update(DynamicHubCompanion receiver) {
assert receiver.arrayHub != null : "update should only be called when a valid arrayHub is available";
return new Result(receiver.arrayHub, false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,8 @@ public long getWritablePatchedOffset() {
public long getWritablePatchedSize() {
return writablePatchedSize;
}

public boolean isWritablePatched(long offset) {
return offset >= writablePatchedOffset && offset < writablePatchedOffset + writablePatchedSize;
}
}
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.core.layered;

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

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

import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.heap.UnknownPrimitiveField;

/**
* Denotes a field which requires a {@link LayeredFieldValueTransformer} when building layered
* images. Note this annotation is only relevant for layered builds and it is legal for a field to
* have both this annotation and {@link UnknownObjectField}/{@link UnknownPrimitiveField}. For
* layered builds, this annotation will take priority and the Unknown field annotation will be
* ignored.
* <p>
* See {@link LayeredFieldValueTransformer} for an explanation of the transformation behavior.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Platforms(Platform.HOSTED_ONLY.class)
public @interface LayeredFieldValue {

Class<? extends LayeredFieldValueTransformer<?>> transformer();
}
Loading