Skip to content

Commit 1427c2c

Browse files
committed
[GR-66215] [GR-60254] Introduce Layered Field Value Transformer.
PullRequest: graal/21926
2 parents e04b7c7 + 46606f9 commit 1427c2c

33 files changed

+1549
-233
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ protected final void scanStaticFieldRoot(AnalysisField field) {
172172
protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason prevReason) {
173173
ScanReason reason = new FieldScan(field, receiver, prevReason);
174174
try {
175-
if (!bb.getUniverse().getHeapScanner().isValueAvailable(field)) {
175+
if (!bb.getUniverse().getHeapScanner().isValueAvailable(field, receiver)) {
176176
/* The value is not available yet. */
177177
return;
178178
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ private void verifyStaticFieldValue(TypeData typeData, AnalysisField field, Java
211211
}
212212

213213
private void verifyInstanceFieldValue(AnalysisField field, JavaConstant receiver, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) {
214-
if (fieldSnapshot instanceof ImageHeapConstant ihc && ihc.isInBaseLayer() && ihc.getHostedObject() == null) {
214+
if (fieldSnapshot instanceof ImageHeapConstant ihc && ihc.isInBaseLayer() && ihc.getHostedObject() == null && !(ihc instanceof ImageHeapRelocatableConstant)) {
215215
/*
216216
* We cannot verify a base layer constant which doesn't have a backing hosted
217217
* object. Since the hosted object is missing the constant would be replaced with

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private void onStaticFieldRead(AnalysisField field) {
153153
} else if (bb.trackPrimitiveValues() && field.getStorageKind().isPrimitive()) {
154154
((PointsToAnalysisField) field).saturatePrimitiveField();
155155
}
156-
} else if (isValueAvailable(field)) {
156+
} else if (isValueAvailable(field, null)) {
157157
JavaConstant fieldValue = readStaticFieldValue(field);
158158
if (fieldValue instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) {
159159
AnalysisError.guarantee(imageHeapConstant.getOrigin() != null, "The origin of the constant %s should have been registered before", imageHeapConstant);
@@ -362,6 +362,10 @@ private ImageHeapArray createImageHeapObjectArray(JavaConstant constant, Analysi
362362
}
363363

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

648652
private void updateInstanceField(AnalysisField field, ImageHeapInstance imageHeapInstance, ScanReason reason, Consumer<ScanReason> onAnalysisModified) {
649-
if (isValueAvailable(field)) {
653+
if (isValueAvailable(field, imageHeapInstance)) {
650654
JavaConstant fieldValue = imageHeapInstance.readFieldValue(field);
651655
markReachable(fieldValue, reason, onAnalysisModified);
652656
notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified);
653657
}
654658
}
655659

656-
public boolean isValueAvailable(@SuppressWarnings("unused") AnalysisField field) {
660+
@SuppressWarnings("unused")
661+
public boolean isValueAvailable(AnalysisField field, JavaConstant receiver) {
657662
return true;
658663
}
659664

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ public static void patchLayeredImageHeap() {
234234
offset = applyLayerCodePointerPatches(data, offset, layerHeapRelocs, negativeCodeBase);
235235

236236
/* Patch references in the image heap. */
237-
applyLayerImageHeapRefPatches(data.add(offset), initialLayerImageHeap);
237+
offset = applyLayerImageHeapRefPatches(data, offset, initialLayerImageHeap);
238+
239+
applyLayerImageHeapFieldUpdatePatches(data, offset, initialLayerImageHeap);
238240

239241
layerSection = layerSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
240242
}
@@ -287,12 +289,17 @@ private static int applyLayerCodePointerPatches(Pointer data, int startOffset, P
287289
return offset;
288290
}
289291

292+
/**
293+
* See {@code CrossLayerConstantRegistryFeature#generateRelocationPatchArray} for more details
294+
* about the layout.
295+
*/
290296
@Uninterruptible(reason = "Thread state not yet set up.")
291-
private static void applyLayerImageHeapRefPatches(Pointer patches, Pointer layerImageHeap) {
297+
private static int applyLayerImageHeapRefPatches(Pointer patches, int startOffset, Pointer layerImageHeap) {
292298
int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize();
293-
long countAsLong = patches.readLong(0);
299+
int offset = startOffset;
300+
long countAsLong = patches.readLong(offset);
294301
int count = UninterruptibleUtils.NumUtil.safeToInt(countAsLong);
295-
int offset = Long.BYTES;
302+
offset += Long.BYTES;
296303
int endOffset = offset + count * Integer.BYTES;
297304
while (offset < endOffset) {
298305
int heapOffset = patches.readInt(offset);
@@ -304,6 +311,62 @@ private static void applyLayerImageHeapRefPatches(Pointer patches, Pointer layer
304311
layerImageHeap.writeLong(heapOffset, referenceEncoding);
305312
}
306313
}
314+
return endOffset;
315+
}
316+
317+
/**
318+
* See {@code CrossLayerUpdaterFeature#generateUpdatePatchArray} for more details about the
319+
* layout.
320+
*/
321+
@Uninterruptible(reason = "Thread state not yet set up.")
322+
private static int applyLayerImageHeapFieldUpdatePatches(Pointer patches, int startOffset, Pointer layerImageHeap) {
323+
long countAsLong = patches.readLong(startOffset);
324+
if (countAsLong == 0) {
325+
// empty - nothing to do
326+
return startOffset + Long.BYTES;
327+
}
328+
329+
int headerSize = patches.readInt(startOffset + Long.BYTES);
330+
331+
int headerOffset = startOffset + Long.BYTES + Integer.BYTES;
332+
int headerEndOffset = headerOffset + headerSize;
333+
334+
// calculate entry offset start
335+
int entryOffset = headerEndOffset;
336+
337+
/* Now update values. */
338+
while (headerOffset < headerEndOffset) {
339+
// read appropriate slot of header
340+
int valueSize = patches.readInt(headerOffset);
341+
headerOffset += Integer.BYTES;
342+
int numEntries = patches.readInt(headerOffset);
343+
headerOffset += Integer.BYTES;
344+
for (int j = 0; j < numEntries; j++) {
345+
int heapOffset = patches.readInt(entryOffset);
346+
entryOffset += Integer.BYTES;
347+
switch (valueSize) {
348+
case 1 -> {
349+
byte value = patches.readByte(entryOffset);
350+
layerImageHeap.writeByte(heapOffset, value);
351+
entryOffset += Byte.BYTES;
352+
}
353+
case 4 -> {
354+
int value = patches.readInt(entryOffset);
355+
layerImageHeap.writeInt(heapOffset, value);
356+
entryOffset += Integer.BYTES;
357+
}
358+
case 8 -> {
359+
long value = patches.readLong(entryOffset);
360+
layerImageHeap.writeLong(heapOffset, value);
361+
entryOffset += Long.BYTES;
362+
}
363+
default -> throw VMError.shouldNotReachHereAtRuntime();
364+
}
365+
}
366+
}
367+
368+
VMError.guarantee((startOffset + Long.BYTES + Integer.BYTES + countAsLong) == entryOffset);
369+
return entryOffset;
307370
}
308371

309372
@Override
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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.core.fieldvaluetransformer;
26+
27+
import org.graalvm.nativeimage.Platform;
28+
import org.graalvm.nativeimage.Platforms;
29+
30+
import com.oracle.svm.core.util.VMError;
31+
32+
import jdk.vm.ci.meta.JavaConstant;
33+
34+
/**
35+
* Similar to {@link FieldValueTransformerWithAvailability}, except now also the value of the
36+
* receiver is taken into consideration when deciding whether a value is available or not. This
37+
* allows availability to be decided at a finer granularity (i.e. for a given field, only some
38+
* values may be available at a given moment). For static fields, the receiver is always null.
39+
*/
40+
@Platforms(Platform.HOSTED_ONLY.class)
41+
public abstract class FieldValueTransformerWithReceiverBasedAvailability implements FieldValueTransformerWithAvailability {
42+
43+
@Override
44+
public final boolean isAvailable() {
45+
throw VMError.intentionallyUnimplemented();
46+
}
47+
48+
/**
49+
* Returns true when the value for this custom computation is available.
50+
*/
51+
public abstract boolean isAvailable(JavaConstant receiver);
52+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
3737
import com.oracle.svm.core.heap.UnknownObjectField;
3838
import com.oracle.svm.core.heap.UnknownPrimitiveField;
39+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
40+
import com.oracle.svm.core.layered.LayeredFieldValue;
41+
import com.oracle.svm.core.layered.LayeredFieldValueTransformer;
3942
import com.oracle.svm.core.meta.SharedType;
4043

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

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

97102
/**
@@ -182,4 +187,45 @@ public void setHubMetadata(RuntimeDynamicHubMetadata hubMetadata) {
182187
public void setReflectionMetadata(RuntimeReflectionMetadata reflectionMetadata) {
183188
this.reflectionMetadata = reflectionMetadata;
184189
}
190+
191+
/**
192+
* In layered builds it is possible for a {@link DynamicHubCompanion#arrayHub} to become
193+
* reachable in a later layer than the layer in which the companion is installed in. When this
194+
* happens we must update the companion's field to point to the newly installed value.
195+
*/
196+
static class ArrayHubTransformer extends LayeredFieldValueTransformer<DynamicHubCompanion> {
197+
boolean appLayer = ImageLayerBuildingSupport.buildingApplicationLayer();
198+
199+
@Override
200+
public boolean isValueAvailable(DynamicHubCompanion receiver) {
201+
/*
202+
* As soon as we have a non-null value we set the field. Otherwise, once the ready for
203+
* compilation phase has been reached no new dynamic hubs will be added and the value
204+
* can set.
205+
*/
206+
return receiver.arrayHub != null || BuildPhaseProvider.isReadyForCompilation();
207+
}
208+
209+
@Override
210+
public Result transform(DynamicHubCompanion receiver) {
211+
/*
212+
* When there is a concrete array hub value, then no update is needed in later layers.
213+
* However, when both the value is null and we are not in the application layer, then we
214+
* may update the value in a later layer.
215+
*/
216+
return new Result(receiver.arrayHub, receiver.arrayHub == null && !appLayer);
217+
}
218+
219+
@Override
220+
public boolean isUpdateAvailable(DynamicHubCompanion receiver) {
221+
/* A concrete new value has been found. */
222+
return receiver.arrayHub != null;
223+
}
224+
225+
@Override
226+
public Result update(DynamicHubCompanion receiver) {
227+
assert receiver.arrayHub != null : "update should only be called when a valid arrayHub is available";
228+
return new Result(receiver.arrayHub, false);
229+
}
230+
}
185231
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapLayoutInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,8 @@ public long getWritablePatchedOffset() {
114114
public long getWritablePatchedSize() {
115115
return writablePatchedSize;
116116
}
117+
118+
public boolean isWritablePatched(long offset) {
119+
return offset >= writablePatchedOffset && offset < writablePatchedOffset + writablePatchedSize;
120+
}
117121
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.core.layered;
26+
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Retention;
29+
import java.lang.annotation.RetentionPolicy;
30+
import java.lang.annotation.Target;
31+
32+
import org.graalvm.nativeimage.Platform;
33+
import org.graalvm.nativeimage.Platforms;
34+
35+
import com.oracle.svm.core.heap.UnknownObjectField;
36+
import com.oracle.svm.core.heap.UnknownPrimitiveField;
37+
38+
/**
39+
* Denotes a field which requires a {@link LayeredFieldValueTransformer} when building layered
40+
* images. Note this annotation is only relevant for layered builds and it is legal for a field to
41+
* have both this annotation and {@link UnknownObjectField}/{@link UnknownPrimitiveField}. For
42+
* layered builds, this annotation will take priority and the Unknown field annotation will be
43+
* ignored.
44+
* <p>
45+
* See {@link LayeredFieldValueTransformer} for an explanation of the transformation behavior.
46+
*/
47+
@Retention(RetentionPolicy.RUNTIME)
48+
@Target(ElementType.FIELD)
49+
@Platforms(Platform.HOSTED_ONLY.class)
50+
public @interface LayeredFieldValue {
51+
52+
Class<? extends LayeredFieldValueTransformer<?>> transformer();
53+
}

0 commit comments

Comments
 (0)