Skip to content

Commit

Permalink
Update documentation for Unsafe.get()/put() to reflect changes
Browse files Browse the repository at this point in the history
made to accommodate cases where offheap allocation is enabled.

Signed-off-by: midronij <[email protected]>
  • Loading branch information
midronij committed Jul 12, 2024
1 parent 4ba798c commit b42906e
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 44 deletions.
93 changes: 50 additions & 43 deletions runtime/compiler/optimizer/InlinerTempForJ9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,13 +876,13 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,

TR::CFG *cfg = comp()->getFlowGraph();

// Generate the tree for the null comparison
TR::Node *nullComparisonNode = NULL;
TR::TreeTop *nullComparisonTree = NULL;
TR::Block *nullComparisonBlock = NULL;

if (typeTestsNeeded || arrayBlockNeeded) // CASES (1), (2), (3)
{
// Generate the tree for the null test
TR::Node *addrLoad =
TR::Node::createWithSymRef(unsafeAddress,
comp()->il.opCodeForDirectLoad(unsafeAddress->getDataType()),
Expand Down Expand Up @@ -951,7 +951,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,
callNodeTreeTop->getNode()->removeAllChildren();
callNodeTreeTop->getPrevTreeTop()->join(callNodeTreeTop->getNextTreeTop());
}

TR::Block *beforeCallBlock = prevTreeTop->getEnclosingBlock();

TR::Block *arrayDirectAccessBlock = NULL;
Expand All @@ -978,7 +978,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,
cfg->addNode(indirectAccessBlock);
cfg->addEdge(TR::CFGEdge::createEdge(indirectAccessBlock, joinBlock, trMemory()));

//fix NULLCHK so that directAccessBlock is executed if object is NULL
//fix NULL test so that directAccessBlock is executed if object is NULL
nullComparisonTree->getNode()->setBranchDestination(directAccessBlock->getEntry());
}
else if (typeTestsNeeded && !arrayBlockNeeded) // CASE (2)
Expand Down Expand Up @@ -1010,7 +1010,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,
}

debugTrace(tracer(), "\t In genCodeForUnsafeGetPut, directAccessBlock is %d\n", directAccessBlock->getNumber());
if (indirectAccessBlock)
if (indirectAccessBlock)
debugTrace(tracer(), "\t In genCodeForUnsafeGetPut, indirectAccessBlock is %d\n", indirectAccessBlock->getNumber());

createAnchorNodesForUnsafeGetPut(directAccessTreeTop, type, isUnsafeGet);
Expand Down Expand Up @@ -1047,7 +1047,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,
isArrayBlock->append(isArrayTreeTop);
cfg->addNode(isArrayBlock);
isArrayNode->setBranchDestination(arrayDirectAccessBlock->getEntry());

directAccessBlock->getEntry()->insertTreeTopsBeforeMe(lowTagCmpBlock->getEntry(), lowTagCmpBlock->getExit());
lowTagCmpTree->getNode()->setBranchDestination(indirectAccessBlock->getEntry());

Expand Down Expand Up @@ -1117,7 +1117,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress,

if (arrayTestNeeded && arrayBlockNeeded)
cfg->removeEdge(nullComparisonBlock, arrayDirectAccessBlock);

if (needNullCheck)
{
TR::TreeTop *treeBeforeCmp = firstComparisonTree ? firstComparisonTree->getPrevTreeTop() : beforeCallBlock->getExit()->getPrevTreeTop();
Expand Down Expand Up @@ -1154,6 +1154,9 @@ b) whether the object is array
c) whether the object is of type java.lang.Class (which might involve testing the
low-order bit of the offset or comparing the <vft-symbol> of the object)
When the type of the object is unknown at compile time, several runtime tests will be
generated to determine which type of read/write should be used.
The pseudocode of the generated inline code for case (1) under normal compilation is :
if (offset is low-tagged)
Expand All @@ -1165,7 +1168,8 @@ The pseudocode of the generated inline code for case (1) under normal compilatio
else
use indirect access
If we cannot get the J9Class of java/lang/Class, we generate following sequence of tests:
If we cannot get the J9Class of java/lang/Class OR if offheap allocation and balanced GC policy is
in use, we generate following sequence of tests:
if (object == NULL)
use direct access
Expand Down Expand Up @@ -1240,18 +1244,21 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy


