Skip to content

Commit 03cab32

Browse files
committed
Add inlined itable walk for Class.isAssignableFrom on Z
For the interface case, skips the superclass tests and goes straight to an inlined itable walk which is guarenteed to give a result. Signed-off-by: Matthew Hall <[email protected]>
1 parent 410dece commit 03cab32

File tree

1 file changed

+71
-9
lines changed

1 file changed

+71
-9
lines changed

runtime/compiler/z/codegen/J9TreeEvaluator.cpp

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11861,9 +11861,55 @@ static TR::SymbolReference *getClassSymRefAndDepth(TR::Node *classNode, TR::Comp
1186111861
return classSymRef;
1186211862
}
1186311863

11864+
static void genITableTest(TR::Node *node, TR::CodeGenerator *cg, TR_S390ScratchRegisterManager *srm, TR::Register *fromClassReg, TR::Register *toClassReg, TR::LabelSymbol *successLabel, TR::LabelSymbol *failLabel)
11865+
{
11866+
TR::Compilation *comp = cg->comp();
11867+
TR_Debug * debugObj = cg->getDebug();
11868+
bool traceCG = comp->getOption(TR_TraceCG);
11869+
TR::LabelSymbol *cacheCastClassLabel = generateLabelSymbol(cg);
11870+
TR::LabelSymbol *startWalkLabel = generateLabelSymbol(cg);
11871+
11872+
TR::Register *iTableReg = srm->findOrCreateScratchRegister();
11873+
// lastITable Test
11874+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/interfaceTest/lastITable"), 1, TR::DebugCounter::Undetermined);
11875+
TR::Instruction *cursor = generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), node, iTableReg,
11876+
generateS390MemoryReference(fromClassReg, offsetof(J9Class, lastITable), cg));
11877+
// last itable is never null
11878+
generateRXInstruction(cg, TR::InstOpCode::getCmpLogicalOpCode(), node, toClassReg,
11879+
generateS390MemoryReference(iTableReg, offsetof(J9ITable, interfaceClass), cg));
11880+
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BE, node, successLabel);
11881+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/interfaceTest/lastITable/fail"), 1, TR::DebugCounter::Undetermined);
11882+
// Itable Test
11883+
cursor = generateS390LabelInstruction(cg, TR::InstOpCode::label, node, startWalkLabel);
11884+
cursor = generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), node, iTableReg,
11885+
generateS390MemoryReference(fromClassReg, offsetof(J9Class, iTable), cg));
11886+
11887+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/interfaceTest/ITableWalk"), 1, TR::DebugCounter::Undetermined);
11888+
11889+
TR::LabelSymbol *startLoop = generateLabelSymbol(cg);
11890+
cursor = generateS390LabelInstruction(cg, TR::InstOpCode::label, node, startLoop);
11891+
if (debugObj && traceCG)
11892+
debugObj->addInstructionComment(cursor, "--> Start of iTable walk");
11893+
11894+
generateRRInstruction(cg, TR::InstOpCode::getLoadTestRegOpCode(), node, iTableReg, iTableReg);
11895+
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BE, node, failLabel);
11896+
generateRXInstruction(cg, TR::InstOpCode::getCmpLogicalOpCode(), node, toClassReg,
11897+
generateS390MemoryReference(iTableReg, offsetof(J9ITable, interfaceClass), cg));
11898+
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BE, node, successLabel);
11899+
generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), node, iTableReg,
11900+
generateS390MemoryReference(iTableReg, offsetof(J9ITable, next), cg));
11901+
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, startLoop);
11902+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/interfaceTest/ITableWalk/fail"), 1, TR::DebugCounter::Undetermined);
11903+
if (debugObj && traceCG)
11904+
debugObj->addInstructionComment(cursor, "to start of loop");
11905+
11906+
srm->reclaimScratchRegister(iTableReg);
11907+
}
11908+
1186411909
TR::Register *J9::Z::TreeEvaluator::inlineCheckAssignableFromEvaluator(TR::Node *node, TR::CodeGenerator *cg)
1186511910
{
1186611911
TR::Compilation *comp = cg->comp();
11912+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromPath/(%s)", comp->signature()), 1, TR::DebugCounter::Undetermined);
1186711913
// recognizedCallTransformer swaps the args - caller class obj is the second argument after the transformation
1186811914
TR::Node *fromClass = node->getFirstChild();
1186911915
TR::Node *toClass = node->getSecondChild();
@@ -11875,6 +11921,7 @@ TR::Register *J9::Z::TreeEvaluator::inlineCheckAssignableFromEvaluator(TR::Node
1187511921
TR::LabelSymbol *failLabel = generateLabelSymbol(cg);
1187611922
TR::LabelSymbol *successLabel = doneLabel;
1187711923
TR::LabelSymbol* cFlowRegionStart = generateLabelSymbol(cg);
11924+
TR::LabelSymbol* ITableTestLabel = generateLabelSymbol(cg);
1187811925

1187911926
TR_S390ScratchRegisterManager *srm = cg->generateScratchRegisterManager(2);
1188011927

@@ -11897,11 +11944,11 @@ TR::Register *J9::Z::TreeEvaluator::inlineCheckAssignableFromEvaluator(TR::Node
1189711944
if (comp->getOption(TR_TraceCG))
1189811945
traceMsg(comp,"%s: Emitting Class Equality Test\n",node->getOpCode().getName());
1189911946
// for isAssignableFrom we can always generate the class equality test since both arguments are classes
11900-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/ClassEqualityTest", comp->signature()), 1, TR::DebugCounter::Undetermined);
11947+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/ClassEqualityTest"), 1, TR::DebugCounter::Undetermined);
1190111948
cursor = generateS390CompareAndBranchInstruction(cg, TR::InstOpCode::getCmpRegOpCode(), node, toClassReg, fromClassReg, TR::InstOpCode::COND_BE, successLabel, false, false);
1190211949
if (debugObj)
1190311950
debugObj->addInstructionComment(cursor, "class equality test");
11904-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/ClassEqualityTest/Fail", comp->signature()), 1, TR::DebugCounter::Undetermined);
11951+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/ClassEqualityTest/Fail"), 1, TR::DebugCounter::Undetermined);
1190511952

1190611953
int32_t toClassDepth = -1;
1190711954
TR::SymbolReference *toClassSymRef = getClassSymRefAndDepth(toClass, comp, toClassDepth);
@@ -11940,30 +11987,45 @@ TR::Register *J9::Z::TreeEvaluator::inlineCheckAssignableFromEvaluator(TR::Node
1194011987
if (comp->getOption(TR_TraceCG))
1194111988
traceMsg(comp,"%s: Emitting CastClassCacheTest\n",node->getOpCode().getName());
1194211989
TR::Register *castClassCacheReg = srm->findOrCreateScratchRegister();
11943-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/Cache", comp->signature()), 1, TR::DebugCounter::Undetermined);
11990+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/Cache"), 1, TR::DebugCounter::Undetermined);
1194411991
generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), node, castClassCacheReg,
1194511992
generateS390MemoryReference(fromClassReg, offsetof(J9Class, castClassCache), cg));
1194611993
cursor = generateS390CompareAndBranchInstruction(cg, TR::InstOpCode::getCmpRegOpCode(), node, castClassCacheReg, toClassReg, TR::InstOpCode::COND_BE, successLabel, false, false);
1194711994
if (debugObj)
1194811995
debugObj->addInstructionComment(cursor, "castclass cache test");
11949-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/Cache/Fail", comp->signature()), 1, TR::DebugCounter::Undetermined);
11996+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/Cache/Fail"), 1, TR::DebugCounter::Undetermined);
1195011997
srm->reclaimScratchRegister(castClassCacheReg);
1195111998
}
1195211999

1195312000
// superclass test
1195412001
if((NULL == toClassSymRef) || !toClassSymRef->isClassInterface(comp))
1195512002
{
11956-
const int32_t flags = (J9AccInterface | J9AccClassArray);
11957-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/SuperclassTest", comp->signature()), 1, TR::DebugCounter::Undetermined);
12003+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/SuperclassTest"), 1, TR::DebugCounter::Undetermined);
1195812004
if (toClassDepth == -1)
1195912005
{
11960-
genTestModifierFlags(cg, node, toClassReg, toClassDepth, helperCallLabel, srm, flags);
12006+
if (feGetEnv("enableInlineItableWalkForIsAssignableFrom")) {
12007+
TR::Register *modifierReg = srm->findOrCreateScratchRegister();
12008+
generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), node, modifierReg,
12009+
generateS390MemoryReference(toClassReg, offsetof(J9Class, classDepthAndFlags), cg));
12010+
genTestModifierFlags(cg, node, toClassReg, toClassDepth, ITableTestLabel, srm, J9AccInterface, modifierReg);
12011+
genTestModifierFlags(cg, node, toClassReg, toClassDepth, helperCallLabel, srm, J9AccClassArray, modifierReg);
12012+
srm->reclaimScratchRegister(modifierReg);
12013+
} else {
12014+
genTestModifierFlags(cg, node, toClassReg, toClassDepth, helperCallLabel, srm, J9AccInterface | J9AccClassArray);
12015+
}
1196112016
}
1196212017
genSuperclassTest(cg, node, toClassReg, toClassDepth, fromClassReg, failLabel, srm);
1196312018
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BE, node, successLabel);
11964-
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/(%s)/SuperclassTest/Fail", comp->signature()), 1, TR::DebugCounter::Undetermined);
12019+
cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "isAssignableFromStats/SuperclassTest/Fail"), 1, TR::DebugCounter::Undetermined);
12020+
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, helperCallLabel);
12021+
}
12022+
// we don't generate ITable test if we know we don't have an interface
12023+
if ((toClassDepth == -1) || (NULL == toClassSymRef || toClassSymRef->isClassInterface(comp)))
12024+
{
12025+
generateS390LabelInstruction(cg, TR::InstOpCode::label, node, ITableTestLabel);
12026+
// no need for helper if we fail interface test - we known the cast class is an interface
12027+
genITableTest(node, cg, srm, fromClassReg, toClassReg, doneLabel, failLabel);
1196512028
}
11966-
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, helperCallLabel);
1196712029
}
1196812030

1196912031
srm->stopUsingRegisters();

0 commit comments

Comments
 (0)