55 size_t numInlineableFunctions,
56 size_t numFunctionCalls,
57 size_t numInlineableCalls,
58 size_t numCallsInlined)
68 static std::unique_ptr<Statistics>
71 return std::make_unique<Statistics>(sourceFile);
104static std::vector<rvsdg::Output *>
107 constexpr bool enableCaching =
false;
112 std::vector<rvsdg::Output *> deps;
115 auto & traced = tracer.
trace(*ctxvar.input->origin());
117 deps.push_back(&routed);
165 const auto callEntryMergeOp =
167 const auto callExitSplitOp =
173 auto & callEntryMergeOutput = *callEntryMerge.
output(0);
174 if (callEntryMergeOutput.nusers() != 1)
176 auto & user = callEntryMergeOutput.
SingleUser();
177 const auto [lambdaEntrySplit, lambdaEntrySplitOp] =
179 if (!lambdaEntrySplitOp)
183 auto & callExitSplitInput = *callExitSplit.
input(0)->
origin();
184 const auto [lambdaExitSplit, lambdaExitSplitOp] =
186 if (!lambdaExitSplitOp)
190 for (
auto & output : lambdaEntrySplit->Outputs())
198 output.divert_users(mergeInput->origin());
204 output.divert_users(undef);
209 for (
auto & output : callExitSplit.
Outputs())
217 output.divert_users(exitMergeInput->origin());
225 if (!entryMergeInput)
226 throw std::runtime_error(
"MemoryStateId in call exit split not found in call entry merge");
227 output.divert_users(entryMergeInput->origin());
251 if (!is<AllocaOperation>(&node))
261 if (!countNode || countNode->ninputs() != 0)
262 throw std::runtime_error(
"Alloca did not have a nullary count origin");
265 const auto newCountNode = countNode->copy(caller.
subregion(), {});
266 const auto newAllocaNode = oldAllocaNode->copy(caller.
subregion(), { newCountNode->output(0) });
269 for (
size_t n = 0; n < newAllocaNode->noutputs(); n++)
271 auto & oldOutput = *oldAllocaNode->output(n);
272 auto & newOutput = *newAllocaNode->output(n);
274 oldOutput.divert_users(&routed);
278 remove(oldAllocaNode);
298 for (
size_t n = 0; n <
arguments.size(); n++)
300 auto callOperand = callNode.
input(n + 1)->
origin();
301 if (IsOrContains<PointerType>(*callOperand->Type()))
311 JLM_ASSERT(contextVars.size() == routedDeps.size());
312 for (
size_t n = 0; n < contextVars.size(); n++)
314 auto dep = routedDeps[n];
315 if (IsOrContains<PointerType>(*dep->Type()))
319 smap.
insert(contextVars[n].inner, dep);
328 for (
size_t n = 0; n < callNode.
noutputs(); n++)
330 const auto resultOrigin = calleeResults[n]->origin();
331 const auto newOrigin = &smap.
lookup(*resultOrigin);
347 if (callEntryMemoryStateMerge && callExitMemoryStateMerge)
363 for (
auto & node : region.
Nodes())
367 for (
auto & subregion : structural->Subregions())
373 else if (is<AllocaOperation>(&node))
385 if (!countNode || countNode->ninputs() != 0)
388 else if (
const auto [simple, callOp] =
393 if (classification->isSetjmpCall())
399 if (classification->isVaStartCall())
424 return context_->functionsCalledOnce.Contains(&callee);
435 if (!classification->IsDirectCall())
438 auto & calleeOutput = classification->GetLambdaOutput();
443 if (callee == &callerLambda)
447 if (!
context_->inlineableFunctions.Contains(callee))
468 for (
auto & subregion : structural.
Subregions())
475 if (is<CallOperation>(&simple))
493 context_->inlineableFunctions.insert(&lambda);
498 if (callSummary.HasOnlyDirectCalls() && callSummary.NumDirectCalls() == 1)
499 context_->functionsCalledOnce.insert(&lambda);
525 context_ = std::make_unique<Context>();
530 context_->inlineableFunctions.Size(),
static jlm::util::StatisticsCollector statisticsCollector
util::HashSet< rvsdg::Output * > arguments
static rvsdg::Input & getCountInput(rvsdg::Node &node)
static rvsdg::Input * tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
static MemoryNodeId mapOutputToMemoryNodeId(const rvsdg::Output &output)
static std::unique_ptr< CallTypeClassifier > ClassifyCall(const rvsdg::SimpleNode &callNode)
Classifies a call node.
static rvsdg::SimpleNode * tryGetMemoryStateEntryMerge(const rvsdg::Node &callNode) noexcept
static rvsdg::SimpleNode * tryGetMemoryStateExitSplit(const rvsdg::Node &callNode) noexcept
static rvsdg::Input & GetIOStateInput(const rvsdg::Node &node) noexcept
static std::unique_ptr< Statistics > create(const util::FilePath &sourceFile)
static constexpr const char * NumCallsInlined_
void stop(size_t numFunctions, size_t numInlineableFunctions, size_t numFunctionCalls, size_t numInlineableCalls, size_t numCallsInlined)
static constexpr const char * NumFunctions_
~Statistics() override=default
static constexpr const char * NumInlineableFunctions_
Statistics(const util::FilePath &sourceFile)
static constexpr const char * NumInlineableCalls_
static constexpr const char * NumFunctionCalls_
Performs function inlining on functions that are determined to be good candidates,...
bool shouldInline(rvsdg::SimpleNode &callNode, rvsdg::LambdaNode &caller, rvsdg::LambdaNode &callee)
void visitInterProceduralRegion(rvsdg::Region ®ion)
void visitLambda(rvsdg::LambdaNode &lambda)
void considerCallForInlining(rvsdg::SimpleNode &callNode, rvsdg::LambdaNode &callerLambda)
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static bool canBeInlined(rvsdg::Region ®ion, bool topLevelRegion)
void visitIntraProceduralRegion(rvsdg::Region ®ion, rvsdg::LambdaNode &lambda)
static void inlineCall(rvsdg::SimpleNode &callNode, rvsdg::LambdaNode &caller, const rvsdg::LambdaNode &callee)
~FunctionInlining() noexcept override
std::unique_ptr< Context > context_
static rvsdg::SimpleNode & createNode(rvsdg::Output &value, rvsdg::Output &ioState)
static MemoryNodeId mapOutputToMemoryNodeId(const rvsdg::Output &output)
static rvsdg::Input * tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
static jlm::rvsdg::Output * Create(rvsdg::Region ®ion, std::shared_ptr< const jlm::rvsdg::Type > type)
Region & GetRootRegion() const noexcept
std::vector< rvsdg::Output * > GetFunctionArguments() const
std::vector< rvsdg::Input * > GetFunctionResults() const
rvsdg::Region * subregion() const noexcept
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
OutputIteratorRange Outputs() noexcept
rvsdg::Region * region() const noexcept
size_t noutputs() const noexcept
void setEnterPhiNodes(bool value) noexcept
Output & trace(Output &output)
rvsdg::Input & SingleUser() noexcept
void divert_users(jlm::rvsdg::Output *new_origin)
A phi node represents the fixpoint of mutually recursive definitions.
rvsdg::Region * subregion() const noexcept
Represent acyclic RVSDG subgraphs.
void copy(Region *target, SubstitutionMap &smap) const
Copy a region with substitutions.
NodeRange Nodes() noexcept
const std::optional< util::FilePath > & SourceFilePath() const noexcept
NodeInput * input(size_t index) const noexcept
NodeOutput * output(size_t index) const noexcept
SubregionIteratorRange Subregions()
void insert(const Output *original, Output *substitute)
Output & lookup(const Output &original) const
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
util::Timer & GetTimer(const std::string &name)
util::Timer & AddTimer(std::string name)
void AddMeasurement(std::string name, T value)
Global memory state passed between functions.
static void tryRerouteMemoryStateMergeAndSplit(rvsdg::SimpleNode &callEntryMerge, rvsdg::SimpleNode &callExitSplit)
static void hoistInlinedAllocas(const rvsdg::LambdaNode &callee, rvsdg::LambdaNode &caller, rvsdg::SubstitutionMap &smap)
static std::vector< rvsdg::Output * > routeContextVariablesToRegion(rvsdg::Region ®ion, const rvsdg::LambdaNode &callee)
CallSummary ComputeCallSummary(const rvsdg::LambdaNode &lambdaNode)
void MatchType(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
Output & RouteToRegion(Output &output, Region ®ion)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Output & traceOutputIntraProcedurally(Output &output)
rvsdg::LambdaNode & getSurroundingLambdaNode(rvsdg::Node &node)
detail::TopDownTraverserGeneric< false > TopDownTraverser
Traverser for visiting every node in a region in a top down order.
size_t numInlineableCalls
util::HashSet< const rvsdg::LambdaNode * > inlineableFunctions
util::HashSet< const rvsdg::LambdaNode * > functionsCalledOnce