Jlm
Loading...
Searching...
No Matches
pull.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
8#include <jlm/rvsdg/gamma.hpp>
11#include <jlm/util/time.hpp>
12
13#include <algorithm>
14
15namespace jlm::llvm
16{
17
19{
20public:
21 ~Statistics() override = default;
22
23 explicit Statistics(const util::FilePath & sourceFile)
24 : util::Statistics(Statistics::Id::PullNodes, sourceFile)
25 {}
26
27 void
28 start(const rvsdg::Graph & graph) noexcept
29 {
30 AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion()));
31 AddTimer(Label::Timer).start();
32 }
33
34 void
35 end(const rvsdg::Graph & graph) noexcept
36 {
37 AddMeasurement(Label::NumRvsdgInputsAfter, rvsdg::ninputs(&graph.GetRootRegion()));
38 GetTimer(Label::Timer).stop();
39 }
40
41 static std::unique_ptr<Statistics>
42 Create(const util::FilePath & sourceFile)
43 {
44 return std::make_unique<Statistics>(sourceFile);
45 }
46};
47
48static bool
49empty(const rvsdg::GammaNode * gamma)
50{
51 for (size_t n = 0; n < gamma->nsubregions(); n++)
52 {
53 if (gamma->subregion(n)->numNodes() != 0)
54 return false;
55 }
56
57 return true;
58}
59
70static bool
71isSingleSuccessor(const rvsdg::Node & node, const rvsdg::Node & singleSuccessor)
72{
73 for (auto & output : node.Outputs())
74 {
75 for (const auto & user : output.Users())
76 {
77 const auto successor = rvsdg::TryGetOwnerNode<rvsdg::Node>(user);
78 if (successor != &singleSuccessor)
79 return false;
80 }
81 }
82
83 return true;
84}
85
86static void
88{
89 /* collect operands */
90 std::vector<std::vector<jlm::rvsdg::Output *>> operands(gamma->nsubregions());
91 for (size_t i = 0; i < node->ninputs(); i++)
92 {
93 auto ev = gamma->AddEntryVar(node->input(i)->origin());
94 std::size_t index = 0;
95 for (auto input : ev.branchArgument)
96 operands[index++].push_back(input);
97 }
98
99 /* copy node into subregions */
100 for (size_t r = 0; r < gamma->nsubregions(); r++)
101 {
102 auto copy = node->copy(gamma->subregion(r), operands[r]);
103
104 /* redirect outputs */
105 for (size_t o = 0; o < node->noutputs(); o++)
106 {
107 for (const auto & user : node->output(o)->Users())
108 {
109 auto entryvar = std::get<rvsdg::GammaNode::EntryVar>(gamma->MapInput(user));
110 entryvar.branchArgument[r]->divert_users(copy->output(o));
111 }
112 }
113 }
114}
115
116static void
118{
119 JLM_ASSERT(isSingleSuccessor(*node, *gamma));
120
121 /* remove entry variables and node */
122 std::vector<rvsdg::GammaNode::EntryVar> entryvars;
123 for (size_t n = 0; n < node->noutputs(); n++)
124 {
125 for (auto & user : node->output(n)->Users())
126 {
127 entryvars.push_back(std::get<rvsdg::GammaNode::EntryVar>(gamma->MapInput(user)));
128 }
129 }
130 gamma->RemoveEntryVars(entryvars);
131 remove(node);
132}
133
134void
136{
137 /* FIXME: This is inefficient. We can do better. */
138 auto evs = gamma->GetEntryVars();
139 size_t index = 0;
140 while (index < evs.size())
141 {
142 const auto & ev = evs[index];
143 auto node = rvsdg::TryGetOwnerNode<rvsdg::Node>(*ev.input->origin());
145 if (node && tmp != node && isSingleSuccessor(*node, *gamma))
146 {
147 pullin_node(gamma, node);
148
149 cleanup(gamma, node);
150
151 evs = gamma->GetEntryVars();
152 index = 0;
153 }
154 else
155 {
156 index++;
157 }
158 }
159}
160
163{
164 std::function<void(const rvsdg::Node &, util::HashSet<rvsdg::Node *> &)> collect =
165 [&collect](const rvsdg::Node & node, util::HashSet<rvsdg::Node *> & dependentNodes)
166 {
167 for (auto & output : node.Outputs())
168 {
169 for (auto & user : output.Users())
170 {
171 if (const auto userNode = rvsdg::TryGetOwnerNode<rvsdg::Node>(user))
172 {
173 if (dependentNodes.insert(userNode))
174 {
175 collect(*userNode, dependentNodes);
176 }
177 }
178 }
179 }
180 };
181
182 util::HashSet<rvsdg::Node *> dependentNodes;
183 collect(node, dependentNodes);
184 return dependentNodes;
185}
186
187std::vector<rvsdg::Node *>
189{
190 if (nodes.IsEmpty())
191 {
192 return {};
193 }
194
195 std::vector<rvsdg::Node *> sortedNodes;
196 for (auto & node : nodes.Items())
197 {
198 sortedNodes.push_back(node);
199 }
200
201 auto depthMap = rvsdg::computeDepthMap(*sortedNodes[0]->region());
202 std::sort(
203 sortedNodes.begin(),
204 sortedNodes.end(),
205 [&depthMap](const auto * node1, const auto * node2)
206 {
207 JLM_ASSERT(depthMap.find(node1) != depthMap.end());
208 JLM_ASSERT(depthMap.find(node2) != depthMap.end());
209 return depthMap[node1] < depthMap[node2];
210 });
211
212 return sortedNodes;
213}
214
215size_t
217{
218 const auto dependentNodes = collectDependentNodes(gammaNode);
219 const auto sortedDependentNodes = sortByDepth(dependentNodes);
220
221 // FIXME: We create too many entry and exit variables for the gamma node as we copy each
222 // node individually instead of the entire subgraph. Rather copy the entire subgraph at once and
223 // create entry and exit variables according to the "outer edges" of this subgraph.
224 for (auto & node : sortedDependentNodes)
225 {
226 // Collect operands for each subregion we copy the node into
227 std::unordered_map<rvsdg::Region *, std::vector<rvsdg::Output *>> subregionOperands;
228 for (auto & input : node->Inputs())
229 {
230 auto & oldOperand = *input.origin();
231 if (rvsdg::TryGetOwnerNode<rvsdg::Node>(oldOperand) == &gammaNode)
232 {
233 auto [branchResults, _] = gammaNode.MapOutputExitVar(oldOperand);
234 for (const auto branchResult : branchResults)
235 {
236 subregionOperands[branchResult->region()].push_back(branchResult->origin());
237 }
238 }
239 else
240 {
241 auto [_, branchArguments] = gammaNode.AddEntryVar(&oldOperand);
242 for (auto branchArgument : branchArguments)
243 {
244 subregionOperands[branchArgument->region()].push_back(branchArgument);
245 }
246 }
247 }
248
249 // Copy node into each subregion and collect outputs of each copy
250 std::unordered_map<rvsdg::Region *, std::vector<rvsdg::Output *>> subregionOutputs;
251 for (auto & subregion : gammaNode.Subregions())
252 {
253 const auto copiedNode = node->copy(&subregion, subregionOperands.at(&subregion));
254 subregionOutputs[&subregion] = rvsdg::outputs(copiedNode);
255 }
256
257 // Adjust outputs of original node
258 for (auto & output : node->Outputs())
259 {
260 std::vector<rvsdg::Output *> branchResultOperands;
261 for (auto & subregion : gammaNode.Subregions())
262 {
263 branchResultOperands.push_back(subregionOutputs[&subregion][output.index()]);
264 }
265
266 auto [_, exitVarOutput] = gammaNode.AddExitVar(branchResultOperands);
267 output.divert_users(exitVarOutput);
268 }
269 }
270
271 return dependentNodes.Size();
272}
273
274static size_t
276{
277 JLM_ASSERT(isSingleSuccessor(*node, *gamma));
278
279 /* collect all gamma inputs */
280 std::unordered_set<const rvsdg::Input *> inputs;
281 for (size_t n = 0; n < node->noutputs(); n++)
282 {
283 for (const auto & user : node->output(n)->Users())
284 {
285 inputs.insert(&user);
286 }
287 }
288
289 /* collect subregions where node is used */
290 std::unordered_set<rvsdg::Region *> subregions;
291 for (const auto & input : inputs)
292 {
293 std::visit(
294 [&subregions](const auto & rolevar)
295 {
296 if constexpr (std::is_same<std::decay_t<decltype(rolevar)>, rvsdg::GammaNode::EntryVar>())
297 {
298 for (const auto & argument : rolevar.branchArgument)
299 {
300 if (argument->nusers() != 0)
301 subregions.insert(argument->region());
302 }
303 }
304 else if constexpr (std::is_same<
305 std::decay_t<decltype(rolevar)>,
307 {
308 for (const auto & argument : rolevar.matchContent)
309 {
310 if (argument->nusers() != 0)
311 subregions.insert(argument->region());
312 }
313 }
314 else
315 {
316 JLM_UNREACHABLE("A gamma input must either be the match variable or an entry variable");
317 }
318 },
319 gamma->MapInput(*input));
320 }
321
322 return subregions.size();
323}
324
325void
327{
328 /*
329 We don't want to pull anything into empty gammas with two subregions,
330 as they are translated to select instructions in the r2j phase.
331 */
332 if (gamma->nsubregions() == 2 && empty(gamma))
333 return;
334
335 auto prednode = rvsdg::TryGetOwnerNode<rvsdg::Node>(*gamma->predicate()->origin());
336
337 /* FIXME: This is inefficient. We can do better. */
338 auto evs = gamma->GetEntryVars();
339 size_t index = 0;
340 while (index < evs.size())
341 {
342 const auto & ev = evs[index];
343 auto node = rvsdg::TryGetOwnerNode<rvsdg::Node>(*ev.input->origin());
344 if (!node || prednode == node || !isSingleSuccessor(*node, *gamma))
345 {
346 index++;
347 continue;
348 }
349
350 if (is_used_in_nsubregions(gamma, node) == 1)
351 {
352 /*
353 FIXME: This function pulls in the node to ALL subregions and
354 not just the one we care about.
355 */
356 pullin_node(gamma, node);
357 cleanup(gamma, node);
358 evs = gamma->GetEntryVars();
359 index = 0;
360 }
361 else
362 {
363 index++;
364 }
365 }
366}
367
368void
370{
371 for (auto & node : rvsdg::TopDownTraverser(region))
372 {
373 if (auto structnode = dynamic_cast<rvsdg::StructuralNode *>(node))
374 {
375 if (auto gamma = dynamic_cast<rvsdg::GammaNode *>(node))
376 pull(gamma);
377
378 for (size_t n = 0; n < structnode->nsubregions(); n++)
379 pull(structnode->subregion(n));
380 }
381 }
382}
383
384static void
386{
387 auto statistics = NodeSinking::Statistics::Create(module.SourceFilePath().value());
388
389 statistics->start(module.Rvsdg());
390 pull(&module.Rvsdg().GetRootRegion());
391 statistics->end(module.Rvsdg());
392
393 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
394}
395
396NodeSinking::~NodeSinking() noexcept = default;
397
398void
399NodeSinking::Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector)
400{
401 pull(module, statisticsCollector);
402}
403
404}
static jlm::util::StatisticsCollector statisticsCollector
std::vector< rvsdg::Node * > nodes
void end(const rvsdg::Graph &graph) noexcept
Definition pull.cpp:35
Statistics(const util::FilePath &sourceFile)
Definition pull.cpp:23
void start(const rvsdg::Graph &graph) noexcept
Definition pull.cpp:28
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition pull.cpp:42
Node Sinking Optimization.
Definition pull.hpp:24
static std::vector< rvsdg::Node * > sortByDepth(const util::HashSet< rvsdg::Node * > &nodes)
Definition pull.cpp:188
static util::HashSet< rvsdg::Node * > collectDependentNodes(const rvsdg::Node &node)
Definition pull.cpp:162
static size_t sinkDependentNodesIntoGamma(rvsdg::GammaNode &gammaNode)
Definition pull.cpp:216
~NodeSinking() noexcept override
Conditional operator / pattern matching.
Definition gamma.hpp:99
void RemoveEntryVars(const std::vector< EntryVar > &entryVars)
Removes the given entry variables.
Definition gamma.cpp:459
ExitVar MapOutputExitVar(const rvsdg::Output &output) const
Maps gamma output to exit variable description.
Definition gamma.cpp:397
std::variant< MatchVar, EntryVar > MapInput(const rvsdg::Input &input) const
Maps gamma input to its role (match variable or entry variable).
Definition gamma.cpp:316
EntryVar AddEntryVar(rvsdg::Output *origin)
Routes a variable into the gamma branches.
Definition gamma.cpp:260
std::vector< EntryVar > GetEntryVars() const
Gets all entry variables for this gamma.
Definition gamma.cpp:305
rvsdg::Input * predicate() const noexcept
Definition gamma.hpp:487
ExitVar AddExitVar(const std::vector< rvsdg::Output * > &values)
Routes per-branch result of gamma to output.
Definition gamma.cpp:362
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
Output * origin() const noexcept
Definition node.hpp:58
NodeInput * input(size_t index) const noexcept
Definition node.hpp:615
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
OutputIteratorRange Outputs() noexcept
Definition node.hpp:657
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
UsersRange Users()
Definition node.hpp:354
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
size_t numNodes() const noexcept
Definition region.hpp:510
const std::optional< util::FilePath > & SourceFilePath() const noexcept
Graph & Rvsdg() noexcept
SubregionIteratorRange Subregions()
rvsdg::Region * subregion(size_t index) const noexcept
size_t nsubregions() const noexcept
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
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
Global memory state passed between functions.
static void pullin_node(rvsdg::GammaNode *gamma, rvsdg::Node *node)
Definition pull.cpp:87
static void cleanup(rvsdg::GammaNode *gamma, rvsdg::Node *node)
Definition pull.cpp:117
static size_t is_used_in_nsubregions(const rvsdg::GammaNode *gamma, const rvsdg::Node *node)
Definition pull.cpp:275
static bool empty(const rvsdg::GammaNode *gamma)
Definition pull.cpp:49
void pullin_top(rvsdg::GammaNode *gamma)
Definition pull.cpp:135
static bool isSingleSuccessor(const rvsdg::Node &node, const rvsdg::Node &singleSuccessor)
Definition pull.cpp:71
void pull(rvsdg::GammaNode *gamma)
Definition pull.cpp:326
std::unordered_map< const Node *, size_t > computeDepthMap(const Region &region)
Definition region.cpp:788
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition node.hpp:1058
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.
A variable routed into all gamma regions.
Definition gamma.hpp:131
The match/discriminator variable of this gamma node.
Definition gamma.hpp:116