Skip to content

Commit 0985ff3

Browse files
authored
Merge pull request #20163 from midronij/unsafe-CAS-nulltest
Generate runtime NULL test for Unsafe.compareAndSwap()
2 parents 7ac6f82 + 870a50c commit 0985ff3

File tree

1 file changed

+74
-49
lines changed

1 file changed

+74
-49
lines changed

runtime/compiler/optimizer/InlinerTempForJ9.cpp

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,15 +1710,19 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
17101710
// Codegens have a fast path for the compare and swaps, but cannot deal with the case where the offset value passed in to a the CAS is low tagged
17111711
// (A low tagged offset value means the object being passed in is a java/lang/Class object, and we want a static field)
17121712

1713-
// Regarding which checks/diamonds get generated, there are three possible cases:
1713+
// Regarding which type checks/diamonds get generated, there are three possible cases:
17141714
// 1.) Only the low tagged check is generated. This will occur either when gencon GC policy is being used, or under
17151715
// balanced GC policy with offheap allocation enabled if the object being operated on is known NOT to be an array
17161716
// at compile time.
1717-
// 2.) No checks are generated. This will occur under balanced GC policy with offheap allocation enabled if the object
1717+
// 2.) No type checks are generated. This will occur under balanced GC policy with offheap allocation enabled if the object
17181718
// being operated on is known to be an array at compile time (since if the object is an array, it can't also be a
17191719
// java/lang/Class object).
17201720
// 3.) Both the array and low tagged checks are generated. This will occur under balanced GC policy with offheap allocation
17211721
// enabled if the type of the object being operated on is unknown at compile time.
1722+
//
1723+
// In addition to type checks, a NULL check on the object address is needed to ensure that we do not try to load dataAddr
1724+
// from a NULL reference. This is only a concern when offheap is enabled AND there is a possibility that the object is an array.
1725+
// so the NULL check will only be generated in cases (2) and (3).
17221726

17231727
// This method assumes the offset node is of type long, and is the second child of the unsafe call.
17241728
TR_InlinerDelimiter delimiter(tracer(),"createUnsafeCASCallDiamond");
@@ -1759,14 +1763,25 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
17591763

17601764
TR::Node *offsetNode = callNode->getChild(2);
17611765

1762-
TR::TreeTop *compareTree;
1766+
TR::TreeTop *compareTree = NULL;
1767+
TR::TreeTop *lowTagAccessTreeTop = NULL;
17631768

1764-
//do not generate low tagged test in case (2)
1765-
if (!arrayTestNeeded && arrayBlockNeeded)
1766-
compareTree = NULL;
1767-
else
1769+
//generate low tagged test/access block in cases (1) and (3)
1770+
if (arrayTestNeeded || !arrayBlockNeeded)
1771+
{
1772+
//create lowtag test treetop
17681773
compareTree = genClassCheckForUnsafeGetPut(offsetNode, /* branchIfLowTagged */ false );
17691774

1775+
//create lowtag access treetop
1776+
lowTagAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1777+
lowTagAccessTreeTop->getNode()->getFirstChild()->setVisitCount(_inliner->getVisitCount());
1778+
1779+
debugTrace(tracer(),"lowTagAccessTreeTop = %p",lowTagAccessTreeTop->getNode());
1780+
}
1781+
1782+
1783+
TR::TreeTop *isNullTreeTop = NULL;
1784+
TR::TreeTop *nonNullAccessTreeTop = NULL;
17701785
TR::TreeTop *isArrayTreeTop = NULL;
17711786
TR::TreeTop *arrayAccessTreeTop = NULL;
17721787
TR::TreeTop *nonArrayAccessTreeTop = NULL;
@@ -1824,31 +1839,19 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
18241839

18251840
CASicallNode->setIsSafeForCGToFastPathUnsafeCall(true);
18261841

1827-
//if array test is being generated, need to create non-array access treetop (same as default)
1828-
if (isArrayTreeTop)
1829-
nonArrayAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1842+
//create NULL test treetop
1843+
TR::Node *objAddr = callNodeTreeTop->getNode()->getChild(0)->getChild(1);
1844+
TR::Node *isNullNode = TR::Node::createif(TR::ifacmpeq, objAddr, TR::Node::create(objAddr, TR::aconst, 0, 0), NULL);
1845+
isNullTreeTop = TR::TreeTop::create(comp(), isNullNode);
18301846
}
18311847
#endif /* J9VM_GC_ENABLE_SPARSE_HEAP_ALLOCATION */
18321848