bool conversionNeeded = comp()->fe()->dataTypeForLoadOrStore(type) != type;

// If we are not able to get javaLangClass it is still inefficient to put direct Access far
// So in that case we will generate lowTagCmpTest to branch to indirect access if true
TR_OpaqueClassBlock *javaLangClass = comp()->getClassClassPointer(/* isVettedForAOT = */ true);

int length;
const char *objTypeSig = unsafeCall->getChild(1)->getSymbolReference() ? unsafeCall->getChild(1)->getSymbolReference()->getTypeSignature(length) : NULL;

// There are three cases where we cannot be sure of the Object type at compile time:
// 1.) Object type info is unknown/inaccessible
// 2.) Object type is known at compile time to be java/lang/Object
// 3.) Object type is known at compile time to be some interface class (e.g.: java/lang/Cloneable, java/io/Serializable)
// There are four cases where we cannot be sure of the Object type at compile time:
// 1.) The object's type signature is unavailable/unknown
// 2.) The signature names java/lang/Object
// 3.) The signature names some interface type, which is a problem because interface types are
// not checked by the verifier
// 4.) The signature belongs to a paramter of the calling method, which is a problem because
// parameters can share slots with variables of other, potentially incompatible types
bool objTypeUnknown;
TR_OpaqueClassBlock *objClass = NULL;

Expand All @@ -1267,7 +1274,7 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy
objTypeUnknown = objClass == NULL || objClass == comp()->getObjectClassPointer() || TR::Compiler->cls.isInterfaceClass(comp(), objClass) || isParameter;
}

// There are two cases where IL for type checks (array, class, lowtag) needs to be generated:
// There are two cases where IL for type tests (array, class, lowtag) needs to be generated:
// 1.) Object type is unknown at compile time
// 2.) Object is known at compile time to be a java/lang/Class object (to match JNI implementation)
bool typeTestsNeeded = objTypeUnknown || objClass == comp()->getClassClassPointer(/* isVettedForAOT = */ true);
Expand Down Expand Up @@ -1412,15 +1419,15 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy
//adjust arguments if object is array and offheap is being used by changing
//object base address (second child) to dataAddr

// CASE 1: conversionNeeded == true (e.g.: Unsafe.putShort()), object is array
// CASE 1: conversionNeeded == true (e.g.: Unsafe.putShort())
// sstorei -> arrayDirectAccessTreeTop->getNode()
// aladd -> address to access (base address + offset)
// aload -> object base address
// lload -> offset
// i2s -> conversion
// iload -> value to put

// CASE 2: conversionNeeded == false (e.g.: Unsafe.putLong()), object is array but class is unknown at compile time
// CASE 2: conversionNeeded == false (e.g.: Unsafe.putLong())
// sstorei -> arrayDirectAccessTreeTop->getNode()
// aladd -> address to access (base address + offset)
// aload -> object base address
Expand All @@ -1429,8 +1436,6 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy

if (TR::Compiler->om.isOffHeapAllocationEnabled() && arrayBlockNeeded)
{
J9JavaVM *vm = comp()->fej9()->getJ9JITConfig()->javaVM;

TR::Node *addrToAccessNode = arrayDirectAccessTreeTop->getNode()->getChild(0);

//change object base address to dataAddr
Expand All @@ -1442,8 +1447,8 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy
objBaseAddrNode->decReferenceCount();
dataAddrNode->incReferenceCount();
}
#endif /* TR_TARGET_64BIT */
#endif /* J9VM_GC_ENABLE_SPARSE_HEAP_ALLOCATION */

