2525package jdk .graal .compiler .replacements ;
2626
2727import java .util .EnumMap ;
28+ import java .util .List ;
2829
30+ import org .graalvm .collections .UnmodifiableEconomicMap ;
2931import org .graalvm .word .LocationIdentity ;
3032
3133import jdk .graal .compiler .api .replacements .Snippet ;
3234import jdk .graal .compiler .api .replacements .Snippet .ConstantParameter ;
35+ import jdk .graal .compiler .debug .Assertions ;
36+ import jdk .graal .compiler .debug .DebugContext ;
3337import 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 ;
3442import jdk .graal .compiler .nodes .FieldLocationIdentity ;
43+ import jdk .graal .compiler .nodes .IfNode ;
3544import jdk .graal .compiler .nodes .NamedLocationIdentity ;
3645import jdk .graal .compiler .nodes .PiNode ;
46+ import jdk .graal .compiler .nodes .ProfileData ;
47+ import jdk .graal .compiler .nodes .calc .CompareNode ;
3748import jdk .graal .compiler .nodes .extended .AbstractBoxingNode ;
3849import jdk .graal .compiler .nodes .extended .BoxNode ;
50+ import jdk .graal .compiler .nodes .extended .BranchProbabilityNode ;
3951import jdk .graal .compiler .nodes .extended .UnboxNode ;
52+ import jdk .graal .compiler .nodes .memory .MemoryAccess ;
4053import jdk .graal .compiler .nodes .spi .CoreProviders ;
4154import jdk .graal .compiler .nodes .spi .LoweringTool ;
4255import jdk .graal .compiler .options .OptionValues ;
4356import jdk .graal .compiler .phases .util .Providers ;
57+ import jdk .vm .ci .meta .DeoptimizationReason ;
4458import jdk .vm .ci .meta .JavaKind ;
4559import 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