From c8c9f9f49b6ac26522a4048648cd7a1e439ca3aa Mon Sep 17 00:00:00 2001 From: Cijie Xia Date: Sat, 22 Aug 2020 13:45:58 -0700 Subject: [PATCH] Add classes for building IDT, doing nested knapsack algorithm and inlining. This is the phase 3/3 of the BenefitInliner contribution. * OMROptions.hpp, OMROptions.hpp: add new options for the BenefitInliner. * AbsVisitor.hpp, IDTBuilder.hpp, OMRIDTBuilder.hpp, OMRIDTBuilder.cpp: new class files for building IDT. * BenefitInliner.hpp, BenefitInliner.cpp, InliningProposal.hpp, InliningProposal.cpp: new class files for doing nested knapsack algorithm and inlining. * Dominators.hpp, Dominators.cpp, StructuralAnalysis.hpp, StructuralAnalysis.cpp: add structural analysis for random CFG. Co-authored-by: Mingwei Li Signed-off-by: Cijie Xia --- compiler/control/OMROptions.cpp | 4 + compiler/control/OMROptions.hpp | 12 +- compiler/optimizer/BenefitInliner.cpp | 282 +++++++++++++++ compiler/optimizer/BenefitInliner.hpp | 100 ++++++ compiler/optimizer/CMakeLists.txt | 7 +- compiler/optimizer/Dominators.cpp | 81 +++++ compiler/optimizer/Dominators.hpp | 2 + compiler/optimizer/StructuralAnalysis.cpp | 46 ++- compiler/optimizer/StructuralAnalysis.hpp | 1 + .../abstractinterpreter/AbsVisitor.hpp | 41 +++ .../abstractinterpreter/IDTBuilder.hpp | 44 +++ .../abstractinterpreter/InliningProposal.cpp | 260 ++++++++++++++ .../abstractinterpreter/InliningProposal.hpp | 116 +++++++ .../abstractinterpreter/OMRIDTBuilder.cpp | 324 ++++++++++++++++++ .../abstractinterpreter/OMRIDTBuilder.hpp | 162 +++++++++ 15 files changed, 1472 insertions(+), 10 deletions(-) create mode 100644 compiler/optimizer/BenefitInliner.cpp create mode 100644 compiler/optimizer/BenefitInliner.hpp create mode 100644 compiler/optimizer/abstractinterpreter/AbsVisitor.hpp create mode 100644 compiler/optimizer/abstractinterpreter/IDTBuilder.hpp create mode 100644 compiler/optimizer/abstractinterpreter/InliningProposal.cpp create mode 100644 compiler/optimizer/abstractinterpreter/InliningProposal.hpp create mode 100644 compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp create mode 100644 compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp diff --git a/compiler/control/OMROptions.cpp b/compiler/control/OMROptions.cpp index 9190177a598..9e338064424 100644 --- a/compiler/control/OMROptions.cpp +++ b/compiler/control/OMROptions.cpp @@ -664,6 +664,7 @@ TR::OptionTable OMR::Options::_jitOptions[] = { {"enableAOTStats", "O\tenable AOT statistics", SET_OPTION_BIT(TR_EnableAOTStats), "F"}, {"enableApplicationThreadYield", "O\tinsert yield points in application threads", SET_OPTION_BIT(TR_EnableAppThreadYield), "F", NOT_IN_SUBSET}, {"enableBasicBlockHoisting", "O\tenable basic block hoisting", TR::Options::enableOptimization, basicBlockHoisting, 0, "P"}, + {"enableBenefitInliner", "O\tenable benefit inliner", SET_OPTION_BIT(TR_EnableBenefitInliner), "F"}, {"enableBlockShuffling", "O\tenable random rearrangement of blocks", TR::Options::enableOptimization, blockShuffling, 0, "P"}, {"enableBranchPreload", "O\tenable return branch preload for each method (for func testing)", SET_OPTION_BIT(TR_EnableBranchPreload), "F"}, {"enableCFGEdgeCounters", "O\tenable CFG edge counters to keep track of taken and non taken branches in compiled code", SET_OPTION_BIT(TR_EnableCFGEdgeCounters), "F"}, @@ -1121,6 +1122,7 @@ TR::OptionTable OMR::Options::_jitOptions[] = { {"tocSize=", "C\tnumber of KiloBytes allocated for table of constants", TR::Options::setStaticNumeric, (intptr_t)&OMR::Options::_tocSizeInKB, 0, "P%d (KB)", NOT_IN_SUBSET}, + {"traceAbstractInterpretation", "L\ttrace benefit inliner abstract interpretation",SET_OPTION_BIT(TR_TraceAbstractInterpretation), "P" }, {"traceAddAndRemoveEdge", "L\ttrace edge addition and removal", SET_OPTION_BIT(TR_TraceAddAndRemoveEdge), "P" }, {"traceAliases", "L\ttrace alias set generation", SET_OPTION_BIT(TR_TraceAliases), "P" }, {"traceAllocationSinking", "L\ttrace allocation sinking", TR::Options::traceOptimization, allocationSinking, 0, "P"}, @@ -1134,6 +1136,8 @@ TR::OptionTable OMR::Options::_jitOptions[] = { {"traceBBVA", "L\ttrace backward bit vector analysis", SET_OPTION_BIT(TR_TraceBBVA), "P" }, {"traceBC", "L\tdump bytecodes", SET_OPTION_BIT(TR_TraceBC), "P" }, {"traceBenefitInlinerIDTGen", "L\ttrace benefit inliner IDT generation", SET_OPTION_BIT(TR_TraceBIIDTGen), "P" }, + {"traceBenefitInlinerProposal", "L\ttrace benefit inliner inlining proposal", SET_OPTION_BIT(TR_TraceBIProposal), "P" }, + {"traceBenefitInlinerSummary", "L\ttrace benefit inliner inlining summary", SET_OPTION_BIT(TR_TraceBISummary), "P" }, {"traceBlockFrequencyGeneration", "L\ttrace block frequency generation", SET_OPTION_BIT(TR_TraceBFGeneration), "P"}, {"traceBlockShuffling", "L\ttrace random rearrangement of blocks", TR::Options::traceOptimization, blockShuffling, 0, "P"}, {"traceBlockSplitter", "L\ttrace block splitter", TR::Options::traceOptimization, blockSplitter, 0, "P"}, diff --git a/compiler/control/OMROptions.hpp b/compiler/control/OMROptions.hpp index 357f7be65c8..87d8edc1dcf 100644 --- a/compiler/control/OMROptions.hpp +++ b/compiler/control/OMROptions.hpp @@ -96,7 +96,7 @@ enum TR_CompilationOptions TR_MimicInterpreterFrameShape = 0x00008000, TR_TraceBC = 0x00010000, - TR_TraceBIIDTGen = 0x00020000, + // Available = 0x00020000, TR_TraceTrees = 0x00040000, TR_TraceCG = 0x00080000, TR_TraceAliases = 0x00100000, @@ -463,11 +463,11 @@ enum TR_CompilationOptions TR_DisableStringBuilderTransformer = 0x00200000 + 12, TR_TraceILGen = 0x00400000 + 12, TR_DisableSharedCacheHints = 0x00800000 + 12, - // Available = 0x01000000 + 12, - // Available = 0x02000000 + 12, - // Available = 0x04000000 + 12, - // Available = 0x08000000 + 12, - // Available = 0x10000000 + 12, + TR_EnableBenefitInliner = 0x01000000 + 12, + TR_TraceAbstractInterpretation = 0x02000000 + 12, + TR_TraceBIIDTGen = 0x04000000 + 12, + TR_TraceBIProposal = 0x08000000 + 12, + TR_TraceBISummary = 0x10000000 + 12, // Available = 0x20000000 + 12, // Available = 0x40000000 + 12, TR_DisableAOTInstanceFieldResolution = 0x80000000 + 12, diff --git a/compiler/optimizer/BenefitInliner.cpp b/compiler/optimizer/BenefitInliner.cpp new file mode 100644 index 00000000000..cd2e20794d6 --- /dev/null +++ b/compiler/optimizer/BenefitInliner.cpp @@ -0,0 +1,282 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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/BenefitInliner.hpp" +#include "optimizer/abstractinterpreter/IDTBuilder.hpp" +#include "il/Node.hpp" +#include "il/Node_inlines.hpp" +#include + + +static bool isWarm(TR::Compilation *comp) + { + return comp->getMethodHotness() >= warm; + } +static bool isHot(TR::Compilation *comp) + { + return comp->getMethodHotness() >= hot; + } +static bool isScorching(TR::Compilation *comp) + { + return ((comp->getMethodHotness() >= scorching) || ((comp->getMethodHotness() >= veryHot) && comp->isProfilingCompilation())) ; + } + + +/** + * Steps of BenefitInliner: + * + * + *1. perform() --> 2. build IDT --> 3. abstract interpretation --> 5. run inliner packing (nested knapsack) --> 6. perform inlining + * | | + * |-- 4. update IDT with inlining summaries -- | + * + * + * Note: Abstract Interpretation is part of the IDT building process. Check the IDTBuilder. + * + */ +int32_t TR::BenefitInlinerWrapper::perform() + { + TR::ResolvedMethodSymbol * sym = comp()->getMethodSymbol(); + + if (sym->mayHaveInlineableCall()) + { + TR::BenefitInliner inliner(optimizer(), this); + inliner.buildInliningDependencyTree(); // IDT + inliner.inlinerPacking(); // nested knapsack + inliner.performInlining(comp()->getMethodSymbol()); + } + + return 1; + } + +void TR::BenefitInliner::buildInliningDependencyTree() + { + TR::IDTBuilder builder(comp()->getMethodSymbol(), _budget, region(), comp(), this); + _inliningDependencyTree = builder.buildIDT(); + + if (comp()->getOption(TR_TraceBIIDTGen)) + _inliningDependencyTree->print(); + + _nextIDTNodeToInlineInto = _inliningDependencyTree->getRoot(); + } + +void TR::BenefitInliner::inlinerPacking() + { + if (_inliningDependencyTree->getTotalCost() <= unsigned(_budget)) + { + _inliningProposal = new (region()) TR::InliningProposal(region(), _inliningDependencyTree); + + TR::deque idtNodeQueue(comp()->trMemory()->currentStackRegion()); + idtNodeQueue.push_back(_inliningDependencyTree->getRoot()); + + while (!idtNodeQueue.empty()) + { + TR::IDTNode* currentNode = idtNodeQueue.front(); + idtNodeQueue.pop_front(); + + _inliningProposal->addNode(currentNode); + + for (uint32_t i = 0; i < currentNode->getNumChildren(); i ++) + { + idtNodeQueue.push_back(currentNode->getChild(i)); + } + } + + return; + } + + _inliningDependencyTree->flattenIDT(); + + const int32_t idtSize = _inliningDependencyTree->getNumNodes(); + const int32_t budget = _budget; + + //initialize InliningProposal Table (idtSize x budget+1) + TR::InliningProposalTable table(idtSize, budget + 1, comp()->trMemory()->currentStackRegion()); + + TR::IDTPriorityQueue preorderPQueue(_inliningDependencyTree, comp()->trMemory()->currentStackRegion()); + for (uint32_t row = 0; row < unsigned(idtSize); row ++) + { + for (uint32_t col = 1; col < unsigned(budget + 1); col ++) + { + TR::InliningProposal currentSet(comp()->trMemory()->currentStackRegion(), _inliningDependencyTree); // [] + TR::IDTNode* currentNode = preorderPQueue.get(row); + + currentSet.addNode(currentNode); //[ currentNode ] + + uint32_t offsetRow = row - 1; + + while (!currentNode->isRoot() + && !table.get(offsetRow, col-currentSet.getCost())->isNodeInProposal(currentNode->getParent())) + { + currentSet.addNode(currentNode->getParent()); + currentNode = currentNode->getParent(); + } + + while ( currentSet.intersects(table.get(offsetRow, col - currentSet.getCost())) + || ( !(currentNode->getParent() && table.get(offsetRow, col - currentSet.getCost())->isNodeInProposal(currentNode->getParent()) ) + && !table.get(offsetRow, col - currentSet.getCost())->isEmpty() + )) + { + offsetRow--; + } + + TR::InliningProposal* newProposal = new (comp()->trMemory()->currentStackRegion()) TR::InliningProposal(comp()->trMemory()->currentStackRegion(), _inliningDependencyTree); + newProposal->unionInPlace(table.get(offsetRow, col - currentSet.getCost()), ¤tSet); + + if (newProposal->getCost() <= col && newProposal->getBenefit() > table.get(row-1, col)->getBenefit()) //only set the new proposal if it fits the budget and has more benefits + table.set(row, col, newProposal); + else + table.set(row, col, table.get(row-1, col)); + } + } + + TR::InliningProposal* result = new (region()) TR::InliningProposal(region(), _inliningDependencyTree); + result->unionInPlace(result, table.get(idtSize-1, budget)); + + if (comp()->getOption(TR_TraceBIProposal)) + { + traceMsg(comp(), "\n#inliner packing:\n"); + result->print(comp()); + } + + _inliningProposal = result; + } + +int32_t TR::BenefitInlinerBase::getInliningBudget(TR::ResolvedMethodSymbol* callerSymbol) + { + const int32_t size = callerSymbol->getResolvedMethod()->maxBytecodeIndex(); + + int32_t callerWeightLimit; + + if (isScorching(comp())) callerWeightLimit = std::max(1500, size * 2); + else if (isHot(comp())) callerWeightLimit = std::max(1500, size + (size >> 2)); + else if (size < 125) callerWeightLimit = 250; + else if (size < 700) callerWeightLimit = std::max(700, size + (size >> 2)); + else callerWeightLimit = size + (size >> 3); + return callerWeightLimit - size; //max size we can inline + } + +bool TR::BenefitInlinerBase::inlineCallTargets(TR::ResolvedMethodSymbol *symbol, TR_CallStack *prevCallStack, TR_InnerPreexistenceInfo *info) + { + if (!_nextIDTNodeToInlineInto) + return false; + + if (comp()->getOption(TR_TraceBIProposal)) + traceMsg(comp(), "#BenefitInliner: inlining into %s\n", _nextIDTNodeToInlineInto->getName(comp()->trMemory())); + + TR_CallStack callStack(comp(), symbol, symbol->getResolvedMethod(), prevCallStack, 1500, true); + + if (info) + callStack._innerPrexInfo = info; + + bool inlined = inlineIntoIDTNode(symbol, &callStack, _nextIDTNodeToInlineInto); + + return inlined; + } + +bool TR::BenefitInlinerBase::inlineIntoIDTNode(TR::ResolvedMethodSymbol *symbol, TR_CallStack *callStack, TR::IDTNode *idtNode) + { + uint32_t inlineCount = 0; + + for (TR::TreeTop * tt = symbol->getFirstTreeTop(); tt; tt = tt->getNextTreeTop()) + { + TR::Node * parent = tt->getNode(); + if (!parent->getNumChildren()) + continue; + + TR::Node * node = parent->getChild(0); + if (!node->getOpCode().isCall()) + continue; + + if (node->getVisitCount() == _visitCount) + continue; + + TR_ByteCodeInfo &bcInfo = node->getByteCodeInfo(); + + //The actual call target to inline + TR::IDTNode *childToInline = idtNode->findChildWithBytecodeIndex(bcInfo.getByteCodeIndex()); + + if (!childToInline) + continue; + + //only inline this call target if it is in inlining proposal + bool shouldInline = _inliningProposal->isNodeInProposal(childToInline); + + if (!shouldInline) + continue; + + _nextIDTNodeToInlineInto = childToInline; + + bool success = analyzeCallSite(callStack, tt, parent, node, childToInline->getCallTarget()); + + _nextIDTNodeToInlineInto = _nextIDTNodeToInlineInto->getParent(); + + if (success) + { + inlineCount++; + +#define MAX_INLINE_COUNT 1000 + if (inlineCount >= MAX_INLINE_COUNT) + { + if (comp()->trace(OMR::inlining)) + traceMsg(comp(), "TR::BenefitInliner: stopping inlining as max inline count of %d reached\n", MAX_INLINE_COUNT); + break; + } + +#undef MAX_INLINE_COUNT + node->setVisitCount(_visitCount); + + } + } + + callStack->commit(); + return inlineCount > 0; + } + +bool TR::BenefitInlinerBase::analyzeCallSite(TR_CallStack * callStack, TR::TreeTop * callNodeTreeTop, TR::Node * parent, TR::Node * callNode, TR_CallTarget *calltargetToInline) + { + + TR::SymbolReference *symRef = callNode->getSymbolReference(); + + TR_CallSite *callsite = TR_CallSite::create(callNodeTreeTop, parent, callNode, + (TR_OpaqueClassBlock*) 0, symRef, (TR_ResolvedMethod*) 0, + comp(), trMemory() , stackAlloc); + + getSymbolAndFindInlineTargets(callStack, callsite); + + if (!callsite->numTargets()) + return false; + + bool success = false; + + for(uint32_t i = 0; i < unsigned(callsite->numTargets()); i++) + { + TR_CallTarget *calltarget = callsite->getTarget(i); + + if (calltarget->_calleeMethod->isSameMethod(calltargetToInline->_calleeMethod) && !calltarget->_alreadyInlined) //we need to inline the exact call target in the IDTNode + { + success = inlineCallTarget(callStack, calltarget, false); + break; + } + } + + return success; + } diff --git a/compiler/optimizer/BenefitInliner.hpp b/compiler/optimizer/BenefitInliner.hpp new file mode 100644 index 00000000000..0dc58f89a43 --- /dev/null +++ b/compiler/optimizer/BenefitInliner.hpp @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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 + *******************************************************************************/ + +#ifndef BENEFIT_INLINER_INCL +#define BENEFIT_INLINER_INCL + +#include "optimizer/Optimization.hpp" +#include "optimizer/OptimizationManager.hpp" +#include "optimizer/Inliner.hpp" +#include "optimizer/abstractinterpreter/IDT.hpp" +#include "optimizer/abstractinterpreter/InliningProposal.hpp" + +namespace TR { +class BenefitInlinerWrapper : public TR::Optimization + { + public: + BenefitInlinerWrapper(TR::OptimizationManager* manager) : TR::Optimization(manager) {}; + + static TR::Optimization* create(TR::OptimizationManager *manager) + { + return new (manager->allocator()) BenefitInlinerWrapper(manager); + } + + virtual int32_t perform(); + + virtual const char * optDetailString() const throw() + { + return "O^O Benefit Inliner: "; + } + }; + +class BenefitInlinerBase : public TR_InlinerBase + { + protected: + BenefitInlinerBase(TR::Optimizer* optimizer, TR::Optimization* optimization) : + TR_InlinerBase(optimizer, optimization), + _inliningProposal(NULL), + _budget(getInliningBudget(comp()->getMethodSymbol())), + _inliningDependencyTree(NULL), + _region(comp()->region()) + {}; + + + virtual bool inlineCallTargets(TR::ResolvedMethodSymbol* symbol, TR_CallStack* callStack, TR_InnerPreexistenceInfo* info); + + bool inlineIntoIDTNode(TR::ResolvedMethodSymbol *symbol, TR_CallStack *callStack, TR::IDTNode *idtNode); + + virtual bool exceedsSizeThreshold(TR_CallSite *callSite, int bytecodeSize, TR::Block * callNodeBlock, TR_ByteCodeInfo & bcInfo, int32_t numLocals=0, TR_ResolvedMethod * caller = 0, TR_ResolvedMethod * calleeResolvedMethod = 0, TR::Node * callNode = 0, bool allConsts = false) + {return false;} + virtual bool supportsMultipleTargetInlining() { return false; }; + virtual bool analyzeCallSite(TR_CallStack * callStack, TR::TreeTop * callNodeTreeTop, TR::Node * parent, TR::Node * callNode, TR_CallTarget *calltargetToInline); + + TR::Region& region() { return _region; }; + + TR::InliningProposal* _inliningProposal; + + protected: + TR::Region _region; + + int32_t getInliningBudget(TR::ResolvedMethodSymbol* callerSymbol); + + int32_t _budget; + + TR::IDT* _inliningDependencyTree; + + TR::IDTNode* _nextIDTNodeToInlineInto; + }; + +class BenefitInliner : public BenefitInlinerBase + { + public: + BenefitInliner(TR::Optimizer* optimizer, TR::Optimization* optimization) : + BenefitInlinerBase(optimizer, optimization) + {}; + + void buildInliningDependencyTree(); + void inlinerPacking(); + }; + +} + +#endif diff --git a/compiler/optimizer/CMakeLists.txt b/compiler/optimizer/CMakeLists.txt index a3377831022..cc7b91ed77a 100644 --- a/compiler/optimizer/CMakeLists.txt +++ b/compiler/optimizer/CMakeLists.txt @@ -103,10 +103,13 @@ compiler_library(optimizer ${CMAKE_CURRENT_LIST_DIR}/ValuePropagationCommon.cpp ${CMAKE_CURRENT_LIST_DIR}/TrivialDeadBlockRemover.cpp ${CMAKE_CURRENT_LIST_DIR}/FEInliner.cpp + ${CMAKE_CURRENT_LIST_DIR}/BenefitInliner.cpp ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/AbsValue.cpp ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/AbsOpStack.cpp ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/AbsOpArray.cpp - ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/InliningMethodSummary.cpp - ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/IDTNode.cpp ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/IDT.cpp + ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/IDTNode.cpp + ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/InliningMethodSummary.cpp + ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/OMRIDTBuilder.cpp + ${CMAKE_CURRENT_LIST_DIR}/abstractinterpreter/InliningProposal.cpp ) diff --git a/compiler/optimizer/Dominators.cpp b/compiler/optimizer/Dominators.cpp index fa307bef1eb..7e0f7d37d09 100644 --- a/compiler/optimizer/Dominators.cpp +++ b/compiler/optimizer/Dominators.cpp @@ -128,6 +128,87 @@ TR_Dominators::TR_Dominators(TR::Compilation *c, bool post) : _info.clear(); } +TR_Dominators::TR_Dominators(TR::Compilation *c, TR::CFG* cfg, bool post) : + _region(c->trMemory()->heapMemoryRegion()), + _compilation(c), + _info(cfg->getNextNodeNumber()+1, BBInfo(_region), _region), + _dfNumbers(cfg->getNextNodeNumber()+1, 0, _region), + _dominators(cfg->getNextNodeNumber()+1, static_cast(NULL), _region) + { + LexicalTimer tlex("TR_Dominators::TR_Dominators", _compilation->phaseTimer()); + + _postDominators = post; + _isValid = true; + _topDfNum = 0; + _visitCount = c->incOrResetVisitCount(); + _trace = comp()->getOption(TR_TraceDominators); + + _cfg = cfg; + _numNodes = cfg->getNumberOfNodes()+1; + + if (trace()) + { + traceMsg(comp(), "Starting %sdominator calculation\n", _postDominators ? "post-" : ""); + traceMsg(comp(), " Number of nodes is %d\n", _numNodes-1); + } + + if (_postDominators) + _dfNumbers[cfg->getStart()->getNumber()] = -1; + else + _dfNumbers[cfg->getEnd()->getNumber()] = -1; + + findDominators(toBlock( _postDominators ? cfg->getEnd() : cfg->getStart() )); + + int32_t i; + for (i = _topDfNum; i > 1; i--) + { + BBInfo &info = getInfo(i); + TR::Block *dominated = info._block; + TR::Block *dominator = getInfo(info._idom)._block; + _dominators[dominated->getNumber()] = dominator; + if (trace()) + traceMsg(comp(), " %sDominator of block_%d is block_%d\n", _postDominators ? "post-" : "", + dominated->getNumber(), dominator->getNumber()); + } + + // The exit block may not be reachable from the entry node. In this case just + // give the exit block the highest depth-first numbering. + // No other blocks should be unreachable. + // + + if (_postDominators) + { + if (_dfNumbers[cfg->getStart()->getNumber()] < 0) + _dfNumbers[cfg->getStart()->getNumber()] = _topDfNum++; + } + else + { + if (_dfNumbers[cfg->getEnd()->getNumber()] < 0) + _dfNumbers[cfg->getEnd()->getNumber()] = _topDfNum++; + } + + // Assert that we've found every node in the cfg. + // + if (_topDfNum != _numNodes-1) + { + _isValid = false; + return; + } + + #if DEBUG + for (block = toBlock(cfg->getFirstNode()); block; block = toBlock(block->getNext())) + { + TR_ASSERT(_dfNumbers[block->getNumber()] >= 0, "Unreachable block in the CFG"); + } + #endif + + if (trace()) + traceMsg(comp(), "End of %sdominator calculation\n", _postDominators ? "post-" : ""); + + // Release no-longer-used data + _info.clear(); + } + TR::Block * TR_Dominators::getDominator(TR::Block *block) { if (block->getNumber() >= _dominators.size()) diff --git a/compiler/optimizer/Dominators.hpp b/compiler/optimizer/Dominators.hpp index 5696d5b08f6..c910eb12d0b 100644 --- a/compiler/optimizer/Dominators.hpp +++ b/compiler/optimizer/Dominators.hpp @@ -53,11 +53,13 @@ class TR_Dominators TR_ALLOC(TR_Memory::Dominators) TR_Dominators(TR::Compilation *, bool post = false); + TR_Dominators(TR::Compilation *, TR::CFG* cfg, bool post = false); TR::Block *getDominator(TR::Block *); int dominates(TR::Block *block, TR::Block *other); TR::Compilation * comp() { return _compilation; } bool trace() { return _trace; } + bool isValid() { return _isValid; } protected: diff --git a/compiler/optimizer/StructuralAnalysis.cpp b/compiler/optimizer/StructuralAnalysis.cpp index 24da1f7d575..d92bfcb4077 100644 --- a/compiler/optimizer/StructuralAnalysis.cpp +++ b/compiler/optimizer/StructuralAnalysis.cpp @@ -168,6 +168,50 @@ void TR_RegionAnalysis::createLeafStructures(TR::CFG *cfg, TR::Region ®ion) } } +/** + * Mainline for performing Region Analysis. + */ +TR_Structure *TR_RegionAnalysis::getRegions(TR::Compilation *comp, TR::CFG* cfg) + { + TR::StackMemoryRegion stackMemoryRegion(*comp->trMemory()); + + // Calculate dominators + // This has the side-effect of renumbering the blocks in depth-first order + // + TR_Dominators dominators = TR_Dominators(comp, cfg); + + if (!dominators.isValid()) + return NULL; + + #if DEBUG + if (debug("verifyDominator")) + { + TR_DominatorVerifier verifyDominator(dominators); + } + #endif + + TR_ASSERT(cfg, "cfg is NULL\n"); + + TR_RegionAnalysis ra(comp, dominators, cfg, stackMemoryRegion); + ra._trace = comp->getOption(TR_TraceSA); + + ra._useNew = !comp->getOption(TR_DisableIterativeSA); + if (ra.trace()) + { + traceMsg(comp, "Blocks before Region Analysis:\n"); + comp->getDebug()->print(comp->getOutFile(), cfg); + } + + ra.createLeafStructures(cfg, stackMemoryRegion); + + // Loop through the node set until there is only one node left - this is the + // root of the control tree. + // + TR_Structure *result = ra.findRegions(stackMemoryRegion); + + return result; + } + /** * Mainline for performing Region Analysis. */ @@ -345,7 +389,6 @@ TR_RegionStructure *TR_RegionAnalysis::findNaturalLoop(StructInfo &node, regionNodes.set(node._nodeIndex); nodesInPath.empty(); bool cyclesFound = false; - int32_t numBackEdges = 0; TR_BitVectorIterator cursor(node._pred); @@ -356,7 +399,6 @@ TR_RegionStructure *TR_RegionAnalysis::findNaturalLoop(StructInfo &node, { // A back-edge has been found. Add its loop nodes to the region // - if (_useNew) { addNaturalLoopNodesIterativeVersion(backEdgeNode, regionNodes, nodesInPath, cyclesFound, node._originalBlock); diff --git a/compiler/optimizer/StructuralAnalysis.hpp b/compiler/optimizer/StructuralAnalysis.hpp index 458e13e9312..4908834d2ed 100644 --- a/compiler/optimizer/StructuralAnalysis.hpp +++ b/compiler/optimizer/StructuralAnalysis.hpp @@ -52,6 +52,7 @@ class TR_RegionAnalysis static TR_Structure *getRegions(TR::Compilation *); static TR_Structure *getRegions(TR::Compilation *, TR::ResolvedMethodSymbol *); + static TR_Structure *getRegions(TR::Compilation *, TR::CFG *); friend class TR_Debug; diff --git a/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp b/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp new file mode 100644 index 00000000000..60030520436 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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 + *******************************************************************************/ + +#ifndef ABS_VISITOR_INCL +#define ABS_VISITOR_INCL + +#include "optimizer/CallInfo.hpp" +#include "infra/vector.hpp" +#include "optimizer/abstractinterpreter/AbsValue.hpp" + +namespace TR { + +/* + * AbsVisitor enables users to define customized callback methods for abstract interpretation. + */ +class AbsVisitor + { + public: + virtual void visitCallSite(TR_CallSite* callSite, int32_t callerIndex, TR::Block* callBlock, TR::vector* arguments) {} + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp b/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp new file mode 100644 index 00000000000..4b795f50f63 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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 + *******************************************************************************/ + + +#ifndef TR_IDT_BUILDER_INCL +#define TR_IDT_BUILDER_INCL + +#include "optimizer/abstractinterpreter/OMRIDTBuilder.hpp" +#include "il/ResolvedMethodSymbol.hpp" +#include "env/Region.hpp" +#include "compile/Compilation.hpp" +#include "optimizer/Inliner.hpp" + + +namespace TR +{ + +class IDTBuilder : public OMR::IDTBuilderConnector + { + public: + IDTBuilder(TR::ResolvedMethodSymbol* symbol, int32_t budget, TR::Region& region, TR::Compilation* comp, TR_InlinerBase* inliner) : + OMR::IDTBuilderConnector(symbol, budget, region, comp, inliner) {} + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/InliningProposal.cpp b/compiler/optimizer/abstractinterpreter/InliningProposal.cpp new file mode 100644 index 00000000000..deb8ee1a259 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningProposal.cpp @@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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/abstractinterpreter/InliningProposal.hpp" +#include "infra/deque.hpp" +#include "compile/Compilation.hpp" +#include "env/Region.hpp" +#include "infra/BitVector.hpp" +#include "env/VerboseLog.hpp" + +TR::InliningProposal::InliningProposal(TR::Region& region, TR::IDT *idt): + _cost(0), + _benefit(0), + _idt(idt), + _region(region), + _nodes(NULL) // Lazy Initialization of BitVector + { + } + +TR::InliningProposal::InliningProposal(const InliningProposal &proposal, TR::Region& region): + _cost(proposal._cost), + _benefit(proposal._benefit), + _idt(proposal._idt), + _region(region) + { + _nodes = new (region) TR_BitVector(proposal._nodes->getHighestBitPosition(), region); + *_nodes = *proposal._nodes; + } + +void TR::InliningProposal::print(TR::Compilation* comp) + { + bool traceBIProposal = comp->getOption(TR_TraceBIProposal); + bool verboseInlining = comp->getOptions()->getVerboseOption(TR_VerboseInlining); + + if (!traceBIProposal && !verboseInlining) //no need to run the following code if neither flag is set + return; + + if (!_nodes) + { + traceMsg(comp, "Inlining Proposal is NULL\n"); + return; + } + + const uint32_t numMethodsToInline = _nodes->elementCount()-1; + + TR_ASSERT_FATAL(_idt, "Must have an IDT"); + + char header[1024]; + sprintf(header,"#Proposal: %d methods inlined into %s, cost: %d", numMethodsToInline, _idt->getRoot()->getName(comp->trMemory()), getCost()); + + if (traceBIProposal) + traceMsg(comp, "%s\n", header); + if (verboseInlining) + TR_VerboseLog::writeLineLocked(TR_Vlog_BI, header); + + TR::deque idtNodeQueue(comp->trMemory()->currentStackRegion()); + idtNodeQueue.push_back(_idt->getRoot()); + + //BFS + while (!idtNodeQueue.empty()) + { + TR::IDTNode* currentNode = idtNodeQueue.front(); + idtNodeQueue.pop_front(); + const int32_t index = currentNode->getGlobalIndex(); + + if (index != -1) //do not print the root node + { + char line[1024]; + sprintf(line, "#Proposal: #%d : #%d %s @%d -> bcsz=%d %s target %s, benefit = %f, cost = %d, budget = %d", + currentNode->getGlobalIndex(), + currentNode->getParentGlobalIndex(), + _nodes->isSet(index + 1) ? "INLINED" : "NOT inlined", + currentNode->getByteCodeIndex(), + currentNode->getByteCodeSize(), + currentNode->getResolvedMethodSymbol()->signature(comp->trMemory()), + currentNode->getName(comp->trMemory()), + currentNode->getBenefit(), + currentNode->getCost(), + currentNode->getBudget() + ); + + if (traceBIProposal) + traceMsg(comp, "%s\n",line); + if (verboseInlining) + TR_VerboseLog::writeLineLocked(TR_Vlog_BI, line); + } + + //process children + const uint32_t numChildren = currentNode->getNumChildren(); + + for (uint32_t i = 0; i < numChildren; i++) + idtNodeQueue.push_back(currentNode->getChild(i)); + + } + + traceMsg(comp, "\n"); + } + + +void TR::InliningProposal::addNode(TR::IDTNode *node) + { + ensureBitVectorInitialized(); + + const int32_t index = node->getGlobalIndex() + 1; + if (_nodes->isSet(index)) + { + return; + } + + _nodes->set(index); + + _cost = 0; + _benefit = 0; + } + +bool TR::InliningProposal::isEmpty() + { + if (!_nodes) + return true; + + return _nodes->isEmpty(); + } + +uint32_t TR::InliningProposal::getCost() + { + if (_cost == 0) + { + computeCostAndBenefit(); + } + + return _cost; + } + +double TR::InliningProposal::getBenefit() + { + if (_benefit == 0) + { + computeCostAndBenefit(); + } + + return _benefit; + } + +void TR::InliningProposal::computeCostAndBenefit() + { + _cost = 0; + _benefit = 0; + + if (!_idt) + return; + + TR_BitVectorIterator bvi(*_nodes); + int32_t idtNodeIndex; + + while (bvi.hasMoreElements()) + { + idtNodeIndex = bvi.getNextElement(); + IDTNode *node = _idt->getNodeByGlobalIndex(idtNodeIndex - 1); + if (node == NULL) + { + continue; + } + _cost += node->getCost(); + _benefit += node->getBenefit(); + } + } + +void TR::InliningProposal::ensureBitVectorInitialized() + { + if (!_nodes) + _nodes = new (_region) TR_BitVector(_region); + } + +bool TR::InliningProposal::isNodeInProposal(TR::IDTNode* node) + { + if (node == NULL) + return false; + if (_nodes == NULL) + return false; + if (_nodes->isEmpty()) + return false; + + const int32_t idx = node->getGlobalIndex() + 1; + + return _nodes->isSet(idx); + } + +void TR::InliningProposal::unionInPlace(TR::InliningProposal *a, TR::InliningProposal *b) + { + ensureBitVectorInitialized(); + a->ensureBitVectorInitialized(); + b->ensureBitVectorInitialized(); + + *_nodes = *a->_nodes; + *_nodes |= *b->_nodes; + _cost = 0; + _benefit = 0; + } + +bool TR::InliningProposal::intersects(TR::InliningProposal* other) + { + if (!_nodes || !other->_nodes) + return false; + + return _nodes->intersects(*other->_nodes); + } + + +TR::InliningProposalTable::InliningProposalTable(uint32_t rows, uint32_t cols, TR::Region& region) : + _rows(rows), + _cols(cols), + _region(region) + { + _table = new (region) InliningProposal**[rows]; + + for (uint32_t i = 0; i < rows; i ++) + { + _table[i] = new (region) InliningProposal*[cols]; + memset(_table[i], 0, sizeof(InliningProposal*)*cols); + } + } + +TR::InliningProposal* TR::InliningProposalTable::get(uint32_t row, uint32_t col) + { + InliningProposal* proposal = NULL; + + if (row <0 || col <0 || row >= _rows || col >= _cols) + proposal = getEmptyProposal(); + else + proposal = _table[row][col] ? _table[row][col] : getEmptyProposal(); + + return proposal; + } + +void TR::InliningProposalTable::set(uint32_t row, uint32_t col, TR::InliningProposal* proposal) + { + TR_ASSERT_FATAL(proposal, "proposal is NULL"); + TR_ASSERT_FATAL(row >=0 && row < _rows, "Invalid row index" ); + TR_ASSERT_FATAL(col >= 0 && col < _cols, "Invalid col index" ); + + _table[row][col] = proposal; + } diff --git a/compiler/optimizer/abstractinterpreter/InliningProposal.hpp b/compiler/optimizer/abstractinterpreter/InliningProposal.hpp new file mode 100644 index 00000000000..13933c7c175 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningProposal.hpp @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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 + *******************************************************************************/ + +#ifndef INLINING_PROPOSAL_INCL +#define INLINING_PROPOSAL_INCL + +#include "env/Region.hpp" +#include "optimizer/abstractinterpreter/IDT.hpp" +#include "optimizer/abstractinterpreter/IDTNode.hpp" +#include "infra/BitVector.hpp" +#include "compile/Compilation.hpp" + +namespace TR { + +/** + *Inlining Proposal records the set of IDTNodes selected to be inlined. + */ +class InliningProposal + { + public: + + InliningProposal(TR::Region& region, TR::IDT *idt); + InliningProposal(const TR::InliningProposal&, TR::Region& region); + + void print(TR::Compilation *comp); + bool isEmpty(); + + uint32_t getCost(); + double getBenefit(); + + /** + * @brief add an IDTNode selected to be inlined to the proposal + * + * @param node the IDTNode + */ + void addNode(TR::IDTNode* node); + + /** + * @brief Check if the node is selected to be inlined + * + * @param node the IDTNode to be checked + * + * @return true if in proposal. false otherwise. + */ + bool isNodeInProposal(TR::IDTNode* node ); + + /** + * @brief Union two proposals. + * + * @param a one proposal + * @param b another proposal + */ + void unionInPlace(TR::InliningProposal *a, TR::InliningProposal* b); + + /** + * @brief Check if self intersects with another proposal. + * + * @param other the proposal + * + * @return true of they intersect. false otherwise. + */ + bool intersects(TR::InliningProposal* other); + + private: + + void computeCostAndBenefit(); + void ensureBitVectorInitialized(); + + TR::Region& _region; + TR_BitVector *_nodes; + uint32_t _cost; + double _benefit; + TR::IDT *_idt; +}; + + +class InliningProposalTable + { + public: + + InliningProposalTable(uint32_t rows, uint32_t cols, TR::Region& region); + TR::InliningProposal* get(uint32_t row, uint32_t col); + void set(uint32_t row, uint32_t col, TR::InliningProposal* proposal); + + TR::Region& region() { return _region; } + + TR::InliningProposal* getEmptyProposal() { return new (region()) TR::InliningProposal(region(), NULL); } + + private: + + uint32_t _rows; + uint32_t _cols; + TR::Region& _region; + TR::InliningProposal ***_table; + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp new file mode 100644 index 00000000000..531d1c9ce78 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp @@ -0,0 +1,324 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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/abstractinterpreter/IDTBuilder.hpp" +#include "optimizer/abstractinterpreter/IDT.hpp" +#include "il/Block.hpp" +#ifdef J9_PROJECT_SPECIFIC +#include "env/j9method.h" +#include "control/RecompilationInfo.hpp" +#endif + +#define COLD_ROOT_CALL_RATIO 0.5 + +OMR::IDTBuilder::IDTBuilder(TR::ResolvedMethodSymbol* symbol, int32_t budget, TR::Region& region, TR::Compilation* comp, TR_InlinerBase* inliner) : + _rootSymbol(symbol), + _rootBudget(budget), + _region(region), + _comp(comp), + _inliner(inliner), + _idt(NULL), + _interpretedMethodMap(InterpretedMethodMapComparator(), InterpretedMethodMapAllocator(region)) + {} + +TR::IDTBuilder* OMR::IDTBuilder::self() + { + return static_cast(this); + } + +TR::IDT* OMR::IDTBuilder::buildIDT() + { + bool traceBIIDTGen = comp()->getOption(TR_TraceBIIDTGen); + + if (traceBIIDTGen) + traceMsg(comp(), "\n+ IDTBuilder: Start building IDT |\n\n"); + + TR_ResolvedMethod* rootMethod = _rootSymbol->getResolvedMethod(); + TR_ByteCodeInfo bcInfo; + + //This is just a fake callsite to make ECS work. + TR_CallSite *rootCallSite = new (region()) TR_CallSite( + rootMethod, + NULL, + NULL, + NULL, + NULL, + rootMethod->containingClass(), + 0, + 0, + rootMethod, + _rootSymbol, + _rootSymbol->getMethodKind() == TR::MethodSymbol::Kinds::Virtual || _rootSymbol->getMethodKind() == TR::MethodSymbol::Kinds::Interface , + _rootSymbol->getMethodKind() == TR::MethodSymbol::Kinds::Interface, + bcInfo, + comp()); + + TR_CallTarget *rootCallTarget = new (region()) TR_CallTarget( + rootCallSite, + _rootSymbol, + rootMethod, + NULL, + rootMethod->containingClass(), + NULL); + + //Initialize IDT + _idt = new (region()) TR::IDT(region(), rootCallTarget, _rootSymbol, _rootBudget, comp()); + + TR::IDTNode* root = _idt->getRoot(); + + //generate the CFG for root call target + TR::CFG* cfg = self()->generateControlFlowGraph(rootCallTarget); + + if (!cfg) //Fail to generate a CFG + return _idt; + + //add the IDT decendants + buildIDT2(root, NULL, -1, _rootBudget, NULL); + + if (traceBIIDTGen) + traceMsg(comp(), "\n+ IDTBuilder: Finish building TR::IDT |\n"); + + return _idt; + } + +void OMR::IDTBuilder::buildIDT2(TR::IDTNode* node, TR::vector* arguments, int32_t callerIndex, int32_t budget, TR_CallStack* callStack) + { + TR::ResolvedMethodSymbol* symbol = node->getResolvedMethodSymbol(); + TR_ResolvedMethod* method = node->getResolvedMethod(); + + TR_CallStack* nextCallStack = new (region()) TR_CallStack(comp(), symbol, method, callStack, budget, true); + + // Check if current method has been interpreted. + // If so, there is no need to run the abstract interpretation on this method since they have the same static properties. + TR::IDTNode* interpretedMethodIDTNode = checkIfMethodIsInterpreted(method); + + if (interpretedMethodIDTNode) + { + // since they are the same method, they should have the same inlining method summary. + node->setInliningMethodSummary(interpretedMethodIDTNode->getInliningMethodSummary()); + uint32_t staticBenefit = computeStaticBenefit(node->getInliningMethodSummary(), arguments); + + node->setStaticBenefit(staticBenefit); + + // Same methods should have same IDT decendents. + // IDT is built in DFS order, at this point, we have all the descendants so it is safe to copy all the descendants. + copyDescendants(interpretedMethodIDTNode, node); + return; + } + else + { + // Abstract interpretation will identify and find callsites thus they will be added to the IDT + TR::IDTBuilderVisitor visitor(self(), node, nextCallStack); + + self()->performAbstractInterpretation(node, visitor, arguments, callerIndex); + + // At this point we have the inlining summary generated by abstract interpretation + // So we can use the summary and the arguments passed from callers to calculate the static benefit. + if (!node->isRoot()) + { + uint32_t staticBenefit = computeStaticBenefit(node->getInliningMethodSummary(), arguments); + node->setStaticBenefit(staticBenefit); + } + + // Save for later use if we encounter any same method. + storeInterpretedMethod(method, node); + } + } + +void OMR::IDTBuilder::copyDescendants(TR::IDTNode* fromNode, TR::IDTNode* toNode) + { + TR_ASSERT_FATAL( + fromNode->getResolvedMethodSymbol()->getResolvedMethod()->getPersistentIdentifier() + == toNode->getResolvedMethodSymbol()->getResolvedMethod()->getPersistentIdentifier(), + "Copying different nodes is not allowed!"); + + for (int32_t i = 0 ; unsigned(i) < fromNode->getNumChildren(); i ++) + { + TR::IDTNode* child = fromNode->getChild(i); + + if (toNode->getBudget() - child->getCost() < 0) + continue; + + if (toNode->getRootCallRatio() * child->getCallRatio() < COLD_ROOT_CALL_RATIO) + continue; + + TR::IDTNode* copiedChild = toNode->addChild( + _idt->getNextGlobalIDTNodeIndex(), + child->getCallTarget(), + child->getResolvedMethodSymbol(), + child->getByteCodeIndex(), + child->getCallRatio(), + _idt->getRegion() + ); + + if (copiedChild) + { + _idt->addCost(copiedChild->getCost()); + _idt->increaseGlobalIDTNodeIndex(); + copiedChild->setInliningMethodSummary(child->getInliningMethodSummary()); + copiedChild->setStaticBenefit(child->getStaticBenefit()); + copyDescendants(child, copiedChild); + } + } + } + +void OMR::IDTBuilder::addNodesToIDT(TR::IDTNode*parent, int32_t callerIndex, TR_CallSite* callSite, float callRatio, TR::vector* arguments, TR_CallStack* callStack) + { + bool traceBIIDTGen = comp()->getOption(TR_TraceBIIDTGen); + + if (callSite == NULL ) + { + if (traceBIIDTGen) + traceMsg(comp(), "Do not have a callsite. Don't add\n"); + return; + } + + if (traceBIIDTGen) + traceMsg(comp(), "+ IDTBuilder: Adding a child Node: %s for TR::IDTNode: %s\n", callSite->signature(comp()->trMemory()), parent->getName(comp()->trMemory())); + + // if (comp()->fej9()->maybeHighlyPolymorphic(comp(), callSite->_callerResolvedMethod, callSite->_cpIndex, callSite->_interfaceMethod, callSite->_receiverClass) && callSite->isInterface()) + // return; + + callSite->findCallSiteTarget(callStack, getInliner()); //Find all call targets + + // eliminate call targets that are not inlinable according to the policy + // thus they won't be added to IDT + getInliner()->applyPolicyToTargets(callStack, callSite); + + if (callSite->numTargets() == 0) + { + if (traceBIIDTGen) + traceMsg(comp(), "Do not have a call target. Don't add\n"); + return; + } + + for (int32_t i = 0 ; i < callSite->numTargets(); i++) + { + TR_CallTarget* callTarget = callSite->getTarget(i); + + int32_t remainingBudget = parent->getBudget() - callTarget->_calleeMethod->maxBytecodeIndex(); + + if (remainingBudget < 0 ) // no budget remains + { + if (traceBIIDTGen) + traceMsg(comp(), "No budget left. Don't add\n"); + continue; + } + + bool isRecursiveCall = callStack->isAnywhereOnTheStack(callTarget->_calleeMethod, 1); + + if (isRecursiveCall) //Stop for recursive call + { + if (traceBIIDTGen) + traceMsg(comp(), "Recursive call. Don't add\n"); + continue; + } + +#ifdef J9_PROJECT_SPECIFIC + // hot and scorching bodies should never be inlined to warm or cooler bodies + if (!callTarget->_calleeMethod->isInterpreted()) + { + TR_PersistentJittedBodyInfo * bodyInfo = ((TR_ResolvedJ9Method*) callTarget->_calleeMethod)->getExistingJittedBodyInfo(); + if (bodyInfo && comp()->getMethodHotness() <= warm && bodyInfo->getHotness() >= hot) + continue; + } +#endif + + // The actual symbol for the callTarget->_calleeMethod. + TR::ResolvedMethodSymbol* calleeMethodSymbol = TR::ResolvedMethodSymbol::create(comp()->trHeapMemory(), callTarget->_calleeMethod, comp()); + + // generate the CFG of this call target and set the block frequencies. + TR::CFG* cfg = self()->generateControlFlowGraph(callTarget); + + if (!cfg) + { + if (traceBIIDTGen) + traceMsg(comp(), "Fail to generate a CFG. Don't add\n"); + continue; + } + + if (parent->getRootCallRatio() * callRatio * callTarget->_frequencyAdjustment < COLD_ROOT_CALL_RATIO) + continue; + + TR::IDTNode* child = parent->addChild( + _idt->getNextGlobalIDTNodeIndex(), + callTarget, + calleeMethodSymbol, + callSite->_byteCodeIndex, + callRatio * callTarget->_frequencyAdjustment, + _idt->getRegion() + ); + + _idt->increaseGlobalIDTNodeIndex(); + _idt->addCost(child->getCost()); + + if (!comp()->incInlineDepth(calleeMethodSymbol, callSite->_bcInfo, callSite->_cpIndex, NULL, !callSite->isIndirectCall(), 0)) + continue; + + // Build the IDT recursively + buildIDT2(child, arguments, callerIndex + 1, child->getBudget(), callStack); + + comp()->decInlineDepth(true); + } + } + +TR::IDTNode* OMR::IDTBuilder::checkIfMethodIsInterpreted(TR_ResolvedMethod* method) + { + TR_OpaqueMethodBlock* persistentIdentifier = method->getPersistentIdentifier(); + + auto iter = _interpretedMethodMap.find(persistentIdentifier); + if (iter == _interpretedMethodMap.end()) //Not interpreted yet + return NULL; + + return iter->second; + } + +void OMR::IDTBuilder::storeInterpretedMethod(TR_ResolvedMethod* method, TR::IDTNode* node) + { + TR_OpaqueMethodBlock* persistentIdentifier = method->getPersistentIdentifier(); + _interpretedMethodMap.insert(std::pair(persistentIdentifier, node)); + } + +uint32_t OMR::IDTBuilder::computeStaticBenefit(TR::InliningMethodSummary* summary, TR::vector* arguments) + { + if (summary == NULL || arguments == NULL) + return 0; + + uint32_t staticBenefit = 0; + + for (size_t i = 0; i < arguments->size(); i ++) + { + TR::AbsValue* arg = arguments->at(i); + staticBenefit += summary->testArgument(arg, i); + } + + return staticBenefit; + } + +void TR::IDTBuilderVisitor::visitCallSite(TR_CallSite* callSite, int32_t callerIndex, TR::Block* callBlock, TR::vector* arguments) + { + float callRatio = (float)callBlock->getFrequency() / (float)_idtNode->getCallTarget()->_cfg->getStart()->asBlock()->getFrequency(); + + if (callBlock->getFrequency() < 6 || callBlock->isCold() || callBlock->isSuperCold()) + return; + + _idtBuilder->addNodesToIDT(_idtNode, callerIndex, callSite, callRatio, arguments, _callStack); + } \ No newline at end of file diff --git a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp new file mode 100644 index 00000000000..307d0514a59 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 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 http://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 + *******************************************************************************/ + +#ifndef OMR_IDT_BUILDER_INCL +#define OMR_IDT_BUILDER_INCL + +/* + * The following #define and typedef must appear before any #includes in this file + */ +#ifndef OMR_IDT_BUILDER_CONNECTOR +#define OMR_IDT_BUILDER_CONNECTOR +namespace OMR { class IDTBuilder; } +namespace OMR { typedef OMR::IDTBuilder IDTBuilderConnector; } +#endif + +#include "il/ResolvedMethodSymbol.hpp" +#include "env/Region.hpp" +#include "compile/Compilation.hpp" +#include "optimizer/Inliner.hpp" +#include "optimizer/abstractinterpreter/AbsVisitor.hpp" +#include "optimizer/abstractinterpreter/IDT.hpp" +#include "optimizer/abstractinterpreter/IDTNode.hpp" +#include "infra/vector.hpp" + +namespace TR { class IDTBuilder; } +namespace TR { class IDTBuilderVisitor; } + +namespace OMR +{ + +class IDTBuilder + { + friend class ::TR::IDTBuilderVisitor; + + public: + + IDTBuilder(TR::ResolvedMethodSymbol* symbol, int32_t budget, TR::Region& region, TR::Compilation* comp, TR_InlinerBase* inliner); + + /** + * @brief building the IDT in DFS order. + * It starts from creating the root IDTNode using the _rootSymbol + * and then builds the IDT recursively. + * It stops when no more call site is found or the budget runs out. + * + * @return the inlining dependency tree + * + */ + TR::IDT* buildIDT(); + + TR::IDTBuilder* self(); + + protected: + + TR::Compilation* comp() { return _comp; }; + TR::Region& region() { return _region; }; + TR_InlinerBase* getInliner() { return _inliner; }; + + /** + * @brief generate the control flow graph of a call target so that the abstract interpretation can use. + * + * @note: This method needs language specific implementation. + * + * @param callTarget the call target to generate CFG for + * @return the control flow graph + */ + TR::CFG* generateControlFlowGraph(TR_CallTarget* callTarget) { TR_UNIMPLEMENTED(); return NULL; } + + /** + * @brief Perform the abstract interpretation on the method in the IDTNode. + * + * @note: This method needs language specific implementation. + * + * @param node the node to be abstract interpreted + * @param visitor the visitor which defines the callback method + * that will be called when visiting a call site during abtract interpretation. + * @param arguments the arguments are the AbsValues passed from the caller method. + * @param callerIndex the caller index + */ + void performAbstractInterpretation(TR::IDTNode* node, TR::IDTBuilderVisitor& visitor, TR::vector* arguments, int32_t callerIndex) { TR_UNIMPLEMENTED(); } + + /** + * @param node the node to build a sub IDT for + * @param arguments the arguments passed from caller method + * @param callerIndex the caller index + * @param budget the budget for the sub IDT + * @param callStack the call stack + */ + void buildIDT2(TR::IDTNode* node, TR::vector* arguments, int32_t callerIndex, int32_t budget, TR_CallStack* callStack); + + /** + * @brief add IDTNode(s) to the IDT + * + * @param parent the parent node to add children for + * @param callerIndex the caller index + * @param callSite the call site + * @param callRatio the call ratio of this callsite + * @param arguments the arguments passed from the caller method. + * @param callStack the call stack + * + * @return void + */ + void addNodesToIDT(TR::IDTNode* parent, int32_t callerIndex, TR_CallSite* callSite, float callRatio, TR::vector* arguments, TR_CallStack* callStack); + + TR::IDTNode* checkIfMethodIsInterpreted(TR_ResolvedMethod* method); + void storeInterpretedMethod(TR_ResolvedMethod* method, TR::IDTNode* node); + + uint32_t computeStaticBenefit(TR::InliningMethodSummary* summary, TR::vector* arguments); + void copyDescendants(TR::IDTNode* fromNode, TR::IDTNode* toNode); + + TR::IDT* _idt; + TR::ResolvedMethodSymbol* _rootSymbol; + + typedef TR::typed_allocator, TR::Region&> InterpretedMethodMapAllocator; + typedef std::less InterpretedMethodMapComparator; + std::map _interpretedMethodMap; + + int32_t _rootBudget; + TR::Region& _region; + TR::Compilation* _comp; + TR_InlinerBase* _inliner; + }; +} + +namespace TR { + +class IDTBuilderVisitor : public TR::AbsVisitor + { + public: + IDTBuilderVisitor(TR::IDTBuilder* idtBuilder, TR::IDTNode* idtNode, TR_CallStack* callStack) : + _idtBuilder(idtBuilder), + _idtNode(idtNode), + _callStack(callStack) + {} + + virtual void visitCallSite(TR_CallSite* callSite, int32_t callerIndex, TR::Block* callBlock, TR::vector* arguments); + + private: + TR::IDTBuilder* _idtBuilder; + TR::IDTNode* _idtNode; + TR_CallStack* _callStack; + }; +} + +#endif