@@ -279,6 +279,221 @@ void J9::RecognizedCallTransformer::process_java_lang_StringCoding_encodeASCII(T
279279 cfg->removeEdge (fallthroughBlock, fallbackPathBlock);
280280}
281281
282+ void J9::RecognizedCallTransformer::process_java_lang_StringLatin1_inflate_BICII (TR ::TreeTop *treetop, TR ::Node *node)
283+ {
284+ /*
285+ * Prior to JDK25, it is possible for an AbstractStringBuilder race condition to trigger a call to
286+ * java/lang/StringLatin1.inflate([BI[CII)V in such a way that an out of bounds exception gets triggered. This
287+ * transformation adds a check for that possibility and calls the slow path if an exception needs to be thrown in
288+ * the future.
289+ */
290+ TransformUtil::createTempsForCall (this , treetop);
291+
292+ /* Input byte array containing the string to inflate. */
293+ TR ::Node *srcObjNode = node->getFirstChild ();
294+
295+ /* Offset into the input byte array. Unit is number of bytes. */
296+ TR ::Node *srcOffNode = node->getSecondChild ();
297+
298+ /* Output char array to be modified with the result. */
299+ TR ::Node *dstObjNode = node->getChild (2 );
300+
301+ /* Offset into the output char array. Unit is number of chars. */
302+ TR ::Node *dstOffNode = node->getChild (3 );
303+
304+ /* Number of bytes to read from source and also number of chars to write to destination. */
305+ TR ::Node *copyLenNode = node->getChild (4 );
306+
307+ TR ::Node *srcArrayLenNode = TR::Node::create (TR ::arraylength, 1 , srcObjNode->duplicateTree ());
308+ srcArrayLenNode->setIsNonNegative (true );
309+
310+ TR ::Node *dstArrayLenNode = TR::Node::create (TR ::arraylength, 1 , dstObjNode->duplicateTree ());
311+ dstArrayLenNode->setIsNonNegative (true );
312+
313+ /*
314+ * The call node is duplicated to set up a fast path and a slow path.
315+ * The fast path has the inlineStringIntrinsic flag set so that it gets transformed by inlineDirectCall.
316+ * The slow path has the skipRecognizedCallTransformation flag set so it doesn't get transformed and
317+ * RecognizedCallTransformer won't look at it again.
318+ */
319+ TR ::TreeTop *slowPathTreeTop = TR::TreeTop::create (comp (), treetop->getNode ()->duplicateTree ());
320+ slowPathTreeTop->getNode ()->getFirstChild ()->setSkipRecognizedCallTransformation (true );
321+
322+ TR ::TreeTop *fastPathTreeTop = TR::TreeTop::create (comp (), treetop->getNode ()->duplicateTree ());
323+ fastPathTreeTop->getNode ()->getFirstChild ()->setIsSafeForCGToInlineStringIntrinsic (true );
324+
325+ /*
326+ * Add the following two checks to confirm that src and dst arrays are big enough:
327+ * srcArray.length >= (srcOffNode+copyLenNode)
328+ * dstArray.length >= (dstOffNode+copyLenNode)
329+ * If either is false, an out of bounds exception will occur so the code must branch to the slow path.
330+ */
331+ TR ::Node *srcOffsetPlusLenNode
332+ = TR::Node::create (TR ::iadd, 2 , srcOffNode->duplicateTree (), copyLenNode->duplicateTree ());
333+ TR ::Node *dstOffsetPlusLenNode
334+ = TR::Node::create (TR ::iadd, 2 , dstOffNode->duplicateTree (), copyLenNode->duplicateTree ());
335+
336+ TR ::Node *srcCmpNode = TR::Node::createif (TR ::ificmplt, srcArrayLenNode, srcOffsetPlusLenNode, NULL );
337+ TR ::TreeTop *srcCmpTree = TR::TreeTop::create (comp (), srcCmpNode);
338+
339+ TR ::Node *dstCmpNode = TR::Node::createif (TR ::ificmplt, dstArrayLenNode, dstOffsetPlusLenNode, NULL );
340+ TR ::TreeTop *dstCmpTree = TR::TreeTop::create (comp (), dstCmpNode);
341+
342+ TR ::Block *callBlock = treetop->getEnclosingBlock ();
343+
344+ /*
345+ * createConditionalBlocksBeforeTree is used to create the source array length check.
346+ * treetop is the current call treetop and the split point.
347+ * srcCmpTree is the check to see if out of bounds access will occur in the src array.
348+ * slowPathTreeTop is the slow path. This is a cold block that is branched to if an OOB exception will happen.
349+ * fastPathTreeTop is the fast path. This is the fallthrough path.
350+ * The slow path and fast path then merge back into each other.
351+ */
352+ callBlock->createConditionalBlocksBeforeTree (treetop, srcCmpTree, slowPathTreeTop, fastPathTreeTop,
353+ comp ()->getFlowGraph (), false , true );
354+
355+ /*
356+ * dstCmpTree is the check to see if out of bounds access will occur in the dst array.
357+ * The check is placed after the src out of bounds access check.
358+ * If the dst array is too small, the code branches to the slow path to handle the OOB exception.
359+ */
360+ TR ::Block *dstCmpBlock = TR::Block::createEmptyBlock (srcCmpNode, comp (), 0 , NULL );
361+ dstCmpBlock->setFrequency (callBlock->getFrequency ());
362+ comp ()->getFlowGraph ()->addNode (dstCmpBlock);
363+
364+ callBlock->getExit ()->join (dstCmpBlock->getEntry ());
365+ dstCmpBlock->getExit ()->join (fastPathTreeTop->getEnclosingBlock ()->getEntry ());
366+ comp ()->getFlowGraph ()->copyExceptionSuccessors (callBlock, dstCmpBlock);
367+
368+ dstCmpBlock->append (dstCmpTree);
369+ dstCmpNode->setBranchDestination (srcCmpNode->getBranchDestination ());
370+
371+ comp ()->getFlowGraph ()->addEdge (TR::CFGEdge::createEdge (callBlock, dstCmpBlock, comp ()->trMemory ()));
372+ comp ()->getFlowGraph ()->addEdge (
373+ TR::CFGEdge::createEdge (dstCmpBlock, fastPathTreeTop->getEnclosingBlock (), comp ()->trMemory ()));
374+ comp ()->getFlowGraph ()->addEdge (
375+ TR::CFGEdge::createEdge (dstCmpBlock, slowPathTreeTop->getEnclosingBlock (), comp ()->trMemory ()));
376+
377+ comp ()->getFlowGraph ()->removeEdge (callBlock, fastPathTreeTop->getEnclosingBlock ());
378+
379+ TR::DebugCounter::prependDebugCounter (comp (),
380+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicLatin1Inflate/fast/(%s)" , comp ()->signature ()),
381+ fastPathTreeTop);
382+ TR::DebugCounter::prependDebugCounter (comp (),
383+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicLatin1Inflate/slow/(%s)" , comp ()->signature ()),
384+ slowPathTreeTop);
385+ }
386+
387+ void J9::RecognizedCallTransformer::process_java_lang_StringUTF16_indexOf_BIBII (TR ::TreeTop *treetop, TR ::Node *node)
388+ {
389+ process_java_lang_StringLatin1_indexOf_BIBII (treetop, node, false /* isLatin1 */ );
390+ }
391+
392+ void J9::RecognizedCallTransformer::process_java_lang_StringLatin1_indexOf_BIBII (TR ::TreeTop *treetop, TR ::Node *node,
393+ bool isLatin1)
394+ {
395+ /*
396+ * The indexOf operation is searching for an index of a substring inside of another string. Prior to JDK25, it is
397+ * possible for an AbstractStringBuilder race condition to trigger a call to indexOf in such a way that an out of
398+ * bounds exception gets triggered. This transformation checks for that possibility and calls the slow path for
399+ * safety if there is a chance it may occur.
400+ */
401+
402+ TransformUtil::createTempsForCall (this , treetop);
403+
404+ /* This is the byte[] containing the characters for the string being searched in. */
405+ TR ::Node *arrayObjNode = node->getFirstChild ();
406+
407+ /*
408+ * This is the number of characters thought to be in the string being searched in. Due to the race condition, it
409+ * may be incorrect.
410+ */
411+ TR ::Node *arrayLenNode = node->getSecondChild ();
412+
413+ /*
414+ * maxArrayLenNode represents the maxmimum number of bytes that can be in the String being searched in without going
415+ * out of bounds.
416+ */
417+ TR ::Node *maxArrayLenNode = TR::Node::create (TR ::arraylength, 1 , arrayObjNode->duplicateTree ());
418+ maxArrayLenNode->setIsNonNegative (true );
419+
420+ /*
421+ * The call node is duplicated to set up a fast path and a slow path.
422+ * The fast path has the inlineStringIntrinsic flag set so that it gets transformed by inlineDirectCall.
423+ * The slow path has the skipRecognizedCallTransformation flag set so it doesn't get transformed and
424+ * RecognizedCallTransformer won't look at it again.
425+ */
426+ TR ::TreeTop *slowPathTreeTop = TR::TreeTop::create (comp (), treetop->getNode ()->duplicateTree ());
427+ slowPathTreeTop->getNode ()->getFirstChild ()->setSkipRecognizedCallTransformation (true );
428+
429+ TR ::TreeTop *fastPathTreeTop = TR::TreeTop::create (comp (), treetop->getNode ()->duplicateTree ());
430+ fastPathTreeTop->getNode ()->getFirstChild ()->setIsSafeForCGToInlineStringIntrinsic (true );
431+
432+ TR ::SymbolReference *newSymbolReference = NULL ;
433+ TR ::DataType dataType = node->getDataType ();
434+
435+ /* If the call node has a reference count >1, a symref is created to hold the result. */
436+ if (node->getReferenceCount () > 1 ) {
437+ newSymbolReference = comp ()->getSymRefTab ()->createTemporary (comp ()->getMethodSymbol (), dataType);
438+ TR::Node::recreate (node, comp ()->il .opCodeForDirectLoad (dataType));
439+ node->setSymbolReference (newSymbolReference);
440+ node->removeAllChildren ();
441+ }
442+
443+ /*
444+ * For latin1, the value of maxArrayLenNode must be greater than or equal to the value of arrayLenNode.
445+ * For UTF16, the value of (maxArrayLenNode >> 1) must be greater than or equal to the value of arrayLenNode.
446+ * If this isn't true, it is possible for the indexOf operation to go out of bounds so the slow path must be taken.
447+ */
448+ if (!isLatin1) {
449+ maxArrayLenNode = TR::Node::create (TR ::ishr, 2 , maxArrayLenNode, TR::Node::create (TR ::iconst, 0 , 1 ));
450+ }
451+ TR ::Node *cmpNode = TR::Node::createif (TR ::ificmpgt, arrayLenNode->duplicateTree (), maxArrayLenNode, NULL );
452+ TR ::TreeTop *cmpTree = TR::TreeTop::create (comp (), cmpNode);
453+
454+ TR ::Block *callBlock = treetop->getEnclosingBlock ();
455+
456+ /*
457+ * createConditionalBlocksBeforeTree puts everything together.
458+ * treetop is the current call treetop and the split point.
459+ * cmpTree is the check to see if out of bounds access is possible.
460+ * slowPathTreeTop is the slow path. This is a cold block that is branched to if OOB is possible.
461+ * fastPathTreeTop is the fast path. This is the fallthrough path.
462+ * The slow path and fast path then merge back into each other.
463+ */
464+ callBlock->createConditionalBlocksBeforeTree (treetop, cmpTree, slowPathTreeTop, fastPathTreeTop,
465+ comp ()->getFlowGraph (), false , true );
466+
467+ /* If the call node has a reference count >1, a symref is used to record the return value from the call. */
468+ if (newSymbolReference) {
469+ TR ::Node *fastPathStoreNode = TR::Node::createWithSymRef (comp ()->il .opCodeForDirectStore (dataType), 1 , 1 ,
470+ fastPathTreeTop->getNode ()->getFirstChild (), newSymbolReference);
471+ TR ::TreeTop *fastPathStoreTree = TR::TreeTop::create (comp (), fastPathStoreNode);
472+ fastPathTreeTop->insertAfter (fastPathStoreTree);
473+
474+ TR ::Node *slowPathStoreNode = TR::Node::createWithSymRef (comp ()->il .opCodeForDirectStore (dataType), 1 , 1 ,
475+ slowPathTreeTop->getNode ()->getFirstChild (), newSymbolReference);
476+ TR ::TreeTop *slowPathStoreTree = TR::TreeTop::create (comp (), slowPathStoreNode);
477+ slowPathTreeTop->insertAfter (slowPathStoreTree);
478+ }
479+
480+ if (isLatin1) {
481+ TR::DebugCounter::prependDebugCounter (comp (),
482+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicLatin1IndexOf/fast/(%s)" , comp ()->signature ()),
483+ fastPathTreeTop);
484+ TR::DebugCounter::prependDebugCounter (comp (),
485+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicLatin1IndexOf/slow/(%s)" , comp ()->signature ()),
486+ slowPathTreeTop);
487+ } else {
488+ TR::DebugCounter::prependDebugCounter (comp (),
489+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicUTF16IndexOf/fast/(%s)" , comp ()->signature ()),
490+ fastPathTreeTop);
491+ TR::DebugCounter::prependDebugCounter (comp (),
492+ TR::DebugCounter::debugCounterName (comp (), " treesIntrinsicUTF16IndexOf/slow/(%s)" , comp ()->signature ()),
493+ slowPathTreeTop);
494+ }
495+ }
496+
282497void J9::RecognizedCallTransformer::process_java_lang_StringLatin1_inflate_BIBII (TR ::TreeTop *treetop, TR ::Node *node)
283498{
284499 /*
@@ -1796,6 +2011,7 @@ bool J9::RecognizedCallTransformer::isInlineable(TR::TreeTop *treetop)
17962011 TR ::RecognizedMethod rm = node->getSymbol ()->castToMethodSymbol ()->getMandatoryRecognizedMethod ();
17972012
17982013 bool isILGenPass = !getLastRun ();
2014+ static const bool disableStringIntrinsicFlagChk = feGetEnv (" TR_DisableStringIntrinsicFlagChk" ) != NULL ;
17992015 if (isILGenPass) {
18002016 switch (rm) {
18012017 case TR ::sun_misc_Unsafe_getAndAddInt:
@@ -1875,6 +2091,19 @@ bool J9::RecognizedCallTransformer::isInlineable(TR::TreeTop *treetop)
18752091 return cg ()->getSupportsInlineEncodeASCII ();
18762092 case TR ::java_lang_StringLatin1_inflate_BIBII:
18772093 return (cg ()->getSupportsArrayTranslateTROTNoBreak () && !comp ()->target ().cpu .isPower ());
2094+ #if JAVA_SPEC_VERSION < 25
2095+ case TR ::java_lang_StringLatin1_inflate_BICII:
2096+ return (!disableStringIntrinsicFlagChk && cg ()->getSupportsInlineStringLatin1Inflate ()
2097+ && !node->isSafeForCGToInlineStringIntrinsic () && !node->checkSkipRecognizedCallTransformation ());
2098+ case TR ::java_lang_StringUTF16_indexOf:
2099+ if (comp ()->target ().cpu .isPower ()) {
2100+ return false ;
2101+ }
2102+ /* Intentional fallthrough. */
2103+ case TR ::java_lang_StringLatin1_indexOf:
2104+ return (!disableStringIntrinsicFlagChk && cg ()->getSupportsInlineStringIndexOfString ()
2105+ && !node->isSafeForCGToInlineStringIntrinsic () && !node->checkSkipRecognizedCallTransformation ());
2106+ #endif /* JAVA_SPEC_VERSION < 25 */
18782107 case TR ::jdk_internal_util_ArraysSupport_vectorizedMismatch:
18792108 return cg ()->getSupportsInlineVectorizedMismatch ();
18802109 default :
@@ -2020,6 +2249,17 @@ void J9::RecognizedCallTransformer::transform(TR::TreeTop *treetop)
20202249 case TR ::java_lang_StringLatin1_inflate_BIBII:
20212250 process_java_lang_StringLatin1_inflate_BIBII (treetop, node);
20222251 break ;
2252+ #if JAVA_SPEC_VERSION < 25
2253+ case TR ::java_lang_StringLatin1_inflate_BICII:
2254+ process_java_lang_StringLatin1_inflate_BICII (treetop, node);
2255+ break ;
2256+ case TR ::java_lang_StringUTF16_indexOf:
2257+ process_java_lang_StringUTF16_indexOf_BIBII (treetop, node);
2258+ break ;
2259+ case TR ::java_lang_StringLatin1_indexOf:
2260+ process_java_lang_StringLatin1_indexOf_BIBII (treetop, node, true );
2261+ break ;
2262+ #endif /* JAVA_SPEC_VERSION < 25 */
20232263 case TR ::java_lang_StrictMath_sqrt:
20242264 case TR ::java_lang_Math_sqrt:
20252265 process_java_lang_StrictMath_and_Math_sqrt (treetop, node);
0 commit comments