Skip to content

Commit

Permalink
Add null/array checks for OffHeap NotStaticField atomic case
Browse files Browse the repository at this point in the history
  • Loading branch information
rmnattas committed Jan 21, 2025
1 parent 4c82e0a commit 2995d6a
Showing 1 changed file with 196 additions and 59 deletions.
255 changes: 196 additions & 59 deletions runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,10 +512,24 @@ void J9::RecognizedCallTransformer::process_java_lang_StrictMath_and_Math_sqrt(T
/*
Transform an Unsafe atomic call to diamonds with equivalent semantics
If OffHeap is enabled, a runtime isArray check is added to determine
if loading the dataAddrPtr is necessary.
yes
isObjectNull [A] ------------------------------------------>
| |
| no |
#if OffHeap |
| yes |
isArray [I] ------------------------> |
| | |
| use the dataAddrPtr of the array |
| xcall atomic method helper [J] |
| | |
| [H] |
| no |
| |
#endif OffHeap |
| yes |
isNotLowTagged [B] ---------------------------------------->
| |
Expand Down Expand Up @@ -562,6 +576,18 @@ ifacmpeq -> <Block_E>
aconst null
end Block_A
#if OffHeap
start Block_I
ifacmpne -> <Block_J>
andi
lloadi <isClassDepthAndFlags>
aloadi <vft-symbol>
aload <object-1>
iconst 0x10000 // array-flag
iconst 0
end Block_I
#endif OffHeap
start Block_B
iflcmpne -> <Block_E>
land
Expand Down Expand Up @@ -600,6 +626,20 @@ xstore
goto --> block_H
end Block_G
#if OffHeap
start Block_J
xcall atomic method helper
aladd
aloadi <contiguousArrayDataAddrField>
aload <object-1>
lload <offset>
xload value
xstore
==>xcall
goto --> block_H
end Block_J
#endif OffHeap
start Block_E
xcall atomic method helper
aladd
Expand Down Expand Up @@ -658,10 +698,52 @@ void J9::RecognizedCallTransformer::processUnsafeAtomicCall(TR::TreeTop* treetop
if (isNotStaticField)
{
// It is safe to skip diamond, the address can be calculated directly via [object+offset]
address = comp()->target().is32Bit() ? TR::Node::create(TR::aiadd, 2, objectNode, TR::Node::create(TR::l2i, 1, offsetNode)) :
TR::Node::create(TR::aladd, 2, objectNode, offsetNode);
if (enableTrace)
traceMsg(comp(), "Field is not static, use the object and offset directly\n");

// Except if OffHeap is used, then check if object is array
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
if (TR::Compiler->om.isOffHeapAllocationEnabled())
{
// Generate null-check and array-check blocks
TR::TransformUtil::createTempsForCall(this, treetop);
objectNode = unsafeCall->getChild(1);
offsetNode = unsafeCall->getChild(2);

// Test if object is null
isObjectNullNode = TR::Node::createif(TR::ifacmpeq, objectNode->duplicateTree(), TR::Node::aconst(0), NULL);
isObjectNullTreeTop = TR::TreeTop::create(comp(), isObjectNullNode);
treetop->insertBefore(isObjectNullTreeTop);
treetop->getEnclosingBlock()->split(treetop, cfg, fixupCommoning);

if (enableTrace)
traceMsg(comp(), "Created isObjectNull test node n%dn, non-null object will fall through to Block_%d\n", isObjectNullNode->getGlobalIndex(), treetop->getEnclosingBlock()->getNumber());

//generate array check treetop
TR::Node *vftLoad = TR::Node::createWithSymRef(TR::aloadi, 1, 1,
objectNode->duplicateTree(),
comp()->getSymRefTab()->findOrCreateVftSymbolRef());

isObjectArrayNode = TR::Node::createif(TR::ificmpne,
comp()->fej9()->testIsClassArrayType(vftLoad),
TR::Node::create(TR::iconst, 0),
NULL);
isObjectArrayTreeTop = TR::TreeTop::create(comp(), isObjectArrayNode, NULL, NULL);
treetop->insertBefore(isObjectArrayTreeTop);
treetop->getEnclosingBlock()->split(treetop, cfg, fixupCommoning);

if (enableTrace)
traceMsg(comp(), "Created isObjectArray test node n%dn, array will branch to array access block\n", isObjectArrayNode->getGlobalIndex());

address = comp()->target().is32Bit() ? TR::Node::create(TR::aiadd, 2, objectNode->duplicateTree(), TR::Node::create(TR::l2i, 1, offsetNode->duplicateTree())) :
TR::Node::create(TR::aladd, 2, objectNode->duplicateTree(), offsetNode->duplicateTree());
}
else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
{
address = comp()->target().is32Bit() ? TR::Node::create(TR::aiadd, 2, objectNode, TR::Node::create(TR::l2i, 1, offsetNode)) :
TR::Node::create(TR::aladd, 2, objectNode, offsetNode);
if (enableTrace)
traceMsg(comp(), "Field is not static, use the object and offset directly\n");
}
}
else
{
Expand Down Expand Up @@ -795,7 +877,8 @@ void J9::RecognizedCallTransformer::processUnsafeAtomicCall(TR::TreeTop* treetop
unsafeCall->removeChild(1); // remove object node
unsafeCall->setSymbolReference(comp()->getSymRefTab()->findOrCreateCodeGenInlinedHelper(helper));

if (!isNotStaticField)
// Setup and connect check blocks if generated
if (isObjectNullTreeTop)
{
// Split so that the return value from the atomic method helper call will be stored into a temp if required
TR::TreeTop *nextTreeTop = treetop->getNextTreeTop();
Expand All @@ -817,54 +900,17 @@ void J9::RecognizedCallTransformer::processUnsafeAtomicCall(TR::TreeTop* treetop
}
}

/* Example of the atomic method helper
* n92n treetop
* n93n icall <atomicFetchAndAdd>
* n94n aladd
* n95n aload <temp slot 5>
* n96n lload <temp slot 3>
* n97n iload <temp slot 4>
*/
// Create another helper call that loads from ramStatics
TR::TreeTop *unsafeCallRamStaticsTT = treetop->duplicateTree();
TR::Node *unsafeCallRamStaticsNode = unsafeCallRamStaticsTT->getNode()->getFirstChild();
TR::Node *addressNode = unsafeCallRamStaticsNode->getFirstChild();
TR::Node *loadNode = addressNode->getFirstChild();

loadNode->setSymbolReference(newSymbolReference); // Use the same symRef as the objectAdjustmentNode
treetop->insertBefore(unsafeCallRamStaticsTT);

// Store the return value from the helper call that loads from ramStatics
if (storeReturnNode)
{
TR::Node *storeNode = TR::Node::createStore(unsafeCall, storeReturnNode->getSymbolReference(), unsafeCallRamStaticsNode);
treetop->insertBefore(TR::TreeTop::create(comp(), storeNode));
}

// Insert goto from the helper call that loads from ramStatics to the final return block
TR::Node *gotoNode = TR::Node::create(unsafeCall, TR::Goto);
gotoNode->setBranchDestination(returnBlock->getEntry());
treetop->insertBefore(TR::TreeTop::create(comp(), gotoNode));

if (enableTrace)
traceMsg(comp(), "Created atomic method helper block_%d that loads from ramStatics treetop n%dn. returnBlock block_%d\n",
unsafeCallRamStaticsTT->getEnclosingBlock()->getNumber(), unsafeCallRamStaticsTT->getNode()->getGlobalIndex(), returnBlock->getNumber());

// Split the block that contains the original helper call into a separate block
treetop->getEnclosingBlock()->split(treetop, cfg, fixupCommoning);

// Create another helper call for array access (offheap only)
#if defined (J9VM_GC_SPARSE_HEAP_ALLOCATION)
if (isObjectArrayTreeTop != NULL)
// If isNotStaticField and array-check is generated then
// setup and connect the null and array check blocks
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
if (isNotStaticField && isObjectArrayTreeTop)
{
// Generate array access block
TR::TreeTop *arrayAccessTreeTop = treetop->duplicateTree();
TR::Node *addrToAccessNode = arrayAccessTreeTop->getNode()->getChild(0)->getChild(0);
TR::Block *arrayAccessBlock = TR::Block::createEmptyBlock(arrayAccessTreeTop->getNode(), comp(),
treetop->getEnclosingBlock()->getFrequency());
arrayAccessBlock->append(arrayAccessTreeTop);
arrayAccessBlock->append(TR::TreeTop::create(comp(), TR::Node::create(arrayAccessTreeTop->getNode(),
TR::Goto, 0,
returnBlock->getEntry())));

//load dataAddr
TR::Node *objBaseAddrNode = addrToAccessNode->getChild(0);
Expand All @@ -877,10 +923,13 @@ void J9::RecognizedCallTransformer::processUnsafeAtomicCall(TR::TreeTop* treetop

//set as array test destination and insert array access into IL trees
// - if object is array, goto array access block
// - else, fall through to lowtag test
unsafeCallRamStaticsTT->getEnclosingBlock()->getExit()->insertTreeTopsAfterMe(arrayAccessBlock->getEntry(), arrayAccessBlock->getExit());
// - else, fall through to non-array access
treetop->getEnclosingBlock()->getExit()->insertTreeTopsAfterMe(arrayAccessBlock->getEntry(), arrayAccessBlock->getExit());
isObjectArrayNode->setBranchDestination(arrayAccessTreeTop->getEnclosingBlock()->getEntry());

treetop->getEnclosingBlock()->append(TR::TreeTop::create(comp(), TR::Node::create(arrayAccessTreeTop->getNode(),
TR::Goto, 0,
returnBlock->getEntry())));
cfg->addNode(arrayAccessBlock);
cfg->addEdge(TR::CFGEdge::createEdge(isObjectArrayTreeTop->getEnclosingBlock(), arrayAccessBlock, comp()->trMemory()));
cfg->addEdge(TR::CFGEdge::createEdge(arrayAccessBlock, returnBlock, comp()->trMemory()));
Expand All @@ -892,23 +941,111 @@ void J9::RecognizedCallTransformer::processUnsafeAtomicCall(TR::TreeTop* treetop
arrayAccessTreeTop->insertTreeTopsAfterMe(TR::TreeTop::create(comp(), storeNode));
}

isObjectNullNode->setBranchDestination(treetop->getEnclosingBlock()->getEntry());
cfg->addEdge(TR::CFGEdge::createEdge(isObjectNullTreeTop->getEnclosingBlock(), treetop->getEnclosingBlock(), comp()->trMemory()));

if (enableTrace)
traceMsg(comp(), "Created array access helper block_%d that loads dataAddr pointer from array object address\n", arrayAccessBlock->getNumber());
}
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
// If not isNotStaticField then setup and connect all
// generated check blocks
if (!isNotStaticField)
{
// Create another helper call that loads from ramStatics
/* Example of the atomic method helper
* n92n treetop
* n93n icall <atomicFetchAndAdd>
* n94n aladd
* n95n aload <temp slot 5>
* n96n lload <temp slot 3>
* n97n iload <temp slot 4>
*/
TR::TreeTop *unsafeCallRamStaticsTT = treetop->duplicateTree();
TR::Node *unsafeCallRamStaticsNode = unsafeCallRamStaticsTT->getNode()->getFirstChild();
TR::Node *addressNode = unsafeCallRamStaticsNode->getFirstChild();
TR::Node *loadNode = addressNode->getFirstChild();

loadNode->setSymbolReference(newSymbolReference); // Use the same symRef as the objectAdjustmentNode
treetop->insertBefore(unsafeCallRamStaticsTT);

// Store the return value from the helper call that loads from ramStatics
if (storeReturnNode)
{
TR::Node *storeNode = TR::Node::createStore(unsafeCall, storeReturnNode->getSymbolReference(), unsafeCallRamStaticsNode);
treetop->insertBefore(TR::TreeTop::create(comp(), storeNode));
}

// Setup CFG edges
cfg->addEdge(unsafeCallRamStaticsTT->getEnclosingBlock(), returnBlock);
// Insert goto from the helper call that loads from ramStatics to the final return block
TR::Node *gotoNode = TR::Node::create(unsafeCall, TR::Goto);
gotoNode->setBranchDestination(returnBlock->getEntry());
treetop->insertBefore(TR::TreeTop::create(comp(), gotoNode));

if (enableTrace)
traceMsg(comp(), "Block_%d contains call to atomic method helper, and is the target of isObjectNull and isNotLowTagged tests\n", treetop->getEnclosingBlock()->getNumber());
if (enableTrace)
traceMsg(comp(), "Created atomic method helper block_%d that loads from ramStatics treetop n%dn. returnBlock block_%d\n",
unsafeCallRamStaticsTT->getEnclosingBlock()->getNumber(), unsafeCallRamStaticsTT->getNode()->getGlobalIndex(), returnBlock->getNumber());

isObjectNullNode->setBranchDestination(treetop->getEnclosingBlock()->getEntry());
cfg->addEdge(TR::CFGEdge::createEdge(isObjectNullTreeTop->getEnclosingBlock(), treetop->getEnclosingBlock(), comp()->trMemory()));
isNotLowTaggedNode->setBranchDestination(treetop->getEnclosingBlock()->getEntry());
cfg->addEdge(TR::CFGEdge::createEdge(isNotLowTaggedTreeTop->getEnclosingBlock(), treetop->getEnclosingBlock(), comp()->trMemory()));
// Split the block that contains the original helper call into a separate block
treetop->getEnclosingBlock()->split(treetop, cfg, fixupCommoning);

cfg->removeEdge(unsafeCallRamStaticsTT->getEnclosingBlock(), treetop->getEnclosingBlock());
// Create another helper call for array access (offheap only)
#if defined (J9VM_GC_SPARSE_HEAP_ALLOCATION)
if (isObjectArrayTreeTop != NULL)
{
TR::TreeTop *arrayAccessTreeTop = treetop->duplicateTree();
TR::Node *addrToAccessNode = arrayAccessTreeTop->getNode()->getChild(0)->getChild(0);
TR::Block *arrayAccessBlock = TR::Block::createEmptyBlock(arrayAccessTreeTop->getNode(), comp(),
treetop->getEnclosingBlock()->getFrequency());
arrayAccessBlock->append(arrayAccessTreeTop);
arrayAccessBlock->append(TR::TreeTop::create(comp(), TR::Node::create(arrayAccessTreeTop->getNode(),
TR::Goto, 0,
returnBlock->getEntry())));

//load dataAddr
TR::Node *objBaseAddrNode = addrToAccessNode->getChild(0);
TR::Node *dataAddrNode = TR::TransformUtil::generateDataAddrLoadTrees(comp(), objBaseAddrNode);
addrToAccessNode->setChild(0, dataAddrNode);

//correct refcounts
objBaseAddrNode->decReferenceCount();
dataAddrNode->incReferenceCount();

//set as array test destination and insert array access into IL trees
// - if object is array, goto array access block
// - else, fall through to lowtag test
unsafeCallRamStaticsTT->getEnclosingBlock()->getExit()->insertTreeTopsAfterMe(arrayAccessBlock->getEntry(), arrayAccessBlock->getExit());
isObjectArrayNode->setBranchDestination(arrayAccessTreeTop->getEnclosingBlock()->getEntry());

cfg->addNode(arrayAccessBlock);
cfg->addEdge(TR::CFGEdge::createEdge(isObjectArrayTreeTop->getEnclosingBlock(), arrayAccessBlock, comp()->trMemory()));
cfg->addEdge(TR::CFGEdge::createEdge(arrayAccessBlock, returnBlock, comp()->trMemory()));

// Store the return value from the helper call for array access
if (storeReturnNode)
{
TR::Node *storeNode = TR::Node::createStore(unsafeCall, storeReturnNode->getSymbolReference(), arrayAccessTreeTop->getNode()->getFirstChild());
arrayAccessTreeTop->insertTreeTopsAfterMe(TR::TreeTop::create(comp(), storeNode));
}

if (enableTrace)
traceMsg(comp(), "Created array access helper block_%d that loads dataAddr pointer from array object address\n", arrayAccessBlock->getNumber());
}
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

// Setup CFG edges
cfg->addEdge(unsafeCallRamStaticsTT->getEnclosingBlock(), returnBlock);

if (enableTrace)
traceMsg(comp(), "Block_%d contains call to atomic method helper, and is the target of isObjectNull and isNotLowTagged tests\n", treetop->getEnclosingBlock()->getNumber());

isObjectNullNode->setBranchDestination(treetop->getEnclosingBlock()->getEntry());
cfg->addEdge(TR::CFGEdge::createEdge(isObjectNullTreeTop->getEnclosingBlock(), treetop->getEnclosingBlock(), comp()->trMemory()));
isNotLowTaggedNode->setBranchDestination(treetop->getEnclosingBlock()->getEntry());
cfg->addEdge(TR::CFGEdge::createEdge(isNotLowTaggedTreeTop->getEnclosingBlock(), treetop->getEnclosingBlock(), comp()->trMemory()));

cfg->removeEdge(unsafeCallRamStaticsTT->getEnclosingBlock(), treetop->getEnclosingBlock());
}
}
}

Expand Down

0 comments on commit 2995d6a

Please sign in to comment.