From 2fb42c45297d604089da7ee7a2ad75a09c2abbb1 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Mon, 21 Oct 2019 18:09:39 -0400 Subject: [PATCH] Add interpreter emulator for ECS in inliner This change adds the infrastructure to emulate the interpreter execution during estimate code size of target in inliner. Also the loop looking for callsites in ECS is refactored and moved to `findAndCreateCallsitesFromBytecodes`. ECS will be changed to call this function in a following commit. Signed-off-by: Yi Zhang --- runtime/compiler/build/files/common.mk | 1 + runtime/compiler/optimizer/CMakeLists.txt | 1 + .../compiler/optimizer/EstimateCodeSize.cpp | 8 - .../compiler/optimizer/EstimateCodeSize.hpp | 8 +- .../optimizer/InterpreterEmulator.cpp | 850 ++++++++++++++++++ .../optimizer/InterpreterEmulator.hpp | 298 ++++++ .../compiler/optimizer/J9EstimateCodeSize.cpp | 92 +- .../compiler/optimizer/J9EstimateCodeSize.hpp | 86 +- 8 files changed, 1248 insertions(+), 96 deletions(-) create mode 100644 runtime/compiler/optimizer/InterpreterEmulator.cpp create mode 100644 runtime/compiler/optimizer/InterpreterEmulator.hpp 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