Skip to content

[GR-61285] Relink static final fields across layers #10729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 22, 2025
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 @@ -937,7 +937,7 @@ public static Field getField(Class<?> declaredClass, String fieldName) {
public static List<Field> getExternalValueFields() throws IOException {
List<Field> externalValues = new ArrayList<>();
addImmutableCollectionsFields(externalValues);
addStaticFinalObjectFields(LocationIdentity.class, externalValues);
externalValues.addAll(getStaticFinalObjectFields(LocationIdentity.class));

try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap())) {
for (String module : List.of("jdk.internal.vm.ci", "jdk.graal.compiler", "com.oracle.graal.graal_enterprise")) {
Expand All @@ -953,7 +953,7 @@ public static List<Field> getExternalValueFields() throws IOException {
className = className.replace('/', '.').substring(0, className.length() - ".class".length());
try {
Class<?> graalClass = Class.forName(className);
addStaticFinalObjectFields(graalClass, externalValues);
externalValues.addAll(getStaticFinalObjectFields(graalClass));
} catch (ClassNotFoundException e) {
throw new GraalError(e);
}
Expand All @@ -970,10 +970,11 @@ public static List<Field> getExternalValueFields() throws IOException {
* {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible
* accessible}.
*/
public static void addStaticFinalObjectFields(Class<?> declaringClass, List<Field> fields) {
public static List<Field> getStaticFinalObjectFields(Class<?> declaringClass) {
if (Enum.class.isAssignableFrom(declaringClass)) {
return;
return List.of();
}
List<Field> fields = new ArrayList<>();
for (Field field : declaringClass.getDeclaredFields()) {
int fieldModifiers = field.getModifiers();
int fieldMask = Modifier.STATIC | Modifier.FINAL;
Expand All @@ -986,6 +987,7 @@ public static void addStaticFinalObjectFields(Class<?> declaringClass, List<Fiel
field.setAccessible(true);
fields.add(field);
}
return fields;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.graalvm.nativeimage.Platforms;

import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.AnalysisFuture;
Expand All @@ -55,6 +56,7 @@ public abstract class ImageHeapConstant implements JavaConstant, TypedConstant,
private static final AtomicInteger currentId = new AtomicInteger(1);

public static final VarHandle isReachableHandle = ReflectionUtil.unreflectField(ConstantData.class, "isReachable", MethodHandles.lookup());
public static final VarHandle originHandle = ReflectionUtil.unreflectField(ConstantData.class, "origin", MethodHandles.lookup());

abstract static class ConstantData {
/**
Expand Down Expand Up @@ -95,6 +97,11 @@ abstract static class ConstantData {
* constant created in the current layer.
*/
private boolean isInBaseLayer;
/**
* An object representing a way to retrieve the value of the constant in the hosted
* universe.
*/
@SuppressWarnings("unused") private volatile AnalysisField origin;

ConstantData(AnalysisType type, JavaConstant hostedObject, int identityHashCode, int id) {
Objects.requireNonNull(type);
Expand Down Expand Up @@ -167,6 +174,14 @@ public boolean isReachable() {
return isReachableHandle.get(constantData) != null;
}

public boolean setOrigin(AnalysisField origin) {
return originHandle.compareAndSet(constantData, null, origin);
}

public AnalysisField getOrigin() {
return constantData.origin;
}

public boolean allowConstantFolding() {
/*
* An object whose type is initialized at run time does not have hosted field values. Only
Expand Down Expand Up @@ -312,6 +327,6 @@ public int hashCode() {
@Override
public String toString() {
return "ImageHeapConstant<" + constantData.type.toJavaName() + ", reachable: " + isReachable() + ", reader installed: " + isReaderInstalled() +
", compressed: " + compressed + ", backed: " + isBackedByHostedObject() + ">";
", compressed: " + compressed + ", backed: " + isBackedByHostedObject() + ", id: " + constantData.id + ">";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ public void onFieldRead(AnalysisField field) {
}
if (isValueAvailable(field)) {
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);
}
markReachable(fieldValue, reason);
notifyAnalysis(field, null, fieldValue, reason);
}
Expand Down Expand Up @@ -196,6 +199,9 @@ public TypeData computeTypeData(AnalysisType type) {
ValueSupplier<JavaConstant> rawFieldValue = readHostedFieldValue(field, null);
data.setFieldTask(field, new AnalysisFuture<>(() -> {
JavaConstant value = createFieldValue(field, rawFieldValue, new FieldScan(field));
if (value instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) {
imageHeapConstant.setOrigin(field);
}
data.setFieldValue(field, value);
return value;
}));
Expand Down Expand Up @@ -735,6 +741,9 @@ void ensureReaderInstalled(JavaConstant constant) {
protected AnalysisFuture<JavaConstant> patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason, Consumer<ScanReason> onAnalysisModified) {
AnalysisFuture<JavaConstant> task = new AnalysisFuture<>(() -> {
JavaConstant value = onFieldValueReachable(field, fieldValue, reason, onAnalysisModified);
if (value instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) {
imageHeapConstant.setOrigin(field);
}
typeData.setFieldValue(field, value);
return value;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ struct PersistedAnalysisMethod {
argumentTypeNames @38 :List(Text);
}
polymorphicSignature :group {
callers @38 :List(MethodId);
callers @39 :List(MethodId);
}
}
}
Expand Down Expand Up @@ -187,15 +187,19 @@ struct PersistedConstant {
classConstant :group {
typeId @12 :TypeId;
}
fieldConstant :group {
originFieldId @13 :FieldId;
requiresLateLoading @14 :Bool;
}
}
}
primitiveData @13 :PrimitiveArray;
primitiveData @15 :PrimitiveArray;
relocatable :group {
key @14 :Text;
key @16 :Text;
}
}
parentConstantId @15 :ConstantId;
parentIndex @16 :Int32;
parentConstantId @17 :ConstantId;
parentIndex @18 :Int32;
}

struct KeyStoreEntry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,14 @@ protected boolean runPointsToAnalysis(String imageName, OptionValues options, De
try {
ConcurrentAnalysisAccessImpl concurrentConfig = new ConcurrentAnalysisAccessImpl(featureHandler, loader, bb, nativeLibraries, debug);
aUniverse.setConcurrentAnalysisAccess(concurrentConfig);
if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
/*
* All the field value transformers should be installed by this point.
* Accessing some fields can trigger reachability callbacks, meaning the
* concurrent analysis access needs to be set.
*/
HostedImageLayerBuildingSupport.singleton().getLoader().relinkTransformedStaticFinalFieldValues();
}
bb.runAnalysis(debug, (universe) -> {
try (StopTimer t2 = TimerCollection.createTimerAndStart(TimerCollection.Registry.FEATURES)) {
bb.getHostVM().notifyClassReachabilityListener(universe, config);
Expand Down Expand Up @@ -1166,6 +1174,14 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature
}
SubstitutionProcessor.extendsTheChain(substitutions, aUniverse.getFeatureSubstitutions());

if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
/*
* The substitution processor need to be installed as some substituted types can be
* loaded at this point.
*/
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,20 @@ public boolean isValueAvailable(AnalysisField field) {
return true;
}

/**
* Returns true if a field value interceptor ({@link FieldValueTransformation} or
* {@link FieldValueComputer}) has been registered for this field. Unlike
* {@link #hasFieldValueTransformer(AnalysisField)}, calling this method is side effect free.
*/
public static boolean hasFieldValueInterceptor(AnalysisField field) {
var interceptor = field.getFieldValueInterceptor();
if (interceptor != null && interceptor != INTERCEPTOR_ACCESSED_MARKER) {
VMError.guarantee(interceptor instanceof FieldValueTransformation || interceptor instanceof FieldValueComputer);
return true;
}
return false;
}

/**
* Returns true if a field value transformer has been registered for this field. After this
* method has been called, it is not possible to install a transformer anymore.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.flow.AnalysisParsedGraph;
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
Expand Down Expand Up @@ -521,6 +522,9 @@ private static void processEffectsOfNode(SimulateClassInitializerClusterMember c
if (field.isStatic() && field.getDeclaringClass().equals(clusterMember.type)) {
var constantValue = storeFieldNode.value().asJavaConstant();
if (constantValue != null) {
if (constantValue instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) {
imageHeapConstant.setOrigin(field);
}
// We use the java kind here and not the storage kind since that's what the
// users of (Analysis)ConstantReflectionProvider expect.
clusterMember.staticFieldValues.put(field, adaptForImageHeap(constantValue, field.getJavaKind()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ public void registerConstantCandidate(String keyName, Object obj) {
VMError.guarantee(previous == null && !constantExists(keyName), "This key has been registered before: %s", keyName);
}

public boolean isConstantRegistered(Object obj) {
return constantCandidates.containsValue(obj);
}

@Override
public void registerHeapConstant(String keyName, Object obj) {
registerConstantCandidate(keyName, obj);
Expand Down
Loading
Loading