genCodeForUnsafeGetPut(unsafeAddress, offset, type, callNodeTreeTop,
prevTreeTop, newSymbolReferenceForAddress,
directAccessTreeTop, arrayDirectAccessTreeTop,
Expand Down Expand Up @@ -1551,11 +1556,11 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
// (A low tagged offset value means the object being passed in is a java/lang/Class object, and we want a static field)

// Regarding which checks/diamonds get generated, there are three possible cases:
// 1.) Only the low tagged check is generated. This will occur either when gencon GC policy is being used, or under
// balanced GC policy with offheap allocation enabled if the object being operated on is known NOT to be an array
// 1.) Only the low tagged check is generated. This will occur either when gencon GC policy is being used, or under
// balanced GC policy with offheap allocation enabled if the object being operated on is known NOT to be an array
// at compile time.
// 2.) No checks are generated. This will occur under balanced GC policy with offheap allocation enabled if the object
// 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
// 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
// java/lang/Class object).
// 3.) Both the array and low tagged checks are generated. This will occur under balanced GC policy with offheap allocation
// enabled if the type of the object being operated on is unknown at compile time.
Expand All @@ -1567,10 +1572,13 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
int length;
const char *objTypeSig = callNode->getChild(1)->getSymbolReference() ? callNode->getChild(1)->getSymbolReference()->getTypeSignature(length) : NULL;

// There are three cases where we cannot be sure of the Object type at compile time:
// 1.) Object type info is unknown/inaccessible
// 2.) Object type is known at compile time to be java/lang/Object
// 3.) Object type is known at compile time to be some interface classes (e.g.: java/lang/Cloneable, java/io/Serializable)
// There are four cases where we cannot be sure of the Object type at compile time:
// 1.) The object's type signature is unavailable/unknown
// 2.) The signature names java/lang/Object
// 3.) The signature names some interface type, which is a problem because interface types are
// not checked by the verifier
// 4.) The signature belongs to a paramter of the calling method, which is a problem because
// parameters can share slots with variables of other, potentially incompatible types
bool objTypeUnknown;

if (objTypeSig == NULL)
Expand Down Expand Up @@ -1611,7 +1619,7 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
#if defined (J9VM_GC_ENABLE_SPARSE_HEAP_ALLOCATION)
if (arrayTestNeeded)
{
//create arrayCHK treetop
//create array test treetop

// ificmpeq -> isArrayNode
// iand -> andNode
Expand Down Expand Up @@ -1646,8 +1654,6 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
// lload -> offset
// lload -> value to compare against
// lload -> value to swap in

J9JavaVM *vm = comp()->fej9()->getJ9JITConfig()->javaVM;

arrayAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
TR::Node *CASicallNode = arrayAccessTreeTop->getNode()->getChild(0);
Expand All @@ -1663,7 +1669,7 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR

CASicallNode->setIsSafeForCGToFastPathUnsafeCall(true);

//if array check is being generated, need to create non-array access treetop (same as default)
//if array test is being generated, need to create non-array access treetop (same as default)
if (isArrayTreeTop)
nonArrayAccessTreeTop = TR::TreeTop::create(comp(),callNodeTreeTop->getNode()->duplicateTree());
}
Expand All @@ -1672,7 +1678,7 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
TR::TreeTop *branchTargetTree;
TR::TreeTop *fallThroughTree;

//only generate if and else trees for low tagged check in cases (1) and (3)
//only generate if and else trees for low tagged test in cases (1) and (3)
if (compareTree != NULL)
{
// genClassCheck generates a ifcmpne offset&mask 1, meaning if it is NOT
Expand Down Expand Up @@ -1711,14 +1717,14 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, isArrayTreeTop, arrayAccessTreeTop, nonArrayAccessTreeTop, comp()->getFlowGraph(), false, false);
nonArrayAccessTreeTop->getEnclosingBlock()->createConditionalBlocksBeforeTree(nonArrayAccessTreeTop, compareTree, branchTargetTree, fallThroughTree, comp()->getFlowGraph(), false, false);
}
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
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
{
callNodeTreeTop->insertAfter(arrayAccessTreeTop);
callNodeTreeTop->getPrevTreeTop()->join(callNodeTreeTop->getNextTreeTop());
callBlock->split(arrayAccessTreeTop->getNextTreeTop(), comp()->getFlowGraph(), true);
callBlock->split(arrayAccessTreeTop, comp()->getFlowGraph(), true);
}
else if (compareTree != NULL) //in case (1), we only generate the low tagged check diamond
else if (compareTree != NULL) //in case (1), we only generate the low tagged test diamond
callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, compareTree, branchTargetTree, fallThroughTree, comp()->getFlowGraph(), false, false);


