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 
11 #include <jlm/rvsdg/gamma.hpp>
12 #include <jlm/rvsdg/MatchType.hpp>
13 #include <jlm/rvsdg/Phi.hpp>
14 #include <jlm/rvsdg/theta.hpp>
15 #include <jlm/rvsdg/traverser.hpp>
16 #include <jlm/util/common.hpp>
17 #include <jlm/util/Hash.hpp>
18 #include <jlm/util/Statistics.hpp>
19 #include <jlm/util/time.hpp>
20 
21 #include <map>
22 #include <unordered_map>
23 
24 namespace jlm::llvm
25 {
26 
28 {
29  const char * MarkTimerLabel_ = "MarkTime";
30  const char * DivertTimerLabel_ = "DivertTime";
31 
32 public:
33  ~Statistics() override = default;
34 
35  explicit Statistics(const util::FilePath & sourceFile)
36  : util::Statistics(Statistics::Id::CommonNodeElimination, sourceFile)
37  {}
38 
39  void
40  startMarkStatistics(const rvsdg::Graph & graph) noexcept
41  {
44  }
45 
46  void
47  endMarkStatistics() noexcept
48  {
50  }
51 
52  void
54  {
56  }
57 
58  void
59  endDivertStatistics(const rvsdg::Graph & graph) noexcept
60  {
63  }
64 
65  static std::unique_ptr<Statistics>
66  Create(const util::FilePath & sourceFile)
67  {
68  return std::make_unique<Statistics>(sourceFile);
69  }
70 };
71 
83 {
84 public:
100  {
102  : leader(&leader)
103  {}
104 
105  // The set leader. Never changes.
106  // Once an output has become a leader, it will never be a follower.
108  // The set of followers. Can both grow and shrink during the marking phase.
109  // Does not include the leader.
111  };
112 
113  using CongruenceSetIndex = size_t;
114  static constexpr auto NoCongruenceSetIndex = std::numeric_limits<CongruenceSetIndex>::max();
115 
121  [[nodiscard]] CongruenceSetIndex
123  {
124  return sets_.size();
125  }
126 
133  [[nodiscard]] CongruenceSetIndex
134  tryGetSetFor(const rvsdg::Output & output) const
135  {
136  if (const auto it = congruenceSetMapping_.find(&output); it != congruenceSetMapping_.end())
137  {
138  return it->second;
139  }
140  return NoCongruenceSetIndex;
141  }
142 
149  [[nodiscard]] CongruenceSetIndex
150  getSetFor(const rvsdg::Output & output) const
151  {
152  const auto index = tryGetSetFor(output);
153  if (index == NoCongruenceSetIndex)
154  throw std::logic_error("Output does not belong to a congruence set");
155  return index;
156  }
157 
163  [[nodiscard]] bool
164  hasSet(const rvsdg::Output & output) const
165  {
166  return tryGetSetFor(output) != NoCongruenceSetIndex;
167  }
168 
178  {
179  // The index of the new set, if this operation actually creates one
180  auto nextSet = sets_.size();
181  auto [it, added] = congruenceSetMapping_.try_emplace(&leader, nextSet);
182 
183  if (!added)
184  {
185  // If the leader already has its own set, we are done
186  if (sets_[it->second].leader == &leader)
187  {
188  return it->second;
189  }
190 
191  // Remove the output from the congruence set it is following
192  sets_[it->second].followers.Remove(&leader);
193  it->second = nextSet;
194  }
195 
196  // Create the new set, lead by \p leader
197  sets_.emplace_back(leader);
198  return nextSet;
199  }
200 
206  const rvsdg::Output &
208  {
209  JLM_ASSERT(index < sets_.size());
210  return *sets_[index].leader;
211  }
212 
221  void
223  {
224  JLM_ASSERT(index < sets_.size());
225 
226  const bool newFollower = sets_[index].followers.insert(&follower);
227 
228  // If the follower is already following the correct set, do nothing
229  if (!newFollower)
230  return;
231 
232  const auto [it, added] = congruenceSetMapping_.try_emplace(&follower, index);
233 
234  // If the follower already belonged to a congruence set, remove it from the old set
235  if (!added)
236  {
237  JLM_ASSERT(it->second != index);
238 
239  if (sets_[it->second].leader == &follower)
240  throw std::logic_error("Cannot turn a leader into a follower");
241 
242  const bool removed = sets_[it->second].followers.Remove(&follower);
243  JLM_ASSERT(removed);
244 
245  it->second = index;
246  }
247  }
248 
256  {
257  JLM_ASSERT(index < sets_.size());
258  return sets_[index].followers;
259  }
260 
261 private:
262  // The list of congruence sets
263  std::vector<CongruenceSet> sets_;
264  // A mapping from each output to the congruence set it belongs to, either as leader or follower
265  std::unordered_map<const rvsdg::Output *, CongruenceSetIndex> congruenceSetMapping_;
266 };
267 
277 static bool
279  const rvsdg::Output & o1,
280  const rvsdg::Output & o2,
282 {
283  if (*o1.Type() != *o2.Type())
284  return false;
285 
286  const auto o1Set = context.getSetFor(o1);
287  const auto o2Set = context.getSetFor(o2);
288 
289  return o1Set == o2Set;
290 }
291 
304 static bool
306  const rvsdg::Node & node1,
307  const rvsdg::Node & node2,
309 {
310  const auto simpleNode1 = dynamic_cast<const rvsdg::SimpleNode *>(&node1);
311  const auto simpleNode2 = dynamic_cast<const rvsdg::SimpleNode *>(&node2);
312  if (!simpleNode1 || !simpleNode2)
313  return false;
314 
315  if (simpleNode1->ninputs() != simpleNode2->ninputs())
316  return false;
317 
318  if (simpleNode1->GetOperation() != simpleNode2->GetOperation())
319  return false;
320 
321  // For each pair of corresponding inputs of simpleNode1 and simpleNode2,
322  // they must have origins that are congruent
323  for (auto & input : simpleNode1->Inputs())
324  {
325  const auto origin1 = input.origin();
326  const auto origin2 = simpleNode2->input(input.index())->origin();
327  if (!areOutputsCongruent(*origin1, *origin2, context))
328  return false;
329  }
330 
331  return true;
332 }
333 
339 void
341 {
342  for (auto & output : leader.Outputs())
343  {
344  context.getOrCreateSetForLeader(output);
345  }
346 }
347 
358 void
360  const rvsdg::Node & leader,
361  const rvsdg::Node & follower,
363 {
364  JLM_ASSERT(leader.noutputs() == follower.noutputs());
365  JLM_ASSERT(leader.region() == follower.region());
366 
367  for (size_t i = 0; i < leader.noutputs(); i++)
368  {
369  const auto & leaderOutput = *leader.output(i);
370  const auto & followerOutput = *follower.output(i);
371  const auto leaderSet = context.getSetFor(leaderOutput);
372  context.addFollower(leaderSet, followerOutput);
373  }
374 }
375 
384 [[nodiscard]] const rvsdg::SimpleNode *
386 {
387  // Nodes with 0 outputs are never congruent with anything, so let the node be its own leader.
388  if (node.noutputs() == 0)
389  return &node;
390 
391  // Check the congruence set of the first output
392  const auto output0Set = context.tryGetSetFor(*node.output(0));
393  // If the output has not gotten a congruence set yet, the node has yet to be marked
395  return nullptr;
396 
397  // A simple node can only be congruent with other simple nodes
398  const auto & output0Leader = context.getLeader(output0Set);
399  const auto & leaderNode = rvsdg::AssertGetOwnerNode<rvsdg::SimpleNode>(output0Leader);
400  JLM_ASSERT(leaderNode.noutputs() == node.noutputs());
401  return &leaderNode;
402 }
403 
404 using TopNodeLeaderList = std::vector<const rvsdg::Node *>;
405 
414 static void
416  const rvsdg::SimpleNode & node,
417  TopNodeLeaderList & leaders,
419 {
420  // If the node has already been marked, check if it is still congruent with its leader
421  if (const auto existingLeaderNode = tryGetLeaderNode(node, context))
422  {
423  // We are our own leader, nothing to check
424  if (existingLeaderNode == &node)
425  {
426  leaders.push_back(&node);
427  return;
428  }
429 
430  // Check if we are still congruent with the leader
431  if (checkNodesCongruent(*existingLeaderNode, node, context))
432  {
433  return;
434  }
435 
436  // This node is no longer congruent with its leader, continue looking for a new leader
437  }
438 
439  // TODO: Use some sort of hashing to make this not O(n * m)
440  // where n is the number of outputs and m is the number of congruence sets
441 
442  // Check if the node is congruent with any existing leader in the TopNodeLeaderList
443  for (auto leader : leaders)
444  {
445  if (checkNodesCongruent(node, *leader, context))
446  {
447  markNodesAsCongruent(*leader, node, context);
448  return;
449  }
450  }
451 
452  // No existing leader top node found, create new congruence sets for each of node's outputs
453  markNodeAsLeader(node, context);
454  leaders.push_back(&node);
455 }
456 
465 static void
467 {
468  // This function should never be called for TopNodes
469  JLM_ASSERT(node.ninputs() > 0);
470 
471  // If node is already in a congruence set, check that it actually belongs there
472  if (const auto leaderNode = tryGetLeaderNode(node, context))
473  {
474  // If node is its own leader, it definitely belongs, and we are done
475  if (leaderNode == &node)
476  return;
477 
478  // Double-check that we are congruent with our leader
479  if (checkNodesCongruent(node, *leaderNode, context))
480  return;
481 
482  // Otherwise we need to continue looking for a new leader
483  }
484 
485  // This function looks at all nodes that take the given output as the origin for its first input.
486  // If the other node is its own leader, and the current node is congruent with it,
487  // the current node becomes a follower.
488  const auto tryFindCongruentUserOf = [&](const rvsdg::Output & output) -> bool
489  {
490  // TODO: It would be possible to maintain a list of only users that are leader nodes,
491  // to avoid needing to check every user
492  for (auto & user : output.Users())
493  {
494  if (user.index() != 0)
495  continue;
496 
497  const auto otherNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(user);
498  if (!otherNode)
499  continue;
500 
501  // Do not compare against ourselves
502  if (otherNode == &node)
503  continue;
504 
505  // Only consider other nodes that are leaders
506  auto otherNodeLeader = tryGetLeaderNode(*otherNode, context);
507  if (otherNode != otherNodeLeader)
508  continue;
509 
510  if (checkNodesCongruent(node, *otherNode, context))
511  {
512  // When nodes are congruent, they should always have the same amount of outputs
513  JLM_ASSERT(node.noutputs() == otherNode->noutputs());
514 
515  markNodesAsCongruent(*otherNode, node, context);
516  return true;
517  }
518  }
519  return false;
520  };
521 
522  // Use the origin of the first input to find other potential candidates
523  const auto origin = node.input(0)->origin();
524  const auto origin0Set = context.getSetFor(*origin);
525  const auto & origin0Leader = context.getLeader(origin0Set);
526  const auto & origin0Followers = context.getFollowers(origin0Set);
527  if (tryFindCongruentUserOf(origin0Leader))
528  return;
529  for (auto follower : origin0Followers.Items())
530  {
531  if (tryFindCongruentUserOf(*follower))
532  return;
533  }
534 
535  // If we got here, no congruent node was found, so make the node its own leader
536  markNodeAsLeader(node, context);
537 }
538 
539 static void
540 markRegion(const rvsdg::Region &, CommonNodeElimination::Context & context);
541 
547 static void
549 {
550  // FIXME: Multiple imports with identical names could in theory be aliases.
551  // For now this function ignores that and makes all imports distinct. This is sound
552  for (auto argument : region.Arguments())
553  {
554  context.getOrCreateSetForLeader(*argument);
555  }
556 }
557 
571 static bool
573  const rvsdg::Region & region,
574  const std::vector<CommonNodeElimination::Context::CongruenceSetIndex> & partitions,
576 {
577  JLM_ASSERT(region.narguments() == partitions.size());
578 
579  const auto numCongruenceSets = context.numCongruenceSets();
580 
581  // Keys in the map are (old congruence set index, provided partition key)
582  // Values in the map are the new congruence set indices
583  std::map<
584  std::pair<
588  newSets;
589  for (auto argument : region.Arguments())
590  {
591  const auto currentPartition = context.tryGetSetFor(*argument);
592  const auto key = std::make_pair(currentPartition, partitions[argument->index()]);
593 
594  // If this argument is the first with the given key, it should be a leader
595  // otherwise it should be a follower
596 
597  if (const auto it = newSets.find(key); it != newSets.end())
598  {
599  // This argument should be a follower of the given congruence set
600  const auto toFollow = it->second;
601 
602  // If we are already a follower, we are done
603  if (currentPartition == toFollow)
604  continue;
605 
606  // Start following our leader
607  context.addFollower(toFollow, *argument);
608  }
609  else
610  {
611  // This argument should be the leader of its congruence set
612  newSets.emplace(key, context.getOrCreateSetForLeader(*argument));
613  }
614  }
615 
616  // Return true iff any new congruence sets were created
617  return context.numCongruenceSets() != numCongruenceSets;
618 }
619 
637 static bool
639  const rvsdg::StructuralNode & node,
641 {
642  bool anyChanges = false;
643 
644  for (auto & subregion : node.Subregions())
645  {
646  if (subregion.narguments() == 0)
647  {
648  markRegion(subregion, context);
649  }
650  else
651  {
652  // create a partitioning of the region arguments
653  std::vector<size_t> partitions(subregion.narguments());
654  // Arguments that do not belong to any input are given partition keys higher than any real
655  // index
656  size_t nextUniquePartitionKey = context.numCongruenceSets();
657 
658  for (const auto argument : subregion.Arguments())
659  {
660  if (const auto input = argument->input())
661  {
662  // If the argument corresponds to an input, use the partition key of the input
663  partitions[argument->index()] = context.getSetFor(*input->origin());
664  }
665  else
666  {
667  // Otherwise make sure the argument is not partitioned with any other argument
668  partitions[argument->index()] = nextUniquePartitionKey++;
669  }
670  }
671 
672  if (partitionArguments(subregion, partitions, context))
673  {
674  anyChanges = true;
675  markRegion(subregion, context);
676  }
677  }
678  }
679 
680  return anyChanges;
681 }
682 
693 static std::optional<CommonNodeElimination::Context::CongruenceSetIndex>
695  rvsdg::GammaNode::ExitVar & exitVar,
697 {
698  std::optional<CommonNodeElimination::Context::CongruenceSetIndex> sharedCongruenceSet;
699 
700  for (auto result : exitVar.branchResult)
701  {
702  if (const auto argument = dynamic_cast<rvsdg::RegionArgument *>(result->origin()))
703  {
704  const auto inputCongruenceSet = context.getSetFor(*argument->input()->origin());
705  if (!sharedCongruenceSet.has_value())
706  {
707  sharedCongruenceSet = inputCongruenceSet;
708  }
709  else if (*sharedCongruenceSet != inputCongruenceSet)
710  {
711  // We have multiple different non-congruent origins
712  return std::nullopt;
713  }
714  }
715  else
716  {
717  // The branch result was not invariant
718  return std::nullopt;
719  }
720  }
721 
722  return sharedCongruenceSet;
723 }
724 
734 [[nodiscard]] static size_t
736  const rvsdg::GammaNode::ExitVar & exitVar,
738 {
739  size_t hash = 1;
740  for (auto branchResult : exitVar.branchResult)
741  {
742  const auto set = context.getSetFor(*branchResult->origin());
743  hash = util::CombineHashes(hash, set);
744  }
745  return hash;
746 }
747 
760 [[nodiscard]] static bool
762  const rvsdg::GammaNode::ExitVar & first,
763  const rvsdg::GammaNode::ExitVar & second,
765 {
766  JLM_ASSERT(
767  &rvsdg::AssertGetOwnerNode<rvsdg::GammaNode>(*first.output)
768  == &rvsdg::AssertGetOwnerNode<rvsdg::GammaNode>(*second.output));
769  const auto numResults = first.branchResult.size();
770  for (size_t i = 0; i < numResults; i++)
771  {
772  const auto firstSet = context.getSetFor(*first.branchResult[i]->origin());
773  const auto secondSet = context.getSetFor(*second.branchResult[i]->origin());
774  if (firstSet != secondSet)
775  return false;
776  }
777  return true;
778 }
779 
789 static void
791  rvsdg::GammaNode::ExitVar & exitVar,
793  std::unordered_map<size_t, CommonNodeElimination::Context::CongruenceSetIndex> & leaderHashes,
795 {
796  JLM_ASSERT(&context.getLeader(congruenceSet) == exitVar.output);
797 
798  size_t hash = getGammaExitVariableHash(exitVar, context);
799  do
800  {
801  const auto [_, inserted] = leaderHashes.emplace(hash, congruenceSet);
802  if (inserted)
803  return;
804 
805  // Open addressing, try next hash
806  hash++;
807  } while (true);
808 }
809 
823 static void
825  const rvsdg::GammaNode::ExitVar & exitVar,
826  const rvsdg::GammaNode & gamma,
827  std::unordered_map<size_t, CommonNodeElimination::Context::CongruenceSetIndex> & leaderHashes,
829 {
830  size_t hash = getGammaExitVariableHash(exitVar, context);
831  do
832  {
833  // If no leader has the same hash already, insert a new leader exit variable
834  const auto [it, inserted] = leaderHashes.emplace(hash, 0);
835  if (inserted)
836  {
837  it->second = context.getOrCreateSetForLeader(*exitVar.output);
838  return;
839  }
840 
841  // There is already an exit variable with the same hash, check if it is a match
842  auto & otherLeader = context.getLeader(it->second);
843  auto otherExitVar = gamma.MapOutputExitVar(otherLeader);
844  if (areGammaExitVariablesCongruent(exitVar, otherExitVar, context))
845  {
846  context.addFollower(it->second, *exitVar.output);
847  return;
848  }
849 
850  // Open addressing, try next hash
851  hash++;
852  } while (true);
853 }
854 
863 static void
865 {
866  markSubregionsFromInputs(gamma, context);
867 
868  // Mapping from exit variable hash to the leader of its congruence set.
869  // The hash function is not collision free, and the table uses open addressing.
870  std::unordered_map<size_t, CommonNodeElimination::Context::CongruenceSetIndex> leaderHashes;
871 
872  // Go through the outputs of the gamma node and create congruence sets for them.
873  for (auto exitVar : gamma.GetExitVars())
874  {
875  // If the exit variable has previously been found to not be invariant, we skip trying again
876  bool skipInvarianceCheck = false;
877 
878  // First check if the exit variable already belongs to a congruence set
879  const auto existingSet = context.tryGetSetFor(*exitVar.output);
881  {
882  // The output already belongs to a congruence set
883 
884  // Check if the output is the leader
885  const auto & exisitingLeader = context.getLeader(existingSet);
886  if (&exisitingLeader == exitVar.output)
887  {
888  // Add the exit var to the hashmap of exit variable leaders
889  insertGammaExitVarInHashmap(exitVar, existingSet, leaderHashes, context);
890 
891  // A leader can never become a follower, so we are done
892  continue;
893  }
894 
895  // Check if the leader is another exit variable of the same gamma
896  if (rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(exisitingLeader) == &gamma)
897  {
898  const auto otherExitVar = gamma.MapOutputExitVar(exisitingLeader);
899  // Double check that the output still belongs to the same congurence set
900  if (areGammaExitVariablesCongruent(exitVar, otherExitVar, context))
901  {
902  // The exit variable should continue to be a follower
903  continue;
904  }
905 
906  // exitVar should find a new leader, but we can skip checking if it is invariant
907  skipInvarianceCheck = true;
908  }
909  }
910 
911  if (!skipInvarianceCheck)
912  {
913  // First check if the exit variable is gamma invariant
914  if (const auto entryVarCongruenceSet = tryGetGammaExitVarCongruenceSet(exitVar, context);
915  entryVarCongruenceSet.has_value())
916  {
917  context.addFollower(*entryVarCongruenceSet, *exitVar.output);
918  continue;
919  }
920  }
921 
922  // This function either finds a new congruent leader for the exitVar,
923  // or makes exitVar itself the leader of its own congruence set.
924  lookupOrInsertGammaExitVarInHashmap(exitVar, gamma, leaderHashes, context);
925  }
926 }
927 
939 static void
941 {
942  bool anyChanges = markSubregionsFromInputs(theta, context);
943  if (!anyChanges)
944  return;
945 
946  const auto loopVars = theta.GetLoopVars();
947 
948  // Use the loop variable post results to refine partitioning of loop variable arguments
949  while (anyChanges)
950  {
951  // Create partition keys for each loop variable
952  std::vector<CommonNodeElimination::Context::CongruenceSetIndex> partitions;
953  for (const auto & loopVar : loopVars)
954  {
955  partitions.push_back(context.getSetFor(*loopVar.post->origin()));
956  }
957 
958  anyChanges = partitionArguments(*theta.subregion(), partitions, context);
959  if (anyChanges)
960  {
961  // Propagate refinement of argument congruence sets into the region
962  markRegion(*theta.subregion(), context);
963  }
964  }
965 
966  // Partition theta outputs
967  std::unordered_map<
970  resultToOutputSetMapping;
971  for (auto & loopVar : loopVars)
972  {
973  // invariant loop variables become followers of the input origin
974  if (rvsdg::ThetaLoopVarIsInvariant(loopVar))
975  {
976  const auto inputOriginSet = context.getSetFor(*loopVar.input->origin());
977  context.addFollower(inputOriginSet, *loopVar.output);
978  continue;
979  }
980 
981  // Other loop variable outputs are partitioned based on the origins of the post results.
982  const auto resultSet = context.getSetFor(*loopVar.post->origin());
983  const auto it = resultToOutputSetMapping.find(resultSet);
984  if (it != resultToOutputSetMapping.end())
985  {
986  context.addFollower(it->second, *loopVar.output);
987  }
988  else
989  {
990  const auto outputSet = context.getOrCreateSetForLeader(*loopVar.output);
991  resultToOutputSetMapping.emplace(resultSet, outputSet);
992  }
993  }
994 }
995 
996 static void
998 {
1000  node,
1001  [&](const rvsdg::GammaNode & gamma)
1002  {
1003  markGamma(gamma, context);
1004  },
1005  [&](const rvsdg::ThetaNode & theta)
1006  {
1007  markTheta(theta, context);
1008  },
1009  [&](const rvsdg::LambdaNode & lambda)
1010  {
1011  // Context variables are congruent if their origins are congruent.
1012  // All other arguments are given distinct congruence sets.
1013  markSubregionsFromInputs(lambda, context);
1014 
1015  // A lambda output is always unique
1016  markNodeAsLeader(lambda, context);
1017  },
1018  [&](const rvsdg::PhiNode & phi)
1019  {
1020  // Context variables are congruent if their origins are congruent.
1021  // Recursion variables are given distinct congruence sets.
1022  markSubregionsFromInputs(phi, context);
1023 
1024  // A phi node is always unique
1025  markNodeAsLeader(phi, context);
1026  },
1027  [&](const rvsdg::DeltaNode & delta)
1028  {
1029  // We skip doing CNE inside delta nodes
1030 
1031  // A delta node is always unique
1032  markNodeAsLeader(delta, context);
1033  });
1034 }
1035 
1043 static void
1045 {
1046  TopNodeLeaderList leaders;
1047 
1048  for (const auto node : rvsdg::TopDownConstTraverser(&region))
1049  {
1051  *node,
1052  [&](const rvsdg::SimpleNode & simple)
1053  {
1054  // Handle top nodes as a special case
1055  if (node->ninputs() == 0)
1056  {
1057  markSimpleTopNode(simple, leaders, context);
1058  }
1059  else
1060  {
1061  markSimpleNode(simple, context);
1062  }
1063  },
1064  [&](const rvsdg::StructuralNode & structural)
1065  {
1066  markStructuralNode(structural, context);
1067  });
1068  }
1069 }
1070 
1071 /* divert phase */
1072 
1073 static void
1075 {
1076  const auto outputSet = context.getSetFor(output);
1077 
1078  auto & leader = context.getLeader(outputSet);
1079  if (&leader == &output)
1080  return;
1081 
1082  output.divert_users(const_cast<rvsdg::Output *>(&leader));
1083 }
1084 
1085 static void
1086 divertInRegion(rvsdg::Region &, CommonNodeElimination::Context &);
1087 
1088 static void
1090 {
1091  bool divertInSubregions = false;
1093  node,
1094  [&]([[maybe_unused]] rvsdg::GammaNode & gamma)
1095  {
1096  divertInSubregions = true;
1097  },
1098  [&]([[maybe_unused]] rvsdg::ThetaNode & theta)
1099  {
1100  divertInSubregions = true;
1101  },
1102  [&]([[maybe_unused]] rvsdg::LambdaNode & lambda)
1103  {
1104  divertInSubregions = true;
1105  },
1106  [&]([[maybe_unused]] rvsdg::PhiNode & phi)
1107  {
1108  divertInSubregions = true;
1109  },
1110  [&]([[maybe_unused]] rvsdg::DeltaNode & delta)
1111  {
1112  // Inside a delta node we can not perform diverting,
1113  // since we never marked the outputs there
1114  });
1115 
1116  if (divertInSubregions)
1117  {
1118  for (auto & subregion : node.Subregions())
1119  {
1120  divertInRegion(subregion, context);
1121  }
1122  }
1123 }
1124 
1125 static void
1127 {
1128  // First divert all region arguments
1129  for (auto argument : region.Arguments())
1130  {
1131  divertOutput(*argument, context);
1132  }
1133 
1134  // Divert all nodes
1135  for (const auto node : rvsdg::TopDownTraverser(&region))
1136  {
1137  // When diverting structural nodes, also recurse into their subregions
1139  *node,
1140  [&](rvsdg::StructuralNode & structural)
1141  {
1142  divertInStructuralNode(structural, context);
1143  });
1144 
1145  // Divert all outputs of the node
1146  for (auto & output : node->Outputs())
1147  {
1148  divertOutput(output, context);
1149  }
1150  }
1151 }
1152 
1154 
1155 void
1157  rvsdg::RvsdgModule & module,
1158  util::StatisticsCollector & statisticsCollector)
1159 {
1160  const auto & rvsdg = module.Rvsdg();
1161  auto & rootRegion = rvsdg.GetRootRegion();
1162 
1163  Context context;
1164  auto statistics = Statistics::Create(module.SourceFilePath().value());
1165 
1166  statistics->startMarkStatistics(rvsdg);
1167  markGraphImports(rootRegion, context);
1168  markRegion(rootRegion, context);
1169  statistics->endMarkStatistics();
1170 
1171  statistics->startDivertStatistics();
1172  divertInRegion(rootRegion, context);
1173  statistics->endDivertStatistics(rvsdg);
1174 
1175  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
1176 }
1177 }
static jlm::util::StatisticsCollector statisticsCollector
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
ExitVar MapOutputExitVar(const rvsdg::Output &output) const
Maps gamma output to exit variable description.
Definition: gamma.cpp:397
std::vector< ExitVar > GetExitVars() const
Gets all exit variables for this gamma.
Definition: gamma.cpp:381
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
NodeOutput * output(size_t index) const noexcept
Definition: simple-node.hpp:88
SubregionIteratorRange Subregions()
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition: theta.cpp:176
rvsdg::Region * subregion() const noexcept
Definition: theta.hpp:79
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Definition: Statistics.hpp:574
Statistics Interface.
Definition: Statistics.hpp:31
util::Timer & GetTimer(const std::string &name)
Definition: Statistics.cpp:137
util::Timer & AddTimer(std::string name)
Definition: Statistics.cpp:158
void AddMeasurement(std::string name, T value)
Definition: Statistics.hpp:177
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 void lookupOrInsertGammaExitVarInHashmap(const rvsdg::GammaNode::ExitVar &exitVar, const rvsdg::GammaNode &gamma, std::unordered_map< size_t, CommonNodeElimination::Context::CongruenceSetIndex > &leaderHashes, CommonNodeElimination::Context &context)
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)
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)
static void insertGammaExitVarInHashmap(rvsdg::GammaNode::ExitVar &exitVar, CommonNodeElimination::Context::CongruenceSetIndex congruenceSet, std::unordered_map< size_t, CommonNodeElimination::Context::CongruenceSetIndex > &leaderHashes, CommonNodeElimination::Context &context)
static size_t getGammaExitVariableHash(const rvsdg::GammaNode::ExitVar &exitVar, 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)
const rvsdg::SimpleNode * tryGetLeaderNode(const rvsdg::SimpleNode &node, CommonNodeElimination::Context &context)
static void markTheta(const rvsdg::ThetaNode &theta, CommonNodeElimination::Context &context)
static bool areGammaExitVariablesCongruent(const rvsdg::GammaNode::ExitVar &first, const rvsdg::GammaNode::ExitVar &second, 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:838
std::size_t CombineHashes(std::size_t hash, Args... args)
Definition: Hash.hpp:63
A variable routed out of all gamma regions as result.
Definition: gamma.hpp:146
rvsdg::Output * output
Output of gamma.
Definition: gamma.hpp:154
std::vector< rvsdg::Input * > branchResult
Variable exit points (results per subregion).
Definition: gamma.hpp:150
static const char * NumRvsdgInputsAfter
Definition: Statistics.hpp:218
static const char * NumRvsdgInputsBefore
Definition: Statistics.hpp:217