51 size_t numInlineableFunctions,
52 size_t numFunctionCalls,
53 size_t numInlineableCalls,
54 size_t numCallsInlined)
64 static std::unique_ptr<Statistics>
67 return std::make_unique<Statistics>(sourceFile);
100 static std::vector<rvsdg::Output *>
107 std::vector<rvsdg::Output *> deps;
110 auto & traced = tracer.
trace(*ctxvar.input->origin());
112 deps.push_back(&routed);
160 const auto callEntryMergeOp =
161 rvsdg::tryGetOperation<CallEntryMemoryStateMergeOperation>(callEntryMerge);
162 const auto callExitSplitOp =
163 rvsdg::tryGetOperation<CallExitMemoryStateSplitOperation>(callExitSplit);
168 auto & callEntryMergeOutput = *callEntryMerge.
output(0);
169 if (callEntryMergeOutput.nusers() != 1)
171 auto & user = callEntryMergeOutput.
SingleUser();
172 const auto [lambdaEntrySplit, lambdaEntrySplitOp] =
173 rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaEntryMemoryStateSplitOperation>(user);
174 if (!lambdaEntrySplitOp)
178 auto & callExitSplitInput = *callExitSplit.
input(0)->
origin();
179 const auto [lambdaExitSplit, lambdaExitSplitOp] =
180 rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaExitMemoryStateMergeOperation>(callExitSplitInput);
181 if (!lambdaExitSplitOp)
185 for (
auto & output : lambdaEntrySplit->Outputs())
193 output.divert_users(mergeInput->origin());
199 output.divert_users(undef);
204 for (
auto & output : callExitSplit.
Outputs())
212 output.divert_users(exitMergeInput->origin());
220 if (!entryMergeInput)
221 throw std::runtime_error(
"MemoryStateId in call exit split not found in call entry merge");
222 output.divert_users(entryMergeInput->origin());
246 if (!is<AllocaOperation>(&node))
250 auto oldAllocaNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(smap.
lookup(*node.output(0)));
255 auto countNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*countOrigin);
256 if (!countNode || countNode->ninputs() != 0)
257 throw std::runtime_error(
"Alloca did not have a nullary count origin");
260 const auto newCountNode = countNode->copy(caller.
subregion(), {});
261 const auto newAllocaNode = oldAllocaNode->copy(caller.
subregion(), { newCountNode->output(0) });
264 for (
size_t n = 0; n < newAllocaNode->noutputs(); n++)
266 auto & oldOutput = *oldAllocaNode->output(n);
267 auto & newOutput = *newAllocaNode->output(n);
269 oldOutput.divert_users(&routed);
292 for (
size_t n = 0; n < arguments.size(); n++)
299 JLM_ASSERT(contextVars.size() == routedDeps.size());
300 for (
size_t n = 0; n < contextVars.size(); n++)
302 smap.
insert(contextVars[n].inner, routedDeps[n]);
311 for (
size_t n = 0; n < callNode.
noutputs(); n++)
313 const auto resultOrigin = calleeResults[n]->origin();
314 const auto newOrigin = &smap.
lookup(*resultOrigin);
330 if (callEntryMemoryStateMerge && callExitMemoryStateMerge)
346 for (
auto & node : region.
Nodes())
350 for (
auto & subregion : structural->Subregions())
356 else if (is<AllocaOperation>(&node))
365 auto countNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*countOutput);
368 if (!countNode || countNode->ninputs() != 0)
371 else if (
const auto [simple, callOp] =
372 rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(node);
376 if (classification->isSetjmpCall())
382 if (classification->isVaStartCall())
407 return context_->functionsCalledOnce.Contains(&callee);
418 if (!classification->IsDirectCall())
421 auto & calleeOutput = classification->GetLambdaOutput();
422 auto callee = rvsdg::TryGetOwnerNode<rvsdg::LambdaNode>(calleeOutput);
426 if (callee == &callerLambda)
430 if (!
context_->inlineableFunctions.Contains(callee))
451 for (
auto & subregion : structural.
Subregions())
453 visitIntraProceduralRegion(subregion, lambda);
458 if (is<CallOperation>(&simple))
476 context_->inlineableFunctions.insert(&lambda);
481 if (callSummary.HasOnlyDirectCalls() && callSummary.NumDirectCalls() == 1)
482 context_->functionsCalledOnce.insert(&lambda);
508 context_ = std::make_unique<Context>();
513 context_->inlineableFunctions.Size(),
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 rvsdg::SimpleNode * tryGetMemoryStateExitSplit(const rvsdg::Node &callNode) noexcept
static std::unique_ptr< CallTypeClassifier > ClassifyCall(const rvsdg::SimpleNode &callNode)
Classifies a call node.
static rvsdg::SimpleNode * tryGetMemoryStateEntryMerge(const rvsdg::Node &callNode) noexcept
static constexpr const char * NumCallsInlined_
void stop(size_t numFunctions, size_t numInlineableFunctions, size_t numFunctionCalls, size_t numInlineableCalls, size_t numCallsInlined)
static std::unique_ptr< Statistics > create(const util::FilePath &sourceFile)
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 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
rvsdg::Region * subregion() const noexcept
std::vector< rvsdg::Input * > GetFunctionResults() const
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)
CallSummary ComputeCallSummary(const rvsdg::LambdaNode &lambdaNode)
static std::vector< rvsdg::Output * > routeContextVariablesToRegion(rvsdg::Region ®ion, const rvsdg::LambdaNode &callee)
void MatchType(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
static void remove(Node *node)
Output & RouteToRegion(Output &output, Region ®ion)
Output & traceOutputIntraProcedurally(Output &output)
rvsdg::LambdaNode & getSurroundingLambdaNode(rvsdg::Node &node)
size_t numInlineableCalls
util::HashSet< const rvsdg::LambdaNode * > inlineableFunctions
util::HashSet< const rvsdg::LambdaNode * > functionsCalledOnce
static const char * Timer