Skip to content

Commit cd4ccaa

Browse files
committed
[GR-64654] Frame immutable field reads float above constructor call
PullRequest: graal/20753
2 parents 4b8e3db + 06a91ff commit cd4ccaa

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -150,6 +150,8 @@ public static class Options {
150150
public static final OptionKey<Boolean> TruffleTrustedNonNullCast = new OptionKey<>(true);
151151
@Option(help = "Whether Truffle trusted type casts are enabled.", type = OptionType.Debug) //
152152
public static final OptionKey<Boolean> TruffleTrustedTypeCast = new OptionKey<>(true);
153+
@Option(help = "Whether Truffle frame field reads are trusted final.", type = OptionType.Debug) //
154+
public static final OptionKey<Boolean> TruffleTrustedFinalFrameFields = new OptionKey<>(true);
153155

154156
}
155157

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleInvocationPlugins.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -48,6 +48,7 @@
4848
import jdk.graal.compiler.nodes.LogicNode;
4949
import jdk.graal.compiler.nodes.NamedLocationIdentity;
5050
import jdk.graal.compiler.nodes.NodeView;
51+
import jdk.graal.compiler.nodes.ParameterNode;
5152
import jdk.graal.compiler.nodes.PiNode;
5253
import jdk.graal.compiler.nodes.ValueNode;
5354
import jdk.graal.compiler.nodes.calc.AddNode;
@@ -149,23 +150,35 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
149150
}
150151
}
151152

152-
ValueNode loadNode = isTrustedImmutable(b, object);
153+
ValueNode loadNode = object;
154+
if (Options.TruffleTrustedFinalFrameFields.getValue(b.getOptions())) {
155+
loadNode = castTrustedFinalFrameField(b, object);
156+
}
153157
b.addPush(JavaKind.Object, PiNode.create(loadNode, piStamp, valueAnchorNode));
154158
return true;
155159
}
156160
});
157161
}
158162

159-
private static ValueNode isTrustedImmutable(GraphBuilderContext b, ValueNode object) {
160-
ValueNode loadNode = object;
161-
if (loadNode instanceof LoadFieldNode cast && canBeImmutable(cast.field())) {
162-
loadNode = b.add(LoadFieldNode.createOverrideImmutable(cast));
163+
private static ValueNode castTrustedFinalFrameField(GraphBuilderContext b, ValueNode object) {
164+
ValueNode result = object;
165+
if (object instanceof LoadFieldNode loadField && canBeImmutable(loadField.object(), loadField.field())) {
166+
result = b.add(LoadFieldNode.createOverrideImmutable(loadField));
163167
}
164-
return loadNode;
168+
return result;
165169
}
166170

167-
private static boolean canBeImmutable(ResolvedJavaField field) {
171+
/**
172+
* Try to decide if a field of an object is immutable during the execution of the current
173+
* compilation unit. This allows us to mark the load from that field as immutable, which enables
174+
* more aggressive folding and hoisting. This method should return {@code true} only if it is
175+
* guaranteed that the field is immutable, else it must return {@code false}. This method is
176+
* only used for a very limited amount of cases, so we can ignore shenanigans such as
177+
* {@code Unsafe} accesses, reflection, or an object escapes before assigning its final fields.
178+
*/
179+
private static boolean canBeImmutable(ValueNode holder, ResolvedJavaField field) {
168180
if (field.isStatic()) {
181+
// Static final fields are constant-folded already, so we don't care
169182
return false;
170183
}
171184
if (!field.isFinal()) {
@@ -177,7 +190,20 @@ private static boolean canBeImmutable(ResolvedJavaField field) {
177190
*/
178191
return false;
179192
}
180-
return true;
193+
// A final field is immutable if its holder object is initialized before the start of the
194+
// execution of the current compilation unit.
195+
if (holder instanceof ParameterNode param) {
196+
// One such case is if the holder object is a parameter that is passed into the current
197+
// compilation unit (except if the parameter is the first one of a constructor).
198+
return param.index() != 0 || !param.graph().method().isConstructor();
199+
}
200+
if (holder instanceof LoadFieldNode loadField) {
201+
// Another case is if the holder object is also an immutable load. Then, it should be
202+
// initialized before being stored into its holder; which, in turns, is initialized
203+
// before the start of the execution of the current compilation unit.
204+
return canBeImmutable(loadField.object(), loadField.field());
205+
}
206+
return false;
181207
}
182208

183209
private static void registerBytecodePlugins(InvocationPlugins plugins, Replacements replacements) {
@@ -200,7 +226,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
200226

201227
TypeReference type = TypeReference.createTrustedWithoutAssumptions(javaType);
202228
Stamp piStamp = StampFactory.object(type, true);
203-
b.addPush(JavaKind.Object, PiNode.create(isTrustedImmutable(b, object), piStamp, null));
229+
b.addPush(JavaKind.Object, PiNode.create(castTrustedFinalFrameField(b, object), piStamp, null));
204230

205231
return true;
206232
}

0 commit comments

Comments
 (0)