blob: a4ebdd10949e360c92005e706b8c9c5a81eaf692 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Intel, Mikhail Y. Fursov
*/
#include "PMFAction.h"
#include "optpass.h"
#include "inliner.h"
#include "LoopTree.h"
#include "Dominator.h"
#include "StlPriorityQueue.h"
#include "VMMagic.h"
#include "FlowGraph.h"
namespace Jitrino {
class HelperConfig {
public:
HelperConfig(VM_RT_SUPPORT id) : helperId(id), doInlining(true), hotnessPercentToInline(0) {}
VM_RT_SUPPORT helperId;
bool doInlining;
U_32 hotnessPercentToInline;
};
struct HelperInlinerFlags {
HelperInlinerFlags(MemoryManager& mm)
: inlinerPipelineName(NULL), pragmaInlineType(NULL), opcodeToHelperMapping(mm), helperConfigs(mm){}
const char* inlinerPipelineName;
Type* pragmaInlineType;
StlMap<Opcode, VM_RT_SUPPORT> opcodeToHelperMapping;
StlMap<VM_RT_SUPPORT, HelperConfig*> helperConfigs;
};
class HelperInlinerAction: public Action {
public:
HelperInlinerAction() : flags(Jitrino::getGlobalMM()) {}
void init();
void registerHelper(Opcode opcode, VM_RT_SUPPORT helperId);
HelperInlinerFlags& getFlags() {return flags;}
protected:
HelperInlinerFlags flags;
};
DEFINE_SESSION_ACTION_WITH_ACTION(HelperInlinerSession, HelperInlinerAction, inline_helpers, "VM helpers inlining");
void HelperInlinerAction::init() {
flags.inlinerPipelineName = getStringArg("pipeline", "inliner_pipeline");
registerHelper(Op_NewObj, VM_RT_NEW_RESOLVED_USING_VTABLE_AND_SIZE);
registerHelper(Op_NewArray, VM_RT_NEW_VECTOR_USING_VTABLE);
registerHelper(Op_TauMonitorEnter, VM_RT_MONITOR_ENTER);
registerHelper(Op_TauMonitorExit, VM_RT_MONITOR_EXIT);
registerHelper(Op_TauStRef, VM_RT_GC_HEAP_WRITE_REF);
registerHelper(Op_TauLdIntfcVTableAddr, VM_RT_GET_INTERFACE_VTABLE_VER0);
registerHelper(Op_TauCheckCast, VM_RT_CHECKCAST);
registerHelper(Op_TauInstanceOf, VM_RT_INSTANCEOF);
registerHelper(Op_IdentHC, VM_RT_GET_IDENTITY_HASHCODE);
}
void HelperInlinerAction::registerHelper(Opcode opcode, VM_RT_SUPPORT helperId) {
MemoryManager& globalMM = getJITInstanceContext().getGlobalMemoryManager();
assert(flags.opcodeToHelperMapping.find(opcode)==flags.opcodeToHelperMapping.end());
flags.opcodeToHelperMapping[opcode] = helperId;
if (flags.helperConfigs.find(helperId)== flags.helperConfigs.end()) {
std::string helperName = CompilationInterface::getRuntimeHelperName(helperId);
HelperConfig* h = new (globalMM) HelperConfig(helperId);
h->doInlining = getBoolArg(helperName.c_str(), false);
h->hotnessPercentToInline = getIntArg((helperName + "_hotnessPercent").c_str(), 0);
flags.helperConfigs[helperId] = h;
}
}
class HelperInliner {
public:
HelperInliner(HelperInlinerSession* _sessionAction, MemoryManager& tmpMM, CompilationContext* _cc, Inst* _inst,
U_32 _hotness, MethodDesc* _helperMethod, VM_RT_SUPPORT _helperId)
: flags(((HelperInlinerAction*)_sessionAction->getAction())->getFlags()), localMM(tmpMM),
cc(_cc), inst(_inst), session(_sessionAction), method(_helperMethod), helperId(_helperId)
{
hotness=_hotness;
irm = cc->getHIRManager();
instFactory = &irm->getInstFactory();
opndManager = &irm->getOpndManager();
typeManager = &irm->getTypeManager();
cfg = &irm->getFlowGraph();
}
~HelperInliner(){};
void run();
U_32 hotness;
static MethodDesc* findHelperMethod(CompilationInterface* ci, VM_RT_SUPPORT helperId);
protected:
void inlineVMHelper(MethodCallInst* call);
void finalizeCall(MethodCallInst* call);
void fixInlineInfo(MethodCallInst* callInst);
HelperInlinerFlags& flags;
MemoryManager& localMM;
CompilationContext* cc;
Inst* inst;
HelperInlinerSession* session;
MethodDesc* method;
VM_RT_SUPPORT helperId;
//cache these values for convenience of use
IRManager* irm;
InstFactory* instFactory;
OpndManager* opndManager;
TypeManager* typeManager;
ControlFlowGraph* cfg;
};
class HelperInlinerCompare {
public:
bool operator()(const HelperInliner* hi1, const HelperInliner* hi2) { return hi1->hotness < hi2->hotness; }
};
void HelperInlinerSession::_run(IRManager& irm) {
CompilationContext* cc = getCompilationContext();
MemoryManager tmpMM("Inline VM helpers");
HelperInlinerAction* action = (HelperInlinerAction*)getAction();
HelperInlinerFlags& flags = action->getFlags();
if (flags.pragmaInlineType== NULL) {
// Avoid class resolution during compilation. VMMagic package should be loaded & resolved at start up.
flags.pragmaInlineType= cc->getVMCompilationInterface()->findClassUsingBootstrapClassloader(PRAGMA_INLINE_TYPE_NAME);
if (flags.pragmaInlineType == NULL) {
Log::out()<<"Helpers inline pass failed! class not found: "<<PRAGMA_INLINE_TYPE_NAME<<std::endl;
return;
}
}
//finding all helper calls
ControlFlowGraph& fg = irm.getFlowGraph();
double entryExecCount = fg.hasEdgeProfile() ? fg.getEntryNode()->getExecCount(): 1;
U_32 maxNodeCount = irm.getOptimizerFlags().hir_node_threshold;
StlPriorityQueue<HelperInliner*, StlVector<HelperInliner*>, HelperInlinerCompare> *helperInlineCandidates =
new (tmpMM) StlPriorityQueue<HelperInliner*, StlVector<HelperInliner*>, HelperInlinerCompare>(tmpMM);
const StlMap<Opcode, VM_RT_SUPPORT>& opcodeToHelper = flags.opcodeToHelperMapping;
const StlMap<VM_RT_SUPPORT, HelperConfig*>& configs = flags.helperConfigs;
const Nodes& nodes = fg.getNodesPostOrder();//process checking only reachable nodes.
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
double execCount = node->getExecCount();
assert (execCount >= 0);
U_32 nodePercent = fg.hasEdgeProfile() ? (U_32)(execCount*100/entryExecCount) : 0;
if (node->isBlockNode()) {
for (Inst* inst = (Inst*)node->getFirstInst(); inst!=NULL; inst = inst->getNextInst()) {
Opcode opcode = inst->getOpcode();
StlMap<Opcode, VM_RT_SUPPORT>::const_iterator o2h = opcodeToHelper.find(opcode);
if (o2h == opcodeToHelper.end()) {
continue;
}
VM_RT_SUPPORT helperId = o2h->second;
StlMap<VM_RT_SUPPORT, HelperConfig*>::const_iterator iconf = configs.find(helperId);
if (iconf == configs.end()) {
continue;
}
HelperConfig* config = iconf->second;
if (!config->doInlining || nodePercent < config->hotnessPercentToInline) {
continue;
}
MethodDesc* md = HelperInliner::findHelperMethod(cc->getVMCompilationInterface(), helperId);
if (md == NULL) {
continue;
}
HelperInliner* inliner = new (tmpMM) HelperInliner(this, tmpMM, cc, inst, nodePercent, md, helperId);
helperInlineCandidates->push(inliner);
}
}
}
//running all inliners
while(!helperInlineCandidates->empty() && (fg.getNodeCount() < maxNodeCount)) {
HelperInliner* inliner = helperInlineCandidates->top();
inliner->run();
helperInlineCandidates->pop();
}
}
void HelperInliner::run() {
if (Log::isEnabled()) {
Log::out() << "Processing inst:"; inst->print(Log::out()); Log::out()<<std::endl;
}
assert(method);
//Convert all inst params into helper params
U_32 numHelperArgs = method->getNumParams();
U_32 numInstArgs = inst->getNumSrcOperands();
Opnd** helperArgs = new (irm->getMemoryManager()) Opnd*[numHelperArgs];
#ifdef _DEBUG
std::fill(helperArgs, helperArgs + numHelperArgs, (Opnd*)NULL);
#endif
U_32 currentHelperArg = 0;
if (inst->isType()) {
Type* type = inst->asTypeInst()->getTypeInfo();
assert(type->isNamedType());
Opnd* typeOpnd = opndManager->createSsaTmpOpnd(typeManager->getUnmanagedPtrType(typeManager->getInt8Type()));
Inst* ldconst = instFactory->makeLdConst(typeOpnd, (POINTER_SIZE_SINT)type->asNamedType()->getVMTypeHandle());
ldconst->insertBefore(inst);
helperArgs[currentHelperArg] = typeOpnd;
currentHelperArg++;
} else if (inst->isToken()) {
TokenInst* tokenInst = inst->asTokenInst();
MethodDesc* methDesc = tokenInst->getEnclosingMethod();
U_32 cpIndex = tokenInst->getToken();
Opnd* classHandleOpnd = opndManager->createSsaTmpOpnd(typeManager->getUnmanagedPtrType(typeManager->getIntPtrType()));
Opnd* cpIndexOpnd = opndManager->createSsaTmpOpnd(typeManager->getUInt32Type());
Inst* ldMethDesc = instFactory->makeLdConst(classHandleOpnd, (POINTER_SIZE_SINT)methDesc->getParentHandle());
Inst* ldCpIndex = instFactory->makeLdConst(cpIndexOpnd, (I_32)cpIndex);
ldMethDesc->insertBefore(inst);
helperArgs[currentHelperArg] = classHandleOpnd;
currentHelperArg++;
ldCpIndex->insertBefore(inst);
helperArgs[currentHelperArg] = cpIndexOpnd;
currentHelperArg++;
}
for (U_32 i = 0; i < numInstArgs; i++) {
Opnd* instArg = inst->getSrc(i);
if (instArg->getType()->tag == Type::Tau) {
continue;
}
assert(currentHelperArg < numHelperArgs);
Type* helperArgType = method->getParamType(currentHelperArg);
Type* instArgType = instArg->getType();
assert(instArgType->isNumeric() == helperArgType->isNumeric());
bool needObjToMagicConversion = (instArgType->isObject() || instArgType->isManagedPtr())
&& helperArgType->isNamedType() && VMMagicUtils::isVMMagicClass(helperArgType->asNamedType()->getName());
Opnd* helperArg = instArg;
if (needObjToMagicConversion) {
Type* dstType = typeManager->getUnmanagedPtrType(typeManager->getInt8Type()); //TODO: use convertVMMagicType2HIR here from translator
helperArg = opndManager->createSsaTmpOpnd(dstType);
Modifier mod = Modifier(Overflow_None)|Modifier(Exception_Never)|Modifier(Strict_No);
instFactory->makeConvUnmanaged(mod, dstType->tag, helperArg, instArg)->insertBefore(inst);
}
helperArgs[currentHelperArg] = helperArg;
currentHelperArg++;
}
assert(helperArgs[numHelperArgs-1]!=NULL);
//Prepare res opnd
Opnd* instResOpnd = inst->getDst();
Type* helperRetType = method->getReturnType();
Type* instResType = instResOpnd->getType();
bool resIsTau = instResType && Type::isTau(instResType->tag);
if (resIsTau) {
assert(helperRetType->isVoid());
instResType = NULL;
instResOpnd = opndManager->getNullOpnd();
}
bool needResConv = instResType && instResType->isObject() && helperRetType->isObject() && VMMagicUtils::isVMMagicClass(helperRetType->asObjectType()->getName());
Opnd* helperResOpnd = !needResConv ? instResOpnd : opndManager->createSsaTmpOpnd(typeManager->getUnmanagedPtrType(typeManager->getInt8Type()));
//Make a call inst
Opnd* tauSafeOpnd = opndManager->createSsaTmpOpnd(typeManager->getTauType());
instFactory->makeTauSafe(tauSafeOpnd)->insertBefore(inst);
MethodCallInst* call = instFactory->makeDirectCall(helperResOpnd, tauSafeOpnd, tauSafeOpnd, numHelperArgs, helperArgs, method)->asMethodCallInst();
assert(inst->getBCOffset()!=ILLEGAL_BC_MAPPING_VALUE);
call->setBCOffset(inst->getBCOffset());
call->insertBefore(inst);
inst->unlink();
finalizeCall(call); //make call last inst in a block
if (needResConv || resIsTau) {
//convert address type to managed object type
Edge* fallEdge = call->getNode()->getUnconditionalEdge();
assert(fallEdge && fallEdge->getTargetNode()->isBlockNode());
Node* fallNode = fallEdge->getTargetNode();
if (fallNode->getInDegree()>1) {
fallNode = irm->getFlowGraph().spliceBlockOnEdge(fallEdge, instFactory->makeLabel());
}
if (needResConv) {
Modifier mod = Modifier(Overflow_None)|Modifier(Exception_Never)|Modifier(Strict_No);
fallNode->prependInst(instFactory->makeConvUnmanaged(mod, Type::Object, instResOpnd, helperResOpnd));
} else {
assert(resIsTau);
Opnd* tauSafeOpnd2 = opndManager->createSsaTmpOpnd(typeManager->getTauType());
Inst* makeTau = instFactory->makeTauSafe(tauSafeOpnd2);
fallNode->prependInst(makeTau);
instFactory->makeCopy(inst->getDst(), tauSafeOpnd2)->insertAfter(makeTau);
}
}
//Inline the call
inlineVMHelper(call);
}
MethodDesc* HelperInliner::findHelperMethod(CompilationInterface* ci, VM_RT_SUPPORT helperId)
{
MethodDesc* md = ci->getMagicHelper(helperId);
if (md == NULL) {
if (Log::isEnabled()) Log::out()<<"WARN: helper's method is not resolved:"<<CompilationInterface::getRuntimeHelperName(helperId)<<std::endl;
return NULL;
}
Class_Handle ch = md->getParentHandle();
if (!VMInterface::isInitialized(ch)) {
if (Log::isEnabled()) Log::out()<<"WARN: class is not initialized:"<<VMInterface::getTypeName(ch)<<std::endl;
return NULL;
}
return md;
}
void HelperInliner::inlineVMHelper(MethodCallInst* call) {
if (Log::isEnabled()) {
Log::out()<<"Inlining VMHelper:";call->print(Log::out());Log::out()<<std::endl;
}
Inliner inliner(session, localMM, *irm, false, false, flags.inlinerPipelineName);
inliner.runInliner(call);
}
void HelperInliner::finalizeCall(MethodCallInst* callInst) {
//if call is not last inst -> make it last inst
if (callInst != callInst->getNode()->getLastInst()) {
cfg->splitNodeAtInstruction(callInst, true, true, instFactory->makeLabel());
}
//every call must have exception edge -> add it
if (callInst->getNode()->getExceptionEdge() == NULL) {
Node* dispatchNode = cfg->getUnwindNode();
if (dispatchNode==NULL) {
dispatchNode = cfg->createDispatchNode(instFactory->makeLabel());
cfg->setUnwindNode(dispatchNode);
cfg->addEdge(dispatchNode, cfg->getExitNode());
}
cfg->addEdge(callInst->getNode(), dispatchNode);
//fix inline info for this edge
fixInlineInfo(callInst);
}
}
typedef StlVector<MethodMarkerInst*> Markers;
static bool prepareMethodMarkersStack(Inst* stopInst, StlVector<bool>& flags, Markers& result, bool forward) {
Node* node = stopInst->getNode();
assert(flags[node->getId()]==false);
flags[node->getId()] = true;
const Edges& edges = node->getEdges(forward);
bool res = forward ? node->isExitNode() : ((Inst*)node->getFirstInst())->isMethodEntry();
for (Edges::const_iterator ite = edges.begin(), ende = edges.end(); ite!=ende; ++ite) {
Edge* e = *ite;
Node* nextNode = e->getNode(forward);
if (flags[nextNode->getId()]) {
continue;
}
res = prepareMethodMarkersStack((Inst*)(forward?nextNode->getFirstInst():nextNode->getLastInst()), flags, result, forward);
if (res) {
break;
}
}
if (!res) {
return false;
}
if (Log::isEnabled()) {
Log::out()<<"Path element :"; FlowGraph::printLabel(Log::out(), node); Log::out()<<std::endl;
}
//process insts in the current block: cache all non-paired markers, avoid caching paired insts
//processing is done in reverse order for 'forward' flag -> from the last inst up to the first when forward='true'
Inst* inst = (Inst*)(forward ? node->getLastInst() : node->getFirstInst());
for (;;inst = forward?inst->getPrevInst():inst->getNextInst()) {
if (inst->isMethodMarker()) {
MethodMarkerInst* markerInst = inst->asMethodMarkerInst();
MethodMarkerInst* pairInst = result.empty() ? (MethodMarkerInst*)NULL :*result.rbegin();
MethodDesc* pairDesc = pairInst ? pairInst->getMethodDesc() : NULL;
if (markerInst->getMethodDesc() == pairDesc && markerInst->isMethodEntryMarker() != pairInst->isMethodEntryMarker()) {
if( Log::isEnabled()) {
Log::out()<<"POP: "; markerInst->print(Log::out()); Log::out()<<std::endl;
}
result.pop_back();
} else {
if( Log::isEnabled()) {
Log::out()<<"PUSH: "; markerInst->print(Log::out());Log::out()<<std::endl;
}
assert((forward && markerInst->isMethodExitMarker()) || (!forward && markerInst->isMethodEntryMarker()));
result.push_back(markerInst);
}
}
if (inst == stopInst) {
break;
}
}
return true;
}
void HelperInliner::fixInlineInfo(MethodCallInst* callInst) {
//the idea of algorithms: find all non-paired entry-markers on dispatch path
// and create new dispatch node with a list of exit-markers
if (Log::isEnabled()) {
Log::out()<<"Fixing inline info for inst:"; inst->print(Log::out()); Log::out()<<std::endl;
}
Edge* dispatchEdge = callInst->getNode()->getExceptionEdge();
assert(dispatchEdge);
Node* dispatchNode = dispatchEdge->getTargetNode();
Markers instsToEntry(localMM);
Markers instsToExit(localMM);
StlVector<bool> flags(localMM);
flags.resize(cfg->getMaxNodeId());
if (Log::isEnabled()) {
Log::out()<<"fixInlineInfo: calculating path to entry:"<<std::endl;
}
std::fill(flags.begin(), flags.end(), false);
prepareMethodMarkersStack(callInst, flags, instsToEntry, false);
// here instsToEntry contains entry markers in topological order: 1, 2, 3
if (Log::isEnabled()) {
Log::out()<<"fixInlineInfo: calculating path to exit:"<<std::endl;
}
std::fill(flags.begin(), flags.end(), false);
prepareMethodMarkersStack((Inst*)dispatchNode->getFirstInst(), flags, instsToExit, true);
// here instsToExit contains exit markers in postorder order: 3, 2, 1
std::reverse(instsToExit.begin(), instsToExit.end());
// here instsToExit contains exit markers in postorder order: 1, 2, 3
assert(instsToEntry.size() >= instsToExit.size());
if (instsToEntry.size() > instsToExit.size()) {
if (Log::isEnabled()) {
Log::out()<<"Insts to entry:"<<instsToEntry.size()<<" insts to exit:"<<instsToExit.size()<<std::endl;
}
Node* newDispatchNode = cfg->createDispatchNode(instFactory->makeLabel());
cfg->replaceEdgeTarget(dispatchEdge, newDispatchNode);
cfg->addEdge(newDispatchNode, dispatchNode);
size_t entrySize = instsToEntry.size();
size_t exitSize = instsToExit.size();
for(size_t i = 0; i < entrySize; i++) {
if (i>=exitSize) {
MethodMarkerInst* start = instsToEntry[i];
assert(start->isMethodEntryMarker());
MethodMarkerInst* end = (MethodMarkerInst*)instFactory->makeMethodMarker(MethodMarkerInst::Exit, start->getMethodDesc());
if (Log::isEnabled()) {
Log::out()<<"Creating pair for "; start->print(Log::out()); Log::out()<<" -> "; end->print(Log::out());Log::out()<<std::endl;
}
instsToExit.push_back(end);
newDispatchNode->prependInst(end);
}
}
}
#ifdef _DEBUG
assert(instsToEntry.size() == instsToExit.size());
size_t size = instsToEntry.size();
for(size_t i = 0; i < size; i++) {
MethodMarkerInst* start = instsToEntry[i];
MethodMarkerInst* end = instsToExit[i];
assert(start->isMethodEntryMarker());
assert(end->isMethodExitMarker());
assert(start->getMethodDesc() == end->getMethodDesc());
}
#endif
}
}//namespace