1833-
TR::TreeTop *branchTargetTree;
1834-
TR::TreeTop *fallThroughTree;
1835-
1836-
//only generate if and else trees for low tagged test in cases (1) and (3)
1837-
if (compareTree != NULL)
1838-
{
1839-
// genClassCheck generates a ifcmpne offset&mask 1, meaning if it is NOT
1840-
// lowtagged (ie offset&mask == 0), the branch will be taken
1841-
branchTargetTree = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1842-
branchTargetTree->getNode()->getFirstChild()->setIsSafeForCGToFastPathUnsafeCall(true);
1843-
1844-
fallThroughTree = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1845-
1849+
//default access tree (non-array, non-lowtagged)
1850+
TR::TreeTop *defaultAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1851+
defaultAccessTreeTop->getNode()->getFirstChild()->setIsSafeForCGToFastPathUnsafeCall(true);
1852+
defaultAccessTreeTop->getNode()->getFirstChild()->setVisitCount(_inliner->getVisitCount());
18461853

1847-
branchTargetTree->getNode()->getFirstChild()->setVisitCount(_inliner->getVisitCount());
1848-
fallThroughTree->getNode()->getFirstChild()->setVisitCount(_inliner->getVisitCount());
1849-
1850-
debugTrace(tracer(),"branchTargetTree = %p fallThroughTree = %p",branchTargetTree->getNode(),fallThroughTree->getNode());
1851-
}
1854+
debugTrace(tracer(),"defaultAccessTreeTop = %p", defaultAccessTreeTop->getNode());
18521855

18531856

18541857
// the call itself may be commoned, so we need to create a temp for the callnode itself
@@ -1867,43 +1870,65 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
18671870

18681871
TR::Block *callBlock = callNodeTreeTop->getEnclosingBlock();
18691872

1870-
if (arrayTestNeeded) //in case (3), we generate the array test diamond, followed by the low tagged check test
1873+
if (arrayTestNeeded) //in case (3), we generate the null test diamond, then the array test diamond, and then the low tagged test diamond
18711874
{
1872-
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, isArrayTreeTop, arrayAccessTreeTop, nonArrayAccessTreeTop, comp()->getFlowGraph(), false, false);
1873-
nonArrayAccessTreeTop->getEnclosingBlock()->createConditionalBlocksBeforeTree(nonArrayAccessTreeTop, compareTree, branchTargetTree, fallThroughTree, comp()->getFlowGraph(), false, false);
1875+
TR::TreeTop *nonNullAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
1876+
1877+
//add null test and array test
1878+
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, isNullTreeTop, defaultAccessTreeTop, nonNullAccessTreeTop, comp()->getFlowGraph(), false, false);
1879+
TR::Block *joinBlock = nonNullAccessTreeTop->getEnclosingBlock()->createConditionalBlocksBeforeTree(nonNullAccessTreeTop, isArrayTreeTop, arrayAccessTreeTop, compareTree, comp()->getFlowGraph(), false, false);
1880+
1881+
TR::CFG *cfg = comp()->getFlowGraph();
1882+
1883+
//add lowtag test
1884+
TR::Block *isLowTaggedBlock = compareTree->getEnclosingBlock();
1885+
cfg->removeEdge(isLowTaggedBlock, joinBlock);
1886+
1887+
//add branch path (default access)
1888+
//note that genClassCheck generates a ifcmpne offset&mask 1, meaning if it is NOT
1889+
//lowtagged (ie offset&mask == 0), the branch will be taken
1890+
TR::Block *defaultAccessBlock = defaultAccessTreeTop->getEnclosingBlock();
1891+
compareTree->getNode()->setBranchDestination(defaultAccessBlock->getEntry());
1892+
cfg->addEdge(TR::CFGEdge::createEdge(isLowTaggedBlock, defaultAccessBlock, trMemory()));
1893+
1894+
//add fallthrough path (lowtag access)
1895+
TR::Block *lowTagAccessBlock = TR::Block::createEmptyBlock(compareTree->getNode(), comp(), isLowTaggedBlock->getFrequency(), isLowTaggedBlock);
1896+
lowTagAccessBlock->append(lowTagAccessTreeTop);
1897+
isLowTaggedBlock->getExit()->insertTreeTopsAfterMe(lowTagAccessBlock->getEntry(), lowTagAccessBlock->getExit());
1898+
cfg->addNode(lowTagAccessBlock);
1899+
cfg->addEdge(TR::CFGEdge::createEdge(isLowTaggedBlock, lowTagAccessBlock, trMemory()));
1900+
cfg->addEdge(TR::CFGEdge::createEdge(lowTagAccessBlock, joinBlock, trMemory()));
18741901
}
1875-
else if (arrayBlockNeeded) //in case (2), no branching is needed: we simply need to replace the original CAS call with the modified array access block
1902+
else if (arrayBlockNeeded) //in case (2), we generate only the null test diamond
18761903
{
1877-
callNodeTreeTop->insertAfter(arrayAccessTreeTop);
1878-
callNodeTreeTop->getPrevTreeTop()->join(callNodeTreeTop->getNextTreeTop());
1879-
callBlock->split(arrayAccessTreeTop->getNextTreeTop(), comp()->getFlowGraph(), true);
1880-
callBlock->split(arrayAccessTreeTop, comp()->getFlowGraph(), true);
1904+
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, isNullTreeTop, defaultAccessTreeTop, arrayAccessTreeTop, comp()->getFlowGraph(), false, false);
18811905
}
18821906
else if (compareTree != NULL) //in case (1), we only generate the low tagged test diamond
1883-
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, compareTree, branchTargetTree, fallThroughTree, comp()->getFlowGraph(), false, false);
1884-
1907+
{
1908+
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, compareTree, defaultAccessTreeTop, lowTagAccessTreeTop, comp()->getFlowGraph(), false, false);
1909+
}
18851910

