diff --git a/runtime/compiler/build/files/common.mk b/runtime/compiler/build/files/common.mk index 445c10f7dc4..c474c41711a 100644 --- a/runtime/compiler/build/files/common.mk +++ b/runtime/compiler/build/files/common.mk @@ -334,6 +334,7 @@ JIT_PRODUCT_SOURCE_FILES+=\ compiler/optimizer/InterProceduralAnalyzer.cpp \ compiler/optimizer/J9EstimateCodeSize.cpp \ compiler/optimizer/J9Inliner.cpp \ + compiler/optimizer/InterpreterEmulator.cpp \ compiler/ras/DebugExt.cpp \ compiler/ras/DebugExtSegmentProvider.cpp \ compiler/ras/HashTable.cpp \ diff --git a/runtime/compiler/optimizer/CMakeLists.txt b/runtime/compiler/optimizer/CMakeLists.txt index 75c5f6a5003..074552765fb 100644 --- a/runtime/compiler/optimizer/CMakeLists.txt +++ b/runtime/compiler/optimizer/CMakeLists.txt @@ -36,6 +36,7 @@ j9jit_files( optimizer/InterProceduralAnalyzer.cpp optimizer/J9CFGSimplifier.cpp optimizer/J9EstimateCodeSize.cpp + optimizer/InterpreterEmulator.cpp optimizer/J9Inliner.cpp optimizer/J9LocalCSE.cpp optimizer/J9OptimizationManager.cpp diff --git a/runtime/compiler/optimizer/EstimateCodeSize.cpp b/runtime/compiler/optimizer/EstimateCodeSize.cpp index 139950a01f3..3cf63f49cb9 100644 --- a/runtime/compiler/optimizer/EstimateCodeSize.cpp +++ b/runtime/compiler/optimizer/EstimateCodeSize.cpp @@ -73,14 +73,6 @@ TR_EstimateCodeSize::release(TR_EstimateCodeSize *estimator) comp->fej9()->releaseCodeEstimator(comp, estimator); } - -void -TR_EstimateCodeSize::markIsCold(flags8_t * flags, int32_t i) - { - _isLeaf = false; - flags[i].set(isCold); - } - bool TR_EstimateCodeSize::calculateCodeSize(TR_CallTarget *calltarget, TR_CallStack *callStack, bool recurseDown) { diff --git a/runtime/compiler/optimizer/EstimateCodeSize.hpp b/runtime/compiler/optimizer/EstimateCodeSize.hpp index e47d764e4ca..9a2dce1eb0e 100644 --- a/runtime/compiler/optimizer/EstimateCodeSize.hpp +++ b/runtime/compiler/optimizer/EstimateCodeSize.hpp @@ -85,6 +85,11 @@ class TR_EstimateCodeSize bool isLeaf() { return _isLeaf; } int32_t getNumOfEstimatedCalls() { return _numOfEstimatedCalls; } + /* + * \brief + * tell whether this callsite has inlineable target + */ + bool isInlineable(TR_CallStack *, TR_CallSite *callsite); TR::Compilation *comp() { return _inliner->comp(); } TR_InlinerTracer *tracer() { return _tracer; } @@ -93,9 +98,6 @@ class TR_EstimateCodeSize virtual bool estimateCodeSize(TR_CallTarget *, TR_CallStack * , bool recurseDown = true) = 0; - bool isInlineable(TR_CallStack *, TR_CallSite *callsite); - - void markIsCold(flags8_t * flags, int32_t i); bool returnCleanup(int32_t); // common tasks requiring completion before returning from estimation diff --git a/runtime/compiler/optimizer/InterpreterEmulator.cpp b/runtime/compiler/optimizer/InterpreterEmulator.cpp new file mode 100644 index 00000000000..68db1a9260c --- /dev/null +++ b/runtime/compiler/optimizer/InterpreterEmulator.cpp @@ -0,0 +1,850 @@ +/******************************************************************************* + * Copyright (c) 2000, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ +#include "optimizer/InterpreterEmulator.hpp" +#include "optimizer/J9EstimateCodeSize.hpp" +#include "env/VMAccessCriticalSection.hpp" +#include "optimizer/PreExistence.hpp" +#include "optimizer/J9CallGraph.hpp" +#include "ilgen/IlGenRequest.hpp" +#include "jilconsts.h" + +void +InterpreterEmulator::maintainStackForIf(TR_J9ByteCode bc) + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + TR_ASSERT_FATAL(bc == J9BCificmpeq || bc == J9BCificmpne, "InterpreterEmulator::maintainStackForIf can only be called with J9BCificmpeq and J9BCificmpne\n"); + int32_t branchBC = _bcIndex + next2BytesSigned(); + int32_t fallThruBC = _bcIndex + 3; + IconstOperand * second = pop()->asIconst(); + IconstOperand * first = pop()->asIconst(); + bool canBranch = true; + bool canFallThru = true; + if (second && first) + { + switch (bc) + { + case J9BCificmpeq: + canBranch = second->intValue == first->intValue; + debugTrace(tracer(), "maintainStackForIf ifcmpeq %d == %d\n", second->intValue, first->intValue); + break; + case J9BCificmpne: + canBranch = second->intValue != first->intValue; + debugTrace(tracer(), "maintainStackForIf ifcmpne %d != %d\n", second->intValue, first->intValue); + break; + } + canFallThru = !canBranch; + } + + if (canBranch) + { + debugTrace(tracer(), "maintainStackForIf canBranch to bcIndex=%d\n", branchBC); + genTarget(branchBC); + } + + if (canFallThru) + { + debugTrace(tracer(), "maintainStackForIf canFallThrough to bcIndex=%d\n", fallThruBC); + genTarget(fallThruBC); + } + } + +void +InterpreterEmulator::maintainStackForGetField() + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + bool isVolatile, isPrivate, isUnresolvedInCP, isFinal; + TR::DataType type = TR::NoType; + uint32_t fieldOffset; + int32_t cpIndex = next2Bytes(); + Operand *newOperand = _unknownOperand; + bool resolved = _calltarget->_calleeMethod->fieldAttributes(comp(), cpIndex, &fieldOffset, &type, &isVolatile, &isFinal, &isPrivate, false, &isUnresolvedInCP, false); + if (top()->getKnownObjectIndex() != TR::KnownObjectTable::UNKNOWN && type == TR::Address) + { + TR::Symbol::RecognizedField recognizedField = TR::Symbol::searchRecognizedField(comp(), _calltarget->_calleeMethod, cpIndex, false); + TR::Symbol *fieldSymbol = NULL; + if (recognizedField != TR::Symbol::UnknownField) + fieldSymbol = TR::Symbol::createRecognizedShadow(trStackMemory(),type, recognizedField); + else + fieldSymbol = TR::Symbol::createShadow(trStackMemory(),type); + if (isFinal) + fieldSymbol->setFinal(); + + if ((resolved || !isUnresolvedInCP) && comp()->fej9()->canDereferenceAtCompileTimeWithFieldSymbol(fieldSymbol, cpIndex, _calltarget->_calleeMethod)) + { + TR::KnownObjectTable *knot = comp()->getKnownObjectTable(); + if (knot) + { + TR::VMAccessCriticalSection dereferenceKnownObjectField(comp()->fej9()); + TR::KnownObjectTable::Index baseObjectIndex = top()->getKnownObjectIndex(); + uintptrj_t baseObjectAddress = *knot->getPointerLocation(baseObjectIndex); + TR_OpaqueClassBlock *baseObjectClass = comp()->fej9()->getObjectClass(baseObjectAddress); + TR_OpaqueClassBlock *fieldDeclaringClass = _calltarget->_calleeMethod->getDeclaringClassFromFieldOrStatic(comp(), cpIndex); + if (fieldDeclaringClass && comp()->fej9()->isInstanceOf(baseObjectClass, fieldDeclaringClass, true) == TR_yes) + { + uintptrj_t fieldAddress = comp()->fej9()->getReferenceFieldAtAddress(baseObjectAddress + fieldOffset); + newOperand = new (trStackMemory()) KnownObjOperand(knot->getIndex(fieldAddress)); + int32_t len; + debugTrace(tracer(), "dereference obj%d (%p)from field %s(offset = %d) of base obj%d(%p)\n", newOperand->getKnownObjectIndex(), (void *)fieldAddress, _calltarget->_calleeMethod->fieldName(cpIndex, len, this->trMemory()), fieldOffset, baseObjectIndex, baseObjectAddress); + } + } + } + else + debugTrace(tracer(), "unresolved field or can't derefence in thunk archetype resolved %d isUnresolvedInCP %d\n", resolved, isUnresolvedInCP); + } + pop(); + push(newOperand); + } + +void +InterpreterEmulator::saveStack(int32_t targetIndex) + { + if (_stack->isEmpty()) + return; + bool createTargetStack = (targetIndex >= 0 && !_stacks[targetIndex]); + if (createTargetStack) + _stacks[targetIndex] = new (trStackMemory()) ByteCodeStack(this->trMemory(), std::max(20, _stack->size())); + } + +void +InterpreterEmulator::initializeIteratorWithState() + { + _iteratorWithState = true; + _unknownOperand = new (trStackMemory()) Operand(); + uint32_t size = this->maxByteCodeIndex() + 5; + _flags = (flags8_t *) this->trMemory()->allocateStackMemory(size * sizeof(flags8_t)); + _stacks = (ByteCodeStack * *) this->trMemory()->allocateStackMemory(size * sizeof(ByteCodeStack *)); + memset(_flags, 0, size * sizeof(flags8_t)); + memset(_stacks, 0, size * sizeof(ByteCodeStack *)); + _stack = new (trStackMemory()) TR_Stack(this->trMemory(), 20, false, stackAlloc); + + genBBStart(0); + setupBBStartContext(0); + this->setIndex(0); + } + +void +InterpreterEmulator::maintainStack(TR_J9ByteCode bc) + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + int slotIndex = -1; + switch (bc) + { + case J9BCgetfield: maintainStackForGetField(); break; + case J9BCaload0: slotIndex = 0; maintainStackForAload(slotIndex); break; + case J9BCaload1: slotIndex = 1; maintainStackForAload(slotIndex); break; + case J9BCaload2: slotIndex = 2; maintainStackForAload(slotIndex); break; + case J9BCaload3: slotIndex = 3; maintainStackForAload(slotIndex); break; + case J9BCaload: slotIndex = nextByte(); maintainStackForAload(slotIndex); break; + case J9BCaloadw: slotIndex = next2Bytes(); maintainStackForAload(slotIndex); break; + + case J9BCinvokespecial: + case J9BCinvokespecialsplit: + maintainStackForDirectCall(_calltarget->_calleeMethod); + break; + case J9BCiconstm1: push (new (trStackMemory()) IconstOperand(-1)); break; + case J9BCiconst0: push (new (trStackMemory()) IconstOperand(0)); break; + case J9BCiconst1: push (new (trStackMemory()) IconstOperand(1)); break; + case J9BCiconst2: push (new (trStackMemory()) IconstOperand(2)); break; + case J9BCiconst3: push (new (trStackMemory()) IconstOperand(3)); break; + case J9BCiconst4: push (new (trStackMemory()) IconstOperand(4)); break; + case J9BCiconst5: push (new (trStackMemory()) IconstOperand(5)); break; + case J9BCifne: + push (new (trStackMemory()) IconstOperand(0)); + maintainStackForIf(J9BCificmpne); + break; + case J9BCifeq: + push (new (trStackMemory()) IconstOperand(0)); + maintainStackForIf(J9BCificmpeq); + break; + case J9BCgoto: + genTarget(bcIndex() + next2BytesSigned()); + break; + case J9BCpop: + case J9BCputfield: + case J9BCputstatic: + pop(); + break; + case J9BCladd: + case J9BCiadd: + case J9BCisub: + popn(2); + pushUnknownOperand(); + break; + case J9BCiload0: + case J9BCiload1: + case J9BCiload2: + case J9BCiload3: + case J9BCgetstatic: + pushUnknownOperand(); + break; + case J9BCgenericReturn: + case J9BCi2l: + break; + //following bytecodes has been handled when creating callsites + case J9BCinvokevirtual: + case J9BCinvokestatic: + case J9BCinvokestaticsplit: + break; + default: + TR_ASSERT_FATAL(0, "unexpected bytecode in thunk archetype %p at bcIndex %d %s (%d)\n", _calltarget, bcIndex(), comp()->fej9()->getByteCodeName(nextByte(0)), bc); + } + } + +void +InterpreterEmulator::maintainStackForAload(int slotIndex) + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + TR_PrexArgInfo *argInfo = _calltarget->_ecsPrexArgInfo; + TR_PrexArgument *prexArgument = argInfo ? argInfo->get(slotIndex): NULL; + TR_ASSERT_FATAL(argInfo, "thunk archetype target doesn't have _ecsPrexArgInfo %p\n", _calltarget); + if (prexArgument && TR_PrexArgument::knowledgeLevel(prexArgument) == KNOWN_OBJECT) + { + debugTrace(tracer(), "aload known obj%d from slot %d\n", prexArgument->getKnownObjectIndex(), slotIndex); + push(new (trStackMemory()) KnownObjOperand(prexArgument->getKnownObjectIndex())); + } + else pushUnknownOperand(); + } + +void +InterpreterEmulator::maintainStackForCall(TR_ResolvedMethod *callerMethod, Operand *result, bool isDirect) + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + int32_t cpIndex = next2Bytes(); + TR::Method * calleeMethod = comp()->fej9()->createMethod(trMemory(), callerMethod->containingClass(), cpIndex); + int32_t argNum = calleeMethod->numberOfExplicitParameters() + (isDirect ? 0: 1); + + for (int i = 1; i <= argNum; i++) + pop(); + + if (result) + push(result); + else if (calleeMethod->returnType() != TR::NoType) + pushUnknownOperand(); + } + +void +InterpreterEmulator::dumpStack() + { + debugTrace(tracer(), "operandStack after %d : %s ", _bcIndex, comp()->fej9()->getByteCodeName(nextByte(0))); + for (int i = 0; i < _stack->size(); i++ ) + { + Operand *x = (*_stack)[i]; + char buffer[20]; + x->printToString(buffer); + debugTrace(tracer(), "[%d]=%s, ", i, buffer); + } + debugTrace(tracer(),"\n"); + } + +Operand * +InterpreterEmulator::getReturnValueForInvokestatic(TR_ResolvedMethod *callee) + { + if (!callee) + return NULL; + Operand *result = NULL; + TR::RecognizedMethod recognizedMethod = callee->getRecognizedMethod(); + TR::IlGeneratorMethodDetails & details = comp()->ilGenRequest().details(); + if (details.isMethodHandleThunk()) + { + J9::MethodHandleThunkDetails & thunkDetails = static_cast(details); + if (!thunkDetails.isCustom()) + recognizedMethod = TR::unknownMethod; + } + + switch (recognizedMethod) + { + case TR::java_lang_invoke_ILGenMacros_isCustomThunk: + result = new (trStackMemory()) IconstOperand(1); + break; + case TR::java_lang_invoke_ILGenMacros_isShareableThunk: + result = new (trStackMemory()) IconstOperand(0); + break; + } + return result; + } + +Operand * +InterpreterEmulator::getReturnValueForInvokevirtual(TR_ResolvedMethod *callee) + { + if (!callee) + return NULL; + Operand *result = NULL; + int argNum = callee->numberOfExplicitParameters(); + TR::KnownObjectTable::Index receiverIndex = topn(argNum)->getKnownObjectIndex(); + if (callee->getRecognizedMethod() == TR::java_lang_invoke_MutableCallSite_getTarget && + receiverIndex != TR::KnownObjectTable::UNKNOWN) + { + TR::VMAccessCriticalSection dereferenceKnownObjectField(comp()->fej9()); + TR::KnownObjectTable *knot = comp()->getKnownObjectTable(); + TR::KnownObjectTable::Index resultIndex = TR::KnownObjectTable::UNKNOWN; + TR_OpaqueClassBlock *mutableCallsiteClass = callee->classOfMethod(); + debugTrace(tracer(), "java_lang_invoke_MutableCallSite_target receiver obj%d(*%p) mutableCallsiteClass %p\n", receiverIndex, knot->getPointerLocation(receiverIndex), mutableCallsiteClass); + if (mutableCallsiteClass) + { + TR::VMAccessCriticalSection dereferenceKnownObjectField(comp()->fej9()); + int32_t targetFieldOffset =comp()->fej9()->getInstanceFieldOffset(mutableCallsiteClass, "target", "Ljava/lang/invoke/MethodHandle;"); + uintptrj_t receiverAddress = *knot->getPointerLocation(receiverIndex); + TR_OpaqueClassBlock *receiverClass = comp()->fej9()->getObjectClass(receiverAddress); + TR_ASSERT_FATAL(comp()->fej9()->isInstanceOf(receiverClass, mutableCallsiteClass, true) == TR_yes, "receiver of mutableCallsite_getTarget must be instance of MutableCallSite (*%p)", knot->getPointerLocation(receiverIndex)); + uintptrj_t fieldAddress = comp()->fej9()->getReferenceFieldAt(receiverAddress, targetFieldOffset); + resultIndex = knot->getIndex(fieldAddress); + result = new (trStackMemory()) MutableCallsiteTargetOperand(resultIndex, receiverIndex); + } + } + return result; + } + +void +InterpreterEmulator::refineResolvedCalleeForInvokestatic(TR_ResolvedMethod *&callee, TR::KnownObjectTable::Index & mcsIndex, TR::KnownObjectTable::Index & mhIndex, bool &isIndirectCall) + { + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); + if (!comp()->getOrCreateKnownObjectTable()) + return; + + bool isVirtual = false; + bool isInterface = false; + switch (callee->getRecognizedMethod()) + { + // refine the ILGenMacros_invokeExact* callees + case TR::java_lang_invoke_ILGenMacros_invokeExact: + case TR::java_lang_invoke_ILGenMacros_invokeExact_X: + case TR::java_lang_invoke_ILGenMacros_invokeExactAndFixup: + { + int argNum = callee->numberOfExplicitParameters(); + if (argNum > 0) + { + Operand *operand = topn(argNum-1); // for the ILGenMacros_invokeExact* methods, the first argument is always the methodhandle object + MutableCallsiteTargetOperand * mcsOperand = operand->asMutableCallsiteTargetOperand(); + if (mcsOperand) + { + mhIndex = mcsOperand->getMethodHandleIndex(); + mcsIndex = mcsOperand->getMutableCallsiteIndex(); + } + else + mhIndex = operand->getKnownObjectIndex(); + } + + if (mhIndex != TR::KnownObjectTable::UNKNOWN) + { + debugTrace(tracer(), "refine java_lang_invoke_MethodHandle_invokeExact with obj%d to archetype specimen at bcIndex=%d\n", mhIndex, _bcIndex); + callee = comp()->fej9()->createMethodHandleArchetypeSpecimen(this->trMemory(), comp()->getKnownObjectTable()->getPointerLocation(mhIndex), _calltarget->_calleeMethod); + } + return; + } + // refine the leaf method handle callees + case TR::java_lang_invoke_InterfaceHandle_interfaceCall: + isInterface = true; + case TR::java_lang_invoke_VirtualHandle_virtualCall: + isVirtual = !isInterface; + isIndirectCall = true; + case TR::java_lang_invoke_DirectHandle_directCall: + { + isIndirectCall = false; + TR_OpaqueMethodBlock *j9method; + int64_t vmSlot; + uintptrj_t jlClass; + TR_J9VMBase *fej9 = comp()->fej9(); + { + { + TR::VMAccessCriticalSection invokeDirectHandleDirectCall(fej9); + uintptrj_t methodHandle = *_calltarget->_calleeMethod->getMethodHandleLocation(); + vmSlot = fej9->getInt64Field(methodHandle, "vmSlot"); + jlClass = fej9->getReferenceField(methodHandle, "defc", "Ljava/lang/Class;"); + debugTrace(tracer(), "refine resolved method for leaf methodHandle [obj%d]\n", comp()->getOrCreateKnownObjectTable()->getIndex(methodHandle)); + } + if (isInterface) + { + TR_OpaqueClassBlock *clazz = fej9->getClassFromJavaLangClass(jlClass); + j9method = (TR_OpaqueMethodBlock*)&(((J9Class *)clazz)->ramMethods[vmSlot]); + } + else if (isVirtual) + { + TR_OpaqueMethodBlock **vtable = (TR_OpaqueMethodBlock**)(((uintptrj_t)fej9->getClassFromJavaLangClass(jlClass)) + J9JIT_INTERP_VTABLE_OFFSET); + int32_t index = (int32_t)((vmSlot - J9JIT_INTERP_VTABLE_OFFSET) / sizeof(vtable[0])); + j9method = vtable[index]; + } + else + { + j9method = (TR_OpaqueMethodBlock*)(intptrj_t)vmSlot; + } + } + TR_ASSERT(j9method, "Must have a j9method to generate a custom call"); + callee = fej9->createResolvedMethod(this->trMemory(), j9method); + return; + } + } + } + +void +InterpreterEmulator::findAndCreateCallsitesFromBytecodes(bool wasPeekingSuccessfull, bool withState) + { + TR::Region findCallsitesRegion(comp()->region()); + if (withState) + initializeIteratorWithState(); + _wasPeekingSuccessfull = wasPeekingSuccessfull; + _currentInlinedBlock = NULL; + TR_J9ByteCode bc = first(); + while (bc != J9BCunknown) + { + if (_InterpreterEmulatorFlags[_bcIndex].testAny(InterpreterEmulator::BytecodePropertyFlag::bbStart)) + { + _currentInlinedBlock = TR_J9EstimateCodeSize::getBlock(comp(), _blocks, _calltarget->_calleeMethod, _bcIndex, *_cfg); + debugTrace(tracer(),"Found current block %p, number %d for bci %d\n", _currentInlinedBlock, (_currentInlinedBlock) ? _currentInlinedBlock->getNumber() : -1, _bcIndex); + } + + + TR_ASSERT_FATAL(!isGenerated(_bcIndex), "InterpreterEmulator::findCallsitesFromBytecodes bcIndex %d has been generated\n", _bcIndex); + _newBCInfo->setByteCodeIndex(_bcIndex); + + switch (bc) + { + case J9BCinvokedynamic: visitInvokedynamic(); break; + case J9BCinvokevirtual: visitInvokevirtual(); break; + case J9BCinvokespecial: + case J9BCinvokespecialsplit: visitInvokespecial(); break; + case J9BCinvokestatic: + case J9BCinvokestaticsplit: visitInvokestatic(); break; + case J9BCinvokeinterface: visitInvokeinterface(); break; + } + + if (_iteratorWithState) + { + maintainStack(bc); + dumpStack(); + } + + _pca.updateArg(bc); + bc = findNextByteCodeToVisit(); + } + } + +TR_J9ByteCode +InterpreterEmulator::findNextByteCodeToVisit() + { + if (!_iteratorWithState) + next(); + else + { + setIsGenerated(_bcIndex); + if (_InterpreterEmulatorFlags[_bcIndex].testAny(InterpreterEmulator::BytecodePropertyFlag::isBranch)) + { + setIndex(Base::findNextByteCodeToGen()); + debugTrace(tracer(), "current bc is branch next bytecode to generate is %d\n", _bcIndex); + } + else next(); + } + + if (_InterpreterEmulatorFlags[_bcIndex].testAny(InterpreterEmulator::BytecodePropertyFlag::bbStart)) + { + if (isGenerated(_bcIndex)) + setIndex(Base::findNextByteCodeToGen()); + } + return current(); + } + +void +InterpreterEmulator::prepareToFindAndCreateCallsites(TR::Block **blocks, flags8_t * flags, TR_CallSite ** callSites, TR::CFG *cfg, TR_ByteCodeInfo *newBCInfo, int32_t recursionDepth, TR_CallStack *callStack) + { + _blocks = blocks; + _InterpreterEmulatorFlags = flags; + _callSites = callSites; + _cfg = cfg; + _newBCInfo = newBCInfo; + _recursionDepth = recursionDepth; + _callStack = callStack; + _nonColdCallExists = false; + _inlineableCallExists = false; + } + +void +InterpreterEmulator::visitInvokedynamic() + { + int32_t cpIndex = next2Bytes(); + bool isInterface = false; + bool isIndirectCall = false; + TR::Method *interfaceMethod = 0; + TR::TreeTop *callNodeTreeTop = 0; + TR::Node *parent = 0; + TR::Node *callNode = 0; + TR::ResolvedMethodSymbol *resolvedSymbol = 0; + Operand *result = NULL; + + TR_ResolvedMethod * owningMethod = _methodSymbol->getResolvedMethod(); + TR::KnownObjectTable *knot = comp()->getOrCreateKnownObjectTable(); + if (knot && !owningMethod->isUnresolvedCallSiteTableEntry(cpIndex)) + { + isIndirectCall = true; + uintptrj_t *entryLocation = (uintptrj_t*)owningMethod->callSiteTableEntryAddress(cpIndex); + // Add callsite handle to known object table + knot->getIndexAt((uintptrj_t*)entryLocation); + TR_ResolvedMethod * resolvedMethod = comp()->fej9()->createMethodHandleArchetypeSpecimen(this->trMemory(), entryLocation, owningMethod); + bool allconsts= false; + + heuristicTrace(tracer(),"numberOfExplicitParameters = %d _pca.getNumPrevConstArgs = %d\n", resolvedMethod->numberOfExplicitParameters() , _pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())); + if (resolvedMethod->numberOfExplicitParameters() > 0 && resolvedMethod->numberOfExplicitParameters() <= _pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())) + allconsts = true; + + TR_CallSite *callsite = new (comp()->trHeapMemory()) TR_J9MethodHandleCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + + findTargetAndUpdateInfoForCallsite(callsite); + } + } + +bool +InterpreterEmulator::isCurrentCallUnresolvedOrCold(TR_ResolvedMethod *resolvedMethod, bool isUnresolvedInCP) + { + bool isIndirectCall = false; + if (current() == J9BCinvokevirtual) + isIndirectCall = true; + return (!resolvedMethod || isUnresolvedInCP || resolvedMethod->isCold(comp(), isIndirectCall)); + } + +void +InterpreterEmulator::debugUnresolvedOrCold(TR_ResolvedMethod *resolvedMethod) + { + int32_t cpIndex = next2Bytes(); + if(tracer()->heuristicLevel()) + { + if (resolvedMethod) + heuristicTrace(tracer(), "Depth %d: Call at bc index %d is Cold. Not searching for targets. Signature %s", _recursionDepth, _bcIndex ,tracer()->traceSignature(resolvedMethod)); + else + { + switch (current()) + { + case J9BCinvokespecialsplit: + cpIndex |= J9_SPECIAL_SPLIT_TABLE_INDEX_FLAG; + break; + case J9BCinvokestaticsplit: + cpIndex |= J9_STATIC_SPLIT_TABLE_INDEX_FLAG; + break; + } + TR::Method *meth = comp()->fej9()->createMethod(this->trMemory(), _calltarget->_calleeMethod->containingClass(), cpIndex); + heuristicTrace(tracer(), "Depth %d: Call at bc index %d is Cold. Not searching for targets. Signature %s", _recursionDepth, _bcIndex, tracer()->traceSignature(meth)); + } + } + } + +void +InterpreterEmulator::visitInvokevirtual() + { + int32_t cpIndex = next2Bytes(); + auto calleeMethod = (TR_ResolvedJ9Method*)_calltarget->_calleeMethod; + bool isUnresolvedInCP; + TR_ResolvedMethod * resolvedMethod = calleeMethod->getResolvedPossiblyPrivateVirtualMethod(comp(), cpIndex, true, &isUnresolvedInCP); + Operand *result = NULL; + if (isCurrentCallUnresolvedOrCold(resolvedMethod, isUnresolvedInCP)) + { + debugUnresolvedOrCold(resolvedMethod); + } + else if (resolvedMethod) + { + bool allconsts= false; + heuristicTrace(tracer(),"numberOfExplicitParameters = %d _pca.getNumPrevConstArgs = %d\n",resolvedMethod->numberOfExplicitParameters() ,_pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())); + if ( resolvedMethod->numberOfExplicitParameters() > 0 && resolvedMethod->numberOfExplicitParameters() <= _pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())) + allconsts = true; + + TR_CallSite *callsite; + bool isIndirectCall = resolvedMethod == NULL || + (!resolvedMethod->isFinal() && !resolvedMethod->isPrivate()); + bool isInterface = false; + TR::Method *interfaceMethod = 0; + TR::TreeTop *callNodeTreeTop = 0; + TR::Node *parent = 0; + TR::Node *callNode = 0; + TR::ResolvedMethodSymbol *resolvedSymbol = 0; + + if (resolvedMethod->convertToMethod()->isArchetypeSpecimen() && resolvedMethod->getMethodHandleLocation()) + { + callsite = new (comp()->trHeapMemory()) TR_J9MethodHandleCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + } + else if (resolvedMethod->getRecognizedMethod() == TR::java_lang_invoke_MethodHandle_invokeExact) + { + callsite = new (comp()->trHeapMemory()) TR_J9MutableCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + } + else if (isIndirectCall) + { + callsite = new (comp()->trHeapMemory()) TR_J9VirtualCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + + } + else + { + callsite = new (comp()->trHeapMemory()) TR_DirectCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + + } + + if(tracer()->debugLevel()) + _pca.printIndexes(comp()); + findTargetAndUpdateInfoForCallsite(callsite); + } + + if (_iteratorWithState) + maintainStackForIndirectCall(_calltarget->_calleeMethod, getReturnValueForInvokevirtual(resolvedMethod)); + } + +void +InterpreterEmulator::visitInvokespecial() + { + int32_t cpIndex = next2Bytes(); + bool isUnresolvedInCP; + TR_ResolvedMethod *resolvedMethod = _calltarget->_calleeMethod->getResolvedSpecialMethod(comp(), (current() == J9BCinvokespecialsplit)?cpIndex |= J9_SPECIAL_SPLIT_TABLE_INDEX_FLAG:cpIndex, &isUnresolvedInCP); + if (isCurrentCallUnresolvedOrCold(resolvedMethod, isUnresolvedInCP)) + { + debugUnresolvedOrCold(resolvedMethod); + } + else + { + bool allconsts= false; + heuristicTrace(tracer(),"numberOfExplicitParameters = %d _pca.getNumPrevConstArgs = %d\n",resolvedMethod->numberOfExplicitParameters() ,_pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())); + if (resolvedMethod->numberOfExplicitParameters() > 0 && resolvedMethod->numberOfExplicitParameters() <= _pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())) + allconsts = true; + + bool isIndirectCall = false; + bool isInterface = false; + TR::Method *interfaceMethod = 0; + TR::TreeTop *callNodeTreeTop = 0; + TR::Node *parent = 0; + TR::Node *callNode = 0; + TR::ResolvedMethodSymbol *resolvedSymbol = 0; + TR_CallSite *callsite = new (comp()->trHeapMemory()) TR_DirectCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), -1, cpIndex, + resolvedMethod, resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + findTargetAndUpdateInfoForCallsite(callsite); + } + } + +void +InterpreterEmulator::visitInvokestatic() + { + int32_t cpIndex = next2Bytes(); + bool isUnresolvedInCP; + TR_ResolvedMethod *resolvedMethod = _calltarget->_calleeMethod->getResolvedStaticMethod(comp(), (current() == J9BCinvokestaticsplit) ? cpIndex |= J9_STATIC_SPLIT_TABLE_INDEX_FLAG:cpIndex, &isUnresolvedInCP); + TR_ResolvedMethod *origResolvedMethod = resolvedMethod; + if (isCurrentCallUnresolvedOrCold(resolvedMethod, isUnresolvedInCP)) + { + debugUnresolvedOrCold(resolvedMethod); + } + else + { + bool allconsts= false; + + heuristicTrace(tracer(),"numberOfExplicitParameters = %d _pca.getNumPrevConstArgs = %d\n",resolvedMethod->numberOfExplicitParameters() ,_pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())); + if (resolvedMethod->numberOfExplicitParameters() > 0 && resolvedMethod->numberOfExplicitParameters() <= _pca.getNumPrevConstArgs(resolvedMethod->numberOfExplicitParameters())) + allconsts = true; + + TR::KnownObjectTable::Index mhIndex = TR::KnownObjectTable::UNKNOWN; + TR::KnownObjectTable::Index mcsIndex = TR::KnownObjectTable::UNKNOWN; + bool isIndirectCall = false; + if (_iteratorWithState) + refineResolvedCalleeForInvokestatic(resolvedMethod, mcsIndex, mhIndex, isIndirectCall); + + bool isInterface = false; + TR_CallSite *callsite = NULL; + TR::Method *interfaceMethod = 0; + TR::TreeTop *callNodeTreeTop = 0; + TR::Node *parent = 0; + TR::Node *callNode = 0; + TR::ResolvedMethodSymbol *resolvedSymbol = 0; + + if (resolvedMethod->convertToMethod()->isArchetypeSpecimen() && + resolvedMethod->getMethodHandleLocation() && + mcsIndex == TR::KnownObjectTable::UNKNOWN) + { + callsite = new (comp()->trHeapMemory()) TR_J9MethodHandleCallSite( _calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + } + else if (resolvedMethod->convertToMethod()->isArchetypeSpecimen() && + resolvedMethod->getMethodHandleLocation() && + mcsIndex != TR::KnownObjectTable::UNKNOWN) + { + TR_J9MutableCallSite *mcs = new (comp()->trHeapMemory()) TR_J9MutableCallSite( _calltarget->_calleeMethod, callNodeTreeTop, parent, + callNode, interfaceMethod, resolvedMethod->classOfMethod(), + (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + if (mcsIndex != TR::KnownObjectTable::UNKNOWN) + { + if (comp()->getKnownObjectTable()) + mcs->setMCSReferenceLocation(comp()->getKnownObjectTable()->getPointerLocation(mcsIndex)); + } + callsite = mcs; + } + else if (isIndirectCall) + { + callsite = new (comp()->trHeapMemory()) TR_J9VirtualCallSite( + _calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, + interfaceMethod, resolvedMethod->classOfMethod(), -1, cpIndex, + resolvedMethod, resolvedSymbol, isIndirectCall, isInterface, + *_newBCInfo, comp(), _recursionDepth, allconsts); + } + else + { + callsite = new (comp()->trHeapMemory()) TR_DirectCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, interfaceMethod, + resolvedMethod->classOfMethod(), -1, cpIndex, resolvedMethod, resolvedSymbol, + isIndirectCall, isInterface, *_newBCInfo, comp(), + _recursionDepth, allconsts); + } + findTargetAndUpdateInfoForCallsite(callsite); + } + + if (_iteratorWithState) + maintainStackForDirectCall(_calltarget->_calleeMethod, getReturnValueForInvokestatic(origResolvedMethod)); + } + +void +InterpreterEmulator::visitInvokeinterface() + { + int32_t cpIndex = next2Bytes(); + auto calleeMethod = (TR_ResolvedJ9Method*)_calltarget->_calleeMethod; + TR_ResolvedMethod *resolvedMethod = calleeMethod->getResolvedImproperInterfaceMethod(comp(), cpIndex); + bool isIndirectCall = true; + bool isInterface = true; + if (resolvedMethod) + { + isInterface = false; + isIndirectCall = !resolvedMethod->isPrivate() && + !resolvedMethod->convertToMethod()->isFinalInObject(); + } + + TR::Method * interfaceMethod = NULL; + if (isInterface) + interfaceMethod = comp()->fej9()->createMethod(this->trMemory(), _calltarget->_calleeMethod->containingClass(), cpIndex); + + TR::TreeTop *callNodeTreeTop = 0; + TR::Node *parent = 0; + TR::Node *callNode = 0; + TR::ResolvedMethodSymbol *resolvedSymbol = 0; + + uint32_t explicitParams = 0; + if (isInterface) + explicitParams = interfaceMethod->numberOfExplicitParameters(); + else + explicitParams = resolvedMethod->numberOfExplicitParameters(); + + bool allconsts= false; + heuristicTrace(tracer(), "numberOfExplicitParameters = %d _pca.getNumPrevConstArgs = %d\n", explicitParams, _pca.getNumPrevConstArgs(explicitParams)); + if (explicitParams > 0 && explicitParams <= _pca.getNumPrevConstArgs(explicitParams)) + allconsts = true; + + TR_CallSite *callsite = NULL; + if (isInterface) + { + TR_OpaqueClassBlock * thisClass = NULL; + callsite = new (comp()->trHeapMemory()) TR_J9InterfaceCallSite( + _calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, + interfaceMethod, thisClass, -1, cpIndex, resolvedMethod, + resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, + comp(), _recursionDepth, allconsts); + } + else if (isIndirectCall) + { + callsite = new (comp()->trHeapMemory()) TR_J9VirtualCallSite( + _calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, + interfaceMethod, resolvedMethod->classOfMethod(), (int32_t) resolvedMethod->virtualCallSelector(cpIndex), cpIndex, + resolvedMethod, resolvedSymbol, isIndirectCall, isInterface, + *_newBCInfo, comp(), _recursionDepth, allconsts); + } + else + { + callsite = new (comp()->trHeapMemory()) TR_DirectCallSite( + _calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, + interfaceMethod, resolvedMethod->classOfMethod(), -1, cpIndex, + resolvedMethod, resolvedSymbol, isIndirectCall, isInterface, + *_newBCInfo, comp(), _recursionDepth, allconsts); + } + + if(tracer()->debugLevel()) + { + _pca.printIndexes(comp()); + } + findTargetAndUpdateInfoForCallsite(callsite); + } + +void +InterpreterEmulator::findTargetAndUpdateInfoForCallsite(TR_CallSite *callsite) + { + callsite->_callerBlock = _currentInlinedBlock; + if (current() == J9BCinvokevirtual || current() == J9BCinvokeinterface) + { + if (_wasPeekingSuccessfull) + { + TR_PrexArgInfo::propagateReceiverInfoIfAvailable(_methodSymbol, callsite, _calltarget->_ecsPrexArgInfo, tracer()); + if (tracer()->heuristicLevel()) + { + alwaysTrace(tracer(), "propagateReceiverInfoIfAvailable :"); + if (callsite->_ecsPrexArgInfo) + tracer()->dumpPrexArgInfo(callsite->_ecsPrexArgInfo); + } + } + } + + if (_ecs->isInlineable(_callStack, callsite)) + { + _callSites[_bcIndex] = callsite; + _inlineableCallExists = true; + + if (_wasPeekingSuccessfull) + { + TR_PrexArgInfo::propagateArgsFromCaller(_methodSymbol, callsite, _calltarget->_ecsPrexArgInfo, tracer()); + if (tracer()->heuristicLevel()) + { + alwaysTrace(tracer(), "propagateArgs :"); + if (callsite->numTargets() && callsite->getTarget(0)->_ecsPrexArgInfo) + tracer()->dumpPrexArgInfo(callsite->getTarget(0)->_ecsPrexArgInfo); + } + } + + if (!_currentInlinedBlock->isCold()) + _nonColdCallExists = true; + + for (int i = 0; i < callsite->numTargets(); i++) + callsite->getTarget(i)->_originatingBlock = _currentInlinedBlock; + } + else + { + //support counters + _calltarget->addDeadCallee(callsite); + } + } diff --git a/runtime/compiler/optimizer/InterpreterEmulator.hpp b/runtime/compiler/optimizer/InterpreterEmulator.hpp new file mode 100644 index 00000000000..8db5c37795c --- /dev/null +++ b/runtime/compiler/optimizer/InterpreterEmulator.hpp @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2000, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ + +/* + * \class InterpreterEmulator + * + * \brief This class is a bytecode iterator in estimate code size (ECS) of inliner. + * + * \notes The iterator has statelss and with state modes. + * + * Stateless mode is the default mode and can be used to iterate + * through bytecodes in the method. This mode is currently used in the + * in ECS for \ref processBytecodeAndGenerateCFG for all methods and + * \ref findAndCreateCallsitesFromBytecodes when the target is not a methodhandle + * thunk archetype. + * + * With state mode is used to emulate the interpreter execution with + * an operand stack maintained during bytecode iteration to keep useful + * information like known object and constant integer so + * that inliner can make better decision when creating callsites. For + * example, inliner can avoid creating callsites on dead path + * \ref maintainStackForIf or can refine callee method based on known + * receiver info \ref refineResolvedCalleeForInvokestatic. Operands that + * can't be reasoned about are represented by a dummy operand \ref _unknownOperand + * which doesn't carry any extra information. Currently, with state mode only + * supports methodhandle thunk archetypes. + */ + +#ifndef INTERPRETER_EMULATOR_INCL +#define INTERPRETER_EMULATOR_INCL + +#include "il/Block.hpp" +#include "ilgen/ByteCodeIteratorWithState.hpp" +#include "ilgen/J9ByteCodeIterator.hpp" +#include "compile/Compilation.hpp" +#include "optimizer/Inliner.hpp" +#include "optimizer/J9Inliner.hpp" +#include "optimizer/J9EstimateCodeSize.hpp" +#include "env/TRMemory.hpp" + +class IconstOperand; +class KnownObjOperand; +class MutableCallsiteTargetOperand; + +/* + * \class Operand + * + * \brief represent an operand on the operand stack + * + * \note this is the most general operand which doesn't carry any specific information. + */ +class Operand + { + public: + TR_ALLOC(TR_Memory::EstimateCodeSize); + virtual IconstOperand *asIconst(){ return NULL;} + virtual KnownObjOperand *asKnownObject(){ return NULL;} + virtual MutableCallsiteTargetOperand* asMutableCallsiteTargetOperand(){ return NULL;} + virtual bool isUnkownOperand(){ return true;} + virtual TR::KnownObjectTable::Index getKnownObjectIndex(){ return TR::KnownObjectTable::UNKNOWN;} + virtual void printToString( char *buffer) + { + sprintf(buffer, "(obj%d)", getKnownObjectIndex()); + } + }; + +/* + * \class KnownOperand + * + * \brief represent operands that can be reasoned about at compile time + */ +class KnownOperand : public Operand + { + public: + TR_ALLOC(TR_Memory::EstimateCodeSize); + virtual bool isUnkownOperand(){ return false;} + }; + +class IconstOperand : public KnownOperand + { + public: + TR_ALLOC(TR_Memory::EstimateCodeSize); + IconstOperand (int x): intValue(x) { } + virtual IconstOperand *asIconst() { return this;} + virtual void printToString( char *buffer) + { + sprintf(buffer, "(iconst=%d)", intValue); + } + int32_t intValue; + }; + +class KnownObjOperand : public KnownOperand + { + public: + TR_ALLOC(TR_Memory::EstimateCodeSize); + KnownObjOperand(TR::KnownObjectTable::Index koi):knownObjIndex(koi){ } + virtual KnownObjOperand *asKnownObject(){ return this;} + virtual TR::KnownObjectTable::Index getKnownObjectIndex(){ return knownObjIndex;} + TR::KnownObjectTable::Index knownObjIndex; + }; + +/* + * \class MutableCallsiteTargetOperand + * + * \note This class is used to support mutable callsite because both the methodhandle object + * and the mutable callsite needs to be tracked so that when creating \c TR_J9MutableCallSite + * the mutable callsite object can be set for the callsite even though it's really the + * methodhandle object that's on the operand stack. + * + * \see getReturnValueForInvokevirtual + * \see refineResolvedCalleeForInvokestatic + * \see visitInvokestatic + */ +class MutableCallsiteTargetOperand : public KnownObjOperand + { + public: + TR_ALLOC(TR_Memory::EstimateCodeSize); + MutableCallsiteTargetOperand (TR::KnownObjectTable::Index methodHandleIndex, TR::KnownObjectTable::Index mutableCallsiteIndex): + KnownObjOperand(methodHandleIndex), + mutableCallsiteIndex(mutableCallsiteIndex){} + virtual MutableCallsiteTargetOperand* asMutableCallsiteTargetOperand(){ return this; } + virtual void printToString(char *buffer) + { + sprintf(buffer, "(mh=%d, mcs=%d)", getMethodHandleIndex(), getMutableCallsiteIndex()); + } + TR::KnownObjectTable::Index getMethodHandleIndex(){ return knownObjIndex; } + TR::KnownObjectTable::Index getMutableCallsiteIndex() { return mutableCallsiteIndex; } + TR::KnownObjectTable::Index mutableCallsiteIndex; + }; + +class InterpreterEmulator : public TR_ByteCodeIteratorWithState + { + typedef TR_ByteCodeIteratorWithState Base; + + public: + InterpreterEmulator( + TR_CallTarget *calltarget, + TR::ResolvedMethodSymbol * methodSymbol, + TR_J9VMBase * fe, + TR::Compilation * comp, + TR_InlinerTracer *tracer, + TR_EstimateCodeSize *ecs) + : Base(methodSymbol, comp), + _calltarget(calltarget), + _tracer(tracer), + _ecs(ecs), + _iteratorWithState(false) + { + TR_J9ByteCodeIterator::initialize(static_cast(methodSymbol->getResolvedMethod()), fe); + _flags = NULL; + _stacks = NULL; + } + TR_InlinerTracer *tracer() { return _tracer; } + /* \brief Initialize data needed for looking for callsites + * + * \param blocks + * blocks generated from bytecodes + * + * \param flags + * flags with bits to indicate property of each bytecode. The flags are set by \ref TR_J9EstimateCodeSize::processBytecodeAndGenerateCFG. + * + * \param callSites + * the call sites array to be filled in with callsites found + * + * \param cfg + * CFG generated \ref TR_J9EstimateCodeSize::processBytecodeAndGenerateCFG from bytecodes + * + * \param recursionDepth + * the depth of inlining layers + * + * \parm callstack + * the call stack from the current inlined call target to the method being compiled + */ + void prepareToFindAndCreateCallsites(TR::Block **blocks, flags8_t * flags, TR_CallSite ** callSites, TR::CFG* cfg, TR_ByteCodeInfo *newBCInfo, int32_t recursionDepth, TR_CallStack *callstack); + + /* + * \brief look for calls in bytecodes and create callsites + * + * \param wasPeekingSuccessfull + * indicate whether trees has been generated by peeking ilgen + * + * \param withState + * whether the bytecode iteration should be with or without state + */ + void findAndCreateCallsitesFromBytecodes(bool wasPeekingSuccessfull, bool withState); + void setBlocks(TR::Block **blocks) { _blocks = blocks; } + TR_StackMemory trStackMemory() { return _trMemory; } + bool _nonColdCallExists; + bool _inlineableCallExists; + + enum BytecodePropertyFlag + { + bbStart = 0x01, // whether the current bytecode is at bbstart + isCold = 0x02, // whther the bytecode is on a cold path + isBranch = 0x04, // whther the bytecode is a branch + isUnsanitizeable = 0x08, + }; + + private: + // the following methods can only be called when the iterator has state + + /* + * Initialize the data structures needed for iterator with state + */ + void initializeIteratorWithState(); + /* + * push and pop operands on stack according to given bytecode + */ + void maintainStack(TR_J9ByteCode bc); + void maintainStackForIf(TR_J9ByteCode bc); + void maintainStackForGetField(); + void maintainStackForAload(int slotIndex); + /* + * \brief helper to pop arguments from the stack and push the result for calls + * + * \param method + * the method being called from the current bytecode + * + * \param result + * the operand reprenting the call return value + * + * \param isDirect + * whether it's a direct call or indirect call + */ + void maintainStackForCall(TR_ResolvedMethod *method, Operand *result, bool isDirect); + void maintainStackForDirectCall(TR_ResolvedMethod *method, Operand *result = NULL) { maintainStackForCall(method, result, true /* isDirect */); } + void maintainStackForIndirectCall(TR_ResolvedMethod *method, Operand *result = NULL) { maintainStackForCall(method, result, false/* isDirect */); } + /* + * \brief refine the callee method based on operands when possible + */ + void refineResolvedCalleeForInvokestatic(TR_ResolvedMethod *&callee, TR::KnownObjectTable::Index & mcsIndex, TR::KnownObjectTable::Index & mhIndex, bool & isIndirectCall); + Operand *getReturnValueForInvokevirtual(TR_ResolvedMethod *callee); + Operand *getReturnValueForInvokestatic(TR_ResolvedMethod *callee); + void dumpStack(); + void pushUnknownOperand() { Base::push(_unknownOperand); } + // doesn't need to handle execeptions yet as they don't exist in method handle thunk archetypes + virtual void findAndMarkExceptionRanges(){ } + virtual void saveStack(int32_t targetIndex); + + // the following methods can be used in both stateless and with state mode + + /* + * \brief look for and set the next bytecode index to visit + * + * \return the bytecode value to visit + */ + TR_J9ByteCode findNextByteCodeToVisit(); + + /* + * \brief tell whether the given bcIndex has been generated. + * + * \note This query is used to avoid regenerating bytecodes which shouldn't happen at stateless mode + */ + bool isGenerated(int32_t bcIndex) { return _iteratorWithState ? Base::isGenerated(bcIndex): false; } + void visitInvokedynamic(); + void visitInvokevirtual(); + void visitInvokespecial(); + void visitInvokestatic(); + void visitInvokeinterface(); + void findTargetAndUpdateInfoForCallsite(TR_CallSite *callsite); + bool isCurrentCallUnresolvedOrCold(TR_ResolvedMethod *resolvedMethod, bool isUnresolvedInCP); + void debugUnresolvedOrCold(TR_ResolvedMethod *resolvedMethod); + + TR_InlinerTracer *_tracer; + TR_EstimateCodeSize *_ecs; + Operand * _unknownOperand; // used whenever the iterator can't reason about an operand + TR_CallTarget *_calltarget; // the target method to inline + bool _iteratorWithState; + flags8_t * _InterpreterEmulatorFlags; // flags with bits to indicate property of each bytecode. + TR_CallSite ** _callSites; + TR::CFG* _cfg; + TR_ByteCodeInfo *_newBCInfo; + int32_t _recursionDepth; + TR_CallStack *_callStack; // the call stack from the current inlined call target to the method being compiled + bool _wasPeekingSuccessfull; + TR::Block *_currentInlinedBlock; + TR_prevArgs _pca; + }; +#endif diff --git a/runtime/compiler/optimizer/J9EstimateCodeSize.cpp b/runtime/compiler/optimizer/J9EstimateCodeSize.cpp index 77dd3950eef..07d28325f96 100644 --- a/runtime/compiler/optimizer/J9EstimateCodeSize.cpp +++ b/runtime/compiler/optimizer/J9EstimateCodeSize.cpp @@ -37,13 +37,13 @@ #include "optimizer/PreExistence.hpp" #include "optimizer/J9CallGraph.hpp" #include "optimizer/J9EstimateCodeSize.hpp" +#include "optimizer/InterpreterEmulator.hpp" #include "ras/LogTracer.hpp" #include "runtime/J9Profiler.hpp" // Empirically determined value const float TR_J9EstimateCodeSize::STRING_COMPRESSION_ADJUSTMENT_FACTOR = 0.75f; -#define NUM_PREV_BC 5 /* DEFINEs are ugly in general, but putting @@ -236,80 +236,8 @@ class NeedsPeekingHeuristic }; #undef heuristicTraceIfTracerIsNotNull -class TR_prevArgs -{ - public: - TR_prevArgs() { for (int32_t i = 0 ; i < NUM_PREV_BC ; i++ ) { _prevBC[i] = J9BCunknown ; } } - - void printIndexes(TR::Compilation *comp) - { - for (int32_t i = 0 ; i < NUM_PREV_BC ; i++) - { - if(comp->getDebug()) - traceMsg(comp,"_prevBC[%d] = %s\n" ,i,((TR_J9VM*)(comp->fej9()))->getByteCodeName(_prevBC[i])); - } - } - - void updateArg(TR_J9ByteCode bc ) - { - for(int32_t i=NUM_PREV_BC-2 ; i>=0 ; i-- ) - { - _prevBC[i+1] = _prevBC[i]; - } - _prevBC[0] = bc; - } - - bool isArgAtIndexReceiverObject (int32_t index) - { - if ( index < NUM_PREV_BC && _prevBC[index] == J9BCaload0) - { - return true; - } - else - return false; - } - - int32_t getNumPrevConstArgs(int32_t numparms) - { - int32_t count=0; - - for(int32_t i=0 ; i < NUM_PREV_BC && i < numparms ; i++) - { - switch (_prevBC[i]) - { - case J9BCaconstnull: - case J9BCiconstm1: - case J9BCiconst0: - case J9BCiconst1: - case J9BCiconst2: - case J9BCiconst3: - case J9BCiconst4: - case J9BCiconst5: - case J9BClconst0: - case J9BClconst1: - case J9BCfconst0: - case J9BCfconst1: - case J9BCfconst2: - case J9BCdconst0: - case J9BCdconst1: - case J9BCldc: case J9BCldcw: case J9BCldc2lw: case J9BCldc2dw: - case J9BCbipush: case J9BCsipush: - count++; - break; - default: - break; - } - } - return count; - } - - - protected: - TR_J9ByteCode _prevBC[NUM_PREV_BC]; -}; - - -static void setupNode(TR::Node *node, uint32_t bcIndex, +void +TR_J9EstimateCodeSize::setupNode(TR::Node *node, uint32_t bcIndex, TR_ResolvedMethod *feMethod, TR::Compilation *comp) { node->getByteCodeInfo().setDoNotProfile(0); @@ -319,10 +247,8 @@ static void setupNode(TR::Node *node, uint32_t bcIndex, } -// TODO: use a smaller object to represent the block -//#define Block TR::CFGNode -#define Block TR::Block -static Block * getBlock(TR::Compilation *comp, Block * * blocks, +TR::Block * +TR_J9EstimateCodeSize::getBlock(TR::Compilation *comp, TR::Block * * blocks, TR_ResolvedMethod *feMethod, int32_t i, TR::CFG & cfg) { if (!blocks[i]) @@ -334,7 +260,7 @@ static Block * getBlock(TR::Compilation *comp, Block * * blocks, NULL, TR::BBEnd, 0)); startTree->join(endTree); - blocks[i] = Block::createBlock(startTree, endTree, cfg); + blocks[i] = TR::Block::createBlock(startTree, endTree, cfg); blocks[i]->setBlockBCIndex(i); blocks[i]->setNumber(cfg.getNextNodeNumber()); @@ -384,8 +310,9 @@ static TR::ILOpCodes convertBytecodeToIL (TR_J9ByteCode bc) return TR::BadILOp; } -static void setupLastTreeTop(Block *currentBlock, TR_J9ByteCode bc, - uint32_t bcIndex, Block *destinationBlock, TR_ResolvedMethod *feMethod, +void +TR_J9EstimateCodeSize::setupLastTreeTop(TR::Block *currentBlock, TR_J9ByteCode bc, + uint32_t bcIndex, TR::Block *destinationBlock, TR_ResolvedMethod *feMethod, TR::Compilation *comp) { TR::Node *node = TR::Node::createOnStack(NULL, convertBytecodeToIL(bc), 0); @@ -418,7 +345,6 @@ TR_J9EstimateCodeSize::isInExceptionRange(TR_ResolvedMethod * feMethod, return false; } -#undef Block static bool cameFromArchetypeSpecimen(TR_ResolvedMethod *method) { diff --git a/runtime/compiler/optimizer/J9EstimateCodeSize.hpp b/runtime/compiler/optimizer/J9EstimateCodeSize.hpp index 97d49ef2f09..af0b85a27be 100644 --- a/runtime/compiler/optimizer/J9EstimateCodeSize.hpp +++ b/runtime/compiler/optimizer/J9EstimateCodeSize.hpp @@ -46,7 +46,7 @@ class TR_J9EstimateCodeSize : public TR_EstimateCodeSize TR_J9EstimateCodeSize() : TR_EstimateCodeSize(), _optimisticSize(0), _lastCallBlockFrequency(-1) { } int32_t getOptimisticSize() { return _optimisticSize; } - + /** \brief * The inliner weight adjustment factor used for java/lang/String* compression related methods. */ @@ -76,9 +76,16 @@ class TR_J9EstimateCodeSize : public TR_EstimateCodeSize */ static bool adjustEstimateForStringCompression(TR_ResolvedMethod* method, int32_t& value, float factor); + static TR::Block *getBlock(TR::Compilation *comp, TR::Block * * blocks, TR_ResolvedMethod *feMethod, int32_t i, TR::CFG & cfg); + + static void setupNode(TR::Node *node, uint32_t bcIndex, TR_ResolvedMethod *feMethod, TR::Compilation *comp); + static void setupLastTreeTop(TR::Block *currentBlock, TR_J9ByteCode bc, + uint32_t bcIndex, TR::Block *destinationBlock, TR_ResolvedMethod *feMethod, + TR::Compilation *comp); + protected: bool estimateCodeSize(TR_CallTarget *, TR_CallStack * , bool recurseDown = true); - + /** \brief * Generates a CFG for the calltarget->_calleeMethod. * @@ -126,5 +133,80 @@ class TR_J9EstimateCodeSize : public TR_EstimateCodeSize int32_t _optimisticSize; // size if we assume we are doing a partial inline }; +#define NUM_PREV_BC 5 +class TR_prevArgs +{ + public: + TR_prevArgs() { for (int32_t i = 0 ; i < NUM_PREV_BC ; i++ ) { _prevBC[i] = J9BCunknown ; } } + + void printIndexes(TR::Compilation *comp) + { + for (int32_t i = 0 ; i < NUM_PREV_BC ; i++) + { + if(comp->getDebug()) + traceMsg(comp,"_prevBC[%d] = %s\n" ,i,((TR_J9VM*)(comp->fej9()))->getByteCodeName(_prevBC[i])); + } + } + + void updateArg(TR_J9ByteCode bc ) + { + for(int32_t i=NUM_PREV_BC-2 ; i>=0 ; i-- ) + { + _prevBC[i+1] = _prevBC[i]; + } + _prevBC[0] = bc; + } + + bool isArgAtIndexReceiverObject (int32_t index) + { + if ( index < NUM_PREV_BC && _prevBC[index] == J9BCaload0) + { + return true; + } + else + return false; + } + + int32_t getNumPrevConstArgs(int32_t numparms) + { + int32_t count=0; + + for(int32_t i=0 ; i < NUM_PREV_BC && i < numparms ; i++) + { + switch (_prevBC[i]) + { + case J9BCaconstnull: + case J9BCiconstm1: + case J9BCiconst0: + case J9BCiconst1: + case J9BCiconst2: + case J9BCiconst3: + case J9BCiconst4: + case J9BCiconst5: + case J9BClconst0: + case J9BClconst1: + case J9BCfconst0: + case J9BCfconst1: + case J9BCfconst2: + case J9BCdconst0: + case J9BCdconst1: + case J9BCldc: case J9BCldcw: case J9BCldc2lw: case J9BCldc2dw: + case J9BCbipush: case J9BCsipush: + count++; + break; + default: + break; + } + } + return count; + } + + + protected: + TR_J9ByteCode _prevBC[NUM_PREV_BC]; +}; + + + #endif