Jlm
reduction.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
8 #include <jlm/rvsdg/gamma.hpp>
11 #include <jlm/rvsdg/traverser.hpp>
12 #include <jlm/util/Statistics.hpp>
13 
14 namespace jlm::llvm
15 {
16 
17 void
19 {
20  AddMeasurement(Label::NumRvsdgNodesBefore, rvsdg::nnodes(&graph.GetRootRegion()));
21  AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion()));
22  AddTimer(Label::Timer).start();
23 }
24 
25 void
27 {
28  AddMeasurement(Label::NumRvsdgNodesAfter, rvsdg::nnodes(&graph.GetRootRegion()));
29  AddMeasurement(Label::NumRvsdgInputsAfter, rvsdg::ninputs(&graph.GetRootRegion()));
30  GetTimer(Label::Timer).stop();
31 }
32 
33 bool
34 NodeReduction::Statistics::AddIteration(const rvsdg::Region & region, size_t numIterations)
35 {
36  const auto it = NumIterations_.find(&region);
37  NumIterations_[&region] = numIterations;
38  return it != NumIterations_.end();
39 }
40 
41 std::optional<size_t>
43 {
44  if (const auto it = NumIterations_.find(&region); it != NumIterations_.end())
45  {
46  return it->second;
47  }
48 
49  return std::nullopt;
50 }
51 
52 NodeReduction::~NodeReduction() noexcept = default;
53 
56 {}
57 
58 void
60  rvsdg::RvsdgModule & rvsdgModule,
61  util::StatisticsCollector & statisticsCollector)
62 {
63  const auto & graph = rvsdgModule.Rvsdg();
64 
65  Statistics_ = Statistics::Create(rvsdgModule.SourceFilePath().value());
66  Statistics_->Start(graph);
67 
68  ReduceNodesInRegion(graph.GetRootRegion());
69 
70  Statistics_->End(graph);
71  statisticsCollector.CollectDemandedStatistics(std::move(Statistics_));
72 }
73 
74 void
76 {
77  bool reductionPerformed = false;
78  size_t numIterations = 0;
79  do
80  {
81  numIterations++;
82  reductionPerformed = false;
83 
84  for (const auto node : rvsdg::TopDownTraverser(&region))
85  {
87  *node,
88  [this, &reductionPerformed](rvsdg::StructuralNode & structuralNode)
89  {
90  reductionPerformed |= ReduceStructuralNode(structuralNode);
91  },
92  [&reductionPerformed](rvsdg::SimpleNode & simpleNode)
93  {
94  reductionPerformed |= ReduceSimpleNode(simpleNode);
95  });
96  }
97 
98  if (reductionPerformed)
99  {
100  // Let's remove all dead nodes in this region to avoid reductions on
101  // dead nodes in the next iteration.
102  region.prune(false);
103  }
104  } while (reductionPerformed);
105 
106  Statistics_->AddIteration(region, numIterations);
107 }
108 
109 bool
111 {
112  bool reductionPerformed = false;
113 
114  // Reduce structural nodes
115  if (dynamic_cast<const rvsdg::GammaNode *>(&structuralNode))
116  {
117  reductionPerformed |= ReduceGammaNode(structuralNode);
118  }
119 
120  if (reductionPerformed)
121  {
122  // We can not go through the subregions as the structural node might already have been removed.
123  return true;
124  }
125 
126  // Reduce all nodes in the subregions
127  for (size_t n = 0; n < structuralNode.nsubregions(); n++)
128  {
129  const auto subregion = structuralNode.subregion(n);
130  ReduceNodesInRegion(*subregion);
131  }
132 
133  return false;
134 }
135 
136 bool
138 {
139  JLM_ASSERT(dynamic_cast<const rvsdg::GammaNode *>(&gammaNode));
140 
141  // FIXME: We can not apply the reduction below due to a bug. See github issue #303
142  // rvsdg::ReduceGammaControlConstant
143 
144  return ReduceGammaWithStaticallyKnownPredicate(gammaNode);
145 }
146 
147 bool
149 {
150  if (is<LoadNonVolatileOperation>(&simpleNode))
151  {
152  return ReduceLoadNode(simpleNode);
153  }
154  if (is<StoreNonVolatileOperation>(&simpleNode))
155  {
156  return ReduceStoreNode(simpleNode);
157  }
158  if (is<MemoryStateMergeOperation>(&simpleNode))
159  {
160  return ReduceMemoryStateMergeNode(simpleNode);
161  }
162  if (is<MemoryStateJoinOperation>(&simpleNode))
163  {
164  return rvsdg::ReduceNode<MemoryStateJoinOperation>(NormalizeMemoryStateJoinNode, simpleNode);
165  }
166  if (is<MemoryStateSplitOperation>(&simpleNode))
167  {
168  return ReduceMemoryStateSplitNode(simpleNode);
169  }
170  if (is<LambdaEntryMemoryStateSplitOperation>(&simpleNode))
171  {
172  return rvsdg::ReduceNode<LambdaEntryMemoryStateSplitOperation>(
174  simpleNode);
175  }
176  if (is<CallExitMemoryStateSplitOperation>(&simpleNode))
177  {
178  return rvsdg::ReduceNode<CallExitMemoryStateSplitOperation>(
180  simpleNode);
181  }
182  if (is<LambdaExitMemoryStateMergeOperation>(&simpleNode))
183  {
184  return ReduceLambdaExitMemoryStateMergeNode(simpleNode);
185  }
186  if (is<rvsdg::UnaryOperation>(&simpleNode))
187  {
188  // FIXME: handle the unary node
189  // See github issue #304
190  return false;
191  }
192  if (is<rvsdg::BinaryOperation>(&simpleNode))
193  {
194  return ReduceBinaryNode(simpleNode);
195  }
196 
197  return false;
198 }
199 
200 bool
202 {
203  JLM_ASSERT(is<LoadNonVolatileOperation>(&simpleNode));
204 
205  return rvsdg::ReduceNode<LoadNonVolatileOperation>(NormalizeLoadNode, simpleNode);
206 }
207 
208 bool
210 {
211  JLM_ASSERT(is<StoreNonVolatileOperation>(&simpleNode));
212 
213  return rvsdg::ReduceNode<StoreNonVolatileOperation>(NormalizeStoreNode, simpleNode);
214 }
215 
216 bool
218 {
219  JLM_ASSERT(is<rvsdg::BinaryOperation>(&simpleNode));
220 
221  return rvsdg::ReduceNode<rvsdg::BinaryOperation>(rvsdg::NormalizeBinaryOperation, simpleNode);
222 }
223 
224 bool
226 {
227  JLM_ASSERT(is<MemoryStateMergeOperation>(&simpleNode));
228 
229  return rvsdg::ReduceNode<MemoryStateMergeOperation>(NormalizeMemoryStateMergeNode, simpleNode);
230 }
231 
232 bool
234 {
235  JLM_ASSERT(is<MemoryStateSplitOperation>(&simpleNode));
236 
237  return rvsdg::ReduceNode<MemoryStateSplitOperation>(NormalizeMemoryStateSplitNode, simpleNode);
238 }
239 
240 bool
242 {
243  JLM_ASSERT(is<LambdaExitMemoryStateMergeOperation>(&simpleNode));
244  return rvsdg::ReduceNode<LambdaExitMemoryStateMergeOperation>(
246  simpleNode);
247 }
248 
249 std::optional<std::vector<rvsdg::Output *>>
251  const LoadNonVolatileOperation & operation,
252  const std::vector<rvsdg::Output *> & operands)
253 {
254  static std::vector<rvsdg::NodeNormalization<LoadNonVolatileOperation>> loadNodeNormalizations(
260 
261  return rvsdg::NormalizeSequence<LoadNonVolatileOperation>(
262  loadNodeNormalizations,
263  operation,
264  operands);
265 }
266 
267 std::optional<std::vector<rvsdg::Output *>>
269  const StoreNonVolatileOperation & operation,
270  const std::vector<rvsdg::Output *> & operands)
271 {
272  static std::vector<rvsdg::NodeNormalization<StoreNonVolatileOperation>> storeNodeNormalizations(
279 
280  return rvsdg::NormalizeSequence<StoreNonVolatileOperation>(
281  storeNodeNormalizations,
282  operation,
283  operands);
284 }
285 
286 std::optional<std::vector<rvsdg::Output *>>
288  const MemoryStateMergeOperation & operation,
289  const std::vector<rvsdg::Output *> & operands)
290 {
291  static std::vector<rvsdg::NodeNormalization<MemoryStateMergeOperation>> normalizations(
296 
297  return rvsdg::NormalizeSequence<MemoryStateMergeOperation>(normalizations, operation, operands);
298 }
299 
300 std::optional<std::vector<rvsdg::Output *>>
302  const MemoryStateJoinOperation & operation,
303  const std::vector<rvsdg::Output *> & operands)
304 {
305  static std::vector<rvsdg::NodeNormalization<MemoryStateJoinOperation>> normalizations(
308 
309  return rvsdg::NormalizeSequence<MemoryStateJoinOperation>(normalizations, operation, operands);
310 }
311 
312 std::optional<std::vector<rvsdg::Output *>>
314  const MemoryStateSplitOperation & operation,
315  const std::vector<rvsdg::Output *> & operands)
316 {
317  static std::vector<rvsdg::NodeNormalization<MemoryStateSplitOperation>> normalizations(
321 
322  return rvsdg::NormalizeSequence<MemoryStateSplitOperation>(normalizations, operation, operands);
323 }
324 
325 std::optional<std::vector<rvsdg::Output *>>
327  const CallExitMemoryStateSplitOperation & operation,
328  const std::vector<rvsdg::Output *> & operands)
329 {
330  static std::vector<rvsdg::NodeNormalization<CallExitMemoryStateSplitOperation>> normalizations(
332 
333  return rvsdg::NormalizeSequence<CallExitMemoryStateSplitOperation>(
334  normalizations,
335  operation,
336  operands);
337 }
338 
339 std::optional<std::vector<rvsdg::Output *>>
341  const LambdaEntryMemoryStateSplitOperation & operation,
342  const std::vector<rvsdg::Output *> & operands)
343 {
344  static std::vector<rvsdg::NodeNormalization<LambdaEntryMemoryStateSplitOperation>> normalizations(
346 
347  return rvsdg::NormalizeSequence<LambdaEntryMemoryStateSplitOperation>(
348  normalizations,
349  operation,
350  operands);
351 }
352 
353 std::optional<std::vector<rvsdg::Output *>>
355  const LambdaExitMemoryStateMergeOperation & operation,
356  const std::vector<rvsdg::Output *> & operands)
357 {
358  static std::vector<rvsdg::NodeNormalization<LambdaExitMemoryStateMergeOperation>> normalizations(
362 
363  return rvsdg::NormalizeSequence<LambdaExitMemoryStateMergeOperation>(
364  normalizations,
365  operation,
366  operands);
367 }
368 
369 }
static std::optional< std::vector< rvsdg::Output * > > NormalizeLambdaExitMemoryStateMerge(const CallExitMemoryStateSplitOperation &callExitSplitOperation, const std::vector< rvsdg::Output * > &operands)
static std::optional< std::vector< rvsdg::Output * > > NormalizeCallEntryMemoryStateMerge(const LambdaEntryMemoryStateSplitOperation &lambdaEntrySplitOperation, const std::vector< rvsdg::Output * > &operands)
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadFromAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreToAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
static std::optional< std::vector< rvsdg::Output * > > NormalizeAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
static std::optional< std::vector< rvsdg::Output * > > NormalizeIOBarrierAllocaAddress(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the LoadNonVolatileOperation from an IOBarrierOperation when it can b...
Definition: Load.cpp:340
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadStoreState(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
If the producer of a load's address is an alloca operation, then we can remove all state edges origin...
Definition: Load.cpp:318
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Load.cpp:329
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadAlloca(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
If the producer of a load's address is an alloca operation, then we can remove all state edges origin...
Definition: Load.cpp:307
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadStore(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Forwards the value from a store operation.
Definition: Load.cpp:244
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateOperands(const MemoryStateJoinOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes duplicated operands from the MemoryStateJoinOperation.
static std::optional< std::vector< rvsdg::Output * > > NormalizeSingleOperand(const MemoryStateJoinOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes the MemoryStateJoinOperation as it has only a single operand, i.e., no joining is performed.
static std::optional< std::vector< rvsdg::Output * > > NormalizeNestedMerges(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Fuses nested merges into a single merge.
static std::optional< std::vector< rvsdg::Output * > > NormalizeMergeSplit(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Fuses nested splits into a single merge.
static std::optional< std::vector< rvsdg::Output * > > NormalizeSingleOperand(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes the MemoryStateMergeOperation as it has only a single operand, i.e., no merging is performed.
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateOperands(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes duplicated operands from the MemoryStateMergeOperation.
static std::optional< std::vector< rvsdg::Output * > > NormalizeNestedSplits(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Fuses nested splits into a single split.
static std::optional< std::vector< rvsdg::Output * > > NormalizeSingleResult(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes the MemoryStateSplitOperation as it has only a single result, i.e., no splitting is performed...
static std::optional< std::vector< rvsdg::Output * > > NormalizeSplitMerge(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes an idempotent split-merge pair.
bool AddIteration(const rvsdg::Region &region, size_t numIterations)
Definition: reduction.cpp:34
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition: reduction.hpp:171
void End(const rvsdg::Graph &graph) noexcept
Definition: reduction.cpp:26
std::unordered_map< const rvsdg::Region *, size_t > NumIterations_
Definition: reduction.hpp:177
void Start(const rvsdg::Graph &graph) noexcept
Definition: reduction.cpp:18
std::optional< size_t > GetNumIterations(const rvsdg::Region &region) const noexcept
Definition: reduction.cpp:42
static bool ReduceLambdaExitMemoryStateMergeNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:241
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateMergeNode(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:287
void ReduceNodesInRegion(rvsdg::Region &region)
Definition: reduction.cpp:75
static bool ReduceMemoryStateSplitNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:233
std::unique_ptr< Statistics > Statistics_
Definition: reduction.hpp:143
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Definition: reduction.cpp:59
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadNode(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:250
static std::optional< std::vector< rvsdg::Output * > > NormalizeLambdaEntryMemoryStateSplitNode(const LambdaEntryMemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:340
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateSplitNode(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:313
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreNode(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:268
bool ReduceStructuralNode(rvsdg::StructuralNode &structuralNode)
Definition: reduction.cpp:110
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateJoinNode(const MemoryStateJoinOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:301
static bool ReduceSimpleNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:148
static bool ReduceBinaryNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:217
static bool ReduceStoreNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:209
static bool ReduceMemoryStateMergeNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:225
~NodeReduction() noexcept override
static std::optional< std::vector< rvsdg::Output * > > NormalizeCallExitMemoryStateSplitNode(const CallExitMemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:326
static bool ReduceGammaNode(rvsdg::StructuralNode &gammaNode)
Definition: reduction.cpp:137
static bool ReduceLoadNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:201
static std::optional< std::vector< rvsdg::Output * > > NormalizeLambdaExitMemoryStateMergeNode(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:354
static std::optional< std::vector< rvsdg::Output * > > NormalizeIOBarrierAllocaAddress(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the StoreNonVolatileOperation from an IOBarrierOperation when it can ...
Definition: Store.cpp:245
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreMux(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Swaps a memory state merge operation and a store operation.
Definition: Store.cpp:201
static std::optional< std::vector< rvsdg::Output * > > normalizeStoreAllocaSingleUser(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.cpp:273
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreAlloca(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes unnecessary state from a store node when its address originates directly from an alloca node.
Definition: Store.cpp:223
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreStore(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes a duplicated store to the same address.
Definition: Store.cpp:212
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Store.cpp:234
Conditional operator / pattern matching.
Definition: gamma.hpp:99
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
void prune(bool recursive)
Definition: region.cpp:323
const std::optional< util::FilePath > & SourceFilePath() const noexcept
Definition: RvsdgModule.hpp:73
Graph & Rvsdg() noexcept
Definition: RvsdgModule.hpp:57
size_t nsubregions() const noexcept
rvsdg::Region * subregion(size_t index) const noexcept
Transformation(std::string_view Name)
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Definition: Statistics.hpp:563
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
std::optional< std::vector< rvsdg::Output * > > NormalizeBinaryOperation(const BinaryOperation &operation, const std::vector< rvsdg::Output * > &operands)
Applies the reductions implemented in the binary operations reduction functions.
Definition: binary.cpp:112
bool ReduceGammaWithStaticallyKnownPredicate(Node &node)
Definition: gamma.cpp:158
size_t nnodes(const jlm::rvsdg::Region *region) noexcept
Definition: region.cpp:629
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049
size_t ninputs(const rvsdg::Region *region) noexcept
Definition: region.cpp:682