18861911
// the original call will be deleted by createConditionalBlocksBeforeTree, but if the refcount was > 1, we need to insert stores.
18871912
if (newSymbolReference)
18881913
{
1889-
if (compareTree != NULL) //case (1) and (3) only
1890-
{
1891-
TR::Node *branchTargetStoreNode = TR::Node::createWithSymRef(comp()->il.opCodeForDirectStore(dataType), 1, 1, branchTargetTree->getNode()->getFirstChild(), newSymbolReference);
1892-
TR::TreeTop *branchTargetStoreTree = TR::TreeTop::create(comp(), branchTargetStoreNode);
1914+
TR::Node *defaultAccessStoreNode = TR::Node::createWithSymRef(comp()->il.opCodeForDirectStore(dataType), 1, 1, defaultAccessTreeTop->getNode()->getFirstChild(), newSymbolReference);
1915+
TR::TreeTop *defaultAccessStoreTree = TR::TreeTop::create(comp(), defaultAccessStoreNode);
18931916

1894-
branchTargetTree->insertAfter(branchTargetStoreTree);
1917+
defaultAccessTreeTop->insertAfter(defaultAccessStoreTree);
18951918

1896-
debugTrace(tracer(),"Inserted store tree %p for branch target (taken) side of the diamond", branchTargetStoreNode);
1919+
debugTrace(tracer(),"Inserted store tree %p for branch target (taken) side of the diamond", defaultAccessStoreNode);
18971920

1898-
TR::Node *fallThroughStoreNode = TR::Node::createWithSymRef(comp()->il.opCodeForDirectStore(dataType), 1, 1, fallThroughTree->getNode()->getFirstChild(), newSymbolReference);
1899-
TR::TreeTop *fallThroughStoreTree = TR::TreeTop::create(comp(), fallThroughStoreNode);
1921+
if (compareTree != NULL) //cases (1) and (3) only
1922+
{
1923+
TR::Node *lowTagAccessStoreNode = TR::Node::createWithSymRef(comp()->il.opCodeForDirectStore(dataType), 1, 1, lowTagAccessTreeTop->getNode()->getFirstChild(), newSymbolReference);
1924+
TR::TreeTop *lowTagAccessStoreTree = TR::TreeTop::create(comp(), lowTagAccessStoreNode);
19001925

1901-
fallThroughTree->insertAfter(fallThroughStoreTree);
1926+
lowTagAccessTreeTop->insertAfter(lowTagAccessStoreTree);
19021927

1903-
debugTrace(tracer(),"Inserted store tree %p for fall-through side of the diamond", fallThroughStoreNode);
1928+
debugTrace(tracer(),"Inserted store tree %p for fall-through side of the diamond", lowTagAccessStoreNode);
19041929
}
19051930

1906-
if (arrayAccessTreeTop != NULL) //case (1) only
1931+
if (arrayAccessTreeTop != NULL) //cases (2) and (3) only
19071932
{
19081933
TR::Node *arrayAccessStoreNode = TR::Node::createWithSymRef(comp()->il.opCodeForDirectStore(dataType), 1, 1, arrayAccessTreeTop->getNode()->getFirstChild(), newSymbolReference);
19091934
TR::TreeTop *arrayAccessStoreTree = TR::TreeTop::create(comp(), arrayAccessStoreNode);

0 commit comments

Comments
 (0)