25
25
package jdk .graal .compiler .replacements ;
26
26
27
27
import java .util .EnumMap ;
28
+ import java .util .List ;
28
29
30
+ import org .graalvm .collections .UnmodifiableEconomicMap ;
29
31
import org .graalvm .word .LocationIdentity ;
30
32
31
33
import jdk .graal .compiler .api .replacements .Snippet ;
32
34
import jdk .graal .compiler .api .replacements .Snippet .ConstantParameter ;
35
+ import jdk .graal .compiler .debug .Assertions ;
36
+ import jdk .graal .compiler .debug .DebugContext ;
33
37
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 ;
34
42
import jdk .graal .compiler .nodes .FieldLocationIdentity ;
43
+ import jdk .graal .compiler .nodes .IfNode ;
35
44
import jdk .graal .compiler .nodes .NamedLocationIdentity ;
36
45
import jdk .graal .compiler .nodes .PiNode ;
46
+ import jdk .graal .compiler .nodes .ProfileData ;
47
+ import jdk .graal .compiler .nodes .calc .CompareNode ;
37
48
import jdk .graal .compiler .nodes .extended .AbstractBoxingNode ;
38
49
import jdk .graal .compiler .nodes .extended .BoxNode ;
50
+ import jdk .graal .compiler .nodes .extended .BranchProbabilityNode ;
39
51
import jdk .graal .compiler .nodes .extended .UnboxNode ;
52
+ import jdk .graal .compiler .nodes .memory .MemoryAccess ;
40
53
import jdk .graal .compiler .nodes .spi .CoreProviders ;
41
54
import jdk .graal .compiler .nodes .spi .LoweringTool ;
42
55
import jdk .graal .compiler .options .OptionValues ;
43
56
import jdk .graal .compiler .phases .util .Providers ;
57
+ import jdk .vm .ci .meta .DeoptimizationReason ;
44
58
import jdk .vm .ci .meta .JavaKind ;
45
59
import jdk .vm .ci .meta .ResolvedJavaField ;
46
60
@@ -254,7 +268,9 @@ public void lower(BoxNode box, LoweringTool tool) {
254
268
args .add ("valueOfCounter" , valueOfCounter );
255
269
SnippetTemplate template = template (tool , box , args );
256
270
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 );
258
274
}
259
275
260
276
public void lower (UnboxNode unbox , LoweringTool tool ) {
@@ -267,4 +283,72 @@ public void lower(UnboxNode unbox, LoweringTool tool) {
267
283
template .instantiate (tool .getMetaAccess (), unbox , SnippetTemplate .DEFAULT_REPLACER , args );
268
284
}
269
285
}
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
+ }
270
354
}
0 commit comments