Jlm
ScalarEvolution.hpp
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 
6 #ifndef JLM_LLVM_OPT_SCALAR_EVOLUTION_HPP
7 #define JLM_LLVM_OPT_SCALAR_EVOLUTION_HPP
8 
9 #include <jlm/rvsdg/theta.hpp>
11 
12 #include <string>
13 
14 namespace jlm::llvm
15 {
16 class SCEV
17 {
18  friend class ScalarEvolution;
19 
20 public:
21  virtual ~SCEV() noexcept = default;
22 
23  virtual std::string
24  DebugString() const = 0;
25 
26  virtual std::unique_ptr<SCEV>
27  Clone() const = 0;
28 };
29 
30 class SCEVUnknown final : public SCEV
31 {
32 public:
33  explicit SCEVUnknown()
34  {}
35 
36  std::string
37  DebugString() const override
38  {
39  return "Unknown";
40  }
41 
42  std::unique_ptr<SCEV>
43  Clone() const override
44  {
45  return std::make_unique<SCEVUnknown>();
46  }
47 };
48 
49 class SCEVInit final : public SCEV
50 {
51 public:
52  explicit SCEVInit(const rvsdg::Output & pre)
53  : PrePointer_{ &pre }
54  {}
55 
56  const rvsdg::Output *
57  GetPrePointer() const
58  {
59  return PrePointer_;
60  }
61 
62  std::string
63  DebugString() const override
64  {
65  std::ostringstream oss;
66  oss << "Init(" << PrePointer_->debug_string() << ")";
67  return oss.str();
68  }
69 
70  std::unique_ptr<SCEV>
71  Clone() const override
72  {
73  return std::make_unique<SCEVInit>(*PrePointer_);
74  }
75 
76 private:
78 };
79 
80 class SCEVPlaceholder final : public SCEV
81 {
82 public:
83  explicit SCEVPlaceholder(const rvsdg::Output & pre)
84  : PrePointer_{ &pre }
85  {}
86 
87  const rvsdg::Output *
88  GetPrePointer() const
89  {
90  return PrePointer_;
91  }
92 
93  std::string
94  DebugString() const override
95  {
96  std::ostringstream oss;
97  oss << "PH(" << PrePointer_->debug_string() << ")";
98  return oss.str();
99  }
100 
101  std::unique_ptr<SCEV>
102  Clone() const override
103  {
104  return std::make_unique<SCEVPlaceholder>(*PrePointer_);
105  }
106 
107 private:
109 };
110 
111 class SCEVConstant final : public SCEV
112 {
113 public:
114  explicit SCEVConstant(const int64_t value)
115  : Value_{ value }
116  {}
117 
118  int64_t
119  GetValue() const
120  {
121  return Value_;
122  }
123 
124  std::string
125  DebugString() const override
126  {
127  return std::to_string(Value_);
128  }
129 
130  std::unique_ptr<SCEV>
131  Clone() const override
132  {
133  return std::make_unique<SCEVConstant>(Value_);
134  }
135 
136 private:
137  int64_t Value_;
138 };
139 
140 class SCEVAddExpr final : public SCEV
141 {
142 public:
144  : LeftOperand_{},
145  RightOperand_{}
146  {}
147 
148  SCEVAddExpr(std::unique_ptr<SCEV> left, std::unique_ptr<SCEV> right)
149  : LeftOperand_{ std::move(left) },
150  RightOperand_{ std::move(right) }
151  {}
152 
153  SCEV *
155  {
156  return LeftOperand_.get();
157  }
158 
159  SCEV *
161  {
162  return RightOperand_.get();
163  }
164 
165  void
166  SetLeftOperand(std::unique_ptr<SCEV> op)
167  {
168  LeftOperand_ = std::move(op);
169  }
170 
171  void
172  SetRightOperand(std::unique_ptr<SCEV> op)
173  {
174  RightOperand_ = std::move(op);
175  }
176 
177  std::string
178  DebugString() const override
179  {
180  std::ostringstream oss;
181  const std::string leftStr = LeftOperand_ ? LeftOperand_->DebugString() : "null";
182  const std::string rightStr = RightOperand_ ? RightOperand_->DebugString() : "null";
183  oss << "(" << leftStr << " + " << rightStr << ")";
184  return oss.str();
185  }
186 
187  std::unique_ptr<SCEV>
188  Clone() const override
189  {
190  std::unique_ptr<SCEV> leftClone = LeftOperand_ ? LeftOperand_->Clone() : nullptr;
191  std::unique_ptr<SCEV> rightClone = RightOperand_ ? RightOperand_->Clone() : nullptr;
192  return std::make_unique<SCEVAddExpr>(std::move(leftClone), std::move(rightClone));
193  }
194 
195 private:
196  std::unique_ptr<SCEV> LeftOperand_;
197  std::unique_ptr<SCEV> RightOperand_;
198 };
199 
200 class SCEVChainRecurrence final : public SCEV
201 {
202  friend class ScalarEvolution;
203 
204 public:
205  explicit SCEVChainRecurrence(const rvsdg::ThetaNode & theta)
206  : Operands_{},
207  Loop_{ &theta }
208  {}
209 
210  void
211  AddOperand(std::unique_ptr<SCEV> scev)
212  {
213  Operands_.push_back(std::move(scev));
214  }
215 
216  const rvsdg::ThetaNode *
217  GetLoop() const
218  {
219  return Loop_;
220  }
221 
222  SCEV *
224  {
225  return Operands_[0].get();
226  }
227 
228  void
229  AddOperandToFront(const std::unique_ptr<SCEV> & initScev)
230  {
231  Operands_.insert(Operands_.begin(), initScev->Clone());
232  }
233 
234  std::vector<const SCEV *>
235  GetOperands() const
236  {
237  std::vector<const SCEV *> operands{};
238  for (auto & op : Operands_)
239  {
240  operands.push_back(op.get());
241  }
242  return operands;
243  }
244 
245  SCEV *
246  GetOperand(const size_t index) const
247  {
248  return Operands_.at(index).get();
249  }
250 
251  std::string
252  DebugString() const override
253  {
254  std::ostringstream oss;
255  oss << "{";
256  for (size_t i = 0; i < Operands_.size(); ++i)
257  {
258  oss << Operands_.at(i)->DebugString();
259  if (i < Operands_.size() - 1)
260  oss << ",+,";
261  }
262  oss << "}" << "<" << Loop_->DebugString() << ">";
263  return oss.str();
264  }
265 
266  std::unique_ptr<SCEV>
267  Clone() const override
268  {
269  auto copy = std::make_unique<SCEVChainRecurrence>(*Loop_);
270  for (const auto & op : Operands_)
271  {
272  copy->AddOperand(op->Clone());
273  }
274  return copy;
275  }
276 
277 protected:
278  std::vector<std::unique_ptr<SCEV>> Operands_;
280 };
281 
282 class SCEVNAryAddExpr final : public SCEV
283 {
284  friend class ScalarEvolution;
285 
286 public:
287  explicit SCEVNAryAddExpr()
288  : Operands_{}
289  {}
290 
291  template<typename... Args>
292  explicit SCEVNAryAddExpr(Args &&... operands)
293  : Operands_{}
294  {
295  (AddOperand(std::forward<Args>(operands)), ...);
296  }
297 
298  template<typename... Args>
299  void
300  AddOperands(Args &&... operands)
301  {
302  (AddOperand(std::forward<Args>(operands)), ...);
303  }
304 
305  void
306  AddOperand(std::unique_ptr<SCEV> scev)
307  {
308  Operands_.push_back(std::move(scev));
309  }
310 
311  std::vector<const SCEV *>
312  GetOperands() const
313  {
314  std::vector<const SCEV *> operands{};
315  for (auto & op : Operands_)
316  {
317  operands.push_back(op.get());
318  }
319  return operands;
320  }
321 
322  SCEV *
323  GetOperand(const size_t index) const
324  {
325  return Operands_.at(index).get();
326  }
327 
328  std::string
329  DebugString() const override
330  {
331  std::ostringstream oss;
332  oss << "(";
333  for (size_t i = 0; i < Operands_.size(); ++i)
334  {
335  oss << Operands_.at(i)->DebugString();
336  if (i < Operands_.size() - 1)
337  oss << " + ";
338  }
339  oss << ")";
340  return oss.str();
341  }
342 
343  std::unique_ptr<SCEV>
344  Clone() const override
345  {
346  auto copy = std::make_unique<SCEVNAryAddExpr>();
347  for (const auto & op : Operands_)
348  {
349  copy->AddOperand(op->Clone());
350  }
351  return copy;
352  }
353 
354 protected:
355  std::vector<std::unique_ptr<SCEV>> Operands_;
356 };
357 
359 {
360  class Context;
361  class Statistics;
362 
363 public:
365  InductionVariableSet; // Stores the pointers to the output result from the subregion for the
366  // induction variables
367 
368  typedef std::unordered_map<const rvsdg::Output *, std::unordered_map<const rvsdg::Output *, int>>
370 
371  ~ScalarEvolution() noexcept override;
372 
373  ScalarEvolution();
374 
375  ScalarEvolution(const ScalarEvolution &) = delete;
376 
378 
380  operator=(const ScalarEvolution &) = delete;
381 
383  operator=(ScalarEvolution &&) = delete;
384 
385  void
386  Run(rvsdg::RvsdgModule & rvsdgModule, util::StatisticsCollector & statisticsCollector) override;
387 
388  std::unordered_map<const rvsdg::Output *, std::unique_ptr<SCEVChainRecurrence>>
389  PerformSCEVAnalysis(const rvsdg::ThetaNode & thetaNode);
390 
391  static bool
392  StructurallyEqual(const SCEV & a, const SCEV & b);
393 
394 private:
395  std::unordered_map<const rvsdg::ThetaNode *, InductionVariableSet> InductionVariableMap_;
396  std::unordered_map<const rvsdg::Output *, std::unique_ptr<SCEV>> UniqueSCEVs_;
397  std::unordered_map<const rvsdg::Output *, std::unique_ptr<SCEVChainRecurrence>>
398  ChainRecurrenceMap_;
399 
400  void
401  AnalyzeRegion(const rvsdg::Region & region);
402 
403  std::unique_ptr<SCEV>
404  GetOrCreateSCEVForOutput(const rvsdg::Output & output);
405 
406  std::optional<const SCEV *>
407  TryGetSCEVForOutput(const rvsdg::Output & output);
408 
410  CreateDependencyGraph(const rvsdg::ThetaNode & thetaNode) const;
411 
412  static std::unordered_map<const rvsdg::Output *, int>
413  FindDependenciesForSCEV(const SCEV & currentSCEV, const rvsdg::Output & currentIV);
414 
415  static std::vector<const rvsdg::Output *>
416  TopologicalSort(const IVDependencyGraph & dependencyGraph);
417 
418  std::unique_ptr<SCEVChainRecurrence>
419  CreateChainRecurrence(
420  const rvsdg::Output & IV,
421  const SCEV & scevTree,
422  const rvsdg::ThetaNode & thetaNode);
423 
424  static std::unique_ptr<SCEV>
425  ApplyFolding(SCEV * lhsOperand, SCEV * rhsOperand);
426 
427  static bool
428  IsValidInductionVariable(const rvsdg::Output & variable, IVDependencyGraph & dependencyGraph);
429 
436  static bool
437  IsUnknown(const SCEVChainRecurrence & chrec);
438 
439  static bool
440  HasCycleThroughOthers(
441  const rvsdg::Output & currentIV,
442  const rvsdg::Output & originalIV,
443  IVDependencyGraph & dependencyGraph,
444  std::unordered_set<const rvsdg::Output *> & visited,
445  std::unordered_set<const rvsdg::Output *> & recursionStack);
446 
447  std::unique_ptr<Context> Context_;
448 };
449 
450 }
451 
452 #endif
void SetLeftOperand(std::unique_ptr< SCEV > op)
std::unique_ptr< SCEV > LeftOperand_
SCEV * GetRightOperand() const
SCEVAddExpr(std::unique_ptr< SCEV > left, std::unique_ptr< SCEV > right)
std::unique_ptr< SCEV > RightOperand_
std::unique_ptr< SCEV > Clone() const override
SCEV * GetLeftOperand() const
std::string DebugString() const override
void SetRightOperand(std::unique_ptr< SCEV > op)
SCEV * GetOperand(const size_t index) const
const rvsdg::ThetaNode * GetLoop() const
std::unique_ptr< SCEV > Clone() const override
std::string DebugString() const override
SCEVChainRecurrence(const rvsdg::ThetaNode &theta)
const rvsdg::ThetaNode * Loop_
void AddOperandToFront(const std::unique_ptr< SCEV > &initScev)
std::vector< std::unique_ptr< SCEV > > Operands_
void AddOperand(std::unique_ptr< SCEV > scev)
std::vector< const SCEV * > GetOperands() const
std::unique_ptr< SCEV > Clone() const override
SCEVConstant(const int64_t value)
std::string DebugString() const override
const rvsdg::Output * PrePointer_
std::string DebugString() const override
const rvsdg::Output * GetPrePointer() const
std::unique_ptr< SCEV > Clone() const override
SCEVInit(const rvsdg::Output &pre)
void AddOperand(std::unique_ptr< SCEV > scev)
std::vector< std::unique_ptr< SCEV > > Operands_
SCEVNAryAddExpr(Args &&... operands)
std::string DebugString() const override
std::unique_ptr< SCEV > Clone() const override
void AddOperands(Args &&... operands)
SCEV * GetOperand(const size_t index) const
std::vector< const SCEV * > GetOperands() const
std::string DebugString() const override
std::unique_ptr< SCEV > Clone() const override
const rvsdg::Output * PrePointer_
SCEVPlaceholder(const rvsdg::Output &pre)
const rvsdg::Output * GetPrePointer() const
std::unique_ptr< SCEV > Clone() const override
std::string DebugString() const override
virtual ~SCEV() noexcept=default
virtual std::unique_ptr< SCEV > Clone() const =0
virtual std::string DebugString() const =0
util::HashSet< const rvsdg::Output * > InductionVariableSet
~ScalarEvolution() noexcept override
std::unordered_map< const rvsdg::Output *, std::unordered_map< const rvsdg::Output *, int > > IVDependencyGraph
Represents an RVSDG transformation.
Global memory state passed between functions.
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049