Skip to content

Commit 37d1963

Browse files
author
Christian Wimmer
committed
Do not expose that static fields are stored in arrays, to avoid memory graph problems in the compiler
1 parent eff4d0f commit 37d1963

File tree

4 files changed

+94
-35
lines changed

4 files changed

+94
-35
lines changed

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

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,37 @@
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+
55+
import jdk.vm.ci.meta.JavaConstant;
56+
import jdk.vm.ci.meta.JavaKind;
57+
import jdk.vm.ci.meta.ResolvedJavaMethod;
3858

3959
/**
4060
* Static fields are represented as two arrays in the native image heap: one for Object fields and
@@ -44,11 +64,22 @@
4464
* Implementation notes: The arrays are created after static analysis, but before compilation. We
4565
* need to know how many static fields are reachable in order to compute the appropriate size for
4666
* the arrays, which is only available after static analysis.
67+
*
68+
* When bytecode is parsed before static analysis, the arrays are not available yet. Therefore, the
69+
* accessor functions {@link #getStaticObjectFields()}} and {@link #getStaticPrimitiveFields()} are
70+
* intrinsified to a {@link StaticFieldBaseNode}, which is then during compilation lowered to the
71+
* constant arrays. This also solves memory graph problems in the Graal compiler: Direct
72+
* loads/stores using the arrays, for example via Unsafe or VarHandle, alias with static field
73+
* loads/stores that have dedicated {@link LocationIdentity}. If the arrays are already exposed in
74+
* the high-level optimization phases of Graal, the compiler would miss the alias since the location
75+
* identities for arrays are considered non-aliasing with location identities for fields. Replacing
76+
* the {@link StaticFieldBaseNode} with a {@link ConstantNode} only in the low tier of the compiler
77+
* solves this problem.
4778
*/
4879
public final class StaticFieldsSupport {
4980

50-
@UnknownObjectField(types = {Object[].class}) private Object[] staticObjectFields;
51-
@UnknownObjectField(types = {byte[].class}) private byte[] staticPrimitiveFields;
81+
Object[] staticObjectFields;
82+
byte[] staticPrimitiveFields;
5283

5384
@Platforms(Platform.HOSTED_ONLY.class)
5485
protected StaticFieldsSupport() {
@@ -61,30 +92,74 @@ public static void setData(Object[] staticObjectFields, byte[] staticPrimitiveFi
6192
support.staticPrimitiveFields = Objects.requireNonNull(staticPrimitiveFields);
6293
}
6394

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() {
95+
/* Intrinsified by the graph builder plugin below. */
96+
public static Object getStaticObjectFields() {
7197
Object[] result = ImageSingletons.lookup(StaticFieldsSupport.class).staticObjectFields;
7298
assert result != null;
7399
return result;
74100
}
75101

76-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
77-
public static byte[] getStaticPrimitiveFields() {
102+
/* Intrinsified by the graph builder plugin below. */
103+
public static Object getStaticPrimitiveFields() {
78104
byte[] result = ImageSingletons.lookup(StaticFieldsSupport.class).staticPrimitiveFields;
79105
assert result != null;
80106
return result;
81107
}
108+
109+
public static FloatingNode createStaticFieldBaseNode(boolean primitive) {
110+
return new StaticFieldBaseNode(primitive);
111+
}
82112
}
83113

84114
@AutomaticFeature
85-
class StaticFieldsFeature implements Feature {
115+
final class StaticFieldsFeature implements GraalFeature {
86116
@Override
87117
public void afterRegistration(AfterRegistrationAccess access) {
88118
ImageSingletons.add(StaticFieldsSupport.class, new StaticFieldsSupport());
89119
}
120+
121+
@Override
122+
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, Plugins plugins, ParsingReason reason) {
123+
Registration r = new Registration(plugins.getInvocationPlugins(), StaticFieldsSupport.class);
124+
r.register0("getStaticObjectFields", new InvocationPlugin() {
125+
@Override
126+
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused) {
127+
b.addPush(JavaKind.Object, new StaticFieldBaseNode(false));
128+
return true;
129+
}
130+
});
131+
r.register0("getStaticPrimitiveFields", new InvocationPlugin() {
132+
@Override
133+
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused) {
134+
b.addPush(JavaKind.Object, new StaticFieldBaseNode(true));
135+
return true;
136+
}
137+
});
138+
}
139+
}
140+
141+
@NodeInfo(cycles = CYCLES_0, size = SIZE_1)
142+
final class StaticFieldBaseNode extends FloatingNode implements Lowerable {
143+
public static final NodeClass<StaticFieldBaseNode> TYPE = NodeClass.create(StaticFieldBaseNode.class);
144+
145+
private final boolean primitive;
146+
147+
/**
148+
* We must not expose that the stamp will eventually be an array, to avoid memory graph
149+
* problems. See the comment on {@link StaticFieldsSupport}.
150+
*/
151+
protected StaticFieldBaseNode(boolean primitive) {
152+
super(TYPE, StampFactory.objectNonNull());
153+
this.primitive = primitive;
154+
}
155+
156+
@Override
157+
public void lower(LoweringTool tool) {
158+
if (tool.getLoweringStage() == LoweringTool.StandardLoweringStage.LOW_TIER) {
159+
StaticFieldsSupport support = ImageSingletons.lookup(StaticFieldsSupport.class);
160+
JavaConstant constant = SubstrateObjectConstant.forObject(primitive ? support.staticPrimitiveFields : support.staticObjectFields);
161+
assert constant.isNonNull();
162+
replaceAndDelete(ConstantNode.forConstant(constant, tool.getMetaAccess(), graph()));
163+
}
164+
}
90165
}

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)