Jlm
ScalarEvolution.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2025 Andreas Lilleby Hjulstad <andreas.lilleby.hjulstad@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
7 #include <jlm/llvm/ir/Trace.hpp>
9 #include <jlm/rvsdg/lambda.hpp>
11 #include <jlm/rvsdg/theta.hpp>
12 #include <jlm/util/Statistics.hpp>
13 
14 #include <algorithm>
15 #include <queue>
16 
17 namespace jlm::llvm
18 {
19 
21 {
22 public:
23  ~Context() = default;
24 
25  Context() = default;
26 
27  Context(const Context &) = delete;
28 
29  Context(Context &&) = delete;
30 
31  Context &
32  operator=(const Context &) = delete;
33 
34  Context &
35  operator=(Context &&) = delete;
36 
37  void
38  InsertChrec(const rvsdg::ThetaNode & thetaNode, std::unique_ptr<SCEVChainRecurrence> & chrec)
39  {
40  ChrecMap_[&thetaNode].insert(std::move(chrec));
41  }
42 
43  int
44  GetNumOfChrecsWithOrder(const int n) const
45  {
46  int count = 0;
47  for (auto & [theta, chrecs] : ChrecMap_)
48  {
49  for (auto & chrec : chrecs.Items())
50  {
51  // Count chrecs with specific order
52  if (static_cast<int>(chrec->GetOperands().size()) == n + 1)
53  count++;
54  }
55  }
56  return count;
57  }
58 
59  size_t
61  {
62  size_t total = 0;
63  for (const auto & [theta, chrecs] : ChrecMap_)
64  {
65  total += chrecs.Size();
66  }
67  return total;
68  }
69 
70  void
72  {
73  LoopVars_.push_back(&var);
74  }
75 
76  size_t
78  {
79  return LoopVars_.size();
80  }
81 
82  static std::unique_ptr<Context>
84  {
85  return std::make_unique<Context>();
86  }
87 
88 private:
89  std::unordered_map<const rvsdg::ThetaNode *, util::HashSet<std::unique_ptr<SCEVChainRecurrence>>>
91  std::vector<const rvsdg::Output *> LoopVars_;
92 };
93 
95 {
96 
97 public:
98  ~Statistics() noexcept override = default;
99 
100  explicit Statistics(const util::FilePath & sourceFile)
101  : util::Statistics(Id::ScalarEvolution, sourceFile)
102  {}
103 
104  void
105  Start() noexcept
106  {
108  }
109 
110  void
111  Stop(const Context & context) noexcept
112  {
114  AddMeasurement(Label::NumTotalRecurrences, context.GetNumOfTotalChrecs());
115  AddMeasurement(Label::NumConstantRecurrences, context.GetNumOfChrecsWithOrder(0));
116  AddMeasurement(Label::NumFirstOrderRecurrences, context.GetNumOfChrecsWithOrder(1));
117  AddMeasurement(Label::NumSecondOrderRecurrences, context.GetNumOfChrecsWithOrder(2));
118  AddMeasurement(Label::NumThirdOrderRecurrences, context.GetNumOfChrecsWithOrder(3));
119  AddMeasurement(Label::NumLoopVariablesTotal, context.GetNumTotalLoopVars());
120  }
121 
122  static std::unique_ptr<Statistics>
123  Create(const util::FilePath & sourceFile)
124  {
125  return std::make_unique<Statistics>(sourceFile);
126  }
127 };
128 
130  : Transformation("ScalarEvolution")
131 {}
132 
133 ScalarEvolution::~ScalarEvolution() noexcept = default;
134 
135 void
137  rvsdg::RvsdgModule & rvsdgModule,
138  util::StatisticsCollector & statisticsCollector)
139 {
140  auto statistics = Statistics::Create(rvsdgModule.SourceFilePath().value());
141  statistics->Start();
142 
143  Context_ = Context::Create();
144  InductionVariableMap_.clear();
145  const rvsdg::Region & rootRegion = rvsdgModule.Rvsdg().GetRootRegion();
146  AnalyzeRegion(rootRegion);
147 
148  statistics->Stop(*Context_);
149  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
150 };
151 
152 bool
154 {
155  for (const auto operand : chrec.GetOperands())
156  {
157  if (dynamic_cast<const SCEVUnknown *>(operand))
158  {
159  return true;
160  }
161  }
162  return false;
163 }
164 
165 void
167 {
168  for (const auto & node : region.Nodes())
169  {
170  if (const auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(&node))
171  {
172  for (auto & subregion : structuralNode->Subregions())
173  {
174  AnalyzeRegion(subregion);
175  }
176  if (const auto thetaNode = dynamic_cast<const rvsdg::ThetaNode *>(structuralNode))
177  {
178  // Add number of loop vars in theta (for statistics)
179  for (const auto loopVar : thetaNode->GetLoopVars())
180  {
181  Context_.get()->AddLoopVar(*loopVar.pre);
182  }
183 
184  auto chrecMap = PerformSCEVAnalysis(*thetaNode);
185  for (auto & [output, chrec] : chrecMap)
186  {
187  if (!IsUnknown(*chrec))
188  Context_.get()->InsertChrec(*thetaNode, chrec);
189  }
190  }
191  }
192  }
193 }
194 
195 std::optional<const SCEV *>
197 {
198  if (const auto it = UniqueSCEVs_.find(&output); it != UniqueSCEVs_.end())
199  return it->second.get();
200  return std::nullopt;
201 }
202 
203 std::unique_ptr<SCEV>
205 {
206  if (const auto existing = TryGetSCEVForOutput(output))
207  {
208  return (*existing)->Clone();
209  }
210 
211  std::unique_ptr<SCEV> result;
212  if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(output))
213  {
214  // We know this is a loop variable, create a placeholder SCEV for now, and compute the
215  // expression later
216  result = std::make_unique<SCEVPlaceholder>(output);
217  }
218  if (const auto simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output))
219  {
220  if (rvsdg::is<IntegerConstantOperation>(simpleNode->GetOperation()))
221  {
222  const auto constOp =
223  dynamic_cast<const IntegerConstantOperation *>(&simpleNode->GetOperation());
224  const auto value = constOp->Representation().to_int();
225  result = std::make_unique<SCEVConstant>(value);
226  }
227  if (rvsdg::is<IntegerAddOperation>(simpleNode->GetOperation()))
228  {
229  JLM_ASSERT(simpleNode->ninputs() == 2);
230  const auto lhs = simpleNode->input(0)->origin();
231  const auto rhs = simpleNode->input(1)->origin();
232  result = std::make_unique<SCEVAddExpr>(
235  }
236  }
237  // TODO: Handle more cases
238 
239  if (!result)
240  // If none of the cases match, return an unknown SCEV expression
241  result = std::make_unique<SCEVUnknown>();
242 
243  // Save the result in the cache
244  UniqueSCEVs_[&output] = result->Clone();
245 
246  return result;
247 }
248 
249 std::unordered_map<const rvsdg::Output *, int>
250 ScalarEvolution::FindDependenciesForSCEV(const SCEV & currentSCEV, const rvsdg::Output & currentIV)
251 {
252  std::unordered_map<const rvsdg::Output *, int> dependencies{};
253  if (dynamic_cast<const SCEVConstant *>(&currentSCEV)
254  || dynamic_cast<const SCEVUnknown *>(&currentSCEV))
255  {
256  return dependencies;
257  }
258  if (const auto placeholderSCEV = dynamic_cast<const SCEVPlaceholder *>(&currentSCEV))
259  {
260  if (const auto dependency = placeholderSCEV->GetPrePointer())
261  dependencies[dependency]++;
262  }
263  if (const auto addSCEV = dynamic_cast<const SCEVAddExpr *>(&currentSCEV))
264  {
265  // Recursively find dependencies on lhs and rhs
266  std::unordered_map<const rvsdg::Output *, int> lhsDependencies =
267  FindDependenciesForSCEV(*addSCEV->GetLeftOperand(), currentIV);
268  std::unordered_map<const rvsdg::Output *, int> rhsDependencies =
269  FindDependenciesForSCEV(*addSCEV->GetRightOperand(), currentIV);
270 
271  // Merge lhsDependencies into dependencies
272  for (const auto & [ptr, count] : lhsDependencies)
273  dependencies[ptr] += count;
274 
275  // Do the same for rhs
276  for (const auto & [ptr, count] : rhsDependencies)
277  dependencies[ptr] += count;
278  }
279 
280  return dependencies;
281 }
282 
285 {
286  IVDependencyGraph graph{};
287  for (const auto loopVar : thetaNode.GetLoopVars())
288  {
289  const auto post = loopVar.post;
290  const auto scev = UniqueSCEVs_.at(post->origin())->Clone();
291 
292  const auto pre = loopVar.pre;
293  const std::unordered_map<const rvsdg::Output *, int> dependencies =
294  FindDependenciesForSCEV(*scev.get(), *pre);
295  graph[pre] = dependencies;
296  }
297 
298  return graph;
299 }
300 
301 // Implementation of Kahn's algorithm for topological sort
302 std::vector<const rvsdg::Output *>
304 {
305  const size_t numVertices = dependencyGraph.size();
306  std::unordered_map<const rvsdg::Output *, int> indegree(numVertices);
307  std::queue<const rvsdg::Output *> q{};
308  for (const auto & [node, deps] : dependencyGraph)
309  {
310  for (const auto & dep : deps)
311  {
312  if (const auto ptr = dep.first; ptr == node)
313  continue; // Ignore self-edges
314  // To begin with, the indegree is just the number of incoming edges
315  indegree[node] += 1;
316  }
317  if (indegree[node] == 0)
318  {
319  // Add nodes with no incoming edges to the queue, we know that these have no dependencies
320  q.push(node);
321  }
322  }
323 
324  std::vector<const rvsdg::Output *> result{};
325  while (!q.empty())
326  {
327  const rvsdg::Output * currentNode = q.front();
328  q.pop();
329  result.push_back(currentNode);
330 
331  for (const auto & [node, deps] : dependencyGraph)
332  {
333  if (node == currentNode)
334  continue;
335 
336  for (const auto & dep : deps)
337  {
338  const auto ptr = dep.first;
339  if (ptr == node)
340  continue; // Skip self-edges
341  if (ptr == currentNode)
342  {
343  // Update the indegree of nodes depending on this one
344  indegree[node] -= 1;
345  if (indegree[node] == 0)
346  q.push(node);
347  }
348  }
349  }
350  }
351  JLM_ASSERT(result.size() == numVertices && "Dependency graph can't contain cycles!");
352  return result;
353 }
354 
355 bool
357  const rvsdg::Output & variable,
358  ScalarEvolution::IVDependencyGraph & dependencyGraph)
359 {
360  if (dependencyGraph[&variable].size() >= 1)
361  return true;
362  return false;
363 }
364 
365 std::unordered_map<const rvsdg::Output *, std::unique_ptr<SCEVChainRecurrence>>
367 {
368  for (const auto loopVar : thetaNode.GetLoopVars())
369  {
370  const auto post = loopVar.post;
371  GetOrCreateSCEVForOutput(*post->origin());
372  }
373  auto dependencyGraph = CreateDependencyGraph(thetaNode);
374 
375  InductionVariableSet validIVs{};
376  for (const auto loopVar : thetaNode.GetLoopVars())
377  {
378  if (!DependsOnLoopVariable(*loopVar.pre, dependencyGraph))
379  {
380  // If the expression doesn't depend on atleast one loop variable (including itself), it is not
381  // an induction variable. Replace it with a SCEVUnknown
382  UniqueSCEVs_.insert_or_assign(loopVar.post->origin(), std::make_unique<SCEVUnknown>());
383  }
384  else if (IsValidInductionVariable(*loopVar.pre, dependencyGraph))
385  validIVs.insert(loopVar.pre);
386  }
387 
388  // Filter the dependency graph to only contain the IVs that are valid and update dependencies
389  // accordingly
390  auto filteredDependencyGraph = dependencyGraph;
391  for (auto it = filteredDependencyGraph.begin(); it != filteredDependencyGraph.end();)
392  {
393  if (!validIVs.Contains(it->first))
394  {
395  for (auto & [node, deps] : filteredDependencyGraph)
396  deps.erase(it->first);
397  it = filteredDependencyGraph.erase(it);
398  }
399  else
400  ++it;
401  }
402 
403  const auto order = TopologicalSort(filteredDependencyGraph);
404 
405  std::vector<const rvsdg::Output *> allVars{};
406  // Add valid IVs to the set (in the correct order)
407  for (const auto & indVarPre : order)
408  allVars.push_back(indVarPre);
409  for (const auto loopVar : thetaNode.GetLoopVars())
410  {
411  if (std::find(order.begin(), order.end(), loopVar.pre) == order.end())
412  // This is not a valid IV, so it hasn't been added yet
413  allVars.push_back(loopVar.pre);
414  }
415 
416  for (const auto loopVarPre : allVars)
417  {
418  if (std::find(order.begin(), order.end(), loopVarPre) != order.end())
419  {
420  const auto loopVar = thetaNode.MapPreLoopVar(*loopVarPre);
421  const auto post = loopVar.post;
422  const auto scev = UniqueSCEVs_.at(post->origin()).get();
423 
424  auto chainRecurrence = CreateChainRecurrence(*loopVarPre, *scev, thetaNode);
425 
426  if (chainRecurrence->GetOperands().size() == 1
427  && StructurallyEqual(*chainRecurrence->GetOperand(0), SCEVConstant(0)))
428  {
429  // If the recurrence is empty ({0}), delete the old unique_ptr and create a new one without
430  // any operands. This effectively removes trailing zeroes for constants
431  chainRecurrence.reset();
432  chainRecurrence = std::make_unique<SCEVChainRecurrence>(thetaNode);
433  }
434 
435  // Find the start value for the recurrence
436  if (auto const constantInteger = tryGetConstantSignedInteger(*loopVar.input->origin()))
437  {
438  // If the input value is a constant, create a SCEV representation and set it as start value
439  // (first operand in rec)
440  chainRecurrence->AddOperandToFront(std::make_unique<SCEVConstant>(*constantInteger));
441  }
442  else
443  {
444  // If not, create a SCEVInit node representing the start value
445  chainRecurrence->AddOperandToFront(std::make_unique<SCEVInit>(*loopVarPre));
446  }
447  ChainRecurrenceMap_.emplace(loopVarPre, std::move(chainRecurrence));
448  }
449  else
450  {
451  auto unknownChainRecurrence = std::make_unique<SCEVChainRecurrence>(thetaNode);
452  unknownChainRecurrence->AddOperand(std::make_unique<SCEVUnknown>());
453  ChainRecurrenceMap_.emplace(loopVarPre, std::move(unknownChainRecurrence));
454  }
455  }
456 
457  std::unordered_map<const rvsdg::Output *, std::unique_ptr<SCEVChainRecurrence>> chrecMap{};
458  for (const auto loopVar : thetaNode.GetLoopVars())
459  {
460  auto storedRec = ChainRecurrenceMap_.at(loopVar.pre)->Clone();
461  // Workaround for the fact that Clone() is an overridden method that returns a unique_ptr of
462  // SCEV
463  chrecMap[loopVar.pre] = std::unique_ptr<SCEVChainRecurrence>(
464  dynamic_cast<SCEVChainRecurrence *>(storedRec.release()));
465  }
466 
467  return chrecMap;
468 }
469 
470 std::unique_ptr<SCEVChainRecurrence>
472  const rvsdg::Output & IV,
473  const SCEV & scevTree,
474  const rvsdg::ThetaNode & thetaNode)
475 {
476  auto chrec = std::make_unique<SCEVChainRecurrence>(thetaNode);
477 
478  if (const auto scevConstant = dynamic_cast<const SCEVConstant *>(&scevTree))
479  {
480  // This is a constant, we add it as the only operand
481  chrec->AddOperand(scevConstant->Clone());
482  return chrec;
483  }
484  if (const auto scevPlaceholder = dynamic_cast<const SCEVPlaceholder *>(&scevTree))
485  {
486  if (scevPlaceholder->GetPrePointer() == &IV)
487  {
488  // Since we are only interested in the step value, and not the initial value, we can ignore
489  // ourselves by adding a 0, which is the identity element
490  chrec->AddOperand(std::make_unique<SCEVConstant>(0));
491  return chrec;
492  }
493  if (ChainRecurrenceMap_.find(scevPlaceholder->GetPrePointer()) != ChainRecurrenceMap_.end())
494  {
495  // We have a dependency of another IV
496  // Get it's saved value. This is safe to do due to the topological ordering
497  auto storedRec = ChainRecurrenceMap_.at(scevPlaceholder->GetPrePointer())->Clone();
498  return std::unique_ptr<SCEVChainRecurrence>(
499  dynamic_cast<SCEVChainRecurrence *>(storedRec.release()));
500  }
501  chrec->AddOperand(std::make_unique<SCEVUnknown>());
502  return chrec;
503  }
504  if (const auto scevAddExpr = dynamic_cast<const SCEVAddExpr *>(&scevTree))
505  {
506  const auto lhsChrec = CreateChainRecurrence(IV, *scevAddExpr->GetLeftOperand(), thetaNode);
507  const auto rhsChrec = CreateChainRecurrence(IV, *scevAddExpr->GetRightOperand(), thetaNode);
508 
509  const auto lhsSize = lhsChrec->GetOperands().size();
510  const auto rhsSize = rhsChrec->GetOperands().size();
511  for (size_t i = 0; i < std::max(lhsSize, rhsSize); ++i)
512  {
513  SCEV * lhsOperand{};
514  SCEV * rhsOperand{};
515  if (i <= lhsSize - 1)
516  lhsOperand = lhsChrec->GetOperand(i);
517 
518  if (i <= rhsSize - 1)
519  rhsOperand = rhsChrec->GetOperand(i);
520  chrec->AddOperand(ApplyFolding(lhsOperand, rhsOperand));
521  }
522  return chrec;
523  }
524  JLM_ASSERT(false && "Unknown SCEV type in CreateChainRecurrence!");
525  return chrec;
526 }
527 
528 bool
530 {
531  return c && c->GetValue() != 0;
532 }
533 
534 std::unique_ptr<SCEV>
535 ScalarEvolution::ApplyFolding(SCEV * lhsOperand, SCEV * rhsOperand)
536 {
537  /* Apply folding rules
538  *
539  * We have the following folding rules from the CR algebra:
540  * G + {e,+,f} => {G + e,+,f} (1)
541  * {e,+,f} + {g,+,h} => {e + g,+,f + h} (2)
542  *
543  * And by generalizing rule 2, we have that:
544  * {G,+,0} + {e,+,f} = {G + e,+,0 + f} = {G + e,+,f}
545  *
546  * Since we represent constants in the SCEVTree as recurrences consisting of only a SCEVConstant
547  * node, we can therefore pad the constant recurrence with however many zeroes we need for the
548  * length of the other recurrence. This effectively let's us apply both rules in one go.
549  *
550  * Now, this becomes a bit complicated when we factor in SCEVInit nodes. These nodes represent
551  * the initial value of an IV in the case where the exact value is unknown at compile time. E.g.
552  * function argument or result from a call-instruction. In the cases where we have to fold one or
553  * more of these init-nodes, we create an n-ary add expression (add expression with an arbitrary
554  * number of operands), and add this to the chrec. Folding two of these n-ary add expressions will
555  * result in another n-ary add expression, which consists of all the operands in both the left and
556  * the right expression.
557  */
558 
559  const auto lhsUnknown = dynamic_cast<SCEVUnknown *>(lhsOperand);
560  const auto rhsUnknown = dynamic_cast<SCEVUnknown *>(rhsOperand);
561  const auto lhsInit = dynamic_cast<SCEVInit *>(lhsOperand);
562  const auto rhsInit = dynamic_cast<SCEVInit *>(rhsOperand);
563  const auto lhsNAryAddExpr = dynamic_cast<SCEVNAryAddExpr *>(lhsOperand);
564  const auto rhsNAryAddExpr = dynamic_cast<SCEVNAryAddExpr *>(rhsOperand);
565  const auto lhsConstant = dynamic_cast<SCEVConstant *>(lhsOperand);
566  const auto rhsConstant = dynamic_cast<SCEVConstant *>(rhsOperand);
567 
568  // The if-chain below goes through each of the possible combinations of lhs and rhs values
569  if (lhsUnknown || rhsUnknown)
570  {
571  // If one of the sides is unknown. Return unknown
572  return std::make_unique<SCEVUnknown>();
573  }
574  if (lhsInit && rhsInit)
575  {
576  // We have two init nodes. Create a nAryAdd with lhsInit and rhsInit
577  return std::make_unique<SCEVNAryAddExpr>(lhsInit->Clone(), rhsInit->Clone());
578  }
579  if ((lhsInit && rhsNAryAddExpr) || (rhsInit && lhsNAryAddExpr))
580  {
581  // We have an init and an add expr. Add init to the add expr
582  const auto * init = lhsInit ? lhsInit : rhsInit;
583  auto * nAryAddExpr = lhsNAryAddExpr ? lhsNAryAddExpr : rhsNAryAddExpr;
584  nAryAddExpr->AddOperand(init->Clone());
585  return nAryAddExpr->Clone();
586  }
587  if ((lhsInit && isNonZeroConstant(rhsConstant)) || (rhsInit && isNonZeroConstant(lhsConstant)))
588  {
589  // We have an init and a nonzero constant. Create a nAryAdd with init and constant
590  const auto * init = lhsInit ? lhsInit : rhsInit;
591  const auto * constant = lhsConstant ? lhsConstant : rhsConstant;
592  return std::make_unique<SCEVNAryAddExpr>(init->Clone(), constant->Clone());
593  }
594  if (lhsInit || rhsInit)
595  {
596  // Only one operand. Add it
597  const auto * init = lhsInit ? lhsInit : rhsInit;
598  return init->Clone();
599  }
600  if (lhsNAryAddExpr && rhsNAryAddExpr)
601  {
602  // We have two add expressions. Add the rhs operands to the lhs add expr
603  for (auto op : rhsNAryAddExpr->GetOperands())
604  {
605  lhsNAryAddExpr->AddOperand(op->Clone());
606  }
607  return lhsNAryAddExpr->Clone();
608  }
609  if ((lhsNAryAddExpr && isNonZeroConstant(rhsConstant))
610  || (rhsNAryAddExpr && isNonZeroConstant(lhsConstant)))
611  {
612  // We have an add expr and a nonzero constant. Add the constant to the add expr
613  auto * nAryAddExpr = lhsNAryAddExpr ? lhsNAryAddExpr : rhsNAryAddExpr;
614  auto * constant = lhsConstant ? lhsConstant : rhsConstant;
615  nAryAddExpr->AddOperand(constant->Clone());
616  return nAryAddExpr->Clone();
617  }
618  if (lhsNAryAddExpr || rhsNAryAddExpr)
619  {
620  const auto * nAryAddExpr = lhsNAryAddExpr ? lhsNAryAddExpr : rhsNAryAddExpr;
621  return nAryAddExpr->Clone();
622  }
623  if (lhsConstant && rhsConstant)
624  {
625  // Two constants, get their value, and combine them (fold)
626  const auto lhsValue = lhsConstant->GetValue();
627  const auto rhsValue = rhsConstant->GetValue();
628 
629  return std::make_unique<SCEVConstant>(lhsValue + rhsValue);
630  }
631  if (lhsConstant || rhsConstant)
632  {
633  const auto * constant = lhsConstant ? lhsConstant : rhsConstant;
634  return constant->Clone();
635  }
636  return std::make_unique<SCEVUnknown>();
637 }
638 
639 bool
641  const rvsdg::Output & variable,
642  IVDependencyGraph & dependencyGraph)
643 {
644  // First check that variable has only one self-reference
645  if (dependencyGraph[&variable][&variable] != 1)
646  return false;
647 
648  // Then check for cycles through other variables
649  std::unordered_set<const rvsdg::Output *> visited{};
650  std::unordered_set<const rvsdg::Output *> recursionStack{};
651  return !HasCycleThroughOthers(variable, variable, dependencyGraph, visited, recursionStack);
652 }
653 
654 bool
656  const rvsdg::Output & currentIV,
657  const rvsdg::Output & originalIV,
658  IVDependencyGraph & dependencyGraph,
659  std::unordered_set<const rvsdg::Output *> & visited,
660  std::unordered_set<const rvsdg::Output *> & recursionStack)
661 {
662  visited.insert(&currentIV);
663  recursionStack.insert(&currentIV);
664 
665  for (const auto & [depPtr, depCount] : dependencyGraph[&currentIV])
666  {
667  // Ignore self-references
668  if (depPtr == &currentIV)
669  continue;
670 
671  // Found a cycle back to the ORIGINAL node we started from
672  // This means the original IV is explicitly part of the cycle
673  if (depPtr == &originalIV)
674  return true;
675 
676  // Already explored this branch, no cycle containing the original IV
677  if (visited.find(depPtr) != visited.end())
678  continue;
679 
680  // Recursively check dependencies, keeping track of the original node
681  if (HasCycleThroughOthers(*depPtr, originalIV, dependencyGraph, visited, recursionStack))
682  return true;
683  }
684 
685  recursionStack.erase(&currentIV);
686  return false;
687 }
688 
689 bool
691 {
692  if (typeid(a) != typeid(b))
693  return false;
694 
695  if (auto * ca = dynamic_cast<const SCEVConstant *>(&a))
696  {
697  auto * cb = dynamic_cast<const SCEVConstant *>(&b);
698  return ca->GetValue() == cb->GetValue();
699  }
700 
701  if (auto * ia = dynamic_cast<const SCEVInit *>(&a))
702  {
703  auto * ib = dynamic_cast<const SCEVInit *>(&b);
704  return ia->GetPrePointer() == ib->GetPrePointer();
705  }
706 
707  if (dynamic_cast<const SCEVUnknown *>(&a))
708  return true;
709 
710  if (auto * aa = dynamic_cast<const SCEVAddExpr *>(&a))
711  {
712  auto * ab = dynamic_cast<const SCEVAddExpr *>(&b);
713  return StructurallyEqual(*aa->GetLeftOperand(), *ab->GetLeftOperand())
714  && StructurallyEqual(*aa->GetRightOperand(), *ab->GetRightOperand());
715  }
716 
717  if (auto * cha = dynamic_cast<const SCEVChainRecurrence *>(&a))
718  {
719  auto * chb = dynamic_cast<const SCEVChainRecurrence *>(&b);
720  if (cha->GetLoop() != chb->GetLoop())
721  return false;
722  if (cha->GetOperands().size() != chb->GetOperands().size())
723  return false;
724  for (size_t i = 0; i < cha->GetOperands().size(); ++i)
725  {
726  if (!StructurallyEqual(*cha->GetOperands()[i], *chb->GetOperands()[i]))
727  return false;
728  }
729  return true;
730  }
731 
732  if (auto * naa = dynamic_cast<const SCEVNAryAddExpr *>(&a))
733  {
734  auto * nab = dynamic_cast<const SCEVNAryAddExpr *>(&b);
735  if (naa->GetOperands().size() != nab->GetOperands().size())
736  return false;
737  for (size_t i = 0; i < naa->GetOperands().size(); ++i)
738  {
739  if (!StructurallyEqual(*naa->GetOperands()[i], *nab->GetOperands()[i]))
740  return false;
741  }
742  return true;
743  }
744 
745  return false;
746 }
747 }
const IntegerValueRepresentation & Representation() const noexcept
std::vector< const SCEV * > GetOperands() const
const rvsdg::Output * GetPrePointer() const
int GetNumOfChrecsWithOrder(const int n) const
std::vector< const rvsdg::Output * > LoopVars_
Context & operator=(const Context &)=delete
Context(const Context &)=delete
void AddLoopVar(const rvsdg::Output &var)
Context & operator=(Context &&)=delete
void InsertChrec(const rvsdg::ThetaNode &thetaNode, std::unique_ptr< SCEVChainRecurrence > &chrec)
std::unordered_map< const rvsdg::ThetaNode *, util::HashSet< std::unique_ptr< SCEVChainRecurrence > > > ChrecMap_
static std::unique_ptr< Context > Create()
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
~Statistics() noexcept override=default
void Stop(const Context &context) noexcept
std::optional< const SCEV * > TryGetSCEVForOutput(const rvsdg::Output &output)
static std::unordered_map< const rvsdg::Output *, int > FindDependenciesForSCEV(const SCEV &currentSCEV, const rvsdg::Output &currentIV)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEV > > UniqueSCEVs_
void AnalyzeRegion(const rvsdg::Region &region)
std::unique_ptr< SCEV > GetOrCreateSCEVForOutput(const rvsdg::Output &output)
static std::vector< const rvsdg::Output * > TopologicalSort(const IVDependencyGraph &dependencyGraph)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEVChainRecurrence > > ChainRecurrenceMap_
~ScalarEvolution() noexcept override
IVDependencyGraph CreateDependencyGraph(const rvsdg::ThetaNode &thetaNode) const
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEVChainRecurrence > > PerformSCEVAnalysis(const rvsdg::ThetaNode &thetaNode)
static bool HasCycleThroughOthers(const rvsdg::Output &currentIV, const rvsdg::Output &originalIV, IVDependencyGraph &dependencyGraph, std::unordered_set< const rvsdg::Output * > &visited, std::unordered_set< const rvsdg::Output * > &recursionStack)
std::unique_ptr< Context > Context_
static bool IsUnknown(const SCEVChainRecurrence &chrec)
std::unordered_map< const rvsdg::Output *, std::unordered_map< const rvsdg::Output *, int > > IVDependencyGraph
static bool IsValidInductionVariable(const rvsdg::Output &variable, IVDependencyGraph &dependencyGraph)
static bool StructurallyEqual(const SCEV &a, const SCEV &b)
static std::unique_ptr< SCEV > ApplyFolding(SCEV *lhsOperand, SCEV *rhsOperand)
std::unique_ptr< SCEVChainRecurrence > CreateChainRecurrence(const rvsdg::Output &IV, const SCEV &scevTree, const rvsdg::ThetaNode &thetaNode)
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
NodeRange Nodes() noexcept
Definition: region.hpp:328
LoopVar MapPreLoopVar(const rvsdg::Output &argument) const
Maps variable at start of loop iteration to full varibale description.
Definition: theta.cpp:140
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition: theta.cpp:176
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.
bool isNonZeroConstant(const SCEVConstant *c)
bool DependsOnLoopVariable(const rvsdg::Output &variable, ScalarEvolution::IVDependencyGraph &dependencyGraph)
std::optional< int64_t > tryGetConstantSignedInteger(const rvsdg::Output &output)
Definition: Trace.cpp:46
rvsdg::Input * post
Variable after iteration (output result from subregion).
Definition: theta.hpp:62
static const char * NumFirstOrderRecurrences
Definition: Statistics.hpp:236
static const char * NumSecondOrderRecurrences
Definition: Statistics.hpp:237
static const char * Timer
Definition: Statistics.hpp:240
static const char * NumTotalRecurrences
Definition: Statistics.hpp:234
static const char * NumLoopVariablesTotal
Definition: Statistics.hpp:233
static const char * NumConstantRecurrences
Definition: Statistics.hpp:235
static const char * NumThirdOrderRecurrences
Definition: Statistics.hpp:238