From f52d4692ab2c29f616292df887687ffec223596c Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Mon, 1 Jul 2024 17:14:23 +0200 Subject: [PATCH] Add MetaAccessExtensionProvider.getStaticFieldForAccess This avoids the assumption that the staticFieldBase is a j.l.Class or even that the staticFieldBase uniquely maps to a type. Use getStaticFieldForAccess in VarHandleFeature. --- .../spi/MetaAccessExtensionProvider.java | 13 ++ .../hotspot/HotSpotBackendFactory.java | 7 +- .../HotSpotMetaAccessExtensionProvider.java | 36 +++- .../nodes/extended/UnsafeAccessNode.java | 48 +----- .../pointsto/standalone/PointsToAnalyzer.java | 2 +- .../AnalysisMetaAccessExtensionProvider.java | 38 +++- .../SubstrateMetaAccessExtensionProvider.java | 15 +- .../RuntimeCompilationFeature.java | 11 ++ .../svm/hosted/HostedConfiguration.java | 5 +- .../svm/hosted/NativeImageGenerator.java | 2 +- .../svm/hosted/jdk/VarHandleFeature.java | 163 ++++++++++++++---- 11 files changed, 254 insertions(+), 86 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/spi/MetaAccessExtensionProvider.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/spi/MetaAccessExtensionProvider.java index 8c1a803f0a23..92464f70e1d5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/spi/MetaAccessExtensionProvider.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/spi/MetaAccessExtensionProvider.java @@ -24,8 +24,10 @@ */ package jdk.graal.compiler.core.common.spi; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -63,4 +65,15 @@ public interface MetaAccessExtensionProvider { * given type. */ boolean canVirtualize(ResolvedJavaType instanceType); + + /** + * Given an unsafe access, try to find the static field that is being accessed. + * + * @param base the base object used in the access. + * @param offset the offset used in the access. + * @param accessKind the kind of data being accessed. + * @return the static field being accessed or null if this is not a static field access or if it + * can't be determined. + */ + ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset, JavaKind accessKind); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBackendFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBackendFactory.java index 51eaab75bcc3..fc827f368388 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBackendFactory.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBackendFactory.java @@ -75,6 +75,7 @@ import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider; import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; +import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; @@ -100,8 +101,8 @@ protected HotSpotPlatformConfigurationProvider createConfigInfoProvider(GraalHot return new HotSpotPlatformConfigurationProvider(config, barrierSet); } - protected HotSpotMetaAccessExtensionProvider createMetaAccessExtensionProvider() { - return new HotSpotMetaAccessExtensionProvider(); + protected HotSpotMetaAccessExtensionProvider createMetaAccessExtensionProvider(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { + return new HotSpotMetaAccessExtensionProvider(metaAccess, constantReflection); } protected HotSpotReplacementsImpl createReplacements(TargetDescription target, HotSpotProviders p, BytecodeProvider bytecodeProvider) { @@ -168,7 +169,7 @@ public final HotSpotBackend createBackend(HotSpotGraalRuntimeProvider graalRunti } HotSpotMetaAccessExtensionProvider metaAccessExtensionProvider; try (InitTimer rt = timer("create MetaAccessExtensionProvider")) { - metaAccessExtensionProvider = createMetaAccessExtensionProvider(); + metaAccessExtensionProvider = createMetaAccessExtensionProvider(metaAccess, constantReflection); } HotSpotStampProvider stampProvider; try (InitTimer rt = timer("create stamp provider")) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotMetaAccessExtensionProvider.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotMetaAccessExtensionProvider.java index 1619ac3a84c7..52add764a4be 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotMetaAccessExtensionProvider.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotMetaAccessExtensionProvider.java @@ -25,13 +25,25 @@ package jdk.graal.compiler.hotspot.meta; import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; - +import jdk.vm.ci.hotspot.HotSpotObjectConstant; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public class HotSpotMetaAccessExtensionProvider implements MetaAccessExtensionProvider { + private final ConstantReflectionProvider constantReflection; + private final ResolvedJavaType jlClassType; + + public HotSpotMetaAccessExtensionProvider(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { + this.constantReflection = constantReflection; + this.jlClassType = metaAccess.lookupJavaType(Class.class); + } + @Override public JavaKind getStorageKind(JavaType type) { return type.getJavaKind(); @@ -56,4 +68,26 @@ public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect public boolean canVirtualize(ResolvedJavaType instanceType) { return true; } + + @Override + public ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset, JavaKind accessKind) { + if (accessKind.getSlotCount() <= 0) { + throw new IllegalArgumentException("Unexpected access kind: " + accessKind); + } + if (!(base instanceof HotSpotObjectConstant objectConstant)) { + return null; + } + ResolvedJavaType type = constantReflection.asJavaType(base); + // check that it's indeed a j.l.Class when we get a result since constant reflection will + // also return a type if the constant wraps a ResolvedJavaType + if (type == null || !objectConstant.getType().equals(jlClassType)) { + return null; + } + for (ResolvedJavaField field : type.getStaticFields()) { + if (field.getOffset() == offset && accessKind == field.getJavaKind()) { + return field; + } + } + return null; + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/UnsafeAccessNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/UnsafeAccessNode.java index 65eea2a526dc..5bd95c7ea6ce 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/UnsafeAccessNode.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/UnsafeAccessNode.java @@ -27,11 +27,10 @@ import static jdk.graal.compiler.nodeinfo.NodeCycles.CYCLES_2; import static jdk.graal.compiler.nodeinfo.NodeSize.SIZE_1; -import java.nio.ByteOrder; - import org.graalvm.word.LocationIdentity; import jdk.graal.compiler.core.common.memory.MemoryOrderMode; +import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; import jdk.graal.compiler.core.common.type.Stamp; import jdk.graal.compiler.graph.Node; import jdk.graal.compiler.graph.NodeClass; @@ -46,7 +45,6 @@ import jdk.graal.compiler.nodes.spi.CanonicalizerTool; import jdk.graal.compiler.nodes.spi.TrackedUnsafeAccess; import jdk.graal.compiler.nodes.type.StampTool; -import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; @@ -117,8 +115,8 @@ public Node canonical(CanonicalizerTool tool) { ResolvedJavaType receiverType = StampTool.typeOrNull(object(), tool.getMetaAccess()); if (receiverType != null) { ResolvedJavaField field = null; - if (offset().isConstant()) { - field = getStaticFieldUnsafeAccess(tool.getConstantReflection()); + if (offset().isJavaConstant()) { + field = getStaticFieldUnsafeAccess(tool.getMetaAccessExtensionProvider()); if (field == null) { long constantOffset = offset().asJavaConstant().asLong(); field = receiverType.findInstanceFieldWithOffset(constantOffset, accessKind()); @@ -164,7 +162,7 @@ public Node canonical(CanonicalizerTool tool) { * * @return the static field, if any, that this node is reading */ - private ResolvedJavaField getStaticFieldUnsafeAccess(ConstantReflectionProvider constantReflection) { + private ResolvedJavaField getStaticFieldUnsafeAccess(MetaAccessExtensionProvider metaAccessExtension) { if (!object().isJavaConstant() || !offset().isJavaConstant() || object().isNullConstant() || offset().isNullConstant()) { return null; @@ -172,42 +170,6 @@ private ResolvedJavaField getStaticFieldUnsafeAccess(ConstantReflectionProvider JavaConstant objectConstant = object().asJavaConstant(); JavaConstant offsetConstant = offset().asJavaConstant(); assert objectConstant != null && offsetConstant != null : "Verified by the check at the beginning."; - ResolvedJavaType staticReceiverType = constantReflection.asJavaType(objectConstant); - if (staticReceiverType == null) { - // object is not of type Class so it is not a static field - return null; - } - return findStaticFieldWithOffset(staticReceiverType, offsetConstant.asLong(), accessKind); - } - - public static ResolvedJavaField findStaticFieldWithOffset(ResolvedJavaType type, long offset, JavaKind expectedEntryKind) { - try { - ResolvedJavaField[] declaredFields = type.getStaticFields(); - return findFieldWithOffset(offset, expectedEntryKind, declaredFields); - } catch (UnsupportedOperationException e) { - return null; - } + return metaAccessExtension.getStaticFieldForAccess(objectConstant, offsetConstant.asLong(), accessKind); } - - /** - * NOTE GR-18873: this is a copy-paste implementation derived from - * {@code jdk.vm.ci.hotspot.HotSpotResolvedObjectTypeImpl#findStaticFieldWithOffset}. - */ - private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) { - for (ResolvedJavaField field : declaredFields) { - long resolvedFieldOffset = field.getOffset(); - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN && - expectedEntryKind.isPrimitive() && - !expectedEntryKind.equals(JavaKind.Void) && - field.getJavaKind().isPrimitive()) { - resolvedFieldOffset += field.getJavaKind().getByteCount() - - Math.min(field.getJavaKind().getByteCount(), 4 + expectedEntryKind.getByteCount()); - } - if (resolvedFieldOffset == offset) { - return field; - } - } - return null; - } - } diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java index 11afc0d740ec..53f48d3a8a0c 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java @@ -148,7 +148,7 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options) { AnalysisMetaAccess aMetaAccess = new StandaloneAnalysisMetaAccess(aUniverse, originalMetaAccess); StandaloneConstantReflectionProvider aConstantReflection = new StandaloneConstantReflectionProvider(aUniverse, HotSpotJVMCIRuntime.runtime()); StandaloneConstantFieldProvider aConstantFieldProvider = new StandaloneConstantFieldProvider(aMetaAccess); - AnalysisMetaAccessExtensionProvider aMetaAccessExtensionProvider = new AnalysisMetaAccessExtensionProvider(); + AnalysisMetaAccessExtensionProvider aMetaAccessExtensionProvider = new AnalysisMetaAccessExtensionProvider(aUniverse); HostedProviders aProviders = new HostedProviders(aMetaAccess, null, aConstantReflection, aConstantFieldProvider, originalProviders.getForeignCalls(), originalProviders.getLowerer(), originalProviders.getReplacements(), originalProviders.getStampProvider(), snippetReflection, new WordTypes(aMetaAccess, wordKind), diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccessExtensionProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccessExtensionProvider.java index b651d5592286..6b9f6b8c24df 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccessExtensionProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccessExtensionProvider.java @@ -25,15 +25,25 @@ package com.oracle.graal.pointsto.meta; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.TypedConstant; +import com.oracle.graal.pointsto.util.GraalAccess; + import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; import jdk.graal.compiler.debug.GraalError; - +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public class AnalysisMetaAccessExtensionProvider implements MetaAccessExtensionProvider { + private final AnalysisUniverse aUniverse; + + public AnalysisMetaAccessExtensionProvider(AnalysisUniverse aUniverse) { + this.aUniverse = aUniverse; + } @Override public JavaKind getStorageKind(JavaType type) { @@ -43,7 +53,7 @@ public JavaKind getStorageKind(JavaType type) { @Override public boolean canConstantFoldDynamicAllocation(ResolvedJavaType t) { AnalysisType type = (AnalysisType) t; - if (type.universe.sealed()) { + if (aUniverse.sealed()) { /* Static analysis has finished, e.g., we are applying static analysis results. */ return type.isInstantiated(); } else { @@ -61,4 +71,28 @@ public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect public boolean canVirtualize(ResolvedJavaType instanceType) { return true; } + + @Override + public ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset, JavaKind accessKind) { + JavaConstant hostedObject; + if (base instanceof ImageHeapConstant imageHeapConstant) { + hostedObject = imageHeapConstant.getHostedObject(); + if (hostedObject == null) { + return null; + } + assert !(hostedObject instanceof ImageHeapConstant); + } else if (!(base instanceof TypedConstant)) { + /* + * Ideally, this path should be unreachable, i.e., we should only see TypedConstant. But + * the image heap scanning during static analysis is not implemented cleanly enough and + * invokes this method both with image heap constants and original HotSpot object + * constants. See AnalysisMetaAccess.lookupJavaType. + */ + hostedObject = base; + } else { + return null; + } + MetaAccessExtensionProvider original = GraalAccess.getOriginalProviders().getMetaAccessExtensionProvider(); + return aUniverse.lookup(original.getStaticFieldForAccess(hostedObject, offset, accessKind)); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateMetaAccessExtensionProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateMetaAccessExtensionProvider.java index 2aa1a4b4ba85..ea80bdecb662 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateMetaAccessExtensionProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateMetaAccessExtensionProvider.java @@ -25,13 +25,14 @@ package com.oracle.svm.core.graal.code; -import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; - import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; +import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -78,4 +79,14 @@ public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect public boolean canVirtualize(ResolvedJavaType instanceType) { return true; } + + @Override + public ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset, JavaKind accessKind) { + /* + * The base of unsafe static field accesses is not constant until low tier for SVM. See + * com.oracle.svm.core.StaticFieldsSupport for details on the static field base during + * analysis, compilation, and JIT compilation. + */ + return null; + } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java index 0ebf0fe2cf26..62d5d91f9c9d 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java @@ -150,6 +150,7 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -1141,6 +1142,16 @@ public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect public boolean canVirtualize(ResolvedJavaType instanceType) { return true; } + + @Override + public ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset, JavaKind accessKind) { + /* + * The base of unsafe static field accesses is not constant until low tier for SVM. See + * com.oracle.svm.core.StaticFieldsSupport for details on the static field base during + * analysis, compilation, and JIT compilation. + */ + return null; + } } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index 9937c546c2d8..a7a60d0bd807 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -43,6 +43,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccessExtensionProvider; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.results.StrengthenGraphs; import com.oracle.objectfile.ObjectFile; @@ -248,8 +249,8 @@ public MethodTypeFlowBuilder createMethodTypeFlowBuilder(PointsToAnalysis bb, Po return new SVMMethodTypeFlowBuilder(bb, method, flowsGraph, graphKind); } - public MetaAccessExtensionProvider createAnalysisMetaAccessExtensionProvider() { - return new AnalysisMetaAccessExtensionProvider(); + public MetaAccessExtensionProvider createAnalysisMetaAccessExtensionProvider(AnalysisUniverse aUniverse) { + return new AnalysisMetaAccessExtensionProvider(aUniverse); } public MetaAccessExtensionProvider createCompilationMetaAccessExtensionProvider(@SuppressWarnings("unused") MetaAccessProvider metaAccess) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index c73378d625a4..441750e596e9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -1257,7 +1257,7 @@ private static HostedProviders createHostedProviders(TargetDescription target, A HostedSnippetReflectionProvider aSnippetReflection = new HostedSnippetReflectionProvider(null, aWordTypes); - MetaAccessExtensionProvider aMetaAccessExtensionProvider = HostedConfiguration.instance().createAnalysisMetaAccessExtensionProvider(); + MetaAccessExtensionProvider aMetaAccessExtensionProvider = HostedConfiguration.instance().createAnalysisMetaAccessExtensionProvider(aUniverse); LoweringProvider aLoweringProvider = SubstrateLoweringProvider.createForHosted(aMetaAccess, null, platformConfig, aMetaAccessExtensionProvider); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/VarHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/VarHandleFeature.java index 54567fbd0037..27dfcfe43cfa 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/VarHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/VarHandleFeature.java @@ -50,6 +50,8 @@ import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.util.ReflectionUtil; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; @@ -184,31 +186,80 @@ public static void eagerlyInitializeVarHandle(VarHandle varHandle) { private static Map, VarHandleInfo> buildInfos() { Map, VarHandleInfo> infos = new HashMap<>(); - for (String typeName : new String[]{"Booleans", "Bytes", "Chars", "Doubles", "Floats", "Ints", "Longs", "Shorts", "References"}) { - buildInfo(infos, false, "receiverType", + for (Map.Entry type : Map.of( + "Booleans", JavaKind.Boolean, + "Bytes", JavaKind.Byte, + "Chars", JavaKind.Char, + "Doubles", JavaKind.Double, + "Floats", JavaKind.Float, + "Ints", JavaKind.Int, + "Longs", JavaKind.Long, + "Shorts", JavaKind.Short, + "References", JavaKind.Object).entrySet()) { + String typeName = type.getKey(); + JavaKind kind = type.getValue(); + Function kindGetter = o -> kind; + buildInfo(infos, false, kindGetter, ReflectionUtil.lookupClass(false, "java.lang.invoke.VarHandle" + typeName + "$FieldInstanceReadOnly"), ReflectionUtil.lookupClass(false, "java.lang.invoke.VarHandle" + typeName + "$FieldInstanceReadWrite")); - buildInfo(infos, true, "base", + buildInfo(infos, true, kindGetter, ReflectionUtil.lookupClass(false, "java.lang.invoke.VarHandle" + typeName + "$FieldStaticReadOnly"), ReflectionUtil.lookupClass(false, "java.lang.invoke.VarHandle" + typeName + "$FieldStaticReadWrite")); } Class staticAccessorClass = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$StaticAccessor"); - infos.put(staticAccessorClass, new VarHandleInfo(true, createOffsetFieldGetter(staticAccessorClass, "staticOffset"), - createTypeFieldGetter(staticAccessorClass, "staticBase"))); + infos.put(staticAccessorClass, new StaticVarHandleInfo(createKindGetter(staticAccessorClass, "fieldType"), createOffsetFieldGetter(staticAccessorClass, "staticOffset"), + createBaseFieldGetter(staticAccessorClass, "staticBase"))); Class accessorClass = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$Accessor"); Function> accessorTypeGetter = obj -> ((MethodHandle) obj).type().parameterType(0); - infos.put(accessorClass, new VarHandleInfo(false, createOffsetFieldGetter(accessorClass, "fieldOffset"), accessorTypeGetter)); + infos.put(accessorClass, new InstanceVarHandleInfo(createKindGetter(accessorClass, "fieldType"), createOffsetFieldGetter(accessorClass, "fieldOffset"), accessorTypeGetter)); return infos; } - private static void buildInfo(Map, VarHandleInfo> infos, boolean isStatic, String typeFieldName, Class readOnlyClass, Class readWriteClass) { + private static void buildInfo(Map, VarHandleInfo> infos, boolean isStatic, Function kindGetter, Class readOnlyClass, Class readWriteClass) { + assert readOnlyClass.isAssignableFrom(readWriteClass); ToLongFunction offsetGetter = createOffsetFieldGetter(readOnlyClass, "fieldOffset"); - Function> typeGetter = createTypeFieldGetter(readOnlyClass, typeFieldName); - VarHandleInfo readOnlyInfo = new VarHandleInfo(isStatic, offsetGetter, typeGetter); - infos.put(readOnlyClass, readOnlyInfo); - infos.put(readWriteClass, readOnlyInfo); + VarHandleInfo info; + if (isStatic) { + info = new StaticVarHandleInfo(kindGetter, offsetGetter, createBaseFieldGetter(readOnlyClass, "base")); + } else { + info = new InstanceVarHandleInfo(kindGetter, offsetGetter, createTypeFieldGetter(readOnlyClass, "receiverType")); + } + infos.put(readOnlyClass, info); + infos.put(readWriteClass, info); + } + + private static Function createKindGetter(Class accessorClass, String fieldName) { + Field classField = ReflectionUtil.lookupField(accessorClass, fieldName); + return obj -> { + try { + Class c = (Class) classField.get(obj); + if (!c.isPrimitive()) { + return JavaKind.Object; + } else if (c == boolean.class) { + return JavaKind.Boolean; + } else if (c == byte.class) { + return JavaKind.Byte; + } else if (c == char.class) { + return JavaKind.Char; + } else if (c == short.class) { + return JavaKind.Short; + } else if (c == int.class) { + return JavaKind.Int; + } else if (c == long.class) { + return JavaKind.Long; + } else if (c == float.class) { + return JavaKind.Float; + } else if (c == double.class) { + return JavaKind.Double; + } else { + throw VMError.shouldNotReachHere(c.toString()); + } + } catch (IllegalAccessException e) { + throw VMError.shouldNotReachHere(e); + } + }; } private static ToLongFunction createOffsetFieldGetter(Class clazz, String offsetFieldName) { @@ -222,6 +273,17 @@ private static ToLongFunction createOffsetFieldGetter(Class clazz, St }; } + private static Function createBaseFieldGetter(Class clazz, String baseFieldName) { + Field typeField = ReflectionUtil.lookupField(clazz, baseFieldName); + return obj -> { + try { + return typeField.get(obj); + } catch (IllegalAccessException e) { + throw VMError.shouldNotReachHere(e); + } + }; + } + private static Function> createTypeFieldGetter(Class clazz, String typeFieldName) { Field typeField = ReflectionUtil.lookupField(clazz, typeFieldName); return obj -> { @@ -256,9 +318,10 @@ private Object registerReachableHandle(Object obj, ObjectScanner.ScanReason reas } /** - * Find the original {@link Field} referenced by a VarHandle that accesses an instance field or - * a static field. The VarHandle stores the field offset and the holder type. We iterate all - * fields of that type and look for the field with a matching offset. + * Find the original {@linkplain ResolvedJavaField field} referenced by a VarHandle that + * accesses an instance field or a static field. The VarHandle stores the field offset and the + * holder type. We iterate all fields of that type and look for the field with a matching + * offset. */ ResolvedJavaField findVarHandleOriginalField(Object varHandle) { VarHandleInfo info = infos.get(varHandle.getClass()); @@ -267,19 +330,7 @@ ResolvedJavaField findVarHandleOriginalField(Object varHandle) { * be done in the original universe of the hosting VM, because the field offsets in the * objects at image build time are also from those original fields. */ - ResolvedJavaType originalType = GraalAccess.getOriginalProviders().getMetaAccess().lookupJavaType(info.typeGetter().apply(varHandle)); - long originalFieldOffset = info.offsetGetter().applyAsLong(varHandle); - /* - * For instance fields, we need to search the whole class hierarchy. For static fields, we - * must only search the exact class. - */ - for (ResolvedJavaField field : info.isStatic() ? originalType.getStaticFields() : originalType.getInstanceFields(true)) { - long fieldOffset = field.getOffset(); - if (fieldOffset == originalFieldOffset) { - return field; - } - } - throw VMError.shouldNotReachHere("Could not find field referenced in VarHandle: " + originalType + ", offset = " + originalFieldOffset + ", isStatic = " + info.isStatic()); + return info.findOriginalField(varHandle); } AnalysisField findVarHandleAnalysisField(Object varHandle) { @@ -292,8 +343,58 @@ HostedField findVarHandleHostedField(Object varHandle) { } -record VarHandleInfo( - boolean isStatic, - ToLongFunction offsetGetter, - Function> typeGetter) { +abstract sealed class VarHandleInfo permits StaticVarHandleInfo, InstanceVarHandleInfo { + final Function kindGetter; + final ToLongFunction offsetGetter; + + VarHandleInfo(Function kindGetter, ToLongFunction offsetGetter) { + this.kindGetter = kindGetter; + this.offsetGetter = offsetGetter; + } + + abstract ResolvedJavaField findOriginalField(Object varHandle); +} + +final class StaticVarHandleInfo extends VarHandleInfo { + final Function baseGetter; + + StaticVarHandleInfo(Function kindGetter, ToLongFunction offsetGetter, Function baseGetter) { + super(kindGetter, offsetGetter); + this.baseGetter = baseGetter; + } + + @Override + ResolvedJavaField findOriginalField(Object varHandle) { + long offset = offsetGetter.applyAsLong(varHandle); + Object base = baseGetter.apply(varHandle); + JavaKind kind = kindGetter.apply(varHandle); + JavaConstant baseHandle = GraalAccess.getOriginalProviders().getSnippetReflection().forObject(base); + ResolvedJavaField result = GraalAccess.getOriginalProviders().getMetaAccessExtensionProvider().getStaticFieldForAccess(baseHandle, offset, kind); + if (result == null) { + throw VMError.shouldNotReachHere("Could not find static field referenced in VarHandle: base = " + base + ", offset = " + offset + ", kind = " + kind); + } + return result; + } +} + +final class InstanceVarHandleInfo extends VarHandleInfo { + final Function> typeGetter; + + InstanceVarHandleInfo(Function kindGetter, ToLongFunction offsetGetter, Function> typeGetter) { + super(kindGetter, offsetGetter); + this.typeGetter = typeGetter; + } + + @Override + ResolvedJavaField findOriginalField(Object varHandle) { + long offset = offsetGetter.applyAsLong(varHandle); + Class clazz = typeGetter.apply(varHandle); + ResolvedJavaType type = GraalAccess.getOriginalProviders().getMetaAccess().lookupJavaType(clazz); + JavaKind kind = kindGetter.apply(varHandle); + ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, kind); + if (result == null) { + throw VMError.shouldNotReachHere("Could not find instance field referenced in VarHandle: type = " + type + ", offset = " + offset + ", kind = " + kind); + } + return result; + } }