Skip to content

Commit 5df1a4e

Browse files
author
Christian Wimmer
committed
[GR-33693] Do not expose that static fields are stored in arrays, to avoid memory graph problems in the compiler.
PullRequest: graal/9754
2 parents d06d8b7 + 18658d3 commit 5df1a4e

File tree

4 files changed

+123
-37
lines changed

4 files changed

+123
-37
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/StaticFieldsSupport.java

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,38 @@
2424
*/
2525
package 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+
2730
import 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;
2945
import org.graalvm.nativeimage.ImageSingletons;
3046
import org.graalvm.nativeimage.Platform;
3147
import org.graalvm.nativeimage.Platforms;
32-
import org.graalvm.nativeimage.hosted.Feature;
48+
import org.graalvm.word.LocationIdentity;
3349

3450
import 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;
3752
import 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
@@ -44,11 +65,24 @@
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
*/
4880
public 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
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
import com.oracle.svm.core.hub.DynamicHub;
8080
import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport;
8181
import com.oracle.svm.core.meta.SharedField;
82-
import com.oracle.svm.core.meta.SubstrateObjectConstant;
8382
import com.oracle.svm.core.snippets.SubstrateIsArraySnippets;
8483

8584
import jdk.vm.ci.code.CodeUtil;
@@ -151,8 +150,7 @@ public int arrayLengthOffset() {
151150
public ValueNode staticFieldBase(StructuredGraph graph, ResolvedJavaField f) {
152151
SharedField field = (SharedField) f;
153152
assert field.isStatic();
154-
Object fields = field.getStorageKind() == JavaKind.Object ? StaticFieldsSupport.getStaticObjectFields() : StaticFieldsSupport.getStaticPrimitiveFields();
155-
return ConstantNode.forConstant(SubstrateObjectConstant.forObject(fields), getProviders().getMetaAccess(), graph);
153+
return graph.unique(StaticFieldsSupport.createStaticFieldBaseNode(field.getStorageKind() != JavaKind.Object));
156154
}
157155

158156
private static ValueNode maybeUncompress(ValueNode node) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -677,26 +677,12 @@ private static boolean isValidField(Field targetField, boolean isSunMiscUnsafe)
677677
return true;
678678
}
679679

680-
private static final Function<CoreProviders, JavaConstant> primitiveStaticFieldBaseConstant = new Function<CoreProviders, JavaConstant>() {
681-
@Override
682-
public JavaConstant apply(CoreProviders providers) {
683-
return StaticFieldsSupport.dataAvailable() ? SubstrateObjectConstant.forObject(StaticFieldsSupport.getStaticPrimitiveFields()) : null;
684-
}
685-
};
686-
private static final Function<CoreProviders, JavaConstant> objectStaticFieldBaseConstant = new Function<CoreProviders, JavaConstant>() {
687-
@Override
688-
public JavaConstant apply(CoreProviders providers) {
689-
return StaticFieldsSupport.dataAvailable() ? SubstrateObjectConstant.forObject(StaticFieldsSupport.getStaticObjectFields()) : null;
690-
}
691-
};
692-
693680
private static boolean processStaticFieldBase(GraphBuilderContext b, Field targetField, boolean isSunMiscUnsafe) {
694681
if (!isValidField(targetField, isSunMiscUnsafe)) {
695682
return false;
696683
}
697684

698-
b.addPush(JavaKind.Object, LazyConstantNode.create(StampFactory.objectNonNull(),
699-
targetField.getType().isPrimitive() ? primitiveStaticFieldBaseConstant : objectStaticFieldBaseConstant, b));
685+
b.addPush(JavaKind.Object, StaticFieldsSupport.createStaticFieldBaseNode(targetField.getType().isPrimitive()));
700686
return true;
701687
}
702688

substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIGeneratedMethodSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ static WordBase getFieldOffsetFromId(JNIFieldId fieldId) {
8181
return JNIAccessibleField.getOffsetFromId(fieldId);
8282
}
8383

84-
static byte[] getStaticPrimitiveFieldsArray() {
84+
static Object getStaticPrimitiveFieldsArray() {
8585
return StaticFieldsSupport.getStaticPrimitiveFields();
8686
}
8787

88-
static Object[] getStaticObjectFieldsArray() {
88+
static Object getStaticObjectFieldsArray() {
8989
return StaticFieldsSupport.getStaticObjectFields();
9090
}
9191

0 commit comments

Comments
 (0)