From 80c61ca3c744f1d524b2542960049af01a1c80e9 Mon Sep 17 00:00:00 2001 From: Mingwei Li Date: Thu, 18 May 2023 05:59:59 -0600 Subject: [PATCH] Add classes for building and handling the IDT and InliningMethodSummary for the BenefitInliner. IDT.cpp, IDTNode.cpp, InliningMethodSummary.cpp This is the phase 2/3 of the BenefitInliner contribution. Co-authored-by: Cijie Xia Co-authored-by: dino li Co-authored-by: Qasim Khawaja Co-authored-by: Siva C. Nandipati Signed-off-by: Mingwei Li --- compiler/control/OMROptions.cpp | 1 + compiler/control/OMROptions.hpp | 2 +- compiler/env/VerboseLog.hpp | 1 + compiler/optimizer/CMakeLists.txt | 3 + .../optimizer/abstractinterpreter/IDT.cpp | 174 +++++++++++++++++ .../optimizer/abstractinterpreter/IDT.hpp | 142 ++++++++++++++ .../optimizer/abstractinterpreter/IDTNode.cpp | 174 +++++++++++++++++ .../optimizer/abstractinterpreter/IDTNode.hpp | 142 ++++++++++++++ .../InliningMethodSummary.cpp | 182 ++++++++++++++++++ .../InliningMethodSummary.hpp | 128 ++++++++++++ 10 files changed, 948 insertions(+), 1 deletion(-) create mode 100644 compiler/optimizer/abstractinterpreter/IDT.cpp create mode 100644 compiler/optimizer/abstractinterpreter/IDT.hpp create mode 100644 compiler/optimizer/abstractinterpreter/IDTNode.cpp create mode 100644 compiler/optimizer/abstractinterpreter/IDTNode.hpp create mode 100644 compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp create mode 100644 compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp diff --git a/compiler/control/OMROptions.cpp b/compiler/control/OMROptions.cpp index 2d71ee7a32..902c9e8a11 100644 --- a/compiler/control/OMROptions.cpp +++ b/compiler/control/OMROptions.cpp @@ -1132,6 +1132,7 @@ TR::OptionTable OMR::Options::_jitOptions[] = { {"traceBasicBlockPeepHole", "L\ttrace basic blocks peepHole", TR::Options::traceOptimization, basicBlockPeepHole, 0, "P"}, {"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" }, {"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 a1ec00deab..43a81abc6b 100644 --- a/compiler/control/OMROptions.hpp +++ b/compiler/control/OMROptions.hpp @@ -96,7 +96,7 @@ enum TR_CompilationOptions TR_MimicInterpreterFrameShape = 0x00008000, TR_TraceBC = 0x00010000, - // Available = 0x00020000, + TR_TraceBIIDTGen = 0x00020000, TR_TraceTrees = 0x00040000, TR_TraceCG = 0x00080000, TR_TraceAliases = 0x00100000, diff --git a/compiler/env/VerboseLog.hpp b/compiler/env/VerboseLog.hpp index a2287fd13c..6bf4bb8741 100644 --- a/compiler/env/VerboseLog.hpp +++ b/compiler/env/VerboseLog.hpp @@ -71,6 +71,7 @@ enum TR_VlogTag TR_Vlog_PROFILING, TR_Vlog_JITServer, TR_Vlog_AOTCOMPRESSION, + TR_Vlog_BI, //(benefit inliner) TR_Vlog_FSD, TR_Vlog_VECTOR_API, TR_Vlog_CHECKPOINT_RESTORE, diff --git a/compiler/optimizer/CMakeLists.txt b/compiler/optimizer/CMakeLists.txt index 1a25f64e4d..df180c5f12 100644 --- a/compiler/optimizer/CMakeLists.txt +++ b/compiler/optimizer/CMakeLists.txt @@ -106,4 +106,7 @@ compiler_library(optimizer ${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 ) diff --git a/compiler/optimizer/abstractinterpreter/IDT.cpp b/compiler/optimizer/abstractinterpreter/IDT.cpp new file mode 100644 index 0000000000..72ea8ad78e --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDT.cpp @@ -0,0 +1,174 @@ +/******************************************************************************* + * 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 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] 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/IDT.hpp" +#include "infra/String.hpp" + +TR::IDT::IDT(TR::Region& region, TR_CallTarget* callTarget, TR::ResolvedMethodSymbol* symbol, uint32_t budget, TR::Compilation* comp): + _region(region), + _nextIdx(-1), + _comp(comp), + _root(new (_region) IDTNode(getNextGlobalIDTNodeIndex(), callTarget, symbol, -1, 1, NULL, budget)), + _indices(NULL), + _totalCost(0) + { + increaseGlobalIDTNodeIndex(); + } + +void TR::IDT::print() + { + bool verboseInlining = comp()->getOptions()->getVerboseOption(TR_VerboseInlining); + bool traceBIIDTGen = comp()->getOption(TR_TraceBIIDTGen); + + if (!verboseInlining && !traceBIIDTGen) + return; + const uint32_t candidates = getNumNodes() - 1; + // print header line + TR::StringBuf line(comp()->trMemory()->currentStackRegion()); + line.appendf("#IDT: %d candidate methods inlinable into %s with a budget %d", + candidates, + getRoot()->getName(comp()->trMemory()), + getRoot()->getBudget()); + + TR_VerboseLog::CriticalSection vlogLock(verboseInlining); + if (verboseInlining) + { + TR_VerboseLog::writeLine(TR_Vlog_BI, "%s", line.text()); + } + if (traceBIIDTGen) + traceMsg(comp(), "%s\n", line.text()); + + if (candidates <= 0) + return; + + // print the IDT nodes in BFS + TR::deque idtNodeQueue(comp()->trMemory()->currentStackRegion()); + + idtNodeQueue.push_back(getRoot()); + while (!idtNodeQueue.empty()) + { + TR::IDTNode* currentNode = idtNodeQueue.front(); + idtNodeQueue.pop_front(); + + int32_t index = currentNode->getGlobalIndex(); + + // skip root node + if (index != -1) + { + line.clear(); + line.appendf("#IDT: #%d: #%d inlinable @%d -> bcsz=%d %s target %s, static benefit = %d, benefit = %f, cost = %d, budget = %d, callratio = %f, rootcallratio = %f", + index, + currentNode->getParentGlobalIndex(), + currentNode->getByteCodeIndex(), + currentNode->getByteCodeSize(), + currentNode->getResolvedMethodSymbol()->signature(comp()->trMemory()), + currentNode->getName(comp()->trMemory()), + currentNode->getStaticBenefit(), + currentNode->getBenefit(), + currentNode->getCost(), + currentNode->getBudget(), + currentNode->getCallRatio(), + currentNode->getRootCallRatio() + ); + if (verboseInlining) + TR_VerboseLog::writeLine(TR_Vlog_BI, "%s", line.text()); + + if (traceBIIDTGen) + traceMsg(comp(), "%s\n", line.text()); + } + + // process children + for (uint32_t i = 0; i < currentNode->getNumChildren(); i ++) + idtNodeQueue.push_back(currentNode->getChild(i)); + } + } + +void TR::IDT::flattenIDT() + { + if (_indices != NULL) + return; + + // initialize nodes index array + uint32_t numNodes = getNumNodes(); + _indices = new (_region) TR::IDTNode *[numNodes](); + + // add all the descendents of the root node to the indices array + TR::deque idtNodeQueue(comp()->trMemory()->currentStackRegion()); + idtNodeQueue.push_back(getRoot()); + + while (!idtNodeQueue.empty()) + { + TR::IDTNode* currentNode = idtNodeQueue.front(); + idtNodeQueue.pop_front(); + + const int32_t index = currentNode->getGlobalIndex(); + TR_ASSERT_FATAL(_indices[index + 1] == 0, "Callee index not unique!\n"); + + _indices[index + 1] = currentNode; + + for (uint32_t i = 0; i < currentNode->getNumChildren(); i ++) + { + idtNodeQueue.push_back(currentNode->getChild(i)); + } + } + } + +TR::IDTNode *TR::IDT::getNodeByGlobalIndex(int32_t index) + { + TR_ASSERT_FATAL(_indices, "Call flattenIDT() first"); + TR_ASSERT_FATAL(index < getNextGlobalIDTNodeIndex(), "Index out of range!"); + TR_ASSERT_FATAL(index >= -1, "Index too low!"); + return _indices[index + 1]; + } + +TR::IDTPriorityQueue::IDTPriorityQueue(TR::IDT* idt, TR::Region& region) : + _entries(region), + _idt(idt), + _pQueue(IDTNodeCompare(), IDTNodeVector(region)) + { + _pQueue.push(idt->getRoot()); + } + +TR::IDTNode* TR::IDTPriorityQueue::get(uint32_t index) + { + const size_t entriesSize = _entries.size(); + + const uint32_t idtSize = size(); + TR_ASSERT_FATAL(index < idtSize, "IDTPriorityQueue::get index out of bound!"); + // already in entries + if (entriesSize > index) + return _entries.at(index); + + // not in entries yet. Update entries. + while (_entries.size() <= index) + { + TR::IDTNode *newEntry = _pQueue.top(); + _pQueue.pop(); + + _entries.push_back(newEntry); + for (uint32_t j = 0; j < newEntry->getNumChildren(); j++) + { + _pQueue.push(newEntry->getChild(j)); + } + } + + return _entries.at(index); + } diff --git a/compiler/optimizer/abstractinterpreter/IDT.hpp b/compiler/optimizer/abstractinterpreter/IDT.hpp new file mode 100644 index 0000000000..a3d8be0e4d --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDT.hpp @@ -0,0 +1,142 @@ +/******************************************************************************* + * 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 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] 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 IDT_INCL +#define IDT_INCL + +#include "compile/Compilation.hpp" +#include "optimizer/CallInfo.hpp" +#include "optimizer/abstractinterpreter/IDTNode.hpp" +#include "env/Region.hpp" +#include "env/VerboseLog.hpp" +#include "il/ResolvedMethodSymbol.hpp" +#include + +namespace TR { + +/** + * IDT stands for Inlining Dependency Tree + * It is a structure that holds all candidate methods to be inlined. + * + * The parent-child relationship in the IDT corresponds to the caller-callee relationship. + */ +class IDT + { + public: + IDT(TR::Region& region, TR_CallTarget*, TR::ResolvedMethodSymbol* symbol, uint32_t budget, TR::Compilation* comp); + + TR::IDTNode* getRoot() { return _root; } + + TR::Region& getRegion() { return _region; } + + void addCost(uint32_t cost) { _totalCost += cost; } + uint32_t getTotalCost() { return _totalCost; } + + /** + * @brief Get the total number of nodes in this IDT. + * + * @return the total number of node + */ + uint32_t getNumNodes() { return _nextIdx + 1; } + + /** + * @brief Get the next avaible IDTNode index. + * + * @return the next index + */ + int32_t getNextGlobalIDTNodeIndex() { return _nextIdx; } + + /** + * @brief Increase the next available IDTNode index by 1. + * This should only be called when successfully adding an IDTNode to the IDT + */ + void increaseGlobalIDTNodeIndex() { _nextIdx ++; } + + /** + * @brief Get the IDTNode using index. + * Before using this method for accessing IDTNode, flattenIDT() must be called. + * + * @return the IDT node + */ + TR::IDTNode *getNodeByGlobalIndex(int32_t index); + + /** + * @brief Flatten all the IDTNodes into a list. + */ + void flattenIDT(); + + void print(); + + private: + TR::Compilation* comp() { return _comp; } + + TR::Compilation *_comp; + TR::Region& _region; + int32_t _nextIdx; + uint32_t _totalCost; + TR::IDTNode* _root; + TR::IDTNode** _indices; + }; + +/** + * A topological sort of the IDT in which the lowest-benefit node is listed first and lowest cost used to + * break the tie whenever there is a choice. + * + * This topological sort is to prepare an ordered list of inlining options for the algorithm described + * in following patent: + * https://patents.google.com/patent/US10055210B2/en + * + * This patent describes the topological sort as the following quote: + * "constraints on the order of node consideration, where the constraints serve to ensure every node is + * considered only after all of the node's ancestors are considered, and nodes are included in order of + * increasing cumulative benefit with lowest cumulative cost used to break ties to allow the generation + * of all intermediate solutions at a given cost budget to simplify backtracking in subsequent iterations + * of the algorithm". + */ +class IDTPriorityQueue + { + public: + IDTPriorityQueue(TR::IDT* idt, TR::Region& region); + uint32_t size() { return _idt->getNumNodes(); } + + TR::IDTNode* get(uint32_t index); + + private: + struct IDTNodeCompare + { + bool operator()(TR::IDTNode *left, TR::IDTNode *right) + { + TR_ASSERT_FATAL(left && right, "Comparing against null"); + if (left->getBenefit() == right->getBenefit()) return left->getCost() > right->getCost(); + else return left->getBenefit() > right->getBenefit(); + } + }; + + typedef TR::vector IDTNodeVector; + typedef std::priority_queue IDTNodePriorityQueue; + + TR::IDT* _idt; + IDTNodePriorityQueue _pQueue; + IDTNodeVector _entries; + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/IDTNode.cpp b/compiler/optimizer/abstractinterpreter/IDTNode.cpp new file mode 100644 index 0000000000..07d1cffa71 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDTNode.cpp @@ -0,0 +1,174 @@ +/******************************************************************************* + * 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 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] 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/IDTNode.hpp" + +#define SINGLE_CHILD_BIT 1 + +TR::IDTNode::IDTNode( + int32_t idx, + TR_CallTarget* callTarget, + TR::ResolvedMethodSymbol* symbol, + uint32_t byteCodeIndex, + float callRatio, + IDTNode *parent, + uint32_t budget) : + _idx(idx), + _callTarget(callTarget), + _staticBenefit(0), + _byteCodeIndex(byteCodeIndex), + _symbol(symbol), + _children(NULL), + _parent(parent), + _budget(budget), + _callRatio(callRatio), + _rootCallRatio(parent ? parent->_rootCallRatio * callRatio : 1), + _inliningMethodSummary(NULL) + { + } + +TR::IDTNode* TR::IDTNode::addChild( + int32_t idx, + TR_CallTarget* callTarget, + TR::ResolvedMethodSymbol* symbol, + uint32_t byteCodeIndex, + float callRatio, + TR::Region& region) + { + uint32_t budget = getBudget() - callTarget->_calleeMethod->maxBytecodeIndex(); + + TR::IDTNode* newChild = new (region) TR::IDTNode( + idx, + callTarget, + symbol, + byteCodeIndex, + callRatio, + this, + budget); + + // The case where there is no children + if (getNumChildren() == 0) + { + setOnlyChild(newChild); + return newChild; + } + + // The case where there is 1 child + if (getNumChildren() == 1) + { + TR::IDTNode* onlyChild = getOnlyChild(); + _children = new (region) TR::vector(region); + TR_ASSERT_FATAL(!((uintptr_t)_children & SINGLE_CHILD_BIT), "Misaligned memory address.\n"); + _children->push_back(onlyChild); + } + + _children->push_back(newChild); + return _children->back(); + } + +uint32_t TR::IDTNode::getNumDescendants() + { + const uint32_t numChildren = getNumChildren(); + uint32_t sum = 0; + for (uint32_t i =0; i < numChildren; i ++) + { + sum += 1 + getChild(i)->getNumDescendants(); + } + return sum; + } + +uint32_t TR::IDTNode::getRecursiveCost() + { + const uint32_t numChildren = getNumChildren(); + uint32_t cost = getCost(); + for (uint32_t i = 0; i < numChildren; i ++) + { + IDTNode *child = getChild(i); + cost += child->getRecursiveCost(); + } + + return cost; + } + +uint32_t TR::IDTNode::getNumChildren() + { + if (_children == NULL) + return 0; + + if (getOnlyChild() != NULL) + return 1; + + uint32_t num = static_cast(_children->size()); + return num; + } + +TR::IDTNode* TR::IDTNode::getChild(uint32_t index) + { + const uint32_t numChildren = getNumChildren(); + TR_ASSERT_FATAL(index < numChildren, "Child index out of range!\n"); + + if (index == 0 && numChildren == 1) // only one child + return getOnlyChild(); + + return (*_children)[index]; + } + +double TR::IDTNode::getBenefit() + { + // 1 is used to avoid multiplying with benefit of 0 + // 10 is the equal weight for every benefit + return _rootCallRatio * (1 + _staticBenefit) * 10; + } + +TR::IDTNode* TR::IDTNode::findChildWithBytecodeIndex(uint32_t bcIndex) + { + const uint32_t size = getNumChildren(); + + if (size == 0) + return NULL; + + if (size == 1) + { + TR::IDTNode* onlyChild = getOnlyChild(); + return onlyChild->getByteCodeIndex() == bcIndex ? onlyChild : NULL; + } + + for (uint32_t i = 0; i < size; i ++) + { + if ((*_children)[i]->getByteCodeIndex() == bcIndex) + return (*_children)[i]; + } + + return NULL; + } + +TR::IDTNode* TR::IDTNode::getOnlyChild() + { + if (((uintptr_t)_children) & SINGLE_CHILD_BIT) + return (TR::IDTNode *)((uintptr_t)(_children) & ~SINGLE_CHILD_BIT); + return NULL; + } + +void TR::IDTNode::setOnlyChild(TR::IDTNode* child) + { + TR_ASSERT_FATAL(!((uintptr_t)child & SINGLE_CHILD_BIT), "Misaligned memory address.\n"); + _children = (TR::vector*)((uintptr_t)child | SINGLE_CHILD_BIT); + } diff --git a/compiler/optimizer/abstractinterpreter/IDTNode.hpp b/compiler/optimizer/abstractinterpreter/IDTNode.hpp new file mode 100644 index 0000000000..19d866f240 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/IDTNode.hpp @@ -0,0 +1,142 @@ +/******************************************************************************* + * 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 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] 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 IDT_NODE_INCL +#define IDT_NODE_INCL + +#include "env/Region.hpp" +#include "il/ResolvedMethodSymbol.hpp" +#include "optimizer/CallInfo.hpp" +#include "optimizer/abstractinterpreter/InliningMethodSummary.hpp" + +namespace TR { class IDTNode; } + +namespace TR { + +/** + * IDTNode is the abstract representation of a candidate method containing useful information for inlining. + */ +class IDTNode + { + public: + IDTNode( + int32_t idx, + TR_CallTarget* callTarget, + TR::ResolvedMethodSymbol* symbol, + uint32_t byteCodeIndex, + float callRatio, + TR::IDTNode *parent, + uint32_t budget); + + /** + * @brief Add a child + * + * @param idx the global index of the child to be added + * @param callTarget the call target + * @param symbol the method symbol + * @param byteCodeIndex the call site bytecode index + * @param callRatio call ratio of the method + * @param region the region where the child node will be allocated + * + * @return the newly created node + */ + TR::IDTNode* addChild( + int32_t idx, + TR_CallTarget* callTarget, + TR::ResolvedMethodSymbol* symbol, + uint32_t byteCodeIndex, + float callRatio, + TR::Region& region); + + TR::InliningMethodSummary* getInliningMethodSummary() { return _inliningMethodSummary; } + void setInliningMethodSummary(TR::InliningMethodSummary* inliningMethodSummary) { _inliningMethodSummary = inliningMethodSummary; } + + const char* getName(TR_Memory* mem) { return _symbol->signature(mem); } + + TR::IDTNode *getParent() { return _parent; } + + int32_t getGlobalIndex() { return _idx; } + int32_t getParentGlobalIndex() { return isRoot() ? -2 : getParent()->getGlobalIndex(); } + + double getBenefit(); + + uint32_t getStaticBenefit() { return _staticBenefit; }; + + void setStaticBenefit(uint32_t staticBenefit) { _staticBenefit = staticBenefit; } + + uint32_t getNumDescendants(); + + uint32_t getCost() { return isRoot() ? 0 : getByteCodeSize(); } + uint32_t getRecursiveCost(); + + uint32_t getNumChildren(); + TR::IDTNode *getChild(uint32_t index); + + bool isRoot() { return _parent == NULL; }; + + TR::IDTNode* findChildWithBytecodeIndex(uint32_t bcIndex); + + TR::ResolvedMethodSymbol* getResolvedMethodSymbol() { return _symbol; } + TR_ResolvedMethod* getResolvedMethod() { return _callTarget->_calleeMethod; } + + uint32_t getBudget() { return _budget; }; + + TR_CallTarget *getCallTarget() { return _callTarget; } + + uint32_t getByteCodeIndex() { return _byteCodeIndex; } + uint32_t getByteCodeSize() { return _callTarget->_calleeMethod->maxBytecodeIndex(); } + + float getCallRatio() { return _callRatio; } + double getRootCallRatio() { return _rootCallRatio; } + + private: + TR_CallTarget* _callTarget; + TR::ResolvedMethodSymbol* _symbol; + + TR::IDTNode *_parent; + + int32_t _idx; + uint32_t _byteCodeIndex; + + TR::vector* _children; + uint32_t _staticBenefit; + + uint32_t _budget; + + float _callRatio; + double _rootCallRatio; + + TR::InliningMethodSummary *_inliningMethodSummary; + + /** + * @brief It is common that a large number of IDTNodes have only one child. + * So instead of allocating a deque containing only one element, + * we use the _children as the address of that only child to save memory usage. + * + * @return NULL if there are more than one or no children. The child node if there is only one child. + */ + TR::IDTNode* getOnlyChild(); + + void setOnlyChild(TR::IDTNode* child); + }; +} + +#endif diff --git a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp new file mode 100644 index 0000000000..a80d2ce782 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.cpp @@ -0,0 +1,182 @@ +/******************************************************************************* + * 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 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] 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/InliningMethodSummary.hpp" + +uint32_t TR::InliningMethodSummary::testArgument(TR::AbsValue* arg, uint32_t argPos) + { + if (!arg) + return 0; + + if (arg->isTop()) + return 0; + + if (_optsByArg.size() <= argPos || _optsByArg[argPos] == NULL || _optsByArg[argPos]->size() == 0) + return 0; + + uint32_t benefit = 0; + + for (size_t i = 0; i < _optsByArg[argPos]->size(); i ++) + { + TR::PotentialOptimizationPredicate* predicate = (*_optsByArg[argPos])[i]; + if (predicate->test(arg)) + { + benefit += 1; + } + } + + return benefit; + } + +void TR::InliningMethodSummary::trace(TR::Compilation* comp) + { + traceMsg(comp, "Inlining Method Summary:\n"); + + if (_optsByArg.size() == 0) + { + traceMsg(comp, "EMPTY\n\n"); + return; + } + + for (size_t i = 0; i < _optsByArg.size(); i ++) + { + if (_optsByArg[i] != NULL) + { + for (size_t j = 0; j < _optsByArg[i]->size(); j ++) + { + TR::PotentialOptimizationPredicate* predicate = (*_optsByArg[i])[j]; + + traceMsg(comp, "%s @%d for Argument %d ", predicate->getName(), predicate->getBytecodeIndex(), i); + predicate->trace(comp); + traceMsg(comp, "\n"); + } + } + } + + } + + +void TR::InliningMethodSummary::addPotentialOptimizationByArgument(TR::PotentialOptimizationPredicate* predicate, uint32_t argPos) + { + if (_optsByArg.size() <= argPos) + _optsByArg.resize(argPos + 1); + + if (!_optsByArg[argPos]) + _optsByArg[argPos] = new (region()) PredicateContainer(region()); + + _optsByArg[argPos]->push_back(predicate); + } + +const char* TR::PotentialOptimizationPredicate::getName() + { + switch (_kind) + { + case Kind::BranchFolding: + return "Branch Folding"; + case Kind::CheckCastFolding: + return "CheckCast Folding"; + case Kind::NullCheckFolding: + return "NullCheck Folding"; + case Kind::InstanceOfFolding: + return "InstanceOf Folding"; + default: + TR_ASSERT_FATAL(false, "Unexpected Kind"); + return "Unknown Kind"; + } + } + +bool TR::PotentialOptimizationVPPredicate::holdPartialOrderRelation(TR::VPConstraint* valueConstraint, TR::VPConstraint* testConstraint) + { + if (testConstraint->asIntConstraint()) // partial relation for int constraint + { + if (testConstraint->getLowInt() <= valueConstraint->getLowInt() && testConstraint->getHighInt() >= valueConstraint->getHighInt()) + return true; + else + return false; + } + else if (testConstraint->asClassPresence()) // partial relation for nullness + { + if (testConstraint->isNonNullObject() && valueConstraint->isNonNullObject()) + return true; + else if (testConstraint->isNullObject() && valueConstraint->isNullObject()) + return true; + else + return false; + } + else if (testConstraint->asClassType()) // testing for checkcast + { + TR_ASSERT_FATAL(testConstraint->getClassType()->asResolvedClass(), "testConstraint unexpectedly admits unresolved class type"); + if (valueConstraint->isNullObject()) + return true; // VPClassType always accepts null + + if (valueConstraint->isClassObject() == TR_yes) + return false; // valueConstraint->getClass() doesn't mean the right thing + + TR_OpaqueClassBlock *valueClass = valueConstraint->getClass(); + if (valueClass == NULL) + return false; // unknown type + + TR_YesNoMaybe isSubtype = _vp->fe()->isInstanceOf( + valueClass, testConstraint->getClass(), valueConstraint->isFixedClass(), true); + + return isSubtype == TR_yes; + } + else if (testConstraint->asClass()) // partial relation for instanceof + { + TR_ASSERT_FATAL(testConstraint->isClassObject() != TR_yes, "testConstraint unexpectedly admits class object"); + TR_ASSERT_FATAL(testConstraint->getClass() != NULL, "testConstraint class unexpectedly admits null"); + TR_ASSERT_FATAL(testConstraint->isNonNullObject(), "testConstraint unexpectedly admits null"); + TR_ASSERT_FATAL(testConstraint->getPreexistence() == NULL, "testConstraint has unexpected pre-existence info"); + TR_ASSERT_FATAL(testConstraint->getArrayInfo() == NULL, "testConstraint has unexpected array info"); + TR_ASSERT_FATAL(testConstraint->getObjectLocation() == NULL, "testContraint has an unexpected location"); + if (valueConstraint->isNullObject()) + return true; + + if (valueConstraint->isClassObject() == TR_yes) + return false; // valueConstraint->getClass() doesn't mean the right thing + + TR_OpaqueClassBlock *valueClass = valueConstraint->getClass(); + if (valueClass == NULL) + return false; // unknown type + + TR_YesNoMaybe isInstance = _vp->fe()->isInstanceOf( + valueClass, testConstraint->getClass(), valueConstraint->isFixedClass(), true); + + return (valueConstraint->isNonNullObject() && isInstance == TR_yes) || isInstance == TR_no; + } + + return false; + } + +bool TR::PotentialOptimizationVPPredicate::test(TR::AbsValue *value) + { + if (value->isTop()) + return false; + + TR::AbsVPValue* vpValue = static_cast(value); + return holdPartialOrderRelation(vpValue->getConstraint(), _constraint); + } + +void TR::PotentialOptimizationVPPredicate::trace(TR::Compilation* comp) + { + traceMsg(comp, "Predicate Constraint: "); + _constraint->print(_vp); + } diff --git a/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp new file mode 100644 index 0000000000..208aff1ce9 --- /dev/null +++ b/compiler/optimizer/abstractinterpreter/InliningMethodSummary.hpp @@ -0,0 +1,128 @@ +/******************************************************************************* + * 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 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] 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_METHOD_SUMMARY_INCL +#define INLINING_METHOD_SUMMARY_INCL + +#include "optimizer/VPConstraint.hpp" +#include "optimizer/ValuePropagation.hpp" +#include "optimizer/abstractinterpreter/AbsValue.hpp" + +namespace TR { class PotentialOptimizationPredicate; } + +namespace TR { + +/** + * The Inlining Method Summary captures potential optimization opportunities of inlining one particular method + * and also specifies the constraints that are the maximal safe values to make the optimizations happen. + */ +class InliningMethodSummary + { + public: + + InliningMethodSummary(TR::Region& region) : + _region(region), + _optsByArg(region) + {} + + /** + * @brief calculate the total static benefits from a particular argument after inlining. + * + * @param arg the argument + * @param argPos the position of the argument + * + * @return the total static benefit + */ + uint32_t testArgument(TR::AbsValue* arg, uint32_t argPos); + + void trace(TR::Compilation* comp); + + void addPotentialOptimizationByArgument(TR::PotentialOptimizationPredicate* predicate, uint32_t argPos); + + private: + + TR::Region& region() { return _region; } + + typedef TR::vector PredicateContainer; + TR::vector _optsByArg; + TR::Region &_region; + }; + +class PotentialOptimizationPredicate + { + public: + + enum Kind + { + BranchFolding, + NullCheckFolding, + InstanceOfFolding, + CheckCastFolding + }; + + PotentialOptimizationPredicate(uint32_t bytecodeIndex, TR::PotentialOptimizationPredicate::Kind kind) : + _bytecodeIndex(bytecodeIndex), + _kind(kind) + {} + + virtual void trace(TR::Compilation* comp)=0; + + /** + * @brief Test whether the given value is a safe value given the optimization's constraint. + * + * @param value the value to be tested against the constraint + * + * @return true if it is a safe value to unlock the optimization. false otherwise. + */ + virtual bool test(TR::AbsValue* value)=0; + + const char* getName(); + uint32_t getBytecodeIndex() { return _bytecodeIndex; } + + protected: + + uint32_t _bytecodeIndex; + TR::PotentialOptimizationPredicate::Kind _kind; + }; + +class PotentialOptimizationVPPredicate : public PotentialOptimizationPredicate + { + public: + PotentialOptimizationVPPredicate(TR::VPConstraint* constraint, uint32_t bytecodeIndex, TR::PotentialOptimizationPredicate::Kind kind, TR::ValuePropagation* vp) : + PotentialOptimizationPredicate(bytecodeIndex, kind), + _constraint(constraint), + _vp(vp) + {} + + virtual bool test(TR::AbsValue *value); + virtual void trace(TR::Compilation* comp); + + private: + + bool holdPartialOrderRelation(TR::VPConstraint* valueConstraint, TR::VPConstraint* testConstraint); + + TR::ValuePropagation* _vp; + TR::VPConstraint* _constraint; + + }; +} + +#endif