54 size_t numInlineableFunctions,
55 size_t numFunctionCalls,
56 size_t numInlineableCalls,
57 size_t numCallsInlined)
67 static std::unique_ptr<Statistics>
70 return std::make_unique<Statistics>(sourceFile);
103 static std::vector<rvsdg::Output *>
106 constexpr
bool enableCaching =
false;
111 std::vector<rvsdg::Output *> deps;
114 auto & traced = tracer.
trace(*ctxvar.input->origin());
116 deps.push_back(&routed);
164 const auto callEntryMergeOp =
165 rvsdg::tryGetOperation<CallEntryMemoryStateMergeOperation>(callEntryMerge);
166 const auto callExitSplitOp =
167 rvsdg::tryGetOperation<CallExitMemoryStateSplitOperation>(callExitSplit);
172 auto & callEntryMergeOutput = *callEntryMerge.
output(0);
173 if (callEntryMergeOutput.nusers() != 1)
175 auto & user = callEntryMergeOutput.
SingleUser();
176 const auto [lambdaEntrySplit, lambdaEntrySplitOp] =
177 rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaEntryMemoryStateSplitOperation>(user);
178 if (!lambdaEntrySplitOp)
182 auto & callExitSplitInput = *callExitSplit.
input(0)->
origin();
183 const auto [lambdaExitSplit, lambdaExitSplitOp] =
184 rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaExitMemoryStateMergeOperation>(callExitSplitInput);
185 if (!lambdaExitSplitOp)
189 for (
auto & output : lambdaEntrySplit->Outputs())
197 output.divert_users(mergeInput->origin());
203 output.divert_users(undef);
208 for (
auto & output : callExitSplit.
Outputs())
216 output.divert_users(exitMergeInput->origin());
224 if (!entryMergeInput)
225 throw std::runtime_error(
"MemoryStateId in call exit split not found in call entry merge");
226 output.divert_users(entryMergeInput->origin());
250 if (!is<AllocaOperation>(&node))
254 auto oldAllocaNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(smap.
lookup(*node.output(0)));
259 auto countNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*countOrigin);
260 if (!countNode || countNode->ninputs() != 0)
261 throw std::runtime_error(
"Alloca did not have a nullary count origin");
264 const auto newCountNode = countNode->copy(caller.
subregion(), {});
265 const auto newAllocaNode = oldAllocaNode->copy(caller.
subregion(), { newCountNode->output(0) });
268 for (
size_t n = 0; n < newAllocaNode->noutputs(); n++)
270 auto & oldOutput = *oldAllocaNode->output(n);
271 auto & newOutput = *newAllocaNode->output(n);
273 oldOutput.divert_users(&routed);
296 for (
size_t n = 0; n < arguments.size(); n++)
303 JLM_ASSERT(contextVars.size() == routedDeps.size());
304 for (
size_t n = 0; n < contextVars.size(); n++)
306 smap.
insert(contextVars[n].inner, routedDeps[n]);
315 for (
size_t n = 0; n < callNode.
noutputs(); n++)
317 const auto resultOrigin = calleeResults[n]->origin();
318 const auto newOrigin = &smap.
lookup(*resultOrigin);
334 if (callEntryMemoryStateMerge && callExitMemoryStateMerge)
350 for (
auto & node : region.
Nodes())
354 for (
auto & subregion : structural->Subregions())
360 else if (is<AllocaOperation>(&node))
369 auto countNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*countOutput);
372 if (!countNode || countNode->ninputs() != 0)
375 else if (
const auto [simple, callOp] =
376 rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(node);
380 if (classification->isSetjmpCall())
386 if (classification->isVaStartCall())
411 return context_->functionsCalledOnce.Contains(&callee);
422 if (!classification->IsDirectCall())
425 auto & calleeOutput = classification->GetLambdaOutput();
426 auto callee = rvsdg::TryGetOwnerNode<rvsdg::LambdaNode>(calleeOutput);
430 if (callee == &callerLambda)
434 if (!
context_->inlineableFunctions.Contains(callee))
455 for (
auto & subregion : structural.
Subregions())
457 visitIntraProceduralRegion(subregion, lambda);
462 if (is<CallOperation>(&simple))
480 context_->inlineableFunctions.insert(&lambda);
485 if (callSummary.HasOnlyDirectCalls() && callSummary.NumDirectCalls() == 1)
486 context_->functionsCalledOnce.insert(&lambda);
512 context_ = std::make_unique<Context>();
517 context_->inlineableFunctions.Size(),
static jlm::util::StatisticsCollector statisticsCollector
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