2424 */
2525package com .oracle .svm .core ;
2626
27+ import static org .graalvm .compiler .nodeinfo .NodeCycles .CYCLES_0 ;
28+ import static org .graalvm .compiler .nodeinfo .NodeSize .SIZE_1 ;
29+
2730import java .util .Objects ;
2831
32+ import org .graalvm .compiler .api .replacements .SnippetReflectionProvider ;
33+ import org .graalvm .compiler .core .common .type .StampFactory ;
34+ import org .graalvm .compiler .graph .NodeClass ;
35+ import org .graalvm .compiler .nodeinfo .NodeInfo ;
36+ import org .graalvm .compiler .nodes .ConstantNode ;
37+ import org .graalvm .compiler .nodes .calc .FloatingNode ;
38+ import org .graalvm .compiler .nodes .graphbuilderconf .GraphBuilderConfiguration .Plugins ;
39+ import org .graalvm .compiler .nodes .graphbuilderconf .GraphBuilderContext ;
40+ import org .graalvm .compiler .nodes .graphbuilderconf .InvocationPlugin ;
41+ import org .graalvm .compiler .nodes .graphbuilderconf .InvocationPlugins .Registration ;
42+ import org .graalvm .compiler .nodes .spi .Lowerable ;
43+ import org .graalvm .compiler .nodes .spi .LoweringTool ;
44+ import org .graalvm .compiler .phases .util .Providers ;
2945import org .graalvm .nativeimage .ImageSingletons ;
3046import org .graalvm .nativeimage .Platform ;
3147import org .graalvm .nativeimage .Platforms ;
32- import org .graalvm .nativeimage . hosted . Feature ;
48+ import org .graalvm .word . LocationIdentity ;
3349
3450import com .oracle .svm .core .annotate .AutomaticFeature ;
35- import com .oracle .svm .core .annotate .Uninterruptible ;
36- import com .oracle .svm .core .annotate .UnknownObjectField ;
51+ import com .oracle .svm .core .graal .GraalFeature ;
3752import com .oracle .svm .core .meta .SharedField ;
53+ import com .oracle .svm .core .meta .SubstrateObjectConstant ;
54+ import com .oracle .svm .core .util .VMError ;
55+
56+ import jdk .vm .ci .meta .JavaConstant ;
57+ import jdk .vm .ci .meta .JavaKind ;
58+ import jdk .vm .ci .meta .ResolvedJavaMethod ;
3859
3960/**
4061 * Static fields are represented as two arrays in the native image heap: one for Object fields and
4465 * Implementation notes: The arrays are created after static analysis, but before compilation. We
4566 * need to know how many static fields are reachable in order to compute the appropriate size for
4667 * the arrays, which is only available after static analysis.
68+ *
69+ * When bytecode is parsed before static analysis, the arrays are not available yet. Therefore, the
70+ * accessor functions {@link #getStaticObjectFields()}} and {@link #getStaticPrimitiveFields()} are
71+ * intrinsified to a {@link StaticFieldBaseNode}, which is then during compilation lowered to the
72+ * constant arrays. This also solves memory graph problems in the Graal compiler: Direct
73+ * loads/stores using the arrays, for example via Unsafe or VarHandle, alias with static field
74+ * loads/stores that have dedicated {@link LocationIdentity}. If the arrays are already exposed in
75+ * the high-level optimization phases of Graal, the compiler would miss the alias since the location
76+ * identities for arrays are considered non-aliasing with location identities for fields. Replacing
77+ * the {@link StaticFieldBaseNode} with a {@link ConstantNode} only in the low tier of the compiler
78+ * solves this problem.
4779 */
4880public final class StaticFieldsSupport {
4981
50- @ UnknownObjectField (types = {Object [].class }) private Object [] staticObjectFields ;
51- @ UnknownObjectField (types = {byte [].class }) private byte [] staticPrimitiveFields ;
82+ @ Platforms (Platform .HOSTED_ONLY .class ) //
83+ private Object [] staticObjectFields ;
84+ @ Platforms (Platform .HOSTED_ONLY .class ) //
85+ private byte [] staticPrimitiveFields ;
5286
5387 @ Platforms (Platform .HOSTED_ONLY .class )
5488 protected StaticFieldsSupport () {
@@ -61,30 +95,98 @@ public static void setData(Object[] staticObjectFields, byte[] staticPrimitiveFi
6195 support .staticPrimitiveFields = Objects .requireNonNull (staticPrimitiveFields );
6296 }
6397
64- @ Platforms (Platform .HOSTED_ONLY .class )
65- public static boolean dataAvailable () {
66- return ImageSingletons .lookup (StaticFieldsSupport .class ).staticObjectFields != null ;
67- }
68-
69- @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
70- public static Object [] getStaticObjectFields () {
98+ /* Intrinsified by the graph builder plugin below. */
99+ public static Object getStaticObjectFields () {
71100 Object [] result = ImageSingletons .lookup (StaticFieldsSupport .class ).staticObjectFields ;
72- assert result != null ;
101+ VMError . guarantee ( result != null , "arrays that hold static fields are only available after static analysis" ) ;
73102 return result ;
74103 }
75104
76- @ Uninterruptible ( reason = "Called from uninterruptible code." , mayBeInlined = true )
77- public static byte [] getStaticPrimitiveFields () {
105+ /* Intrinsified by the graph builder plugin below. */
106+ public static Object getStaticPrimitiveFields () {
78107 byte [] result = ImageSingletons .lookup (StaticFieldsSupport .class ).staticPrimitiveFields ;
79- assert result != null ;
108+ VMError . guarantee ( result != null , "arrays that hold static fields are only available after static analysis" ) ;
80109 return result ;
81110 }
111+
112+ public static FloatingNode createStaticFieldBaseNode (boolean primitive ) {
113+ return new StaticFieldBaseNode (primitive );
114+ }
82115}
83116
84117@ AutomaticFeature
85- class StaticFieldsFeature implements Feature {
118+ final class StaticFieldsFeature implements GraalFeature {
86119 @ Override
87120 public void afterRegistration (AfterRegistrationAccess access ) {
88121 ImageSingletons .add (StaticFieldsSupport .class , new StaticFieldsSupport ());
89122 }
123+
124+ @ Override
125+ public void registerInvocationPlugins (Providers providers , SnippetReflectionProvider snippetReflection , Plugins plugins , ParsingReason reason ) {
126+ Registration r = new Registration (plugins .getInvocationPlugins (), StaticFieldsSupport .class );
127+ r .register0 ("getStaticObjectFields" , new InvocationPlugin () {
128+ @ Override
129+ public boolean apply (GraphBuilderContext b , ResolvedJavaMethod targetMethod , Receiver unused ) {
130+ b .addPush (JavaKind .Object , new StaticFieldBaseNode (false ));
131+ return true ;
132+ }
133+ });
134+ r .register0 ("getStaticPrimitiveFields" , new InvocationPlugin () {
135+ @ Override
136+ public boolean apply (GraphBuilderContext b , ResolvedJavaMethod targetMethod , Receiver unused ) {
137+ b .addPush (JavaKind .Object , new StaticFieldBaseNode (true ));
138+ return true ;
139+ }
140+ });
141+ }
142+ }
143+
144+ @ NodeInfo (cycles = CYCLES_0 , size = SIZE_1 )
145+ final class StaticFieldBaseNode extends FloatingNode implements Lowerable {
146+ public static final NodeClass <StaticFieldBaseNode > TYPE = NodeClass .create (StaticFieldBaseNode .class );
147+
148+ private final boolean primitive ;
149+
150+ /**
151+ * We must not expose that the stamp will eventually be an array, to avoid memory graph
152+ * problems. See the comment on {@link StaticFieldsSupport}.
153+ */
154+ protected StaticFieldBaseNode (boolean primitive ) {
155+ super (TYPE , StampFactory .objectNonNull ());
156+ this .primitive = primitive ;
157+ }
158+
159+ /**
160+ * At first glance, this method looks like a circular dependency:
161+ * {@link StaticFieldsSupport#getStaticPrimitiveFields} is intrinsified to a
162+ * {@link StaticFieldBaseNode}, and {@link StaticFieldBaseNode} is lowered by calling
163+ * {@link StaticFieldsSupport#getStaticPrimitiveFields}. So why does this code work?
164+ *
165+ * The intrinsification to the {@link StaticFieldBaseNode} is only effective for code executed
166+ * at image run time. So when executed during AOT compilation, {@link StaticFieldBaseNode#lower}
167+ * really invokes {@link StaticFieldsSupport#getStaticPrimitiveFields}, which returns the proper
168+ * result.
169+ *
170+ * For an image that uses Graal as a JIT compiler, {@link StaticFieldBaseNode#lower} is
171+ * reachable at run time. But it is AOT compiled, and lowering during that AOT compilation again
172+ * invokes {@link StaticFieldsSupport#getStaticPrimitiveFields}.
173+ *
174+ * So in summary, this code works because there is proper "bootstrapping" during AOT compilation
175+ * where the intrinsification is not applied.
176+ */
177+ @ Override
178+ public void lower (LoweringTool tool ) {
179+ if (tool .getLoweringStage () != LoweringTool .StandardLoweringStage .LOW_TIER ) {
180+ /*
181+ * Lowering to a ConstantNode must only happen after the memory graph has been built,
182+ * i.e., when the information that static fields are stored in an array is no longer
183+ * misleading alias analysis.
184+ */
185+ return ;
186+ }
187+
188+ JavaConstant constant = SubstrateObjectConstant .forObject (primitive ? StaticFieldsSupport .getStaticPrimitiveFields () : StaticFieldsSupport .getStaticObjectFields ());
189+ assert constant .isNonNull ();
190+ replaceAndDelete (ConstantNode .forConstant (constant , tool .getMetaAccess (), graph ()));
191+ }
90192}
0 commit comments