Skip to content

Commit

Permalink
Merge pull request #5508 from themaplelab/jack-inliner-phase-2
Browse files Browse the repository at this point in the history
BenefitInliner phase 2/3 Classes for building the IDT.
  • Loading branch information
jdmpapin authored Jun 27, 2023
2 parents 0c448df + 80c61ca commit d384988
Show file tree
Hide file tree
Showing 10 changed files with 948 additions and 1 deletion.
1 change: 1 addition & 0 deletions compiler/control/OMROptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,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"},
Expand Down
2 changes: 1 addition & 1 deletion compiler/control/OMROptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/env/VerboseLog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions compiler/optimizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
174 changes: 174 additions & 0 deletions compiler/optimizer/abstractinterpreter/IDT.cpp
Original file line number Diff line number Diff line change
@@ -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<TR::IDTNode*, TR::Region&> 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<TR::IDTNode*, TR::Region&> 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);
}
142 changes: 142 additions & 0 deletions compiler/optimizer/abstractinterpreter/IDT.hpp
Original file line number Diff line number Diff line change
@@ -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 <queue>

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<IDTNode*, TR::Region&> IDTNodeVector;
typedef std::priority_queue<IDTNode*, IDTNodeVector, IDTNodeCompare> IDTNodePriorityQueue;

TR::IDT* _idt;
IDTNodePriorityQueue _pQueue;
IDTNodeVector _entries;
};
}

#endif
Loading

0 comments on commit d384988

Please sign in to comment.