Skip to content

Commit 2a3b5fd

Browse files
committed
[GR-65745] Boxing snippet profiles.
PullRequest: graal/21067
2 parents bc7f3c3 + 49a0dd6 commit 2a3b5fd

File tree

2 files changed

+139
-1
lines changed

2 files changed

+139
-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: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,36 @@
2525
package jdk.graal.compiler.replacements;
2626

2727
import java.util.EnumMap;
28+
import java.util.List;
2829

30+
import org.graalvm.collections.UnmodifiableEconomicMap;
2931
import org.graalvm.word.LocationIdentity;
3032

3133
import jdk.graal.compiler.api.replacements.Snippet;
3234
import jdk.graal.compiler.api.replacements.Snippet.ConstantParameter;
35+
import jdk.graal.compiler.debug.Assertions;
36+
import jdk.graal.compiler.debug.DebugContext;
3337
import jdk.graal.compiler.debug.GraalError;
38+
import jdk.graal.compiler.graph.Node;
39+
import jdk.graal.compiler.graph.NodeBitMap;
40+
import jdk.graal.compiler.nodes.AbstractBeginNode;
41+
import jdk.graal.compiler.nodes.DeoptimizeNode;
3442
import jdk.graal.compiler.nodes.FieldLocationIdentity;
43+
import jdk.graal.compiler.nodes.IfNode;
3544
import jdk.graal.compiler.nodes.NamedLocationIdentity;
3645
import jdk.graal.compiler.nodes.PiNode;
46+
import jdk.graal.compiler.nodes.ProfileData;
47+
import jdk.graal.compiler.nodes.calc.CompareNode;
3748
import jdk.graal.compiler.nodes.extended.AbstractBoxingNode;
3849
import jdk.graal.compiler.nodes.extended.BoxNode;
50+
import jdk.graal.compiler.nodes.extended.BranchProbabilityNode;
3951
import jdk.graal.compiler.nodes.extended.UnboxNode;
52+
import jdk.graal.compiler.nodes.memory.MemoryAccess;
4053
import jdk.graal.compiler.nodes.spi.CoreProviders;
4154
import jdk.graal.compiler.nodes.spi.LoweringTool;
4255
import jdk.graal.compiler.options.OptionValues;
4356
import jdk.graal.compiler.phases.util.Providers;
57+
import jdk.vm.ci.meta.DeoptimizationReason;
4458
import jdk.vm.ci.meta.JavaKind;
4559
import jdk.vm.ci.meta.ResolvedJavaField;
4660

@@ -254,7 +268,9 @@ public void lower(BoxNode box, LoweringTool tool) {
254268
args.add("valueOfCounter", valueOfCounter);
255269
SnippetTemplate template = template(tool, box, args);
256270
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);
271+
272+
UnmodifiableEconomicMap<Node, Node> duplicates = template.instantiate(tool.getMetaAccess(), box, SnippetTemplate.DEFAULT_REPLACER, args);
273+
assignPrimitiveCacheProfiles(box.getBoxingKind(), duplicates);
258274
}
259275

260276
public void lower(UnboxNode unbox, LoweringTool tool) {
@@ -267,4 +283,72 @@ public void lower(UnboxNode unbox, LoweringTool tool) {
267283
template.instantiate(tool.getMetaAccess(), unbox, SnippetTemplate.DEFAULT_REPLACER, args);
268284
}
269285
}
286+
287+
/**
288+
* Boxing primitive values typically utilizes a primitive cache, but profiling information for
289+
* this cache is absent within snippets. This function injects the necessary profiles to address
290+
* that gap.
291+
*/
292+
private static void assignPrimitiveCacheProfiles(JavaKind kind, UnmodifiableEconomicMap<Node, Node> duplicates) {
293+
NodeBitMap controlFlow = null;
294+
for (Node originalNode : duplicates.getKeys()) {
295+
if (originalNode instanceof IfNode originalIf) {
296+
if (isBoundsCheck(originalIf)) {
297+
// Ignore the bounds check of the cache, that is not relevant
298+
continue;
299+
}
300+
if (controlFlow == null) {
301+
controlFlow = originalIf.graph().createNodeBitMap();
302+
}
303+
controlFlow.mark(originalIf);
304+
ProfileData.ProfileSource source = originalIf.getProfileData().getProfileSource();
305+
assert source.isUnknown() || source.isInjected() : Assertions.errorMessage(originalIf, originalIf.getProfileData(), "Profile should be unknown inside the snippet");
306+
}
307+
}
308+
if (controlFlow == null) {
309+
return;
310+
}
311+
GraalError.guarantee(controlFlow.count() == 1, "Must only have a single control flow element - the branch into the cache but found more %s", controlFlow);
312+
313+
IfNode cacheIf = (IfNode) controlFlow.first();
314+
IfNode inlinedNode = (IfNode) duplicates.get(cacheIf);
315+
316+
ProfileData.BranchProbabilityData b = null;
317+
switch (kind) {
318+
case Byte:
319+
// cache contains all byte values, should not see any control flow and thus never
320+
// enter this branch
321+
throw GraalError.shouldNotReachHere("Byte.valueOf should all go to cache and never contain control flow " + controlFlow);
322+
case Boolean:
323+
// only two cases, truly 50 / 50
324+
b = ProfileData.BranchProbabilityData.injected(0.5);
325+
break;
326+
case Char:
327+
case Short:
328+
case Int:
329+
case Long:
330+
AbstractBeginNode trueSucc = cacheIf.trueSuccessor();
331+
GraalError.guarantee(trueSucc.next() instanceof IfNode, "Must have the bounds check next but found %s", trueSucc.next());
332+
IfNode boundsIf = (IfNode) trueSucc.next();
333+
GraalError.guarantee(isBoundsCheck(boundsIf), "Must have the bounds check next but found %s", boundsIf);
334+
List<Node> anchored = boundsIf.trueSuccessor().anchored().snapshot();
335+
GraalError.guarantee(anchored.stream().filter(x -> x instanceof MemoryAccess m && NamedLocationIdentity.isArrayLocation(m.getLocationIdentity())).count() == 1,
336+
"Remaining control flow should read from the cache but is %s", boundsIf.trueSuccessor());
337+
338+
b = ProfileData.BranchProbabilityData.injected(BranchProbabilityNode.FREQUENT_PROBABILITY);
339+
break;
340+
default:
341+
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);
342+
}
343+
inlinedNode.setTrueSuccessorProbability(b);
344+
inlinedNode.graph().getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, inlinedNode.graph(), "After updating profile of %s", inlinedNode);
345+
}
346+
347+
private static boolean isBoundsCheck(IfNode ifNode) {
348+
if (ifNode.condition() instanceof CompareNode) {
349+
AbstractBeginNode fs = ifNode.falseSuccessor();
350+
return fs.next() instanceof DeoptimizeNode deopt && deopt.getReason() == DeoptimizationReason.BoundsCheckException;
351+
}
352+
return false;
353+
}
270354
}

0 commit comments

Comments
 (0)