Skip to content

Commit 96fc07b

Browse files
committed
boxing snippets: assign primitive cache profiles
1 parent dbdbfa8 commit 96fc07b

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/BoxingTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,58 @@ public void test300() {
5656
public void testMinus300() {
5757
test("boxSnippet", -300);
5858
}
59+
60+
public static Object boxInt(int arg) {
61+
return arg;
62+
}
63+
64+
public static Object boxLong(long arg) {
65+
return arg;
66+
}
67+
68+
public static Object boxByte(byte arg) {
69+
return arg;
70+
}
71+
72+
public static Object boxChar(char arg) {
73+
return arg;
74+
}
75+
76+
public static Object boxBoolean(boolean arg) {
77+
return arg;
78+
}
79+
80+
public static Object boxShort(short arg) {
81+
return arg;
82+
}
83+
84+
@Test
85+
public void boxInt0() {
86+
test("boxInt", 0);
87+
}
88+
89+
@Test
90+
public void boxByte0() {
91+
test("boxByte", (byte) 0);
92+
}
93+
94+
@Test
95+
public void boxLong0() {
96+
test("boxLong", (long) 0);
97+
}
98+
99+
@Test
100+
public void boxChar0() {
101+
test("boxChar", (char) 0);
102+
}
103+
104+
@Test
105+
public void boxBoolean0() {
106+
test("boxBoolean", false);
107+
}
108+
109+
@Test
110+
public void boxShort0() {
111+
test("boxShort", (short) 0);
112+
}
59113
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/BoxingSnippets.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,33 @@
2626

2727
import java.util.EnumMap;
2828

29+
import org.graalvm.collections.UnmodifiableEconomicMap;
2930
import org.graalvm.word.LocationIdentity;
3031

3132
import jdk.graal.compiler.api.replacements.Snippet;
3233
import jdk.graal.compiler.api.replacements.Snippet.ConstantParameter;
34+
import jdk.graal.compiler.debug.Assertions;
35+
import jdk.graal.compiler.debug.DebugContext;
3336
import jdk.graal.compiler.debug.GraalError;
37+
import jdk.graal.compiler.graph.Node;
38+
import jdk.graal.compiler.graph.NodeBitMap;
39+
import jdk.graal.compiler.nodes.AbstractBeginNode;
40+
import jdk.graal.compiler.nodes.DeoptimizeNode;
3441
import jdk.graal.compiler.nodes.FieldLocationIdentity;
42+
import jdk.graal.compiler.nodes.IfNode;
3543
import jdk.graal.compiler.nodes.NamedLocationIdentity;
3644
import jdk.graal.compiler.nodes.PiNode;
45+
import jdk.graal.compiler.nodes.ProfileData;
46+
import jdk.graal.compiler.nodes.calc.CompareNode;
3747
import jdk.graal.compiler.nodes.extended.AbstractBoxingNode;
3848
import jdk.graal.compiler.nodes.extended.BoxNode;
49+
import jdk.graal.compiler.nodes.extended.BranchProbabilityNode;
3950
import jdk.graal.compiler.nodes.extended.UnboxNode;
4051
import jdk.graal.compiler.nodes.spi.CoreProviders;
4152
import jdk.graal.compiler.nodes.spi.LoweringTool;
4253
import jdk.graal.compiler.options.OptionValues;
4354
import jdk.graal.compiler.phases.util.Providers;
55+
import jdk.vm.ci.meta.DeoptimizationReason;
4456
import jdk.vm.ci.meta.JavaKind;
4557
import jdk.vm.ci.meta.ResolvedJavaField;
4658

@@ -254,7 +266,9 @@ public void lower(BoxNode box, LoweringTool tool) {
254266
args.add("valueOfCounter", valueOfCounter);
255267
SnippetTemplate template = template(tool, box, args);
256268
box.getDebug().log("Lowering integerValueOf in %s: node=%s, template=%s, arguments=%s", box.graph(), box, template, args);
257-
template.instantiate(tool.getMetaAccess(), box, SnippetTemplate.DEFAULT_REPLACER, args);
269+
270+
UnmodifiableEconomicMap<Node, Node> duplicates = template.instantiate(tool.getMetaAccess(), box, SnippetTemplate.DEFAULT_REPLACER, args);
271+
assignPrimitiveCacheProfiles(box.getBoxingKind(), duplicates);
258272
}
259273

260274
public void lower(UnboxNode unbox, LoweringTool tool) {
@@ -267,4 +281,63 @@ public void lower(UnboxNode unbox, LoweringTool tool) {
267281
template.instantiate(tool.getMetaAccess(), unbox, SnippetTemplate.DEFAULT_REPLACER, args);
268282
}
269283
}
284+
285+
/**
286+
* Boxing primitive values typically utilizes a primitive cache, but profiling information for
287+
* this cache is absent within snippets. This function injects the necessary profiles to address
288+
* that gap.
289+
*/
290+
private static void assignPrimitiveCacheProfiles(JavaKind kind, UnmodifiableEconomicMap<Node, Node> duplicates) {
291+
NodeBitMap controlFlow = null;
292+
for (Node originalNode : duplicates.getKeys()) {
293+
if (originalNode instanceof IfNode originalIf) {
294+
if (isBoundsCheck(originalIf)) {
295+
// Ignore the bounds check of the cache, that is not relevant
296+
continue;
297+
}
298+
if (controlFlow == null) {
299+
controlFlow = originalIf.graph().createNodeBitMap();
300+
}
301+
controlFlow.mark(originalIf);
302+
ProfileData.ProfileSource source = originalIf.getProfileData().getProfileSource();
303+
assert source.isUnknown() || source.isInjected() : Assertions.errorMessage(originalIf, originalIf.getProfileData(), "Profile should be unknown inside the snippet");
304+
}
305+
}
306+
if (controlFlow == null) {
307+
return;
308+
}
309+
GraalError.guarantee(controlFlow.count() == 1, "Must only have a single control flow element - the branch into the cache but found more %s", controlFlow);
310+
311+
ProfileData.BranchProbabilityData b = null;
312+
switch (kind) {
313+
case Byte:
314+
// cache contains all byte values, should not see any control flow and thus never
315+
// enter this branch
316+
throw GraalError.shouldNotReachHere("Byte.valueOf should all go to cache and never contain control flow " + controlFlow);
317+
case Boolean:
318+
// only two cases, truly 50 / 50
319+
b = ProfileData.BranchProbabilityData.injected(0.5);
320+
break;
321+
case Char:
322+
case Short:
323+
case Int:
324+
case Long:
325+
b = ProfileData.BranchProbabilityData.injected(BranchProbabilityNode.FREQUENT_PROBABILITY);
326+
break;
327+
default:
328+
throw GraalError.shouldNotReachHere("Unknown control flow in boxing code, did a JDK change trigger this error? Consider adding new logic to set the profile of " + controlFlow);
329+
}
330+
IfNode cacheIf = (IfNode) controlFlow.first();
331+
IfNode inlinedNode = (IfNode) duplicates.get(cacheIf);
332+
inlinedNode.setTrueSuccessorProbability(b);
333+
inlinedNode.graph().getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, inlinedNode.graph(), "After updating profile of %s", inlinedNode);
334+
}
335+
336+
private static boolean isBoundsCheck(IfNode ifNode) {
337+
if (ifNode.condition() instanceof CompareNode) {
338+
AbstractBeginNode fs = ifNode.falseSuccessor();
339+
return fs.next() instanceof DeoptimizeNode deopt && deopt.getReason() == DeoptimizationReason.BoundsCheckException;
340+
}
341+
return false;
342+
}
270343
}

0 commit comments

Comments
 (0)