From 8414472f87a006170ddee15e28f0fda760ecdaac 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. Co-authored-by: Mingwei Li Signed-off-by: Cijie Xia --- compiler/control/OMROptions.cpp | 4 + compiler/control/OMROptions.hpp | 12 +- compiler/optimizer/BenefitInliner.cpp | 286 +++++++++++++++++ compiler/optimizer/BenefitInliner.hpp | 100 ++++++ compiler/optimizer/CMakeLists.txt | 7 +- .../abstractinterpreter/AbsOpArray.cpp | 16 +- .../abstractinterpreter/AbsOpArray.hpp | 8 +- .../abstractinterpreter/AbsOpStack.cpp | 10 +- .../abstractinterpreter/AbsOpStack.hpp | 16 +- .../abstractinterpreter/AbsValue.cpp | 18 +- .../abstractinterpreter/AbsValue.hpp | 12 +- .../abstractinterpreter/AbsVisitor.hpp | 43 +++ .../optimizer/abstractinterpreter/IDT.cpp | 2 +- .../optimizer/abstractinterpreter/IDT.hpp | 2 +- .../abstractinterpreter/IDTBuilder.hpp | 44 +++ .../optimizer/abstractinterpreter/IDTNode.cpp | 2 +- .../optimizer/abstractinterpreter/IDTNode.hpp | 2 +- .../InliningMethodSummary.cpp | 2 +- .../InliningMethodSummary.hpp | 2 +- .../abstractinterpreter/InliningProposal.cpp | 296 ++++++++++++++++++ .../abstractinterpreter/InliningProposal.hpp | 139 ++++++++ .../abstractinterpreter/OMRIDTBuilder.cpp | 246 +++++++++++++++ .../abstractinterpreter/OMRIDTBuilder.hpp | 141 +++++++++ 23 files changed, 1356 insertions(+), 54 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..390a08a4881 --- /dev/null +++ b/compiler/optimizer/BenefitInliner.cpp @@ -0,0 +1,286 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#include "optimizer/BenefitInliner.hpp" +#include "optimizer/abstractinterpreter/IDTBuilder.hpp" +#include "il/Node.hpp" +#include "il/Node_inlines.hpp" +#include + + +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 get enough budget to inline all options + if (_inliningDependencyTree->getTotalCost() <= _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; + } + + /** + * An implementation of knapsack packing algorithm (a modified dynamic programming algorithm) + * + * This algorithm is described in following patent: + * https://patents.google.com/patent/US10055210B2/en + * and this is an implementation of the algorithm on the flowchart FIG.9. + * + * The following comments label which part they belong to the algorithm on the flowchart FIG.9. + */ + _inliningDependencyTree->flattenIDT(); + + const uint32_t idtSize = _inliningDependencyTree->getNumNodes(); + const uint32_t budget = _budget; + + //initialize InliningProposal Table (idtSize x budget+1) + TR::InliningProposalTable table(idtSize, budget + 1, comp()->trMemory()->currentStackRegion()); + + // prepare preorder of inlining options + TR::IDTPriorityQueue pQueue(_inliningDependencyTree, comp()->trMemory()->currentStackRegion()); + for (uint32_t row = 0; row < idtSize; row ++) + { + for (uint32_t col = 1; col <= budget; col ++) + { + TR::InliningProposal currentSet(comp()->trMemory()->currentStackRegion(), _inliningDependencyTree); // [] + TR::IDTNode* currentNode = pQueue.get(row); + + currentSet.addNode(currentNode); //[ currentNode ] + + uint32_t rowOffset = 1; + + // check if proposal is valid + while (!currentNode->isRoot() + && !table.getByOffset(row, rowOffset, col, currentSet.getCost())->isNodeInProposal(currentNode->getParent())) + { + // if no, add proposal predecessor to proposal + currentSet.addNode(currentNode->getParent()); + currentNode = currentNode->getParent(); + } + + // check if intersects base or if is invalid solution + while ( currentSet.intersects(table.getByOffset(row, rowOffset, col, currentSet.getCost())) + || ( !(currentNode->getParent() && table.getByOffset(row, rowOffset, col, currentSet.getCost())->isNodeInProposal(currentNode->getParent()) ) + && !table.getByOffset(row, rowOffset, col, currentSet.getCost())->isEmpty() + )) + { + // if yes, consider prior base at same budget + rowOffset++; + } + + TR::InliningProposal* newProposal = new (comp()->trMemory()->currentStackRegion()) TR::InliningProposal(comp()->trMemory()->currentStackRegion(), _inliningDependencyTree); + newProposal->merge(table.getByOffset(row, rowOffset, col, currentSet.getCost()), ¤tSet); + + // check if cost less than budget and if previous proposal is better + if (newProposal->getCost() <= col && newProposal->getBenefit() > table.getByOffset(row, 1, col, 0)->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.getByOffset(row, 1, col, 0)); + } + } + + // read solution from table at last row and max budget + TR::InliningProposal* result = new (region()) TR::InliningProposal(region(), _inliningDependencyTree); + result->merge(result, table.getByOffset(idtSize, 1, budget, 0)); + + 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; + + //set _nextIDTNodeToInlineInto because we expect to enter inlineCallTargets() recursively + _nextIDTNodeToInlineInto = childToInline; + + bool success = analyzeCallSite(callStack, tt, parent, node, childToInline->getCallTarget()); + + _nextIDTNodeToInlineInto = idtNode; + + if (success) + { + inlineCount++; + 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..e050f69061f --- /dev/null +++ b/compiler/optimizer/BenefitInliner.hpp @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#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), + _region(comp()->region()), + _budget(getInliningBudget(comp()->getMethodSymbol())), + _inliningDependencyTree(NULL), + _nextIDTNodeToInlineInto(NULL) + {} + + + 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 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); + + uint32_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/abstractinterpreter/AbsOpArray.cpp b/compiler/optimizer/abstractinterpreter/AbsOpArray.cpp index 9f7a306c5b9..ff083a23f0e 100644 --- a/compiler/optimizer/abstractinterpreter/AbsOpArray.cpp +++ b/compiler/optimizer/abstractinterpreter/AbsOpArray.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -40,19 +40,19 @@ void TR::AbsOpArray::merge(const TR::AbsOpArray* other, TR::Region& region) TR::AbsValue *selfValue = at(i); TR::AbsValue *otherValue = other->at(i); - if (!selfValue && !otherValue) + if (!selfValue && !otherValue) { continue; } - else if (selfValue && otherValue) + else if (selfValue && otherValue) { TR::AbsValue* mergedVal = selfValue->merge(otherValue); set(i, mergedVal); - } - else if (selfValue) + } + else if (selfValue) { set(i, selfValue); - } + } else { set(i, otherValue->clone(region)); @@ -69,7 +69,7 @@ void TR::AbsOpArray::set(uint32_t index, TR::AbsValue *value) TR::AbsValue* TR::AbsOpArray::at(uint32_t index) const { TR_ASSERT_FATAL(index < size(), "Index out of range! Max array size: %d, Index: %d\n", size(), index); - return _container[index]; + return _container[index]; } void TR::AbsOpArray::print(TR::Compilation* comp) const @@ -80,7 +80,7 @@ void TR::AbsOpArray::print(TR::Compilation* comp) const traceMsg(comp, "A[%d] = ", i); if (!at(i)) traceMsg(comp, "Uninitialized"); - else + else at(i)->print(comp); traceMsg(comp, "\n"); diff --git a/compiler/optimizer/abstractinterpreter/AbsOpArray.hpp b/compiler/optimizer/abstractinterpreter/AbsOpArray.hpp index 309722b7846..be28237b970 100644 --- a/compiler/optimizer/abstractinterpreter/AbsOpArray.hpp +++ b/compiler/optimizer/abstractinterpreter/AbsOpArray.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -41,7 +41,7 @@ class AbsOpArray /** * @brief Clone the operand array - * + * * @param region The memory region where the cloned operand array should be allocated. * @return the cloned operand array */ @@ -49,13 +49,13 @@ class AbsOpArray /** * @brief Perform an in-place merge with another operand array. - * The merge operation does not modify the state of another state + * The merge operation does not modify the state of another state * or store any references of abstract values from another state to be merged with * * @param other The operand array to be merged with. */ void merge(const TR::AbsOpArray* other, TR::Region& region); - + /** * @brief Get the abstract value at index i. * diff --git a/compiler/optimizer/abstractinterpreter/AbsOpStack.cpp b/compiler/optimizer/abstractinterpreter/AbsOpStack.cpp index 00eed4a1bf8..b66084969b6 100644 --- a/compiler/optimizer/abstractinterpreter/AbsOpStack.cpp +++ b/compiler/optimizer/abstractinterpreter/AbsOpStack.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -55,7 +55,7 @@ void TR::AbsOpStack::merge(const TR::AbsOpStack* other, TR::Region& region) void TR::AbsOpStack::print(TR::Compilation* comp) const { traceMsg(comp, "Contents of Abstract Operand Stack:\n"); - + const size_t stackSize = size(); if (stackSize == 0) @@ -63,16 +63,16 @@ void TR::AbsOpStack::print(TR::Compilation* comp) const traceMsg(comp, "\n\n"); return; } - + traceMsg(comp, "\n"); - for (size_t i = 0; i < stackSize; i++) + for (size_t i = 0; i < stackSize; i++) { TR::AbsValue *value = _container[stackSize - i -1 ]; traceMsg(comp, "S[%d] = ", stackSize - i - 1); if (value) value->print(comp); - else + else traceMsg(comp, "Uninitialized"); traceMsg(comp, "\n"); } diff --git a/compiler/optimizer/abstractinterpreter/AbsOpStack.hpp b/compiler/optimizer/abstractinterpreter/AbsOpStack.hpp index 1baeb08cafb..e78cd502cc9 100644 --- a/compiler/optimizer/abstractinterpreter/AbsOpStack.hpp +++ b/compiler/optimizer/abstractinterpreter/AbsOpStack.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -37,10 +37,10 @@ class AbsOpStack explicit AbsOpStack(TR::Region& region) : _container(region) {} - + /** * @brief Clone an operand stack - * + * * @param region The memory region where the cloned operand stack should be allocated. * @return the cloned operand stack */ @@ -48,7 +48,7 @@ class AbsOpStack /** * @brief Perform an in-place merge another operand stack. - * The merge operation does not modify the state of another state + * The merge operation does not modify the state of another state * or store any references of abstract values from another state to be merged with * * @param other the operand stack to be merged with @@ -65,7 +65,7 @@ class AbsOpStack /** * @brief Peek the top value on the operand stack (stack top; not lattice top). * @note the stack must not be empty - * + * * @return the abstract value */ TR::AbsValue* peek() const { TR_ASSERT_FATAL(size() > 0, "Peek an empty stack!"); return _container.back(); } @@ -73,18 +73,18 @@ class AbsOpStack /** * @brief Get and pop a value off of the operand stack. * @note the stack must not be empty. - * + * * @return the abstract value */ TR::AbsValue* pop(); bool empty() const { return _container.empty(); } size_t size() const { return _container.size(); } - + void print(TR::Compilation* comp) const; private: - TR::vector _container; + TR::vector _container; }; } diff --git a/compiler/optimizer/abstractinterpreter/AbsValue.cpp b/compiler/optimizer/abstractinterpreter/AbsValue.cpp index b44e39f345b..0f22293f8dc 100644 --- a/compiler/optimizer/abstractinterpreter/AbsValue.cpp +++ b/compiler/optimizer/abstractinterpreter/AbsValue.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -23,7 +23,7 @@ TR::AbsValue* TR::AbsVPValue::clone(TR::Region& region) const { - TR::AbsVPValue* copy = new (region) TR::AbsVPValue(_vp, _constraint, _dataType, _paramPos); + TR::AbsVPValue* copy = new (region) TR::AbsVPValue(_vp, _constraint, _dataType, _paramPos); return copy; } @@ -31,11 +31,11 @@ TR::AbsValue* TR::AbsVPValue::merge(const TR::AbsValue *other) { if (other == NULL) return this; - - if (_paramPos != other->getParameterPosition()) + + if (_paramPos != other->getParameterPosition()) _paramPos = -1; - if (other->getDataType() != _dataType) + if (other->getDataType() != _dataType) { _dataType = TR::NoType; setToTop(); @@ -45,7 +45,7 @@ TR::AbsValue* TR::AbsVPValue::merge(const TR::AbsValue *other) if (isTop()) return this; - if (other->isTop()) + if (other->isTop()) { setToTop(); return this; @@ -57,16 +57,16 @@ TR::AbsValue* TR::AbsVPValue::merge(const TR::AbsValue *other) return this; } -void TR::AbsVPValue::print(TR::Compilation* comp) const +void TR::AbsVPValue::print(TR::Compilation* comp) const { traceMsg(comp, "AbsValue: Type: %s ", TR::DataType::getName(_dataType)); - + if (_constraint) { traceMsg(comp, "Constraint: "); _constraint->print(_vp); } - else + else { traceMsg(comp, "TOP (unknown) "); } diff --git a/compiler/optimizer/abstractinterpreter/AbsValue.hpp b/compiler/optimizer/abstractinterpreter/AbsValue.hpp index fd0ae04d79e..9e19420a2fd 100644 --- a/compiler/optimizer/abstractinterpreter/AbsValue.hpp +++ b/compiler/optimizer/abstractinterpreter/AbsValue.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * @@ -44,18 +44,18 @@ class AbsValue /** * @brief Clone an abstract value - * + * * @param region The region where the cloned value will be allocated on. * @return the cloned abstract value */ virtual TR::AbsValue* clone(TR::Region& region) const =0; /** - * @brief Merge with another AbsValue. + * @brief Merge with another AbsValue. * @note This is an in-place merge. Self should modify other. - * Also, self should not store any mutable references from other during the merge + * Also, self should not store any mutable references from other during the merge * but immutable references are allowed. - * + * * * @param other Another AbsValue to be merged with * @return Self after the merge @@ -96,7 +96,7 @@ class AbsValue _paramPos(paramPos) {} - int32_t _paramPos; + int32_t _paramPos; TR::DataType _dataType; }; diff --git a/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp b/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp new file mode 100644 index 00000000000..8f6accd9fc0 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/AbsVisitor.hpp @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#ifndef ABS_VISITOR_INCL +#define ABS_VISITOR_INCL + +#include "infra/vector.hpp" + +class TR_CallSite; +namespace TR { class Block; } +namespace TR { class AbsValue; } + +namespace TR { + +/* + * AbsVisitor enables users to define customized callback methods for abstract interpretation. + */ +class AbsVisitor + { + public: + virtual void visitCallSite(TR_CallSite* callSite, TR::Block* callBlock, TR::vector* arguments) = 0; + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/IDT.cpp b/compiler/optimizer/abstractinterpreter/IDT.cpp index 72ea8ad78e5..ddffcc44078 100644 --- a/compiler/optimizer/abstractinterpreter/IDT.cpp +++ b/compiler/optimizer/abstractinterpreter/IDT.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/IDT.hpp b/compiler/optimizer/abstractinterpreter/IDT.hpp index a3d8be0e4dc..6ba154a48c0 100644 --- a/compiler/optimizer/abstractinterpreter/IDT.hpp +++ b/compiler/optimizer/abstractinterpreter/IDT.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp b/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp new file mode 100644 index 00000000000..77f21faab4b --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDTBuilder.hpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + + +#ifndef TR_IDT_BUILDER_INCL +#define TR_IDT_BUILDER_INCL + +#include "optimizer/abstractinterpreter/OMRIDTBuilder.hpp" + +namespace TR { class ResolvedMethodSymbol; } +namespace TR { class Region; } +namespace TR { class Compilation; } +class TR_InlinerBase; + +namespace TR +{ + +class OMR_EXTENSIBLE 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/IDTNode.cpp b/compiler/optimizer/abstractinterpreter/IDTNode.cpp index 07d1cffa717..28da7844e0f 100644 --- a/compiler/optimizer/abstractinterpreter/IDTNode.cpp +++ b/compiler/optimizer/abstractinterpreter/IDTNode.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/IDTNode.hpp b/compiler/optimizer/abstractinterpreter/IDTNode.hpp index 19d866f2408..cb74d3fb4e2 100644 --- a/compiler/optimizer/abstractinterpreter/IDTNode.hpp +++ b/compiler/optimizer/abstractinterpreter/IDTNode.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp index a80d2ce7824..1030d73a70b 100644 --- a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp +++ b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp index 208aff1ce97..68d101a0812 100644 --- a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp +++ b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp @@ -3,7 +3,7 @@ * * 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 + * 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. * diff --git a/compiler/optimizer/abstractinterpreter/InliningProposal.cpp b/compiler/optimizer/abstractinterpreter/InliningProposal.cpp new file mode 100644 index 00000000000..c5888466f5e --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningProposal.cpp @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#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" +#include "infra/String.hpp" + +TR::InliningProposal::InliningProposal(TR::Region& region, TR::IDT *idt): + _region(region), + _nodes(NULL), // Lazy Initialization of BitVector + _cost(0), + _benefit(0), + _idt(idt), + _frozen(false) + { + } + +TR::InliningProposal::InliningProposal(const InliningProposal &proposal, TR::Region& region): + _region(region), + _cost(proposal._cost), + _benefit(proposal._benefit), + _idt(proposal._idt), + _frozen(false) + { + if (proposal._nodes) + { + _nodes = new (region) TR_BitVector(proposal._nodes->getHighestBitPosition(), region); + *_nodes = *proposal._nodes; + } + else + _nodes = NULL; + } + +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; + + TR_VerboseLog::CriticalSection vlogLock(verboseInlining); + + if (!_nodes) + { + if (traceBIProposal) + traceMsg(comp, "Inlining Proposal is NULL\n"); + if (verboseInlining) + TR_VerboseLog::writeLine(TR_Vlog_BI, "%s", "Inlining Proposal is NULL\n"); + return; + } + + const uint32_t numMethodsToInline = _nodes->elementCount()-1; + + TR_ASSERT_FATAL(_idt, "Must have an IDT"); + + TR::StringBuf line(comp->trMemory()->currentStackRegion()); + line.appendf("#Proposal: %d methods inlined into %s, cost: %d", numMethodsToInline, _idt->getRoot()->getName(comp->trMemory()), getCost()); + + if (traceBIProposal) + traceMsg(comp, "%s\n", line.text()); + if (verboseInlining) + TR_VerboseLog::writeLine(TR_Vlog_BI, "%s", line.text()); + + 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 + { + line.clear(); + line.appendf("#Proposal: #%d : #%d %s @%d -> bcsz=%d target %s, benefit = %f, cost = %d, budget = %d", + currentNode->getGlobalIndex(), + currentNode->getParentGlobalIndex(), + _nodes->isSet(index + 1) ? "INLINED" : "NOT inlined", + currentNode->getByteCodeIndex(), + currentNode->getByteCodeSize(), + currentNode->getName(comp->trMemory()), + currentNode->getBenefit(), + currentNode->getCost(), + currentNode->getBudget() + ); + + if (traceBIProposal) + traceMsg(comp, "%s\n", line.text()); + if (verboseInlining) + TR_VerboseLog::writeLine(TR_Vlog_BI, "%s", line.text()); + } + + //process children + const uint32_t numChildren = currentNode->getNumChildren(); + + for (uint32_t i = 0; i < numChildren; i++) + idtNodeQueue.push_back(currentNode->getChild(i)); + + } + if (traceBIProposal) + traceMsg(comp, "\n"); + } + + +void TR::InliningProposal::addNode(TR::IDTNode *node) + { + TR_ASSERT_FATAL(!_frozen, "TR::InliningProposal::addNode proposal is frozen, cannot be mutated"); + 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); + _cost += node->getCost(); + _benefit += node->getBenefit(); + } + } + +void TR::InliningProposal::ensureBitVectorInitialized() + { + TR_ASSERT_FATAL(!_frozen, "TR::InliningProposal::ensureBitVectorInitialized proposal is frozen, cannot be mutated"); + if (!_nodes) + _nodes = new (_region) TR_BitVector(_region); + } + +bool TR::InliningProposal::isNodeInProposal(TR::IDTNode* node) + { + if (_nodes == NULL) + return false; + if (_nodes->isEmpty()) + return false; + + const int32_t idx = node->getGlobalIndex() + 1; + + return _nodes->isSet(idx); + } + +void TR::InliningProposal::merge(TR::InliningProposal *a, TR::InliningProposal *b) + { + TR_ASSERT_FATAL(!_frozen, "TR::InliningProposal::merge proposal is frozen, cannot be mutated"); + if (a->_nodes && b->_nodes) + { + TR_BitVector temp(*a->_nodes); + temp |= *b->_nodes; + ensureBitVectorInitialized(); + *_nodes = temp; + } + else if (a->_nodes) + { + ensureBitVectorInitialized(); + *_nodes = *a->_nodes; + } + else if (b->_nodes) + { + ensureBitVectorInitialized(); + *_nodes = *b->_nodes; + } + else if (_nodes) // this need to be cleared out if both a->_nodes and b->_nodes are null + { + _nodes->empty(); + } + + _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](); + } + + _emptyProposal = new (region) TR::InliningProposal(region, NULL); + _emptyProposal->setFrozen(); + } + +TR::InliningProposal* TR::InliningProposalTable::get(uint32_t row, uint32_t col) + { + TR_ASSERT_FATAL(row < _rows, "TR::InliningProposalTable::get Invalid row index"); + TR_ASSERT_FATAL(col < _cols, "TR::InliningProposalTable::get Invalid col index"); + return _table[row][col] ? _table[row][col] : getEmptyProposal(); + } + +TR::InliningProposal* TR::InliningProposalTable::getByOffset(uint32_t row, uint32_t rowOffset, uint32_t col, uint32_t colOffset) + { + InliningProposal* proposal = NULL; + + // inlinerPacking() may request negative indices + if (rowOffset > row || colOffset > col) + proposal = getEmptyProposal(); + else + proposal = this->get(row - rowOffset, col - colOffset); + + return proposal; + } + +void TR::InliningProposalTable::set(uint32_t row, uint32_t col, TR::InliningProposal* proposal) + { + TR_ASSERT_FATAL(proposal, "TR::InliningProposalTable::set proposal is NULL"); + TR_ASSERT_FATAL(row < _rows, "TR::InliningProposalTable::set Invalid row index" ); + TR_ASSERT_FATAL(col < _cols, "TR::InliningProposalTable::set Invalid col index" ); + + _table[row][col] = proposal; + proposal->setFrozen(); + } diff --git a/compiler/optimizer/abstractinterpreter/InliningProposal.hpp b/compiler/optimizer/abstractinterpreter/InliningProposal.hpp new file mode 100644 index 00000000000..cb3dc9269a4 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningProposal.hpp @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#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 merge(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); + + /** + * @brief Set the proposal as frozen. A frozen proposal cannot be mutated. + */ + void setFrozen() { _frozen = true; } + bool isFrozen() { return _frozen; } + + private: + + void computeCostAndBenefit(); + void ensureBitVectorInitialized(); + + TR::Region& _region; + TR_BitVector *_nodes; + uint32_t _cost; + double _benefit; + TR::IDT *_idt; + bool _frozen; +}; + + +class InliningProposalTable + { + public: + + InliningProposalTable(uint32_t rows, uint32_t cols, TR::Region& region); + TR::InliningProposal* get(uint32_t row, uint32_t col); + + /** + * @brief Get inlining proposal by subtracting offsets from row and column indices. Since the subtracting + * result may be negative, this method can be used to avoid casting indices from `uint32_t` to `int32_t` + * or any potential overflow. + * + * @param row the row index + * @param rowOffset the row offset + * @param col the column index + * @param colOffset the column offset + * + * @return The `TR::InliningProposal*` at `InliningProposalTable[row - rowOffset][col - colOffset]`. + * Or an empty `TR::InliningProposal*`, if the given offset is greater than the given row or column indices. + */ + TR::InliningProposal* getByOffset(uint32_t row, uint32_t rowOffset, uint32_t col, uint32_t colOffset); + void set(uint32_t row, uint32_t col, TR::InliningProposal* proposal); + + TR::Region& region() { return _region; } + + TR::InliningProposal* getEmptyProposal() { return _emptyProposal; } + + private: + + uint32_t _rows; + uint32_t _cols; + TR::Region& _region; + TR::InliningProposal ***_table; + TR::InliningProposal* _emptyProposal; + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp new file mode 100644 index 00000000000..d780707aa5c --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp @@ -0,0 +1,246 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#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) : + _idt(NULL), + _rootSymbol(symbol), + _rootBudget(budget), + _region(region), + _comp(comp), + _inliner(inliner) + {} + +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; + bcInfo.setInvalidCallerIndex(); + bcInfo.setInvalidByteCodeIndex(); + bcInfo.setDoNotProfile(true); + + //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, _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 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); + + // Abstract interpretation will identify and find callsites thus they will be added to the IDT + OMR::IDTBuilder::Visitor visitor(self(), node, nextCallStack); + + self()->performAbstractInterpretation(node, visitor, arguments); + + // 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); + } + } + +void OMR::IDTBuilder::addNodesToIDT(TR::IDTNode*parent, 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())); + + 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; + } + + if (parent->getRootCallRatio() * callRatio * callTarget->_frequencyAdjustment < COLD_ROOT_CALL_RATIO) + 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 + + // 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; + } + + // The actual symbol for the callTarget->_calleeMethod. + TR::ResolvedMethodSymbol* calleeMethodSymbol = TR::ResolvedMethodSymbol::create(comp()->trHeapMemory(), callTarget->_calleeMethod, comp()); + + 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, child->getBudget(), callStack); + + comp()->decInlineDepth(true); + } + } + +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, static_cast(i)); + } + + return staticBenefit; + } + +void OMR::IDTBuilder::Visitor::visitCallSite(TR_CallSite* callSite, 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, callSite, callRatio, arguments, _callStack); + } diff --git a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp new file mode 100644 index 00000000000..01fbcbb8eb5 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.hpp @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2020 + * + * 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] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#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 "optimizer/Inliner.hpp" +#include "optimizer/abstractinterpreter/AbsVisitor.hpp" +#include "optimizer/abstractinterpreter/IDTNode.hpp" +#include "infra/vector.hpp" +#include + +namespace TR { class Compilation; } +namespace TR { class IDT; } +namespace TR { class IDTBuilder; } + +namespace OMR +{ + +class OMR_EXTENSIBLE IDTBuilder + { + 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: + class Visitor : public TR::AbsVisitor + { + public: + Visitor(TR::IDTBuilder* idtBuilder, TR::IDTNode* idtNode, TR_CallStack* callStack) : + _idtBuilder(idtBuilder), + _idtNode(idtNode), + _callStack(callStack) + {} + + virtual void visitCallSite(TR_CallSite* callSite, TR::Block* callBlock, TR::vector* arguments); + + private: + TR::IDTBuilder* _idtBuilder; + TR::IDTNode* _idtNode; + TR_CallStack* _callStack; + }; + + 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. + */ + void performAbstractInterpretation(TR::IDTNode* node, OMR::IDTBuilder::Visitor& visitor, TR::vector* arguments) { TR_UNIMPLEMENTED(); } + + /** + * @param node the node to build a sub IDT for + * @param arguments the arguments passed from caller method + * @param budget the budget for the sub IDT + * @param callStack the call stack + */ + void buildIDT2(TR::IDTNode* node, TR::vector* arguments, int32_t budget, TR_CallStack* callStack); + + /** + * @brief add IDTNode(s) to the IDT + * + * @param parent the parent node to add children for + * @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 + */ + void addNodesToIDT(TR::IDTNode* parent, TR_CallSite* callSite, float callRatio, TR::vector* arguments, TR_CallStack* callStack); + + uint32_t computeStaticBenefit(TR::InliningMethodSummary* summary, TR::vector* arguments); + + TR::IDT* _idt; + TR::ResolvedMethodSymbol* _rootSymbol; + + int32_t _rootBudget; + TR::Region& _region; + TR::Compilation* _comp; + TR_InlinerBase* _inliner; + }; +} + +#endif