Jlm
Loading...
Searching...
No Matches
cne.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
6#include <jlm/hls/ir/hls.hpp>
7#include <jlm/hls/opt/cne.hpp>
9#include <jlm/rvsdg/delta.hpp>
10#include <jlm/rvsdg/gamma.hpp>
11#include <jlm/rvsdg/lambda.hpp>
13#include <jlm/rvsdg/Phi.hpp>
14#include <jlm/rvsdg/theta.hpp>
17#include <jlm/util/time.hpp>
18
19namespace jlm::hls
20{
21
22using namespace jlm::rvsdg;
23
25{
26 const char * MarkTimerLabel_ = "MarkTime";
27 const char * DivertTimerLabel_ = "DivertTime";
28
29public:
30 ~Statistics() override = default;
31
35
36 void
37 start_mark_stat(const Graph & graph) noexcept
38 {
39 AddMeasurement(Label::NumRvsdgNodesBefore, rvsdg::nnodes(&graph.GetRootRegion()));
40 AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion()));
42 }
43
44 void
49
50 void
55
56 void
57 end_divert_stat(const Graph & graph) noexcept
58 {
59 AddMeasurement(Label::NumRvsdgNodesAfter, rvsdg::nnodes(&graph.GetRootRegion()));
60 AddMeasurement(Label::NumRvsdgInputsAfter, rvsdg::ninputs(&graph.GetRootRegion()));
62 }
63
64 static std::unique_ptr<Statistics>
66 {
67 return std::make_unique<Statistics>(sourceFile);
68 }
69};
70
71typedef std::unordered_set<jlm::rvsdg::Output *> congruence_set;
72
74{
75public:
76 inline void
78 {
79 auto s1 = set(o1);
80 auto s2 = set(o2);
81
82 if (s1 == s2)
83 return;
84
85 if (s2->size() < s1->size())
86 {
87 s1 = outputs_[o2];
88 s2 = outputs_[o1];
89 }
90
91 for (auto & o : *s1)
92 {
93 s2->insert(o);
94 outputs_[o] = s2;
95 }
96 }
97
98 inline void
99 mark(const Node * n1, const Node * n2)
100 {
101 JLM_ASSERT(n1->noutputs() == n2->noutputs());
102
103 for (size_t n = 0; n < n1->noutputs(); n++)
104 mark(n1->output(n), n2->output(n));
105 }
106
107 inline bool
109 {
110 if (o1 == o2)
111 return true;
112
113 auto it = outputs_.find(o1);
114 if (it == outputs_.end())
115 return false;
116
117 return it->second->find(o2) != it->second->end();
118 }
119
120 inline bool
121 congruent(const jlm::rvsdg::Input * i1, const jlm::rvsdg::Input * i2) const noexcept
122 {
123 return congruent(i1->origin(), i2->origin());
124 }
125
127 set(jlm::rvsdg::Output * output) noexcept
128 {
129 if (outputs_.find(output) == outputs_.end())
130 {
131 std::unique_ptr<congruence_set> set(new congruence_set({ output }));
132 outputs_[output] = set.get();
133 sets_.insert(std::move(set));
134 }
135
136 return outputs_[output];
137 }
138
139private:
140 std::unordered_set<std::unique_ptr<congruence_set>> sets_;
141 std::unordered_map<const jlm::rvsdg::Output *, congruence_set *> outputs_;
142};
143
145{
146public:
147 void
149 {
150 auto it = sets_.find(o1);
151 if (it != sets_.end())
152 sets_[o1].insert(o2);
153 else
154 sets_[o1] = { o2 };
155
156 it = sets_.find(o2);
157 if (it != sets_.end())
158 sets_[o2].insert(o1);
159 else
160 sets_[o2] = { o1 };
161 }
162
163 bool
165 {
166 auto it = sets_.find(o1);
167 if (it == sets_.end())
168 return false;
169
170 return it->second.find(o2) != it->second.end();
171 }
172
173private:
174 std::unordered_map<const jlm::rvsdg::Output *, std::unordered_set<const jlm::rvsdg::Output *>>
176};
177
178/* mark phase */
179
180static bool
182{
183 if (ctx.congruent(o1, o2) || vs.visited(o1, o2))
184 return true;
185
186 if (*o1->Type() != *o2->Type())
187 return false;
188
190 {
192 {
193 JLM_ASSERT(o1->region()->node() == o2->region()->node());
194 auto loopvar1 = theta1->MapPreLoopVar(*o1);
195 auto loopvar2 = theta2->MapPreLoopVar(*o2);
196 vs.insert(o1, o2);
197 auto i1 = loopvar1.input, i2 = loopvar2.input;
198 if (!congruent(loopvar1.input->origin(), loopvar2.input->origin(), vs, ctx))
199 return false;
200
201 auto output1 = o1->region()->node()->output(i1->index());
202 auto output2 = o2->region()->node()->output(i2->index());
203 return congruent(output1, output2, vs, ctx);
204 }
205 }
206
208 {
210 {
211 if (theta1 == theta2)
212 {
213 vs.insert(o1, o2);
214 auto loopvar1 = theta1->MapOutputLoopVar(*o1);
215 auto loopvar2 = theta2->MapOutputLoopVar(*o2);
216 auto r1 = loopvar1.post;
217 auto r2 = loopvar2.post;
218 return congruent(r1->origin(), r2->origin(), vs, ctx);
219 }
220 }
221 }
222
223 auto n1 = TryGetOwnerNode<Node>(*o1);
224 auto n2 = TryGetOwnerNode<Node>(*o2);
225
226 auto a1 = dynamic_cast<rvsdg::RegionArgument *>(o1);
227 auto a2 = dynamic_cast<rvsdg::RegionArgument *>(o2);
228 if (a1 && dynamic_cast<const LoopNode *>(a1->region()->node()) && a2
229 && dynamic_cast<const LoopNode *>(a2->region()->node()))
230 {
231 JLM_ASSERT(o1->region()->node() == o2->region()->node());
232 if (a1->input() && a2->input())
233 {
234 // input arguments
235 vs.insert(a1, a2);
236 return congruent(a1->input()->origin(), a2->input()->origin(), vs, ctx);
237 }
238 }
239
240 if (dynamic_cast<const rvsdg::GammaNode *>(n1) && n1 == n2)
241 {
242 auto so1 = static_cast<StructuralOutput *>(o1);
243 auto so2 = static_cast<StructuralOutput *>(o2);
244 auto r1 = so1->results.begin();
245 auto r2 = so2->results.begin();
246 for (; r1 != so1->results.end(); r1++, r2++)
247 {
248 JLM_ASSERT(r1->region() == r2->region());
249 if (!congruent(r1->origin(), r2->origin(), vs, ctx))
250 return false;
251 }
252 return true;
253 }
254
256 {
258 {
259 JLM_ASSERT(g1 == g2);
260 auto origin1 = std::visit(
261 [](const auto & rolevar) -> rvsdg::Output *
262 {
263 return rolevar.input->origin();
264 },
265 g1->MapBranchArgument(*o1));
266 auto origin2 = std::visit(
267 [](const auto & rolevar) -> rvsdg::Output *
268 {
269 return rolevar.input->origin();
270 },
271 g2->MapBranchArgument(*o2));
272 return congruent(origin1, origin2, vs, ctx);
273 }
274 }
275
277 && n1->GetOperation() == n2->GetOperation() && n1->ninputs() == n2->ninputs()
278 && o1->index() == o2->index())
279 {
280 for (size_t n = 0; n < n1->ninputs(); n++)
281 {
282 auto origin1 = n1->input(n)->origin();
283 auto origin2 = n2->input(n)->origin();
284 if (!congruent(origin1, origin2, vs, ctx))
285 return false;
286 }
287 return true;
288 }
289
290 return false;
291}
292
293static bool
299
300static void
302{
303 JLM_ASSERT(i1->node() && i1->node() == i2->node());
304 JLM_ASSERT(i1->arguments.size() == i2->arguments.size());
305
306 auto a1 = i1->arguments.begin();
307 auto a2 = i2->arguments.begin();
308 for (; a1 != i1->arguments.end(); a1++, a2++)
309 {
310 JLM_ASSERT(a1->region() == a2->region());
311 if (congruent(a1.ptr(), a2.ptr(), ctx))
312 ctx.mark(a1.ptr(), a2.ptr());
313 }
314}
315
316static void
317mark(jlm::rvsdg::Region *, Context &);
318
319static void
321{
322 /* mark entry variables */
323 for (size_t i1 = 1; i1 < node->ninputs(); i1++)
324 {
325 for (size_t i2 = i1 + 1; i2 < node->ninputs(); i2++)
326 mark_arguments(node->input(i1), node->input(i2), ctx);
327 }
328
329 for (size_t n = 0; n < node->nsubregions(); n++)
330 mark(node->subregion(n), ctx);
331
332 /* mark exit variables */
333 for (size_t o1 = 0; o1 < node->noutputs(); o1++)
334 {
335 for (size_t o2 = o1 + 1; o2 < node->noutputs(); o2++)
336 {
337 if (congruent(node->output(o1), node->output(o2), ctx))
338 ctx.mark(node->output(o1), node->output(o2));
339 }
340 }
341}
342
343static void
345{
346 /* mark loop variables */
347 for (size_t i1 = 0; i1 < theta->ninputs(); i1++)
348 {
349 for (size_t i2 = i1 + 1; i2 < theta->ninputs(); i2++)
350 {
351 auto input1 = theta->input(i1);
352 auto input2 = theta->input(i2);
353 auto loopvar1 = theta->MapInputLoopVar(*input1);
354 auto loopvar2 = theta->MapInputLoopVar(*input2);
355 if (congruent(loopvar1.pre, loopvar2.pre, ctx))
356 {
357 ctx.mark(loopvar1.pre, loopvar2.pre);
358 ctx.mark(loopvar1.output, loopvar2.output);
359 }
360 }
361 }
362
363 mark(theta->subregion(), ctx);
364}
365
366static void
368{
369 /* mark loop variables */
370 for (size_t i1 = 0; i1 < loop->ninputs(); i1++)
371 {
372 for (size_t i2 = i1 + 1; i2 < loop->ninputs(); i2++)
373 {
374 auto input1 = loop->input(i1);
375 auto input2 = loop->input(i2);
376 if (congruent(input1->arguments.first(), input2->arguments.first(), ctx))
377 {
378 ctx.mark(input1->arguments.first(), input2->arguments.first());
379 }
380 }
381 }
382 mark(loop->subregion(), ctx);
383}
384
385static void
387{
388 /* mark dependencies */
389 for (size_t i1 = 0; i1 < node->ninputs(); i1++)
390 {
391 for (size_t i2 = i1 + 1; i2 < node->ninputs(); i2++)
392 {
393 auto input1 = node->input(i1);
394 auto input2 = node->input(i2);
395 if (ctx.congruent(input1, input2))
396 ctx.mark(input1->arguments.first(), input2->arguments.first());
397 }
398 }
399
400 mark(node->subregion(), ctx);
401}
402
403static void
405{
406 auto ctxvars = phi->GetContextVars();
407
408 /* mark dependencies */
409 for (size_t i1 = 0; i1 < ctxvars.size(); ++i1)
410 {
411 for (size_t i2 = i1 + 1; i2 < ctxvars.size(); ++i2)
412 {
413 if (ctx.congruent(ctxvars[i1].input, ctxvars[i2].input))
414 {
415 ctx.mark(ctxvars[i1].inner, ctxvars[i2].inner);
416 }
417 }
418 }
419
420 mark(phi->subregion(), ctx);
421}
422
423static void
425{
427 *node,
428 [&](const GammaNode & node)
429 {
430 mark_gamma(&node, ctx);
431 },
432 [&](const ThetaNode & node)
433 {
434 mark_theta(&node, ctx);
435 },
436 [&](const LoopNode & node)
437 {
438 mark_loop(&node, ctx);
439 },
440 [&](const LambdaNode & node)
441 {
442 mark_lambda(&node, ctx);
443 },
444 [&](const PhiNode & node)
445 {
446 mark_phi(&node, ctx);
447 },
448 [&](const DeltaNode & node) { /* nothing to do */ });
449}
450
451static void
453{
454 if (node->ninputs() == 0)
455 {
456 for (const auto & other : node->region()->TopNodes())
457 {
458 if (&other != node && node->GetOperation() == other.GetOperation())
459 {
460 ctx.mark(node, &other);
461 break;
462 }
463 }
464 return;
465 }
466
467 auto set = ctx.set(node->input(0)->origin());
468 for (const auto & origin : *set)
469 {
470 for (const auto & user : origin->Users())
471 {
473 if (!other || other == node || other->GetOperation() != node->GetOperation()
474 || other->ninputs() != node->ninputs())
475 continue;
476
477 size_t n = 0;
478 for (n = 0; n < node->ninputs(); n++)
479 {
480 if (!ctx.congruent(node->input(n), other->input(n)))
481 break;
482 }
483 if (n == node->ninputs())
484 ctx.mark(node, other);
485 }
486 }
487}
488
489static void
491{
492 for (const auto & node : TopDownTraverser(region))
493 {
494 if (auto simple = dynamic_cast<const jlm::rvsdg::SimpleNode *>(node))
495 mark(simple, ctx);
496 else
497 mark(static_cast<const rvsdg::StructuralNode *>(node), ctx);
498 }
499}
500
501/* divert phase */
502
503static void
505{
506 auto set = ctx.set(output);
507 for (auto & other : *set)
508 other->divert_users(output);
509 set->clear();
510}
511
512static void
514{
515 for (size_t n = 0; n < node->noutputs(); n++)
516 divert_users(node->output(n), ctx);
517}
518
519static void
521{
522 for (size_t n = 0; n < region->narguments(); n++)
523 divert_users(region->argument(n), ctx);
524}
525
526static void
527divert(rvsdg::Region *, Context &);
528
529static void
531{
532 for (const auto & ev : gamma->GetEntryVars())
533 {
534 for (auto input : ev.branchArgument)
535 divert_users(input, ctx);
536 }
537
538 for (auto & subregion : gamma->Subregions())
539 divert(&subregion, ctx);
540
541 divert_outputs(gamma, ctx);
542}
543
544static void
546{
547 auto subregion = theta->subregion();
548
549 for (const auto & lv : theta->GetLoopVars())
550 {
551 JLM_ASSERT(ctx.set(lv.pre)->size() == ctx.set(lv.output)->size());
552 divert_users(lv.pre, ctx);
553 divert_users(lv.output, ctx);
554 }
555
556 divert(subregion, ctx);
557}
558
559static void
561{
562 auto subregion = node->subregion();
563 divert(subregion, ctx);
564}
565
566static void
568{
570 divert(node->subregion(), ctx);
571}
572
573static void
575{
577 divert(phi->subregion(), ctx);
578}
579
580static void
582{
584 *node,
585 [&](rvsdg::GammaNode & node)
586 {
587 divert_gamma(&node, ctx);
588 },
589 [&](rvsdg::ThetaNode & node)
590 {
591 divert_theta(&node, ctx);
592 },
593 [&](LoopNode & node)
594 {
595 divert_loop(&node, ctx);
596 },
597 [&](rvsdg::LambdaNode & node)
598 {
599 divert_lambda(&node, ctx);
600 },
601 [&](rvsdg::PhiNode & node)
602 {
603 divert_phi(&node, ctx);
604 },
605 [&](rvsdg::DeltaNode & node) { /* nothing to do */ });
606}
607
608static void
610{
611 for (const auto & node : TopDownTraverser(region))
612 {
613 if (auto simple = dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
615 else
616 divert(static_cast<rvsdg::StructuralNode *>(node), ctx);
617 }
618}
619
621
622void
624 rvsdg::RvsdgModule & module,
626{
627 const auto & graph = module.Rvsdg();
628
629 Context ctx;
630 auto statistics = Statistics::Create(module.SourceFilePath().value());
631
632 statistics->start_mark_stat(graph);
633 mark(&graph.GetRootRegion(), ctx);
634 statistics->end_mark_stat();
635
636 statistics->start_divert_stat();
637 divert(&graph.GetRootRegion(), ctx);
638 statistics->end_divert_stat(graph);
639
640 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
641}
642
643}
static jlm::util::StatisticsCollector statisticsCollector
Statistics(const util::FilePath &sourceFile)
Definition cne.cpp:32
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition cne.cpp:65
void start_mark_stat(const Graph &graph) noexcept
Definition cne.cpp:37
void end_divert_stat(const Graph &graph) noexcept
Definition cne.cpp:57
Common Node Elimination This is mainly a copy of the CNE optimization in the LLVM backend with the ad...
Definition cne.hpp:24
~CommonNodeElimination() noexcept override
std::unordered_set< std::unique_ptr< congruence_set > > sets_
Definition cne.cpp:140
void mark(const Node *n1, const Node *n2)
Definition cne.cpp:99
bool congruent(jlm::rvsdg::Output *o1, jlm::rvsdg::Output *o2) const noexcept
Definition cne.cpp:108
std::unordered_map< const jlm::rvsdg::Output *, congruence_set * > outputs_
Definition cne.cpp:141
void mark(jlm::rvsdg::Output *o1, jlm::rvsdg::Output *o2)
Definition cne.cpp:77
congruence_set * set(jlm::rvsdg::Output *output) noexcept
Definition cne.cpp:127
bool congruent(const jlm::rvsdg::Input *i1, const jlm::rvsdg::Input *i2) const noexcept
Definition cne.cpp:121
rvsdg::Region * subregion() const noexcept
Definition hls.hpp:725
std::unordered_map< const jlm::rvsdg::Output *, std::unordered_set< const jlm::rvsdg::Output * > > sets_
Definition cne.cpp:175
void insert(const jlm::rvsdg::Output *o1, const jlm::rvsdg::Output *o2)
Definition cne.cpp:148
bool visited(const jlm::rvsdg::Output *o1, const jlm::rvsdg::Output *o2) const
Definition cne.cpp:164
Conditional operator / pattern matching.
Definition gamma.hpp:99
std::vector< EntryVar > GetEntryVars() const
Gets all entry variables for this gamma.
Definition gamma.cpp:305
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
rvsdg::Region * region() const noexcept
Definition node.hpp:761
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
A phi node represents the fixpoint of mutually recursive definitions.
Definition Phi.hpp:46
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
Definition Phi.cpp:50
rvsdg::Region * subregion() const noexcept
Definition Phi.hpp:320
Represents the argument of a region.
Definition region.hpp:41
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
RegionArgument * argument(size_t index) const noexcept
Definition region.hpp:466
TopNodeRange TopNodes() noexcept
Definition region.hpp:356
size_t narguments() const noexcept
Definition region.hpp:460
const SimpleOperation & GetOperation() const noexcept override
NodeInput * input(size_t index) const noexcept
SubregionIteratorRange Subregions()
rvsdg::Region * subregion(size_t index) const noexcept
size_t nsubregions() const noexcept
StructuralOutput * output(size_t index) const noexcept
StructuralInput * input(size_t index) const noexcept
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition theta.cpp:176
rvsdg::Region * subregion() const noexcept
Definition theta.hpp:79
LoopVar MapInputLoopVar(const rvsdg::Input &input) const
Maps variable at entry to full varibale description.
Definition theta.cpp:130
Iterator begin() 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
static void mark_arguments(StructuralInput *i1, StructuralInput *i2, Context &ctx)
Definition cne.cpp:301
static void divert_gamma(rvsdg::GammaNode *gamma, Context &ctx)
Definition cne.cpp:530
static void mark_theta(const rvsdg::ThetaNode *theta, Context &ctx)
Definition cne.cpp:344
static void mark_gamma(const rvsdg::GammaNode *node, Context &ctx)
Definition cne.cpp:320
static void divert(rvsdg::Region *, Context &)
Definition cne.cpp:609
static void divert_theta(rvsdg::ThetaNode *theta, Context &ctx)
Definition cne.cpp:545
static void divert_arguments(rvsdg::Region *region, Context &ctx)
Definition cne.cpp:520
std::unordered_set< jlm::rvsdg::Output * > congruence_set
Definition cne.cpp:71
static void divert_users(jlm::rvsdg::Output *output, Context &ctx)
Definition cne.cpp:504
static bool congruent(Output *o1, Output *o2, VisitorSet &vs, Context &ctx)
Definition cne.cpp:181
static void mark_phi(const rvsdg::PhiNode *phi, Context &ctx)
Definition cne.cpp:404
static void mark_lambda(const rvsdg::LambdaNode *node, Context &ctx)
Definition cne.cpp:386
static void divert_outputs(Node *node, Context &ctx)
Definition cne.cpp:513
static void divert_lambda(rvsdg::LambdaNode *node, Context &ctx)
Definition cne.cpp:567
static void divert_loop(LoopNode *node, Context &ctx)
Definition cne.cpp:560
static void mark_loop(const LoopNode *loop, Context &ctx)
Definition cne.cpp:367
static void divert_phi(rvsdg::PhiNode *phi, Context &ctx)
Definition cne.cpp:574
static void mark(jlm::rvsdg::Region *, Context &)
Definition cne.cpp:490
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
size_t nnodes(const jlm::rvsdg::Region *region) noexcept
Definition region.cpp:808
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.