Jlm
Loading...
Searching...
No Matches
push.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
10#include <jlm/rvsdg/gamma.hpp>
12#include <jlm/rvsdg/Phi.hpp>
13#include <jlm/rvsdg/theta.hpp>
16#include <jlm/util/time.hpp>
17
18#include <algorithm>
19#include <deque>
20
21namespace jlm::llvm
22{
23
25{
26public:
27 ~Statistics() override = default;
28
29 explicit Statistics(const util::FilePath & sourceFile)
30 : util::Statistics(Statistics::Id::PushNodes, sourceFile)
31 {}
32
33 void
34 start(const rvsdg::Graph & graph) noexcept
35 {
36 AddMeasurement(Label::NumRvsdgInputsBefore, jlm::rvsdg::ninputs(&graph.GetRootRegion()));
37 AddTimer(Label::Timer).start();
38 }
39
40 void
41 end(const rvsdg::Graph & graph) noexcept
42 {
43 AddMeasurement(Label::NumRvsdgInputsAfter, jlm::rvsdg::ninputs(&graph.GetRootRegion()));
44 GetTimer(Label::Timer).stop();
45 }
46
47 static std::unique_ptr<Statistics>
48 Create(const util::FilePath & sourceFile)
49 {
50 return std::make_unique<Statistics>(sourceFile);
51 }
52};
53
55{
56public:
57 explicit Context(rvsdg::LambdaNode & lambdaNode)
58 : LambdaSubregion_(lambdaNode.subregion())
59 {}
60
62 getLambdaSubregion() const noexcept
63 {
64 return *LambdaSubregion_;
65 }
66
67 void
68 addTargetRegion(const rvsdg::Node & node, rvsdg::Region & region) noexcept
69 {
70 JLM_ASSERT(TargetRegion_.find(&node) == TargetRegion_.end());
71 TargetRegion_[&node] = &region;
72 }
73
75 getTargetRegion(const rvsdg::Node & node) const noexcept
76 {
77 return *TargetRegion_.at(&node);
78 }
79
80 static std::unique_ptr<Context>
82 {
83 return std::make_unique<Context>(lambdaNode);
84 }
85
86private:
88 std::unordered_map<const rvsdg::Node *, rvsdg::Region *> TargetRegion_{};
89};
90
91NodeHoisting::~NodeHoisting() noexcept = default;
92
94 : Transformation("NodeHoisting")
95{}
96
97bool
99{
100 if (!is<MemoryStateType>(loopVar.output->Type()))
101 return false;
102
103 if (loopVar.pre->nusers() != 1)
104 return false;
105
106 const auto userNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*loopVar.pre->Users().begin());
107 const auto originNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*loopVar.post->origin());
108
109 if (userNode != originNode)
110 return false;
111
112 return true;
113}
114
117{
118 // Handle lambda region arguments
120 {
121 return *output.region();
122 }
123
124 // Handle gamma region arguments
125 if (const auto gammaNode = rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(output))
126 {
127 if (output.Type()->Kind() == rvsdg::TypeKind::State)
128 {
129 // FIXME: This is a bit too conservative. For example, it avoids that load and store nodes are
130 // hoisted out of a gamma node, but we would only like to avoid store nodes being hoisted out.
131 // For load nodes, it is legal to hoist them out if they are not preceded by an IOBarrier.
132 return *output.region();
133 }
134
135 const auto roleVar = gammaNode->MapBranchArgument(output);
136 if (const auto entryVar = std::get_if<rvsdg::GammaNode::EntryVar>(&roleVar))
137 {
138 return computeTargetRegion(*entryVar->input->origin());
139 }
140
141 return *output.region();
142 }
143
144 // Handle theta region arguments
145 if (const auto thetaNode = rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(output))
146 {
147 const auto loopVar = thetaNode->MapPreLoopVar(output);
149 {
150 return computeTargetRegion(*loopVar.input->origin());
151 }
152
154 {
155 return computeTargetRegion(*loopVar.input->origin());
156 }
157
158 return *output.region();
159 }
160
161 // Handle gamma outputs
162 if (const auto gammaNode = rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(output))
163 {
164 return context_->getTargetRegion(*gammaNode);
165 }
166
167 // Handle theta outputs
168 if (const auto thetaNode = rvsdg::TryGetOwnerNode<rvsdg::ThetaNode>(output))
169 {
170 return context_->getTargetRegion(*thetaNode);
171 }
172
173 // Handle simple node outputs
174 if (const auto node = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output))
175 {
176 return context_->getTargetRegion(*node);
177 }
178
179 throw std::logic_error("Unhandled output type!");
180}
181
184{
185 if (node.ninputs() == 0)
186 {
187 // All nullary operations to date have exactly one output
188 JLM_ASSERT(node.noutputs() == 1);
189
190 // Control constants are used to instruct the control flow graph creation,
191 // and will be removed in the back-end, so there is no need to hoist them.
192 const auto outputType = node.output(0)->Type();
193 if (is<rvsdg::ControlType>(outputType))
194 return *node.region();
195
196 // Other constants should be moved to the top-level of the function
197 return context_->getLambdaSubregion();
198 }
199
200 // Compute target regions for all the inputs of the node
201 rvsdg::Region * greatestCommonTargetRegion = nullptr;
202
203 for (auto & input : node.Inputs())
204 {
205 auto & targetRegion = computeTargetRegion(*input.origin());
206 if (&targetRegion == node.region())
207 {
208 // One of the node's predecessors cannot be hoisted, which means we can also not hoist this
209 // node
210 return *node.region();
211 }
212
213 // If we already have a common target region that is lower, keep it
214 if (greatestCommonTargetRegion
215 && greatestCommonTargetRegion->getDepth() >= targetRegion.getDepth())
216 continue;
217 greatestCommonTargetRegion = &targetRegion;
218 }
219
220 // Return the lowestmost common target region in the region tree among all inputs
221 JLM_ASSERT(greatestCommonTargetRegion);
222 return *greatestCommonTargetRegion;
223}
224
225void
227{
228 for (const auto node : rvsdg::TopDownConstTraverser(&region))
229 {
231 *node,
232 [&](const rvsdg::StructuralNode & structuralNode)
233 {
234 // FIXME: We currently do not allow structural nodes (gamma and theta nodes) to be hoisted
235 context_->addTargetRegion(structuralNode, *structuralNode.region());
236
237 // Handle innermost regions
238 for (auto & subregion : structuralNode.Subregions())
239 {
240 markNodes(subregion);
241 }
242 },
243 [&](const rvsdg::SimpleNode & simpleNode)
244 {
245 rvsdg::Region & targetRegion = computeTargetRegion(simpleNode);
246 context_->addTargetRegion(*node, targetRegion);
247 },
248 []()
249 {
250 throw std::logic_error("Unhandled node type!");
251 });
252 }
253}
254
257{
258 if (output.region() == &targetRegion)
259 return output;
260
261 // Handle gamma subregion arguments
262 if (const auto gammaNode = rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(output))
263 {
264 const auto roleVar = gammaNode->MapBranchArgument(output);
265 if (const auto entryVar = std::get_if<rvsdg::GammaNode::EntryVar>(&roleVar))
266 {
267 return getOperandFromTargetRegion(*entryVar->input->origin(), targetRegion);
268 }
269 }
270
271 // Handle theta subregion arguments
272 if (const auto thetaNode = rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(output))
273 {
274 const auto loopVar = thetaNode->MapPreLoopVar(output);
276 return getOperandFromTargetRegion(*loopVar.input->origin(), targetRegion);
277 }
278
279 throw std::logic_error("Unhandled output type!");
280}
281
282std::vector<rvsdg::Output *>
284{
285 std::vector<rvsdg::Output *> operands;
286 for (auto & input : node.Inputs())
287 {
288 auto & operand = getOperandFromTargetRegion(*input.origin(), targetRegion);
289 operands.push_back(&operand);
290 }
291
292 return operands;
293}
294
295void
297{
298 auto & targetRegion = context_->getTargetRegion(node);
299 JLM_ASSERT(&targetRegion != node.region());
300
301 const auto operands = getOperandsFromTargetRegion(node, targetRegion);
302 const auto copiedNode = node.copy(&targetRegion, operands);
303
304 // FIXME: I really would like to have a zip function here, but C++ does not really seem to have
305 // anything better to offer
306 auto itOrg = std::begin(node.Outputs());
307 const auto endOrg = std::end(node.Outputs());
308 auto itCpy = std::begin(copiedNode->Outputs());
309 const auto endCpy = std::end(copiedNode->Outputs());
310 JLM_ASSERT(std::distance(itOrg, endOrg) == std::distance(itCpy, endCpy));
311
312 for (; itOrg != endOrg; ++itOrg, ++itCpy)
313 {
314 auto & outputOrg = *itOrg;
315 auto & outputCpy = *itCpy;
316 auto & newOutputOrg = rvsdg::RouteToRegion(outputCpy, *node.region());
317 outputOrg.divert_users(&newOutputOrg);
318 }
319}
320
321void
323{
324 // FIXME: We a routing unnecessary values through gamma and theta nodes. We should cluster
325 // subgraphs that need to be hoisted to avoid unnecessary routing.
326 for (const auto node : rvsdg::TopDownTraverser(&region))
327 {
328 auto & targetRegion = context_->getTargetRegion(*node);
329 if (&targetRegion != node->region())
330 {
332 }
333
334 // Handle innermost regions
335 if (const auto structuralNode = dynamic_cast<rvsdg::StructuralNode *>(node))
336 {
337 for (auto & subregion : structuralNode->Subregions())
338 {
339 hoistNodes(subregion);
340 }
341 }
342 }
343
344 region.prune(false);
345}
346
347void
349{
350 context_ = Context::create(lambdaNode);
351
352 markNodes(*lambdaNode.subregion());
353 hoistNodes(*lambdaNode.subregion());
354
355 context_.reset();
356}
357
358void
360{
361 for (auto & node : rvsdg::TopDownTraverser(&region))
362 {
364 *node,
365 [&](rvsdg::LambdaNode & lambdaNode)
366 {
367 hoistNodesInLambda(lambdaNode);
368 },
369 [&](rvsdg::PhiNode & phiNode)
370 {
371 hoistNodesInRootRegion(*phiNode.subregion());
372 },
373 [](rvsdg::DeltaNode &)
374 {
375 // Nothing needs to be done
376 },
378 {
379 // Nothing needs to be done
380 },
381 [&]()
382 {
383 throw std::logic_error(util::strfmt("Unhandled node type: ", node->DebugString()));
384 });
385 }
386}
387
388void
390{
391 auto statistics = Statistics::Create(rvsdgModule.SourceFilePath().value());
392
393 statistics->start(rvsdgModule.Rvsdg());
395 statistics->end(rvsdgModule.Rvsdg());
396
397 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
398}
399}
static jlm::util::StatisticsCollector statisticsCollector
Context(rvsdg::LambdaNode &lambdaNode)
Definition push.cpp:57
rvsdg::Region * LambdaSubregion_
Definition push.cpp:87
void addTargetRegion(const rvsdg::Node &node, rvsdg::Region &region) noexcept
Definition push.cpp:68
std::unordered_map< const rvsdg::Node *, rvsdg::Region * > TargetRegion_
Definition push.cpp:88
rvsdg::Region & getTargetRegion(const rvsdg::Node &node) const noexcept
Definition push.cpp:75
static std::unique_ptr< Context > create(rvsdg::LambdaNode &lambdaNode)
Definition push.cpp:81
rvsdg::Region & getLambdaSubregion() const noexcept
Definition push.cpp:62
void end(const rvsdg::Graph &graph) noexcept
Definition push.cpp:41
Statistics(const util::FilePath &sourceFile)
Definition push.cpp:29
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition push.cpp:48
void start(const rvsdg::Graph &graph) noexcept
Definition push.cpp:34
Node Hoisting Transformation.
Definition push.hpp:37
void hoistNodesInRootRegion(rvsdg::Region &region)
Definition push.cpp:359
void hoistNodes(rvsdg::Region &region)
Definition push.cpp:322
void hoistNodesInLambda(rvsdg::LambdaNode &lambdaNode)
Definition push.cpp:348
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Definition push.cpp:389
~NodeHoisting() noexcept override
rvsdg::Region & computeTargetRegion(const rvsdg::Node &node) const
Definition push.cpp:183
static std::vector< rvsdg::Output * > getOperandsFromTargetRegion(rvsdg::Node &node, rvsdg::Region &targetRegion)
Definition push.cpp:283
std::unique_ptr< Context > context_
Definition push.hpp:84
static bool isInvariantMemoryStateLoopVar(const rvsdg::ThetaNode::LoopVar &loopVar)
Definition push.cpp:98
void copyNodeToTargetRegion(rvsdg::Node &node) const
Definition push.cpp:296
void markNodes(const rvsdg::Region &region)
Definition push.cpp:226
static rvsdg::Output & getOperandFromTargetRegion(rvsdg::Output &output, rvsdg::Region &targetRegion)
Definition push.cpp:256
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
Output * origin() const noexcept
Definition node.hpp:58
rvsdg::Region * subregion() const noexcept
Definition lambda.hpp:138
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
OutputIteratorRange Outputs() noexcept
Definition node.hpp:657
rvsdg::Region * region() const noexcept
Definition node.hpp:761
InputIteratorRange Inputs() noexcept
Definition node.hpp:622
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
virtual Node * copy(rvsdg::Region *region, const std::vector< jlm::rvsdg::Output * > &operands) const
Definition node.cpp:369
rvsdg::Region * region() const noexcept
Definition node.cpp:151
UsersRange Users()
Definition node.hpp:354
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
size_t nusers() const noexcept
Definition node.hpp:280
A phi node represents the fixpoint of mutually recursive definitions.
Definition Phi.hpp:46
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
void prune(bool recursive)
Definition region.cpp:326
size_t getDepth() const noexcept
Definition region.hpp:291
const std::optional< util::FilePath > & SourceFilePath() const noexcept
Graph & Rvsdg() noexcept
SubregionIteratorRange Subregions()
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Statistics Interface.
util::Timer & GetTimer(const std::string &name)
util::Timer & AddTimer(std::string name)
void AddMeasurement(std::string name, T value)
void start() noexcept
Definition time.hpp:54
void stop() noexcept
Definition time.hpp:67
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
void MatchTypeWithDefault(T &obj, const Fns &... fns)
Pattern match over subclass type of given object with default handler.
static bool ThetaLoopVarIsInvariant(const ThetaNode::LoopVar &loopVar) noexcept
Definition theta.hpp:227
Output & RouteToRegion(Output &output, Region &region)
Definition node.cpp:381
@ State
Designate a state type.
detail::TopDownTraverserGeneric< true > TopDownConstTraverser
Traverser for visiting every node in a const region in a top down order.
size_t ninputs(const rvsdg::Region *region) noexcept
Definition region.cpp:861
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
detail::TopDownTraverserGeneric< false > TopDownTraverser
Traverser for visiting every node in a region in a top down order.
static std::string strfmt(Args... args)
Definition strfmt.hpp:35
Description of a loop-carried variable.
Definition theta.hpp:50
rvsdg::Output * pre
Variable before iteration (input argument to subregion).
Definition theta.hpp:58
rvsdg::Output * output
Variable at loop exit (output of theta).
Definition theta.hpp:66
rvsdg::Input * post
Variable after iteration (output result from subregion).
Definition theta.hpp:62