Jlm
Loading...
Searching...
No Matches
rhls-dne.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 David Metz <david.c.metz@ntnu.no>
3 * See COPYING for terms of redistribution.
4 */
5
8#include <jlm/hls/ir/hls.hpp>
12
13namespace jlm::hls
14{
15
16static bool
18{
21 const auto subregion = loopNode->subregion();
22 for (const auto argument : subregion->Arguments())
23 {
24 if ((dynamic_cast<BackEdgeArgument *>(argument) && argument->nusers() == 1))
25 {
26 auto & user = *argument->Users().begin();
27 if (const auto result = dynamic_cast<BackEdgeResult *>(&user))
28 {
29 resultIndices.insert(result->index());
30 argumentIndices.insert(argument->index());
31 }
32 }
33 }
34
35 [[maybe_unused]] const auto numRemovedResults = subregion->RemoveResults(resultIndices);
37
38 [[maybe_unused]] const auto numRemovedArguments = subregion->RemoveArguments(argumentIndices);
40
41 return numRemovedArguments != 0;
42}
43
44static bool
46{
47 bool any_changed = false;
48 // go through in reverse because we remove some
49 for (int i = ln->noutputs() - 1; i >= 0; --i)
50 {
51 const auto out = ln->output(i);
52 if (out->nusers() == 0)
53 {
54 ln->removeLoopOutput(out);
55 any_changed = true;
56 }
57 }
58 return any_changed;
59}
60
61static bool
63{
64 bool any_changed = false;
65 // go through in reverse because we remove some
66 for (int i = ln->ninputs() - 1; i >= 0; --i)
67 {
68 const auto in = ln->input(i);
69 JLM_ASSERT(in->arguments.size() == 1);
70 const auto arg = in->arguments.begin();
71 if (arg->nusers() != 1)
72 continue;
73
74 auto & user = *arg->Users().begin();
75 if (const auto result = dynamic_cast<rvsdg::RegionResult *>(&user))
76 {
77 result->output()->divert_users(in->origin());
78 ln->removeLoopOutput(result->output());
79 ln->removeLoopInput(arg->input());
80 any_changed = true;
81 }
82 }
83 return any_changed;
84}
85
86static bool
88{
89 bool any_changed = false;
90 auto sr = ln->subregion();
91 // go through in reverse because we remove some
92 for (int i = ln->ninputs() - 1; i >= 0; --i)
93 {
94 auto in = ln->input(i);
95 JLM_ASSERT(in->arguments.size() == 1);
96 auto arg = in->arguments.begin();
97 if (arg->nusers() == 0)
98 {
99 ln->removeLoopInput(in);
100 any_changed = true;
101 }
102 }
103 // clean up unused arguments - only ones without an input should be left
104 // go through in reverse because we remove some
105 for (int i = sr->narguments() - 1; i >= 0; --i)
106 {
107 auto arg = sr->argument(i);
108 if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
109 {
110 auto result = ba->result();
111 JLM_ASSERT(*result->Type() == *arg->Type());
112 if (arg->nusers() == 0 || (arg->nusers() == 1 && result->origin() == arg))
113 {
114 sr->RemoveResults({ result->index() });
115 sr->RemoveArguments({ arg->index() });
116 }
117 }
118 else
119 {
120 JLM_ASSERT(arg->nusers() != 0);
121 }
122 }
123 return any_changed;
124}
125
126static bool
128{
129 const auto mux_op = util::assertedCast<const MuxOperation>(&dmux_node->GetOperation());
130 JLM_ASSERT(mux_op->discarding);
131 // check if all inputs have the same origin
132 bool all_inputs_same = true;
133 auto first_origin = dmux_node->input(1)->origin();
134 for (size_t i = 2; i < dmux_node->ninputs(); ++i)
135 {
136 if (dmux_node->input(i)->origin() != first_origin)
137 {
138 all_inputs_same = false;
139 break;
140 }
141 }
142 if (all_inputs_same)
143 {
144 dmux_node->output(0)->divert_users(first_origin);
146 return true;
147 }
148 return false;
149}
150
151static bool
153{
154 auto mux_op = util::assertedCast<const MuxOperation>(&ndmux_node->GetOperation());
155 JLM_ASSERT(!mux_op->discarding);
156 // check if all inputs go to outputs of same branch
157 bool all_inputs_same_branch = true;
158 rvsdg::Node * origin_branch = nullptr;
159 for (size_t i = 1; i < ndmux_node->ninputs(); ++i)
160 {
161 if (auto node = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*ndmux_node->input(i)->origin()))
162 {
163 if (dynamic_cast<const BranchOperation *>(&node->GetOperation())
164 && ndmux_node->input(i)->origin()->nusers() == 1)
165 {
166 if (i == 1)
167 {
168 origin_branch = node;
169 continue;
170 }
171 else if (origin_branch == node)
172 {
173 continue;
174 }
175 }
176 }
178 break;
179 }
180 if (all_inputs_same_branch && origin_branch->input(0)->origin() == ndmux_node->input(0)->origin())
181 {
182 // same control origin + all inputs to branch
183 ndmux_node->output(0)->divert_users(origin_branch->input(1)->origin());
185 JLM_ASSERT(origin_branch != nullptr);
187 return true;
188 }
189 return false;
190}
191
192static bool
194{
195 const auto mux_op = util::assertedCast<const MuxOperation>(&ndmux_node->GetOperation());
196 JLM_ASSERT(!mux_op->discarding);
197 // origin is a backedege argument
198 auto backedge_arg = dynamic_cast<BackEdgeArgument *>(ndmux_node->input(2)->origin());
199 if (!backedge_arg)
200 {
201 return false;
202 }
203 // one branch
204 if (ndmux_node->output(0)->nusers() != 1)
205 {
206 return false;
207 }
208 auto branch_in_node =
209 rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*ndmux_node->output(0)->Users().begin());
210 if (!branch_in_node || !dynamic_cast<const BranchOperation *>(&branch_in_node->GetOperation()))
211 {
212 return false;
213 }
214 // one buffer
215 if (branch_in_node->output(1)->nusers() != 1)
216 {
217 return false;
218 }
219 auto buf_in_node =
220 rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*branch_in_node->output(1)->Users().begin());
221 if (!buf_in_node || !dynamic_cast<const BufferOperation *>(&buf_in_node->GetOperation()))
222 {
223 return false;
224 }
225 auto buf_out = buf_in_node->output(0);
226 if (buf_out != backedge_arg->result()->origin())
227 {
228 // no connection back up
229 return false;
230 }
231 // depend on same control
232 auto branch_cond_origin = branch_in_node->input(0)->origin();
233 auto pred_buf_out_node =
236 || !dynamic_cast<const PredicateBufferOperation *>(&pred_buf_out_node->GetOperation()))
237 {
238 return false;
239 }
240 auto pred_buf_cond_origin = pred_buf_out_node->input(0)->origin();
241 // TODO: remove this once predicate buffers decouple combinatorial loops
244 || !dynamic_cast<const BufferOperation *>(&extra_buf_out_node->GetOperation()))
245 {
246 return false;
247 }
248 auto extra_buf_cond_origin = extra_buf_out_node->input(0)->origin();
249
250 if (auto pred_be = dynamic_cast<BackEdgeArgument *>(extra_buf_cond_origin))
251 {
252 extra_buf_cond_origin = pred_be->result()->origin();
253 }
255 {
256 return false;
257 }
258 // divert users
259 branch_in_node->output(0)->divert_users(ndmux_node->input(1)->origin());
260 buf_out->divert_users(backedge_arg);
263 auto region = ndmux_node->region();
265 region->RemoveResults({ backedge_arg->result()->index() });
266 region->RemoveArguments({ backedge_arg->index() });
267 return true;
268}
269
270static bool
272{
274
275 // one branch
276 if (lcb_node->output(0)->nusers() != 1)
277 {
278 return false;
279 }
282 if (!branchNode || !branchOperation || !branchOperation->loop)
283 {
284 return false;
285 }
286 // no user
287 if (branchNode->output(1)->nusers())
288 {
289 return false;
290 }
291 // depend on same control
292 auto branch_cond_origin = branchNode->input(0)->origin();
293 auto pred_buf_out = dynamic_cast<rvsdg::NodeOutput *>(lcb_node->input(0)->origin());
294 if (!pred_buf_out
295 || !dynamic_cast<const PredicateBufferOperation *>(&pred_buf_out->node()->GetOperation()))
296 {
297 return false;
298 }
300 // TODO: remove this once predicate buffers decouple combinatorial loops
302 if (!extra_buf_out
303 || !dynamic_cast<const BufferOperation *>(&extra_buf_out->node()->GetOperation()))
304 {
305 return false;
306 }
308
309 if (auto pred_be = dynamic_cast<BackEdgeArgument *>(extra_buf_cond_origin))
310 {
311 extra_buf_cond_origin = pred_be->result()->origin();
312 }
314 {
315 return false;
316 }
317 // divert users
318 branchNode->output(0)->divert_users(lcb_node->input(1)->origin());
321 return true;
322}
323
324static bool
326{
327 if (split_node->noutputs() == 1)
328 {
329 split_node->output(0)->divert_users(split_node->input(0)->origin());
330 JLM_ASSERT(split_node->IsDead());
332 return true;
333 }
334 // this merges downward and removes unused outputs (should only exist as a result of eliminating
335 // merges)
336 std::vector<rvsdg::Output *> combined_outputs;
337 for (size_t i = 0; i < split_node->noutputs(); ++i)
338 {
339 if (split_node->output(i)->IsDead())
340 continue;
341 auto user = get_mem_state_user(split_node->output(i));
343 {
345 for (size_t j = 0; j < sub_split->noutputs(); ++j)
346 {
347 combined_outputs.push_back(sub_split->output(j));
348 }
349 }
350 else
351 {
352 combined_outputs.push_back(split_node->output(i));
353 }
354 }
355 if (combined_outputs.size() != split_node->noutputs())
356 {
358 *split_node->input(0)->origin(),
359 combined_outputs.size());
360 for (size_t i = 0; i < combined_outputs.size(); ++i)
361 {
362 combined_outputs[i]->divert_users(new_outputs[i]);
363 }
364 return true;
365 }
366 return false;
367}
368
369static bool
371{
372 // remove single merge
373 if (merge_node->ninputs() == 1)
374 {
375 merge_node->output(0)->divert_users(merge_node->input(0)->origin());
376 JLM_ASSERT(merge_node->IsDead());
378 return true;
379 }
380 std::vector<rvsdg::Output *> combined_origins;
381 std::unordered_set<rvsdg::SimpleNode *> splits;
382 for (size_t i = 0; i < merge_node->ninputs(); ++i)
383 {
384 auto origin = merge_node->input(i)->origin();
386 {
388 for (size_t j = 0; j < sub_merge->ninputs(); ++j)
389 {
390 combined_origins.push_back(sub_merge->input(j)->origin());
391 }
392 }
394 {
395 // ensure that there is only one direct connection to a split.
396 // We need to keep one, so that the optimizations for decouple edges work
397 auto split = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*origin);
398 if (!splits.count(split))
399 {
400 splits.insert(split);
401 combined_origins.push_back(origin);
402 }
403 }
404 else
405 {
406 combined_origins.push_back(merge_node->input(i)->origin());
407 }
408 }
409 if (combined_origins.empty())
410 {
411 // if none of the inputs are real keep the first one
412 combined_origins.push_back(merge_node->input(0)->origin());
413 }
414 if (combined_origins.size() != merge_node->ninputs())
415 {
417 merge_node->output(0)->divert_users(new_output);
418 JLM_ASSERT(merge_node->IsDead());
419 return true;
420 }
421 return false;
422}
423
424bool
426 rvsdg::Region & region,
428{
429 bool any_changed = false;
430 bool changed = false;
431 do
432 {
433 changed = false;
434 for (auto & node : rvsdg::BottomUpTraverser(&region))
435 {
436 if (node->IsDead())
437 {
439 {
440 // TODO: fix this once memory connections are explicit
441 continue;
442 }
444 {
445 continue;
446 }
448 {
449 // TODO: fix - this scenario has only stores and should just be optimized away completely
450 continue;
451 }
452 remove(node);
453 changed = true;
454 }
455 else if (dynamic_cast<rvsdg::LambdaNode *>(node))
456 {
457 JLM_UNREACHABLE("This function works on lambda subregions");
458 }
459 else if (auto ln = dynamic_cast<LoopNode *>(node))
460 {
465 changed |= Run(*ln->subregion(), statisticsCollector);
466 }
467 else if (const auto mux = dynamic_cast<const MuxOperation *>(&node->GetOperation()))
468 {
469 if (mux->discarding)
470 {
471 changed |= dead_spec_gamma(node);
472 }
473 else
474 {
475 changed |= dead_nonspec_gamma(node) || dead_loop(node);
476 }
477 }
479 {
480 changed |= dead_loop_lcb(node);
481 }
482 else if (dynamic_cast<const llvm::MemoryStateSplitOperation *>(&node->GetOperation()))
483 {
484 if (fix_mem_split(node))
485 {
486 changed = true;
487 }
488 }
489 else if (dynamic_cast<const llvm::MemoryStateMergeOperation *>(&node->GetOperation()))
490 {
491 if (fix_mem_merge(node))
492 {
493 changed = true;
494 }
495 }
496 if (changed)
497 {
498 // Changes might break bottom up traversal
499 break;
500 }
501 }
503 } while (changed);
504
505 return any_changed;
506}
507
509
513
514void
518{
519 auto & graph = rvsdgModule.Rvsdg();
520 const auto rootRegion = &graph.GetRootRegion();
521 if (rootRegion->numNodes() != 1)
522 {
523 throw util::Error("Root should have only one node now");
524 }
525 const auto lambdaNode =
526 dynamic_cast<const rvsdg::LambdaNode *>(rootRegion->Nodes().begin().ptr());
527 if (!lambdaNode)
528 {
529 throw util::Error("Node needs to be a lambda");
530 }
531 Run(*lambdaNode->subregion(), statisticsCollector);
532}
533
534} // namespace jlm::hls
static jlm::util::StatisticsCollector statisticsCollector
~RhlsDeadNodeElimination() noexcept override
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Definition rhls-dne.cpp:515
static rvsdg::Output * Create(const std::vector< rvsdg::Output * > &operands)
static std::vector< rvsdg::Output * > Create(rvsdg::Output &operand, const size_t numResults)
Output * origin() const noexcept
Definition node.hpp:58
Node * node() const noexcept
Definition node.hpp:572
NodeInput * input(size_t index) const noexcept
Definition node.hpp:615
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
void divert_users(jlm::rvsdg::Output *new_origin)
Definition node.hpp:301
size_t nusers() const noexcept
Definition node.hpp:280
Represents the result of a region.
Definition region.hpp:120
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
Represents an RVSDG transformation.
bool insert(ItemType item)
Definition HashSet.hpp:210
#define JLM_ASSERT(x)
Definition common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
static bool remove_loop_passthrough(LoopNode *ln)
Definition rhls-dne.cpp:62
static bool dead_loop_lcb(rvsdg::Node *lcb_node)
Definition rhls-dne.cpp:271
static bool dead_loop(rvsdg::Node *ndmux_node)
Definition rhls-dne.cpp:193
static bool remove_unused_loop_outputs(LoopNode *ln)
Definition rhls-dne.cpp:45
static bool fix_mem_split(rvsdg::Node *split_node)
Definition rhls-dne.cpp:325
static bool dead_spec_gamma(rvsdg::Node *dmux_node)
Definition rhls-dne.cpp:127
static bool remove_unused_loop_inputs(LoopNode *ln)
Definition rhls-dne.cpp:87
static bool remove_unused_loop_backedges(LoopNode *loopNode)
Definition rhls-dne.cpp:17
static bool fix_mem_merge(rvsdg::Node *merge_node)
Definition rhls-dne.cpp:370
rvsdg::Input * get_mem_state_user(rvsdg::Output *state_edge)
static bool dead_nonspec_gamma(rvsdg::Node *ndmux_node)
Definition rhls-dne.cpp:152
static void remove(Node *node)
Definition region.hpp:1035
detail::BottomUpTraverserGeneric< false > BottomUpTraverser
Traverser for visiting every node in a region in a bottom up order.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872