From cb75ed37ce6d16dbcd5768914d52acb81bdb9236 Mon Sep 17 00:00:00 2001 From: midronij Date: Tue, 24 Oct 2023 09:21:56 -0400 Subject: [PATCH] Update documentation for Unsafe.get()/put() to reflect changes made to accommodate cases where offheap allocation is enabled. Signed-off-by: midronij --- .../compiler/optimizer/InlinerTempForJ9.cpp | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/runtime/compiler/optimizer/InlinerTempForJ9.cpp b/runtime/compiler/optimizer/InlinerTempForJ9.cpp index 9eb12a4f9af..b7519b2c82b 100644 --- a/runtime/compiler/optimizer/InlinerTempForJ9.cpp +++ b/runtime/compiler/optimizer/InlinerTempForJ9.cpp @@ -999,7 +999,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress, traceMsg(comp(),"\t\t Generating an isArray test as j9class of java/lang/Class is NULL"); directAccessBlock->getEntry()->insertTreeTopsBeforeMe(lowTagCmpBlock->getEntry(), lowTagCmpBlock->getExit()); lowTagCmpTree->getNode()->setBranchDestination(indirectAccessBlock->getEntry()); - } + } lowTagCmpBlock->getEntry()->insertTreeTopsBeforeMe(isArrayBlock->getEntry(), isArrayBlock->getExit()); @@ -1013,7 +1013,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress, TR::Block *indirectNextBlock = indirectAccessBlock->getExit()->getNextTreeTop()->getEnclosingBlock(); TR::Block *directPrevBlock = directAccessBlock->getEntry()->getPrevTreeTop()->getEnclosingBlock(); TR::Block *directNextBlock = directAccessBlock->getExit()->getNextTreeTop()->getEnclosingBlock(); - + indirectAccessBlock->getEntry()->setPrevTreeTop(directPrevBlock->getExit()); directPrevBlock->getExit()->setNextTreeTop(indirectAccessBlock->getEntry()); indirectAccessBlock->getExit()->setNextTreeTop(directNextBlock->getEntry()); @@ -1033,7 +1033,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress, //of the conditional branch in the lowTagCmp block so that it can reach the indirectAccessBlock TR::ILOpCodes lowTagCmpOpCode = lowTagCmpTree->getNode()->getOpCodeValue(); TR::ILOpCodes newOpCode = lowTagCmpOpCode == TR::ificmpne ? TR::ificmpeq : TR::iflcmpeq; - + lowTagCmpTree->getNode()->getOpCode().setOpCodeValue(newOpCode); lowTagCmpTree->getNode()->setBranchDestination(indirectAccessBlock->getEntry()); } @@ -1090,7 +1090,7 @@ TR_J9InlinerPolicy::genCodeForUnsafeGetPut(TR::Node* unsafeAddress, if (arrayCheckNeeded && arrayBlockNeeded) cfg->removeEdge(nullComparisonBlock, arrayDirectAccessBlock); - + if (needNullCheck) { TR::TreeTop *treeBeforeCmp = nullComparisonTree->getPrevTreeTop(); @@ -1124,7 +1124,11 @@ indirect write/read. The selection of alternatives is done by looking at three c a) whether the object is NULL b) whether the object is array c) whether the object is of type java.lang.Class -The pseudocode of the generated inline code for case (1) under normal compilation is : + +When the type of the object is unknown at compile time, several runtime checks 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 and gencon GC policy is : if (object == NULL) use direct access else if (offset is not low tagged) @@ -1134,7 +1138,8 @@ else if (object is type of java/lang/Class) else use direct access -If we can not get the J9Class of java/lang/Class, we generate following sequence of tests +If we can not 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 else if (object it Array type) @@ -1207,7 +1212,7 @@ 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); @@ -1222,11 +1227,11 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy int objSigLength = strlen("Ljava/lang/Object;"); int classSigLength = strlen("Ljava/lang/Class;"); - + // There are two cases where IL for type checks (array, class, lowtag) needs to be generated: - // 1.) Object type is unknown at compile time + // 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) - checksNeeded = objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || strncmp(objTypeSig, "Ljava/lang/Class;", classSigLength) == 0; + checksNeeded = (objTypeSig == NULL) || (strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0) || (strncmp(objTypeSig, "Ljava/lang/Class;", classSigLength) == 0); // We need to generate IL for the array check if we have determined that IL type checks are needed (i.e.: checksNeeded == true) AND // at least one of the following conditions holds: @@ -1238,10 +1243,10 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy // We need to generate arrayDirectAccessBlock if at least one of the following conditions holds: // - conversionNeeded == true AND checksNeeded == true // - offheap is enabled AND (object is known be an array at compile time OR object type is unknown at compile time) - arrayBlockNeeded = checksNeeded && conversionNeeded || TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && - (objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || objTypeSig[0] == '['); + arrayBlockNeeded = (checksNeeded && conversionNeeded) || (TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && + (objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || objTypeSig[0] == '[')); } - else // if object is NULL, we want to treat it the same way as we would a non-array, non-class, non-java/lang/Object object (i.e.: use direct access) + else // if object is NULL, we want to treat it the same way as we would a non-array, non-class, non-java/lang/Object object (i.e.: use direct access) { checksNeeded = false; arrayCheckNeeded = false; @@ -1338,7 +1343,7 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy } // We need to generate an arrayDirectAccessBlock AND a directAccessBlock in the following cases: - // 1.) arrayCheckNeeded == true: arrayDirectAccessBlock used for array access, + // 1.) arrayCheckNeeded == true: arrayDirectAccessBlock used for array access, // directAccessBlock used for non-array/non-class object access // 2.) !arrayCheckNeeded && arrayBlockNeeded: arrayDirectAccessBlock used for array access, // directAccessBlock used if object is NULL @@ -1421,14 +1426,14 @@ TR_J9InlinerPolicy::createUnsafePutWithOffset(TR::ResolvedMethodSymbol *calleeSy { adjustedOffset = TR::Node::create(TR::ladd, 2, oldOffset, TR::Node::lconst(-TR::Compiler->om.contiguousArrayHeaderSizeInBytes())); } - + addrToAccessNode->setChild(1, adjustedOffset); - oldOffset->decReferenceCount(); + oldOffset->decReferenceCount(); } #endif /* J9VM_GC_ENABLE_SPARSE_HEAP_ALLOCATION */ TR::Block *joinBlock; - + // SPECIAL CASE: if object is known to be array, we want the following control flow: // if (unsafeAddress == NULL) goto directAccessBlock // else goto arrayDirectAccessBlock @@ -1553,11 +1558,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. @@ -1571,18 +1576,18 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR int objSigLength = strlen("Ljava/lang/Object;"); //only generate array check in case (3) - bool arrayCheckNeeded = TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && + bool arrayCheckNeeded = TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && (objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0); //only generate separate array access block in cases (2) or (3) - bool arrayBlockNeeded = TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && + bool arrayBlockNeeded = TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && (arrayCheckNeeded || objTypeSig[0] == '['); createTempsForUnsafeCall(callNodeTreeTop, callNode); TR::Node *offsetNode = callNode->getChild(2); TR::TreeTop *compareTree; - + //do not generate low tagged check in case (2) if (!arrayCheckNeeded && arrayBlockNeeded) compareTree = NULL; @@ -1711,14 +1716,14 @@ TR_J9InlinerPolicy::createUnsafeCASCallDiamond( TR::TreeTop *callNodeTreeTop, TR callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, isArrayTreeTop, arrayAccessTreeTop, nonArrayAccessTreeTop, comp()->getFlowGraph(), false, false); nonArrayAccessTreeTop->getEnclosingBlock()->createConditionalBlocksBeforeTree(nonArrayAccessTreeTop, compareTree, ifTree, elseTree, 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 check diamond callBlock->createConditionalBlocksBeforeTree(callNodeTreeTop, compareTree, ifTree, elseTree, comp()->getFlowGraph(), false, false); @@ -1800,9 +1805,9 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy int classSigLength = strlen("Ljava/lang/Class;"); // There are two cases where IL for type checks (array, class, lowtag) needs to be generated: - // 1.) Object type is unknown at compile time + // 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) - checksNeeded = objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || strncmp(objTypeSig, "Ljava/lang/Class;", classSigLength) == 0; + checksNeeded = (objTypeSig == NULL) || (strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0) || (strncmp(objTypeSig, "Ljava/lang/Class;", classSigLength) == 0); // We need to generate IL for the array check if we have determined that IL type checks are needed (i.e.: checksNeeded == true) AND // at least one of the following conditions holds: @@ -1814,10 +1819,10 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy // We need to generate arrayDirectAccessBlock if at least one of the following conditions holds: // - conversionNeeded == true AND checksNeeded == true // - offheap is enabled AND (object is known be an array at compile time OR object type is unknown at compile time) - arrayBlockNeeded = checksNeeded && conversionNeeded || TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && - (objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || objTypeSig[0] == '['); + arrayBlockNeeded = (checksNeeded && conversionNeeded) || (TR::Compiler->om.isOffHeapAllocationEnabled() && comp()->target().is64Bit() && + (objTypeSig == NULL || strncmp(objTypeSig, "Ljava/lang/Object;", objSigLength) == 0 || objTypeSig[0] == '[')); } - else // if object is NULL, we want to treat it the same way as we would a non-array, non-class, non-java/lang/Object object (i.e.: use direct access) + else // if object is NULL, we want to treat it the same way as we would a non-array, non-class, non-java/lang/Object object (i.e.: use direct access) { checksNeeded = false; arrayCheckNeeded = false; @@ -1908,7 +1913,7 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy genDirectAccessCodeForUnsafeGetPut(callNodeTreeTop->getNode(), false, true); // We need to generate an arrayDirectAccessBlock AND a directAccessBlock in the following cases: - // 1.) arrayCheckNeeded == true: arrayDirectAccessBlock used for array access, + // 1.) arrayCheckNeeded == true: arrayDirectAccessBlock used for array access, // directAccessBlock used for non-array/non-class object access // 2.) !arrayCheckNeeded && arrayBlockNeeded: arrayDirectAccessBlock used for array access, // directAccessBlock used if object is NULL @@ -1975,12 +1980,12 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy } addrToAccessNode->setChild(1, adjustedOffset); - oldOffset->decReferenceCount(); + oldOffset->decReferenceCount(); } #endif /* J9VM_GC_ENABLE_SPARSE_HEAP_ALLOCATION */ TR::Block *joinBlock; - + // SPECIAL CASE: if object is known to be array, we want the following control flow: // if (unsafeAddress == NULL) goto directAccessBlock // else goto arrayDirectAccessBlock @@ -1998,7 +2003,7 @@ TR_J9InlinerPolicy::createUnsafeGetWithOffset(TR::ResolvedMethodSymbol *calleeSy createAnchorNodesForUnsafeGetPut(arrayDirectAccessTreeTop, type, true); genCodeForUnsafeGetPut(unsafeAddress, callNodeTreeTop, prevTreeTop, newSymbolReferenceForAddress, directAccessTreeTop, - lowTagCmpTree, needNullCheck, true, conversionNeeded, + lowTagCmpTree, needNullCheck, true, conversionNeeded, arrayCheckNeeded, arrayBlockNeeded, checksNeeded, joinBlock, javaLangClass);