Expand Down Expand Up @@ -1791,10 +1797,13 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy
int length;
const char *objTypeSig = unsafeAddress->getSymbolReference() ? unsafeAddress->getSymbolReference()->getTypeSignature(length) : NULL;

// There are three cases where we cannot be sure of the Object type at compile time:
// 1.) Object type info is unknown/inaccessible
// 2.) Object type is known at compile time to be java/lang/Object
// 3.) Object type is known at compile time to be some interface class (e.g.: java/lang/Cloneable, java/io/Serializable)
// There are four cases where we cannot be sure of the Object type at compile time:
// 1.) The object's type signature is unavailable/unknown
// 2.) The signature names java/lang/Object
// 3.) The signature names some interface type, which is a problem because interface types are
// not checked by the verifier
// 4.) The signature belongs to a paramter of the calling method, which is a problem because
// parameters can share slots with variables of other, potentially incompatible types
bool objTypeUnknown;
TR_OpaqueClassBlock *objClass = NULL;

Expand All @@ -1810,7 +1819,7 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy
objTypeUnknown = objClass == NULL || objClass == comp()->getObjectClassPointer() || TR::Compiler->cls.isInterfaceClass(comp(), objClass) || isParameter;
}

// There are two cases where IL for type checks (array, class, lowtag) needs to be generated:
// There are two cases where IL for type tests (array, class, lowtag) needs to be generated:
// 1.) Object type is unknown at compile time
// 2.) Object is known at compile time to be a java/lang/Class object (to match JNI implementation)
bool typeTestsNeeded = objTypeUnknown || objClass == comp()->getClassClassPointer(/* isVettedForAOT = */ true);
Expand Down Expand Up @@ -1929,15 +1938,15 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy
//adjust arguments if object is array and offheap is being used by changing
//object base address (second child) to dataAddr

// CASE 1: conversionNeeded == true (e.g.: Unsafe.getShort()), object is array
// CASE 1: conversionNeeded == true (e.g.: Unsafe.getShort())
// istore -> arrayDirectAccessTreeTop->getNode()
// s2i -> conversion
// sloadi
// aladd -> address to access (base address + offset)
// aload -> object base address
// lload -> offset

// CASE 2: conversionNeeded == false (e.g.: Unsafe.getLong()), object is array but class is unknown at compile time
// CASE 2: conversionNeeded == false (e.g.: Unsafe.getLong())
// lstore -> arrayDirectAccessTreeTop->getNode()
// lloadi
// aladd -> address to access (base address + offset)
Expand All @@ -1946,8 +1955,6 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy

if (TR::Compiler->om.isOffHeapAllocationEnabled() && arrayBlockNeeded)
{
J9JavaVM *vm = comp()->fej9()->getJ9JITConfig()->javaVM;

TR::Node *addrToAccessNode;

if (conversionNeeded) //CASE 1
Expand All @@ -1959,7 +1966,7 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy
TR::Node *objBaseAddrNode = addrToAccessNode->getChild(0);
TR::Node *dataAddrNode = TR::TransformUtil::generateDataAddrLoadTrees(comp(), objBaseAddrNode);
addrToAccessNode->setChild(0, dataAddrNode);

//correct refcounts
objBaseAddrNode->decReferenceCount();
dataAddrNode->incReferenceCount();
Expand Down
2 changes: 1 addition & 1 deletion runtime/compiler/optimizer/J9Inliner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class TR_J9InlinerPolicy : public OMR_InlinerPolicy
* \c Unsafe.put* operation &mdash; \c false.
* \param arrayTestNeeded Indicates whether an array test needs to be generated
* for the type of the \c Object
* \param arrayBlockNeeded Indicates whether a separate access block needs to be
* \param arrayBlockNeeded Indicates whether a separate access block needs to be
* generated to handle the case where the \c Object is an array
* \param typeTestsNeeded Indicates whether any type tests for \c Object need to
* be generated (i.e.: if the type of the \c Object is unknown)
Expand Down

0 comments on commit b42906e

Please sign in to comment.