- Notifications
You must be signed in to change notification settings - Fork 771
Add inlined isAssignableFrom test on X #22629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
| @@ -95,6 +95,11 @@ class OMR_EXTENSIBLE CodeGenerator : public J9::CodeGenerator | |
*/ | ||
bool supportsInliningOfIsAssignableFrom(); | ||
| ||
/** \brief | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line endings check reported a trailing blank on this line. | ||
* Determines if the code generator should generate inlined tests or if not, call to a VM helper. | ||
*/ | ||
bool supportsNonHelperIsAssignableFrom(); | ||
| ||
/* | ||
* \brief Reserve space in the code cache for a specified number of trampolines. | ||
* This is useful for inline caches where the methods are not yet known at | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
| @@ -4003,6 +4003,166 @@ TR::Register *J9::X86::TreeEvaluator::longNumberOfTrailingZeros(TR::Node *node, | |
return resultReg; | ||
} | ||
| ||
inline void generateInlineInterfaceTest(TR::Node* node, TR::CodeGenerator *cg, TR::Register *toClassReg, TR::Register* fromClassReg, TR_X86ScratchRegisterManager *srm, TR::LabelSymbol* successLabel, TR::LabelSymbol* failLabel) | ||
{ | ||
TR_Debug * debugObj = cg->getDebug(); | ||
TR::LabelSymbol *iTableLoopLabel = generateLabelSymbol(cg); | ||
// Obtain I-Table | ||
// iTableReg holds head of J9Class->iTable of obj class | ||
TR::Register* iTableReg = srm->findOrCreateScratchRegister(); | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, iTableReg, generateX86MemoryReference(fromClassReg, offsetof(J9Class, iTable), cg), cg); | ||
// Loop through I-Table | ||
// iTableReg holds iTable list element through the loop | ||
TR::Instruction* cursor = generateLabelInstruction(TR::InstOpCode::label, node, iTableLoopLabel, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->Start of ITable walk"); | ||
} | ||
generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), node, iTableReg, iTableReg, cg); | ||
generateLabelInstruction(TR::InstOpCode::JE4, node, failLabel, cg); | ||
auto interfaceMR = generateX86MemoryReference(iTableReg, offsetof(J9ITable, interfaceClass), cg); | ||
generateMemRegInstruction(TR::InstOpCode::CMPMemReg(), node, interfaceMR, toClassReg, cg); | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, iTableReg, generateX86MemoryReference(iTableReg, offsetof(J9ITable, next), cg), cg); | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, iTableLoopLabel, cg); | ||
srm->reclaimScratchRegister(iTableReg); | ||
// Found from I-Table | ||
cursor = generateLabelInstruction(TR::InstOpCode::JMP4, node, successLabel, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->ITable walk succeeded"); | ||
} | ||
} | ||
| ||
inline void generateInlineSuperclassTest(TR::Node* node, TR::CodeGenerator *cg, TR::Register *toClassReg, TR::Register* fromClassReg, TR_ScratchRegisterManager *srm, TR::LabelSymbol* failLabel, bool use64BitClasses) | ||
{ | ||
TR_Debug * debugObj = cg->getDebug(); | ||
// temp2 holds cast class depth | ||
// class depth mask must be low 16 bits to safely load without the mask. | ||
static_assert(J9AccClassDepthMask == 0xffff, "J9_JAVA_CLASS_DEPTH_MASK must be 0xffff"); | ||
TR::Register* toClassDepthReg = srm->findOrCreateScratchRegister(); | ||
TR::Register* superclassArrayReg = srm->findOrCreateScratchRegister(); | ||
TR::Instruction* cursor = generateRegMemInstruction(cg->comp()->target().is64Bit()? TR::InstOpCode::MOVZXReg8Mem2 : TR::InstOpCode::MOVZXReg4Mem2, node, | ||
toClassDepthReg, generateX86MemoryReference(toClassReg, offsetof(J9Class, classDepthAndFlags), cg), cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->load depth of toClass"); | ||
} | ||
| ||
// cast class depth >= obj class depth, throw | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this might be generating code in a context where failure won't throw an exception, I think "throw" here should be changed to "fail" | ||
generateRegMemInstruction(TR::InstOpCode::CMP2RegMem, node, toClassDepthReg, generateX86MemoryReference(fromClassReg, offsetof(J9Class, classDepthAndFlags), cg), cg); | ||
cursor = generateLabelInstruction(TR::InstOpCode::JAE4, node, failLabel, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->toClass Depth > fromClassDepth - fast fail"); | ||
} | ||
| ||
// check obj class's super class array entry | ||
// An alternative sequences requiring one less register may be: | ||
// SHL toClassDepthReg, 3 for 64-bit or 2 for 32-bit | ||
// ADD toClassDepthReg, [temp3Reg, superclasses offset] | ||
// CMP classClassReg, [toClassDepthReg] | ||
// On 64 bit, the extra reg isn't likely to cause significant register pressure. | ||
// On 32 bit, it could put more register pressure due to limited number of regs. | ||
// Since 64-bit is more prevalent, we opt to optimize for 64bit in this case | ||
cursor = generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, superclassArrayReg, generateX86MemoryReference(fromClassReg, offsetof(J9Class, superclasses), cg), cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->load superclass array"); | ||
} | ||
generateRegMemInstruction(TR::InstOpCode::CMPRegMem(use64BitClasses), node, toClassReg, | ||
generateX86MemoryReference(superclassArrayReg, toClassDepthReg, cg->comp()->target().is64Bit()?3:2, cg), cg); | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, failLabel, cg); | ||
srm->reclaimScratchRegister(toClassDepthReg); | ||
srm->reclaimScratchRegister(superclassArrayReg); | ||
} | ||
| ||
inline TR::Register* generateInlinedIsAssignableFrom(TR::Node* node, TR::CodeGenerator *cg) | ||
{ | ||
TR_Debug * debugObj = cg->getDebug(); | ||
TR::Register *toClassReg = cg->evaluate(node->getSecondChild()); | ||
TR::Register *fromClassReg = cg->evaluate(node->getFirstChild()); | ||
TR::LabelSymbol *doneLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *failLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *startLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *outlinedCallLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *notInterfaceOrArrayLabel = generateLabelSymbol(cg); | ||
startLabel->setStartInternalControlFlow(); | ||
doneLabel->setEndInternalControlFlow(); | ||
| ||
TR::Compilation *comp = cg->comp(); | ||
auto use64BitClasses = comp->target().is64Bit() && | ||
(!TR::Compiler->om.generateCompressedObjectHeaders() || | ||
(comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))); | ||
| ||
TR_X86ScratchRegisterManager* srm = cg->generateScratchRegisterManager(2); | ||
TR_OutlinedInstructionsGenerator og(outlinedCallLabel, node, cg); | ||
TR::Register* resultReg = TR::TreeEvaluator::performCall(node, false, false, cg); | ||
generateLabelInstruction(TR::InstOpCode::JMP4, node, doneLabel, cg); | ||
og.endOutlinedInstructionSequence(); | ||
| ||
generateLabelInstruction(TR::InstOpCode::label, node, startLabel, cg); | ||
// initial result is true | ||
generateRegImmInstruction(TR::InstOpCode::MOV4RegImm4, node, resultReg, 1, cg); | ||
| ||
// no need for null checks. They are inserted prior to isAssignableFrom call in RecognizedCallTransformer | ||
| ||
// equality test | ||
generateRegRegInstruction(TR::InstOpCode::CMPRegReg(use64BitClasses), node, toClassReg, fromClassReg, cg); | ||
generateLabelInstruction(TR::InstOpCode::JE4, node, doneLabel, cg); | ||
| ||
// testing if toClass is an array class | ||
TR::Register* toClassROMClassReg = srm->findOrCreateScratchRegister(); | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, toClassROMClassReg, generateX86MemoryReference(toClassReg, offsetof(J9Class, romClass), cg), cg); | ||
// If toClass is array, call out of line helper | ||
TR::Instruction* cursor = generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node, | ||
generateX86MemoryReference(toClassROMClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassArray, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->Test if array class"); | ||
} | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedCallLabel, cg); | ||
| ||
// test if toClass is an interface | ||
cursor = generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node, | ||
generateX86MemoryReference(toClassROMClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccInterface, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->Test if interface class"); | ||
} | ||
// skip inlined interface test if not an interface | ||
generateLabelInstruction(TR::InstOpCode::JE4, node, notInterfaceOrArrayLabel, cg); | ||
srm->reclaimScratchRegister(toClassROMClassReg); | ||
| ||
generateInlineInterfaceTest(node, cg, toClassReg, fromClassReg, srm, doneLabel, failLabel); | ||
| ||
generateLabelInstruction(TR::InstOpCode::label, node, notInterfaceOrArrayLabel, cg); | ||
generateInlineSuperclassTest(node, cg, toClassReg, fromClassReg, srm, failLabel, use64BitClasses); | ||
generateLabelInstruction(TR::InstOpCode::JMP4, node, doneLabel, cg); | ||
| ||
// fall through to fail label | ||
cursor = generateLabelInstruction(TR::InstOpCode::label, node, failLabel, cg); | ||
if (debugObj) | ||
{ | ||
debugObj->addInstructionComment(cursor, "-->Test failed"); | ||
} | ||
generateRegImmInstruction(TR::InstOpCode::MOV4RegImm4, node, resultReg, 0, cg); | ||
| ||
TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions((uint8_t)0, 3 + srm->numAvailableRegisters(), cg); | ||
srm->addScratchRegistersToDependencyList(deps); | ||
deps->addPostCondition(resultReg, TR::RealRegister::NoReg, cg); | ||
deps->addPostCondition(fromClassReg, TR::RealRegister::NoReg, cg); | ||
if (toClassReg != fromClassReg) | ||
{ | ||
deps->addPostCondition(toClassReg, TR::RealRegister::NoReg, cg); | ||
} | ||
| ||
srm->stopUsingRegisters(); | ||
generateLabelInstruction(TR::InstOpCode::label, node, doneLabel, deps, cg); | ||
| ||
node->setRegister(resultReg); | ||
return resultReg; | ||
} | ||
| ||
inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::CodeGenerator* cg) | ||
{ | ||
TR::Compilation *comp = cg->comp(); | ||
| @@ -4011,6 +4171,8 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
(comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))); | ||
TR::Register *ObjReg = cg->evaluate(node->getFirstChild()); | ||
TR::Register *castClassReg = cg->evaluate(node->getSecondChild()); | ||
| ||
TR_X86ScratchRegisterManager* srm = cg->generateScratchRegisterManager(2); | ||
TR::Register *temp1Reg = cg->allocateRegister(); | ||
TR::Register *temp2Reg = cg->allocateRegister(); | ||
Comment on lines 4176 to 4177 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that with this change, | ||
TR::Register *objClassReg = cg->allocateRegister(); | ||
| @@ -4022,7 +4184,6 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
TR::LabelSymbol *outlinedCallLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *throwLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *isClassLabel = generateLabelSymbol(cg); | ||
TR::LabelSymbol *iTableLoopLabel = generateLabelSymbol(cg); | ||
startLabel->setStartInternalControlFlow(); | ||
fallThruLabel->setEndInternalControlFlow(); | ||
| ||
| @@ -4035,12 +4196,13 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
if (isCheckCastAndNullCheck) | ||
generateLoadJ9Class(node, objClassReg, ObjReg, cg); | ||
| ||
// temp2Reg holds romClass of cast class, for testing array, interface class type | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, temp2Reg, generateX86MemoryReference(castClassReg, offsetof(J9Class, romClass), cg), cg); | ||
TR::Register* castClassRomClassReg = srm->findOrCreateScratchRegister(); | ||
// get romClass of cast class, for testing array, interface class type | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, castClassRomClassReg, generateX86MemoryReference(castClassReg, offsetof(J9Class, romClass), cg), cg); | ||
| ||
// If cast class is array, call out of line helper | ||
generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node, | ||
generateX86MemoryReference(temp2Reg, offsetof(J9ROMClass, modifiers), cg), J9AccClassArray, cg); | ||
generateX86MemoryReference(castClassRomClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassArray, cg); | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedCallLabel, cg); | ||
| ||
// objClassReg holds object class | ||
| @@ -4054,24 +4216,11 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
// Object not array, inline checks | ||
// Check cast class is interface | ||
generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node, | ||
generateX86MemoryReference(temp2Reg, offsetof(J9ROMClass, modifiers), cg), J9AccInterface, cg); | ||
generateX86MemoryReference(castClassRomClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccInterface, cg); | ||
generateLabelInstruction(TR::InstOpCode::JE4, node, isClassLabel, cg); | ||
srm->reclaimScratchRegister(castClassRomClassReg); | ||
| ||
// Obtain I-Table | ||
// temp1Reg holds head of J9Class->iTable of obj class | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, temp1Reg, generateX86MemoryReference(objClassReg, offsetof(J9Class, iTable), cg), cg); | ||
// Loop through I-Table | ||
// temp1Reg holds iTable list element through the loop | ||
generateLabelInstruction(TR::InstOpCode::label, node, iTableLoopLabel, cg); | ||
generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), node, temp1Reg, temp1Reg, cg); | ||
generateLabelInstruction(TR::InstOpCode::JE4, node, throwLabel, cg); | ||
auto interfaceMR = generateX86MemoryReference(temp1Reg, offsetof(J9ITable, interfaceClass), cg); | ||
generateMemRegInstruction(TR::InstOpCode::CMPMemReg(), node, interfaceMR, castClassReg, cg); | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, temp1Reg, generateX86MemoryReference(temp1Reg, offsetof(J9ITable, next), cg), cg); | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, iTableLoopLabel, cg); | ||
| ||
// Found from I-Table | ||
generateLabelInstruction(TR::InstOpCode::JMP4, node, fallThruLabel, cg); | ||
generateInlineInterfaceTest(node, cg, castClassReg, objClassReg, srm, fallThruLabel, throwLabel); | ||
| ||
// cast class is non-interface class | ||
generateLabelInstruction(TR::InstOpCode::label, node, isClassLabel, cg); | ||
| @@ -4080,29 +4229,7 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
generateLabelInstruction(TR::InstOpCode::JE4, node, fallThruLabel, cg); | ||
| ||
// class not equal | ||
// temp2 holds cast class depth | ||
// class depth mask must be low 16 bits to safely load without the mask. | ||
static_assert(J9AccClassDepthMask == 0xffff, "J9_JAVA_CLASS_DEPTH_MASK must be 0xffff"); | ||
generateRegMemInstruction(comp->target().is64Bit()? TR::InstOpCode::MOVZXReg8Mem2 : TR::InstOpCode::MOVZXReg4Mem2, node, | ||
temp2Reg, generateX86MemoryReference(castClassReg, offsetof(J9Class, classDepthAndFlags), cg), cg); | ||
| ||
// cast class depth >= obj class depth, throw | ||
generateRegMemInstruction(TR::InstOpCode::CMP2RegMem, node, temp2Reg, generateX86MemoryReference(objClassReg, offsetof(J9Class, classDepthAndFlags), cg), cg); | ||
generateLabelInstruction(TR::InstOpCode::JAE4, node, throwLabel, cg); | ||
| ||
// check obj class's super class array entry | ||
// temp1Reg holds superClasses array of obj class | ||
// An alternative sequences requiring one less register may be: | ||
// SHL temp2Reg, 3 for 64-bit or 2 for 32-bit | ||
// ADD temp2Reg, [temp3Reg, superclasses offset] | ||
// CMP classClassReg, [temp2Reg] | ||
// On 64 bit, the extra reg isn't likely to cause significant register pressure. | ||
// On 32 bit, it could put more register pressure due to limited number of regs. | ||
// Since 64-bit is more prevalent, we opt to optimize for 64bit in this case | ||
generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, temp1Reg, generateX86MemoryReference(objClassReg, offsetof(J9Class, superclasses), cg), cg); | ||
generateRegMemInstruction(TR::InstOpCode::CMPRegMem(use64BitClasses), node, castClassReg, | ||
generateX86MemoryReference(temp1Reg, temp2Reg, comp->target().is64Bit()?3:2, cg), cg); | ||
generateLabelInstruction(TR::InstOpCode::JNE4, node, throwLabel, cg); | ||
generateInlineSuperclassTest(node, cg, castClassReg, objClassReg, srm, throwLabel, use64BitClasses); | ||
| ||
// throw classCastException | ||
{ | ||
| @@ -4119,8 +4246,8 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
| ||
deps->addPostCondition(ObjReg, TR::RealRegister::NoReg, cg); | ||
deps->addPostCondition(castClassReg, TR::RealRegister::NoReg, cg); | ||
deps->addPostCondition(temp1Reg, TR::RealRegister::NoReg, cg); | ||
deps->addPostCondition(temp2Reg, TR::RealRegister::NoReg, cg); | ||
srm->addScratchRegistersToDependencyList(deps); | ||
srm->stopUsingRegisters(); | ||
deps->addPostCondition(objClassReg, TR::RealRegister::NoReg, cg); | ||
| ||
TR::Node *callNode = outlinedHelperCall->getCallNode(); | ||
| @@ -4144,8 +4271,6 @@ inline void generateInlinedCheckCastForDynamicCastClass(TR::Node* node, TR::Code | |
| ||
generateLabelInstruction(TR::InstOpCode::label, node, fallThruLabel, deps, cg); | ||
| ||
cg->stopUsingRegister(temp1Reg); | ||
cg->stopUsingRegister(temp2Reg); | ||
cg->stopUsingRegister(objClassReg); | ||
| ||
// Decrement use counts on the children | ||
| @@ -4761,8 +4886,13 @@ TR::Register *J9::X86::TreeEvaluator::checkcastinstanceofEvaluator(TR::Node *nod | |
isCheckCast = true; | ||
break; | ||
case TR::instanceof: | ||
case TR::icall: // TR_checkAssignable | ||
break; | ||
case TR::icall: // TR_checkAssignable | ||
if (cg->supportsNonHelperIsAssignableFrom()) { | ||
return generateInlinedIsAssignableFrom(node, cg); | ||
} else { | ||
break; | ||
} | ||
Comment on lines +4893 to +4895 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a nit, but may I suggest removing the | ||
default: | ||
TR_ASSERT(false, "Incorrect Op Code %d.", node->getOpCodeValue()); | ||
break; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line endings check reported a trailing blank on this line.