Jlm
CommonNodeElimination.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3  * Copyright 2025 Håvard Krogstie <krogstie.havard@gmail.com>
4  * See COPYING for terms of redistribution.
5  */
6 
10 #include <jlm/rvsdg/gamma.hpp>
11 #include <jlm/rvsdg/MatchType.hpp>
12 #include <jlm/rvsdg/theta.hpp>
13 #include <jlm/rvsdg/traverser.hpp>
14 #include <jlm/util/Statistics.hpp>
15 #include <jlm/util/time.hpp>
16 #include <map>
17 
18 namespace jlm::llvm
19 {
20 
22 {
23  const char * MarkTimerLabel_ = "MarkTime";
24  const char * DivertTimerLabel_ = "DivertTime";
25 
26 public:
27  ~Statistics() override = default;
28 
29  explicit Statistics(const util::FilePath & sourceFile)
30  : util::Statistics(Statistics::Id::CommonNodeElimination, sourceFile)
31  {}
32 
33  void
34  startMarkStatistics(const rvsdg::Graph & graph) noexcept
35  {
38  }
39 
40  void
41  endMarkStatistics() noexcept
42  {
44  }
45 
46  void
48  {
50  }
51 
52  void
53  endDivertStatistics(const rvsdg::Graph & graph) noexcept
54  {
57  }
58 
59  static std::unique_ptr<Statistics>
60  Create(const util::FilePath & sourceFile)
61  {
62  return std::make_unique<Statistics>(sourceFile);
63  }
64 };
65 
77 {
78 public:
94  {
96  : leader(&leader)
97  {}
98 
99  // The set leader. Never changes.
100  // Once an output has become a leader, it will never be a follower.
102  // The set of followers. Can both grow and shrink during the marking phase.
103  // Does not include the leader.
105  };
106 
107  using CongruenceSetIndex = size_t;
108  static constexpr auto NoCongruenceSetIndex = std::numeric_limits<CongruenceSetIndex>::max();
109 
115  [[nodiscard]] CongruenceSetIndex
117  {
118  return sets_.size();
119  }
120 
127  [[nodiscard]] CongruenceSetIndex
128  tryGetSetFor(const rvsdg::Output & output) const
129  {
130  if (const auto it = congruenceSetMapping_.find(&output); it != congruenceSetMapping_.end())
131  {
132  return it->second;
133  }
134  return NoCongruenceSetIndex;
135  }
136 
143  [[nodiscard]] CongruenceSetIndex
144  getSetFor(const rvsdg::Output & output) const
145  {
146  const auto index = tryGetSetFor(output);
147  if (index == NoCongruenceSetIndex)
148  throw std::logic_error("Output does not belong to a congruence set");
149  return index;
150  }
151 
157  [[nodiscard]] bool
158  hasSet(const rvsdg::Output & output) const
159  {
160  return tryGetSetFor(output) != NoCongruenceSetIndex;
161  }
162 
172  {
173  // The index of the new set, if this operation actually creates one
174  auto nextSet = sets_.size();
175  auto [it, added] = congruenceSetMapping_.try_emplace(&leader, nextSet);
176 
177  if (!added)
178  {
179  // If the leader already has its own set, we are done
180  if (sets_[it->second].leader == &leader)
181  {
182  return it->second;
183  }
184 
185  // Remove the output from the congruence set it is following
186  sets_[it->second].followers.Remove(&leader);
187  it->second = nextSet;
188  }
189 
190  // Create the new set, lead by \p leader
191  sets_.emplace_back(leader);
192  return nextSet;
193  }
194 
200  const rvsdg::Output &
202  {
203  JLM_ASSERT(index < sets_.size());
204  return *sets_[index].leader;
205  }
206 
215  void
217  {
218  JLM_ASSERT(index < sets_.size());
219 
220  const bool newFollower = sets_[index].followers.insert(&follower);
221 
222  // If the follower is already following the correct set, do nothing
223  if (!newFollower)
224  return;
225 
226  const auto [it, added] = congruenceSetMapping_.try_emplace(&follower, index);
227 
228  // If the follower already belonged to a congruence set, remove it from the old set
229  if (!added)
230  {
231  JLM_ASSERT(it->second != index);
232 
233  if (sets_[it->second].leader == &follower)
234  throw std::logic_error("Cannot turn a leader into a follower");
235 
236  const bool removed = sets_[it->second].followers.Remove(&follower);
237  JLM_ASSERT(removed);
238 
239  it->second = index;
240  }
241  }
242 
250  {
251  JLM_ASSERT(index < sets_.size());
252  return sets_[index].followers;
253  }
254 
255 private:
256  // The list of congruence sets
257  std::vector<CongruenceSet> sets_;
258  // A mapping from each output to the congruence set it belongs to, either as leader or follower
259  std::unordered_map<const rvsdg::Output *, CongruenceSetIndex> congruenceSetMapping_;
260 };
261 
271 static bool
273  const rvsdg::Output & o1,
274  const rvsdg::Output & o2,
276 {
277  if (*o1.Type() != *o2.Type())
278  return false;
279 
280  const auto o1Set = context.getSetFor(o1);
281  const auto o2Set = context.getSetFor(o2);
282 
283  return o1Set == o2Set;
284 }
285 
298 static bool
300  const rvsdg::Node & node1,
301  const rvsdg::Node & node2,
303 {
304  const auto simpleNode1 = dynamic_cast<const rvsdg::SimpleNode *>(&node1);
305  const auto simpleNode2 = dynamic_cast<const rvsdg::SimpleNode *>(&node2);
306  if (!simpleNode1 || !simpleNode2)
307  return false;
308 
309  if (simpleNode1->ninputs() != simpleNode2->ninputs())
310  return false;
311 
312  if (simpleNode1->GetOperation() != simpleNode2->GetOperation())
313  return false;
314 
315  // For each pair of corresponding inputs of simpleNode1 and simpleNode2,
316  // they must have origins that are congruent
317  for (auto & input : simpleNode1->Inputs())
318  {
319  const auto origin1 = input.origin();
320  const auto origin2 = simpleNode2->input(input.index())->origin();
321  if (!areOutputsCongruent(*origin1, *origin2, context))
322  return false;
323  }
324 
325  return true;
326 }
327 
333 void
335 {
336  for (auto & output : leader.Outputs())
337  {
338  context.getOrCreateSetForLeader(output);
339  }
340 }
341 
352 void
354  const rvsdg::Node & leader,
355  const rvsdg::Node & follower,
357 {
358  JLM_ASSERT(leader.noutputs() == follower.noutputs());
359  JLM_ASSERT(leader.region() == follower.region());
360 
361  for (size_t i = 0; i < leader.noutputs(); i++)
362  {
363  const auto & leaderOutput = *leader.output(i);
364  const auto & followerOutput = *follower.output(i);
365  const auto leaderSet = context.getSetFor(leaderOutput);
366  context.addFollower(leaderSet, followerOutput);
367  }
368 }
369 
378 [[nodiscard]] const rvsdg::Node *
380 {
381  // Nodes with 0 outputs are never congruent with anything, so let the node be its own leader.
382  if (node.noutputs() == 0)
383  return &node;
384 
385  // Check the congruence set of the first output
386  const auto output0Set = context.tryGetSetFor(*node.output(0));
387  // If the output has not gotten a congruence set yet, the node has yet to be marked
389  return nullptr;
390 
391  // Structural nodes can have outputs that are congruent with completely different nodes
392  // due to invariant values. Structural nodes are always considered their own leaders.
393  if (dynamic_cast<const rvsdg::StructuralNode *>(&node))
394  return &node;
395 
396  // A simple node can only be congruent with other simple nodes
397  const auto & output0Leader = context.getLeader(output0Set);
398  const auto & leaderNode = rvsdg::AssertGetOwnerNode<rvsdg::SimpleNode>(output0Leader);
399  JLM_ASSERT(leaderNode.noutputs() == node.noutputs());
400  return &leaderNode;
401 }
402 
403 using TopNodeLeaderList = std::vector<const rvsdg::Node *>;
404 
413 static void
415  const rvsdg::SimpleNode & node,
416  TopNodeLeaderList & leaders,
418 {
419  // If the node has already been marked, check if it is still congruent with its leader
420  if (const auto existingLeaderNode = tryGetLeaderNode(node, context))
421  {
422  // We are our own leader, nothing to check
423  if (existingLeaderNode == &node)
424  {
425  leaders.push_back(&node);
426  return;
427  }
428 
429  // Check if we are still congruent with the leader
430  if (checkNodesCongruent(*existingLeaderNode, node, context))
431  {
432  return;
433  }
434 
435  // This node is no longer congruent with its leader, continue looking for a new leader
436  }
437 
438  // TODO: Use some sort of hashing to make this not O(n * m)
439  // where n is the number of outputs and m is the number of congruence sets
440 
441  // Check if the node is congruent with any existing leader in the TopNodeLeaderList
442  for (auto leader : leaders)
443  {
444  if (checkNodesCongruent(node, *leader, context))
445  {
446  markNodesAsCongruent(*leader, node, context);
447  return;
448  }
449  }
450 
451  // No existing leader top node found, create new congruence sets for each of node's outputs
452  markNodeAsLeader(node, context);
453  leaders.push_back(&node);
454 }
455 
464 static void
466 {
467  // This function should never be called for TopNodes
468  JLM_ASSERT(node.ninputs() > 0);
469 
470  // If node is already in a congruence set, check that it actually belongs there
471  if (const auto leaderNode = tryGetLeaderNode(node, context))
472  {
473  // If node is its own leader, it definitely belongs, and we are done
474  if (leaderNode == &node)
475  return;
476 
477  // Double-check that we are congruent with our leader
478  if (checkNodesCongruent(node, *leaderNode, context))
479  return;
480 
481  // Otherwise we need to continue looking for a new leader
482  }
483 
484  // This function looks at all nodes that take the given output as the origin for its first input.
485  // If the other node is its own leader, and the current node is congruent with it,
486  // the current node becomes a follower.
487  const auto tryFindCongruentUserOf = [&](const rvsdg::Output & output) -> bool
488  {
489  // TODO: It would be possible to maintain a list of only users that are leader nodes,
490  // to avoid needing to check every user
491  for (auto & user : output.Users())
492  {
493  if (user.index() != 0)
494  continue;
495 
496  const auto otherNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(user);
497  if (!otherNode)
498  continue;
499 
500  // Do not compare against ourselves
501  if (otherNode == &node)
502  continue;
503 
504  // Only consider other nodes that are leaders
505  auto otherNodeLeader = tryGetLeaderNode(*otherNode, context);
506  if (otherNode != otherNodeLeader)
507  continue;
508 
509  if (checkNodesCongruent(node, *otherNode, context))
510  {
511  // When nodes are congruent, they should always have the same amount of outputs
512  JLM_ASSERT(node.noutputs() == otherNode->noutputs());
513 
514  markNodesAsCongruent(*otherNode, node, context);
515  return true;
516  }
517  }
518  return false;
519  };
520 
521  // Use the origin of the first input to find other potential candidates
522  const auto origin = node.input(0)->origin();
523  const auto origin0Set = context.getSetFor(*origin);
524  const auto & origin0Leader = context.getLeader(origin0Set);
525  const auto & origin0Followers = context.getFollowers(origin0Set);
526  if (tryFindCongruentUserOf(origin0Leader))
527  return;
528  for (auto follower : origin0Followers.Items())
529  {
530  if (tryFindCongruentUserOf(*follower))
531  return;
532  }
533 
534  // If we got here, no congruent node was found, so make the node its own leader
535  markNodeAsLeader(node, context);
536 }
537 
538 static void
539 markRegion(const rvsdg::Region &, CommonNodeElimination::Context & context);
540 
546 static void
548 {
549  // FIXME: Multiple imports with identical names could in theory be aliases.
550  // For now this function ignores that and makes all imports distinct. This is sound
551  for (auto argument : region.Arguments())
552  {
553  context.getOrCreateSetForLeader(*argument);
554  }
555 }
556 
570 static bool
572  const rvsdg::Region & region,
573  const std::vector<CommonNodeElimination::Context::CongruenceSetIndex> & partitions,
575 {
576  JLM_ASSERT(region.narguments() == partitions.size());
577 
578  const auto numCongruenceSets = context.numCongruenceSets();
579 
580  // Keys in the map are (old congruence set index, provided partition key)
581  // Values in the map are the new congruence set indices
582  std::map<
583  std::pair<
587  newSets;
588  for (auto argument : region.Arguments())
589  {
590  const auto currentPartition = context.tryGetSetFor(*argument);
591  const auto key = std::make_pair(currentPartition, partitions[argument->index()]);
592 
593  // If this argument is the first with the given key, it should be a leader
594  // otherwise it should be a follower
595 
596  if (const auto it = newSets.find(key); it != newSets.end())
597  {
598  // This argument should be a follower of the given congruence set
599  const auto toFollow = it->second;
600 
601  // If we are already a follower, we are done
602  if (currentPartition == toFollow)
603  continue;
604 
605  // Start following our leader
606  context.addFollower(toFollow, *argument);
607  }
608  else
609  {
610  // This argument should be the leader of its congruence set
611  newSets.emplace(key, context.getOrCreateSetForLeader(*argument));
612  }
613  }
614 
615  // Return true iff any new congruence sets were created
616  return context.numCongruenceSets() != numCongruenceSets;
617 }
618 
636 static bool
638  const rvsdg::StructuralNode & node,
640 {
641  bool anyChanges = false;
642 
643  for (auto & subregion : node.Subregions())
644  {
645  // create a partitioning of the region arguments
646  std::vector<size_t> partitions(subregion.narguments());
647  // Arguments that do not belong to any input are given partition keys higher than any real index
648  size_t nextUniquePartitionKey = context.numCongruenceSets();
649 
650  for (const auto argument : subregion.Arguments())
651  {
652  if (const auto input = argument->input())
653  {
654  // If the argument corresponds to an input, use the partition key of the input
655  partitions[argument->index()] = context.getSetFor(*input->origin());
656  }
657  else
658  {
659  // Otherwise make sure the argument is not partitioned with any other argument
660  partitions[argument->index()] = nextUniquePartitionKey++;
661  }
662  }
663 
664  if (partitionArguments(subregion, partitions, context))
665  {
666  anyChanges = true;
667  markRegion(subregion, context);
668  }
669  }
670 
671  return anyChanges;
672 }
673 
684 static std::optional<CommonNodeElimination::Context::CongruenceSetIndex>
686  rvsdg::GammaNode::ExitVar & exitVar,
688 {
689  std::optional<CommonNodeElimination::Context::CongruenceSetIndex> sharedCongruenceSet;
690 
691  for (auto result : exitVar.branchResult)
692  {
693  if (const auto argument = dynamic_cast<rvsdg::RegionArgument *>(result->origin()))
694  {
695  const auto inputCongruenceSet = context.getSetFor(*argument->input()->origin());
696  if (!sharedCongruenceSet.has_value())
697  {
698  sharedCongruenceSet = inputCongruenceSet;
699  }
700  else if (*sharedCongruenceSet != inputCongruenceSet)
701  {
702  // We have multiple different non-congruent origins
703  return std::nullopt;
704  }
705  }
706  else
707  {
708  // The branch result was not invariant
709  return std::nullopt;
710  }
711  }
712 
713  return sharedCongruenceSet;
714 }
715 
723 static void
725 {
726  markSubregionsFromInputs(gamma, context);
727 
728  if (tryGetLeaderNode(gamma, context))
729  return;
730 
731  // Go through the outputs of the gamma node and create congruence sets for them.
732  // By default, each exit variable output gets a distinct congruence set.
733  // Only if an exit variable is a copy of an entry variable in each region,
734  // and all the entry variables have congruent origins, does the gamma output become a follower.
735  for (auto exitVar : gamma.GetExitVars())
736  {
737  if (const auto entryVarCongruenceSet = tryGetGammaExitVarCongruenceSet(exitVar, context);
738  entryVarCongruenceSet.has_value())
739  {
740  context.addFollower(*entryVarCongruenceSet, *exitVar.output);
741  }
742  else
743  {
744  context.getOrCreateSetForLeader(*exitVar.output);
745  }
746  }
747 }
748 
760 static void
762 {
763  bool anyChanges = markSubregionsFromInputs(theta, context);
764  if (!anyChanges)
765  return;
766 
767  const auto loopVars = theta.GetLoopVars();
768 
769  // Use the loop variable post results to refine partitioning of loop variable arguments
770  while (anyChanges)
771  {
772  // Create partition keys for each loop variable
773  std::vector<CommonNodeElimination::Context::CongruenceSetIndex> partitions;
774  for (const auto & loopVar : loopVars)
775  {
776  partitions.push_back(context.getSetFor(*loopVar.post->origin()));
777  }
778 
779  anyChanges = partitionArguments(*theta.subregion(), partitions, context);
780  if (anyChanges)
781  {
782  // Propagate refinement of argument congruence sets into the region
783  markRegion(*theta.subregion(), context);
784  }
785  }
786 
787  // Partition theta outputs
788  std::unordered_map<
791  resultToOutputSetMapping;
792  for (auto & loopVar : loopVars)
793  {
794  // invariant loop variables become followers of the input origin
795  if (rvsdg::ThetaLoopVarIsInvariant(loopVar))
796  {
797  const auto inputOriginSet = context.getSetFor(*loopVar.input->origin());
798  context.addFollower(inputOriginSet, *loopVar.output);
799  continue;
800  }
801 
802  // Other loop variable outputs are partitioned based on the origins of the post results.
803  const auto resultSet = context.getSetFor(*loopVar.post->origin());
804  const auto it = resultToOutputSetMapping.find(resultSet);
805  if (it != resultToOutputSetMapping.end())
806  {
807  context.addFollower(it->second, *loopVar.output);
808  }
809  else
810  {
811  const auto outputSet = context.getOrCreateSetForLeader(*loopVar.output);
812  resultToOutputSetMapping.emplace(resultSet, outputSet);
813  }
814  }
815 }
816 
817 static void
819 {
821  node,
822  [&](const rvsdg::GammaNode & gamma)
823  {
824  markGamma(gamma, context);
825  },
826  [&](const rvsdg::ThetaNode & theta)
827  {
828  markTheta(theta, context);
829  },
830  [&](const rvsdg::LambdaNode & lambda)
831  {
832  // Context variables are congruent if their origins are congruent.
833  // All other arguments are given distinct congruence sets.
834  markSubregionsFromInputs(lambda, context);
835 
836  // A lambda output is always unique
837  markNodeAsLeader(lambda, context);
838  },
839  [&](const rvsdg::PhiNode & phi)
840  {
841  // Context variables are congruent if their origins are congruent.
842  // Recursion variables are given distinct congruence sets.
843  markSubregionsFromInputs(phi, context);
844 
845  // A phi node is always unique
846  markNodeAsLeader(phi, context);
847  },
848  [&](const rvsdg::DeltaNode & delta)
849  {
850  // We skip doing CNE inside delta nodes
851 
852  // A delta node is always unique
853  markNodeAsLeader(delta, context);
854  });
855 }
856 
864 static void
866 {
867  TopNodeLeaderList leaders;
868 
869  for (const auto node : rvsdg::TopDownConstTraverser(&region))
870  {
872  *node,
873  [&](const rvsdg::SimpleNode & simple)
874  {
875  // Handle top nodes as a special case
876  if (node->ninputs() == 0)
877  {
878  markSimpleTopNode(simple, leaders, context);
879  }
880  else
881  {
882  markSimpleNode(simple, context);
883  }
884  },
885  [&](const rvsdg::StructuralNode & structural)
886  {
887  markStructuralNode(structural, context);
888  });
889  }
890 }
891 
892 /* divert phase */
893 
894 static void
896 {
897  const auto outputSet = context.getSetFor(output);
898 
899  auto & leader = context.getLeader(outputSet);
900  if (&leader == &output)
901  return;
902 
903  output.divert_users(const_cast<rvsdg::Output *>(&leader));
904 }
905 
906 static void
907 divertInRegion(rvsdg::Region &, CommonNodeElimination::Context &);
908 
909 static void
911 {
912  bool divertInSubregions = false;
914  node,
915  [&]([[maybe_unused]] rvsdg::GammaNode & gamma)
916  {
917  divertInSubregions = true;
918  },
919  [&]([[maybe_unused]] rvsdg::ThetaNode & theta)
920  {
921  divertInSubregions = true;
922  },
923  [&]([[maybe_unused]] rvsdg::LambdaNode & lambda)
924  {
925  divertInSubregions = true;
926  },
927  [&]([[maybe_unused]] rvsdg::PhiNode & phi)
928  {
929  divertInSubregions = true;
930  },
931  [&]([[maybe_unused]] rvsdg::DeltaNode & delta)
932  {
933  // Inside a delta node we can not perform diverting,
934  // since we never marked the outputs there
935  });
936 
937  if (divertInSubregions)
938  {
939  for (auto & subregion : node.Subregions())
940  {
941  divertInRegion(subregion, context);
942  }
943  }
944 }
945 
946 static void
948 {
949  // First divert all region arguments
950  for (auto argument : region.Arguments())
951  {
952  divertOutput(*argument, context);
953  }
954 
955  // Divert all nodes
956  for (const auto node : rvsdg::TopDownTraverser(&region))
957  {
958  // When diverting structural nodes, also recurse into their subregions
960  *node,
961  [&](rvsdg::StructuralNode & structural)
962  {
963  divertInStructuralNode(structural, context);
964  });
965 
966  // Divert all outputs of the node
967  for (auto & output : node->Outputs())
968  {
969  divertOutput(output, context);
970  }
971  }
972 }
973 
975 
976 void
978  rvsdg::RvsdgModule & module,
979  util::StatisticsCollector & statisticsCollector)
980 {
981  const auto & rvsdg = module.Rvsdg();
982  auto & rootRegion = rvsdg.GetRootRegion();
983 
984  Context context;
985  auto statistics = Statistics::Create(module.SourceFilePath().value());
986 
987  statistics->startMarkStatistics(rvsdg);
988  markGraphImports(rootRegion, context);
989  markRegion(rootRegion, context);
990  statistics->endMarkStatistics();
991 
992  statistics->startDivertStatistics();
993  divertInRegion(rootRegion, context);
994  statistics->endDivertStatistics(rvsdg);
995 
996  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
997 }
998 }
CongruenceSetIndex getOrCreateSetForLeader(const rvsdg::Output &leader)
void addFollower(CongruenceSetIndex index, const rvsdg::Output &follower)
const util::HashSet< const rvsdg::Output * > & getFollowers(CongruenceSetIndex index) const
bool hasSet(const rvsdg::Output &output) const
CongruenceSetIndex getSetFor(const rvsdg::Output &output) const
CongruenceSetIndex tryGetSetFor(const rvsdg::Output &output) const
const rvsdg::Output & getLeader(CongruenceSetIndex index) const
std::unordered_map< const rvsdg::Output *, CongruenceSetIndex > congruenceSetMapping_
void startMarkStatistics(const rvsdg::Graph &graph) noexcept
void endDivertStatistics(const rvsdg::Graph &graph) noexcept
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Statistics(const util::FilePath &sourceFile)
Common Node Elimination Discovers simple nodes, region arguments and structural node outputs that are...
~CommonNodeElimination() noexcept override
Delta node.
Definition: delta.hpp:129
Conditional operator / pattern matching.
Definition: gamma.hpp:99
std::vector< ExitVar > GetExitVars() const
Gets all exit variables for this gamma.
Definition: gamma.cpp:361
Output * origin() const noexcept
Definition: node.hpp:58
Lambda node.
Definition: lambda.hpp:83
OutputIteratorRange Outputs() noexcept
Definition: node.hpp:657
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
NodeOutput * output(size_t index) const noexcept
Definition: node.hpp:650
size_t ninputs() const noexcept
Definition: node.hpp:609
size_t noutputs() const noexcept
Definition: node.hpp:644
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
void divert_users(jlm::rvsdg::Output *new_origin)
Definition: node.hpp:301
A phi node represents the fixpoint of mutually recursive definitions.
Definition: Phi.hpp:46
Represents the argument of a region.
Definition: region.hpp:41
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
RegionArgumentRange Arguments() noexcept
Definition: region.hpp:272
size_t narguments() const noexcept
Definition: region.hpp:431
NodeInput * input(size_t index) const noexcept
Definition: simple-node.hpp:82
SubregionIteratorRange Subregions()
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition: theta.cpp:176
rvsdg::Region * subregion() const noexcept
Definition: theta.hpp:79
Statistics Interface.
Definition: Statistics.hpp:31
util::Timer & GetTimer(const std::string &name)
Definition: Statistics.cpp:134
util::Timer & AddTimer(std::string name)
Definition: Statistics.cpp:155
void AddMeasurement(std::string name, T value)
Definition: Statistics.hpp:174
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.
static bool partitionArguments(const rvsdg::Region &region, const std::vector< CommonNodeElimination::Context::CongruenceSetIndex > &partitions, CommonNodeElimination::Context &context)
static void markSimpleTopNode(const rvsdg::SimpleNode &node, TopNodeLeaderList &leaders, CommonNodeElimination::Context &context)
static bool markSubregionsFromInputs(const rvsdg::StructuralNode &node, CommonNodeElimination::Context &context)
static void markGamma(const rvsdg::GammaNode &gamma, CommonNodeElimination::Context &context)
static void markGraphImports(const rvsdg::Region &region, CommonNodeElimination::Context &context)
static bool checkNodesCongruent(const rvsdg::Node &node1, const rvsdg::Node &node2, CommonNodeElimination::Context &context)
const rvsdg::Node * tryGetLeaderNode(const rvsdg::Node &node, CommonNodeElimination::Context &context)
static void divertOutput(rvsdg::Output &output, CommonNodeElimination::Context &context)
static void divertInRegion(rvsdg::Region &, CommonNodeElimination::Context &)
void markNodeAsLeader(const rvsdg::Node &leader, CommonNodeElimination::Context &context)
std::vector< const rvsdg::Node * > TopNodeLeaderList
static void markRegion(const rvsdg::Region &, CommonNodeElimination::Context &context)
static bool areOutputsCongruent(const rvsdg::Output &o1, const rvsdg::Output &o2, CommonNodeElimination::Context &context)
void markNodesAsCongruent(const rvsdg::Node &leader, const rvsdg::Node &follower, CommonNodeElimination::Context &context)
static void divertInStructuralNode(rvsdg::StructuralNode &node, CommonNodeElimination::Context &context)
static void markTheta(const rvsdg::ThetaNode &theta, CommonNodeElimination::Context &context)
static std::optional< CommonNodeElimination::Context::CongruenceSetIndex > tryGetGammaExitVarCongruenceSet(rvsdg::GammaNode::ExitVar &exitVar, CommonNodeElimination::Context &context)
static void markStructuralNode(const rvsdg::StructuralNode &node, CommonNodeElimination::Context &context)
static void markSimpleNode(const rvsdg::SimpleNode &node, CommonNodeElimination::Context &context)
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
static bool ThetaLoopVarIsInvariant(const ThetaNode::LoopVar &loopVar) noexcept
Definition: theta.hpp:227
void MatchType(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
size_t ninputs(const rvsdg::Region *region) noexcept
Definition: region.cpp:682
A variable routed out of all gamma regions as result.
Definition: gamma.hpp:146
std::vector< rvsdg::Input * > branchResult
Variable exit points (results per subregion).
Definition: gamma.hpp:150
static const char * NumRvsdgInputsAfter
Definition: Statistics.hpp:215
static const char * NumRvsdgInputsBefore
Definition: Statistics.hpp:214