diff --git a/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java b/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java
index 7d5b5c2841a4..d4040773ca90 100644
--- a/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java
+++ b/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java
@@ -44,6 +44,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
@@ -113,6 +114,13 @@ enum Kind {
* @since 22.3
*/
FieldOffset,
+ /**
+ * The static field base Object as it would be computed by
+ * {@link sun.misc.Unsafe#staticFieldBase(Field)}.
+ *
+ * @since 23.0
+ */
+ StaticFieldBase,
/**
* The int or long field is set to the offset of the first array element of the array class
* {@link #declClass}, as it would be computed by
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java
index d870320c04f5..31a9663c9266 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java
@@ -27,7 +27,6 @@
//Checkstyle: stop
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.AtomicFieldUpdaterOffset;
-import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.FieldOffset;
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.Reset;
import java.lang.ref.ReferenceQueue;
@@ -341,7 +340,6 @@ public static int getCommonPoolParallelism() {
private static Unsafe U;
@Alias @TargetElement(onlyWith = JDK19OrLater.class) //
- @RecomputeFieldValue(kind = FieldOffset, name = "poolIds") //
private static long POOLIDS;
@Substitute
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java
index 3de264d87507..4d29f4f90cfa 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java
@@ -26,6 +26,7 @@
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.AtomicFieldUpdaterOffset;
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.FieldOffset;
+import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.StaticFieldBase;
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.TranslateFieldOffset;
import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
@@ -48,6 +49,7 @@
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.util.GraalAccess;
import com.oracle.svm.core.BuildPhaseProvider;
+import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability;
@@ -133,11 +135,11 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate
constantValue = JavaConstant.defaultForKind(getJavaKind());
break;
case FieldOffset:
- try {
- f = targetClass.getDeclaredField(targetName);
- } catch (NoSuchFieldException e) {
- throw shouldNotReachHere("could not find target field " + targetClass.getName() + "." + targetName + " for alias " + annotated.format("%H.%n"));
- }
+ f = getField(annotated, targetClass, targetName);
+ break;
+ case StaticFieldBase:
+ f = getField(annotated, targetClass, targetName);
+ UserError.guarantee(Modifier.isStatic(f.getModifiers()), "Target field must be static for %s computation of %s", StaticFieldBase, fieldFormat());
break;
case Custom:
if (initialTransformer != null) {
@@ -154,15 +156,24 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate
}
}
boolean isOffsetField = isOffsetRecomputation(kind);
+ boolean isStaticFieldBase = kind == StaticFieldBase;
guarantee(!isFinal || !isOffsetField);
- this.isValueAvailableBeforeAnalysis = customValueAvailableBeforeAnalysis && !isOffsetField;
- this.isValueAvailableOnlyAfterAnalysis = customValueAvailableOnlyAfterAnalysis || isOffsetField;
+ this.isValueAvailableBeforeAnalysis = customValueAvailableBeforeAnalysis && !isOffsetField && !isStaticFieldBase;
+ this.isValueAvailableOnlyAfterAnalysis = customValueAvailableOnlyAfterAnalysis || isOffsetField || isStaticFieldBase;
this.isValueAvailableOnlyAfterCompilation = customValueAvailableOnlyAfterCompilation;
this.targetField = f;
this.fieldValueTransformer = transformer;
this.valueCache = EconomicMap.create();
}
+ private static Field getField(ResolvedJavaField annotated, Class> targetClass, String targetName) {
+ try {
+ return targetClass.getDeclaredField(targetName);
+ } catch (NoSuchFieldException e) {
+ throw UserError.abort("Could not find target field " + targetClass.getName() + "." + targetName + " for alias " + annotated.format("%H.%n"));
+ }
+ }
+
public static boolean isOffsetRecomputation(RecomputeFieldValue.Kind kind) {
return offsetComputationKinds.contains(kind);
}
@@ -282,6 +293,10 @@ public JavaConstant readValue(MetaAccessProvider metaAccess, JavaConstant receiv
case ArrayIndexShift:
constantValue = asConstant(ConfigurationValues.getObjectLayout().getArrayIndexShift(JavaKind.fromJavaClass(targetClass.getComponentType())));
return constantValue;
+ case StaticFieldBase:
+ Object staticFieldsArray = targetField.getType().isPrimitive() ? StaticFieldsSupport.getStaticPrimitiveFields() : StaticFieldsSupport.getStaticObjectFields();
+ constantValue = GraalAccess.getOriginalSnippetReflection().forObject(staticFieldsArray);
+ return constantValue;
}
ReadLock readLock = valueCacheLock.readLock();
@@ -342,9 +357,9 @@ private JavaConstant computeValue(MetaAccessProvider metaAccess, JavaConstant re
originalValue = fetchOriginalValue(metaAccess, receiver, originalSnippetReflection);
Object newValue = fieldValueTransformer.transform(receiverValue, originalValue);
checkValue(newValue);
- result = originalSnippetReflection.forBoxed(annotated.getJavaKind(), newValue);
+ result = originalSnippetReflection.forBoxed(original.getJavaKind(), newValue);
- assert result.getJavaKind() == annotated.getJavaKind();
+ assert result.getJavaKind() == original.getJavaKind();
break;
default:
throw shouldNotReachHere("Field recomputation of kind " + kind + " for " + fieldFormat() + " not yet supported");
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java
index 1e4317f0f664..17f89d9127dd 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java
@@ -29,6 +29,7 @@
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.ArrayIndexScale;
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.ArrayIndexShift;
import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.FieldOffset;
+import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.StaticFieldBase;
import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createStandardInlineInfo;
import java.lang.reflect.Field;
@@ -87,6 +88,7 @@
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.hosted.SVMHost;
import com.oracle.svm.hosted.classinitialization.ClassInitializerGraphBuilderPhase;
+import com.oracle.svm.hosted.phases.ConstantFoldLoadFieldPlugin;
import com.oracle.svm.hosted.snippets.ReflectionPlugins;
import jdk.vm.ci.meta.JavaKind;
@@ -131,6 +133,8 @@ static class Options {
private static ResolvedJavaType resolvedUnsafeClass;
private static ResolvedJavaType resolvedSunMiscUnsafeClass;
+ private ResolvedJavaMethod unsafeStaticFieldOffsetMethod;
+ private ResolvedJavaMethod unsafeStaticFieldBaseMethod;
private ResolvedJavaMethod unsafeObjectFieldOffsetFieldMethod;
private ResolvedJavaMethod sunMiscUnsafeObjectFieldOffsetMethod;
private ResolvedJavaMethod unsafeObjectFieldOffsetClassStringMethod;
@@ -192,6 +196,16 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess)
resolvedUnsafeClass = originalMetaAccess.lookupJavaType(unsafeClass);
resolvedSunMiscUnsafeClass = originalMetaAccess.lookupJavaType(sunMiscUnsafeClass);
+ Method unsafeStaticFieldOffset = unsafeClass.getMethod("staticFieldOffset", Field.class);
+ unsafeStaticFieldOffsetMethod = originalMetaAccess.lookupJavaMethod(unsafeStaticFieldOffset);
+ noCheckedExceptionsSet.add(unsafeStaticFieldOffsetMethod);
+ neverInlineSet.add(unsafeStaticFieldOffsetMethod);
+
+ Method unsafeStaticFieldBase = unsafeClass.getMethod("staticFieldBase", Field.class);
+ unsafeStaticFieldBaseMethod = originalMetaAccess.lookupJavaMethod(unsafeStaticFieldBase);
+ noCheckedExceptionsSet.add(unsafeStaticFieldBaseMethod);
+ neverInlineSet.add(unsafeStaticFieldBaseMethod);
+
Method unsafeObjectFieldOffset = unsafeClass.getMethod("objectFieldOffset", java.lang.reflect.Field.class);
unsafeObjectFieldOffsetFieldMethod = originalMetaAccess.lookupJavaMethod(unsafeObjectFieldOffset);
noCheckedExceptionsSet.add(unsafeObjectFieldOffsetFieldMethod);
@@ -266,6 +280,7 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess)
ReflectionPlugins.registerInvocationPlugins(loader, snippetReflection, annotationSubstitutions, classInitializationPlugin, plugins.getInvocationPlugins(), null,
ParsingReason.UnsafeSubstitutionAnalysis);
+ plugins.appendNodePlugin(new ConstantFoldLoadFieldPlugin(ParsingReason.UnsafeSubstitutionAnalysis));
/*
* Analyzing certain classes leads to false errors. We disable reporting for those classes
@@ -362,8 +377,11 @@ public void computeSubstitutions(SVMHost hostVM, ResolvedJavaType hostType, Opti
for (Invoke invoke : clinitGraph.getInvokes()) {
if (invoke.callTarget() instanceof MethodCallTargetNode) {
- if (isInvokeTo(invoke, unsafeObjectFieldOffsetFieldMethod) || isInvokeTo(invoke, sunMiscUnsafeObjectFieldOffsetMethod)) {
- processUnsafeObjectFieldOffsetFieldInvoke(hostType, invoke);
+ if (isInvokeTo(invoke, unsafeStaticFieldBaseMethod)) {
+ processUnsafeFieldComputation(hostType, invoke, StaticFieldBase);
+ } else if (isInvokeTo(invoke, unsafeObjectFieldOffsetFieldMethod) || isInvokeTo(invoke, sunMiscUnsafeObjectFieldOffsetMethod) ||
+ isInvokeTo(invoke, unsafeStaticFieldOffsetMethod)) {
+ processUnsafeFieldComputation(hostType, invoke, FieldOffset);
} else if (isInvokeTo(invoke, unsafeObjectFieldOffsetClassStringMethod)) {
processUnsafeObjectFieldOffsetClassStringInvoke(hostType, invoke);
} else if (isInvokeTo(invoke, unsafeArrayBaseOffsetMethod)) {
@@ -382,33 +400,39 @@ public void computeSubstitutions(SVMHost hostVM, ResolvedJavaType hostType, Opti
}
/**
- * Process call to Unsafe.objectFieldOffset(Field). The matching logic below
- * applies to the following code pattern:
- *
+ * Process calls to Unsafe.objectFieldOffset(Field),
+ * Unsafe.staticFieldOffset(Field) and Unsafe.staticFieldBase(Field).
+ * The matching logic below applies to the following code patterns:
+ *
* static final long fieldOffset = Unsafe.getUnsafe().objectFieldOffset(X.class.getDeclaredField("f"));
+ *
+ * static final long fieldOffset = Unsafe.getUnsafe().staticFieldOffset(X.class.getDeclaredField("f"));
+ *
+ * static final long fieldOffset = Unsafe.getUnsafe().staticFieldBase(X.class.getDeclaredField("f"));
*/
- private void processUnsafeObjectFieldOffsetFieldInvoke(ResolvedJavaType type, Invoke unsafeObjectFieldOffsetInvoke) {
+ private void processUnsafeFieldComputation(ResolvedJavaType type, Invoke invoke, Kind kind) {
List