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 
10 #include <jlm/rvsdg/binary.hpp>
11 #include <jlm/rvsdg/gamma.hpp>
12 #include <jlm/rvsdg/MatchType.hpp>
15 #include <jlm/rvsdg/traverser.hpp>
16 #include <jlm/util/Statistics.hpp>
17 
18 namespace jlm::llvm
19 {
20 
21 void
23 {
24  AddMeasurement(Label::NumRvsdgNodesBefore, rvsdg::nnodes(&graph.GetRootRegion()));
25  AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion()));
26  AddTimer(Label::Timer).start();
27 }
28 
29 void
31 {
32  AddMeasurement(Label::NumRvsdgNodesAfter, rvsdg::nnodes(&graph.GetRootRegion()));
33  AddMeasurement(Label::NumRvsdgInputsAfter, rvsdg::ninputs(&graph.GetRootRegion()));
34  GetTimer(Label::Timer).stop();
35 }
36 
37 bool
38 NodeReduction::Statistics::AddIteration(const rvsdg::Region & region, size_t numIterations)
39 {
40  const auto it = NumIterations_.find(&region);
41  NumIterations_[&region] = numIterations;
42  return it != NumIterations_.end();
43 }
44 
45 std::optional<size_t>
47 {
48  if (const auto it = NumIterations_.find(&region); it != NumIterations_.end())
49  {
50  return it->second;
51  }
52 
53  return std::nullopt;
54 }
55 
56 NodeReduction::~NodeReduction() noexcept = default;
57 
60 {}
61 
62 void
64  rvsdg::RvsdgModule & rvsdgModule,
66 {
67  const auto & graph = rvsdgModule.Rvsdg();
68 
69  Statistics_ = Statistics::Create(rvsdgModule.SourceFilePath().value());
70  Statistics_->Start(graph);
71 
72  ReduceNodesInRegion(graph.GetRootRegion());
73 
74  Statistics_->End(graph);
76 }
77 
78 void
80 {
81  bool reductionPerformed = false;
82  size_t numIterations = 0;
83  do
84  {
85  numIterations++;
86  reductionPerformed = false;
87 
88  for (const auto node : rvsdg::TopDownTraverser(&region))
89  {
91  *node,
92  [this, &reductionPerformed](rvsdg::StructuralNode & structuralNode)
93  {
94  reductionPerformed |= ReduceStructuralNode(structuralNode);
95  },
96  [&reductionPerformed](rvsdg::SimpleNode & simpleNode)
97  {
98  reductionPerformed |= ReduceSimpleNode(simpleNode);
99  });
100  }
101 
102  if (reductionPerformed)
103  {
104  // Let's remove all dead nodes in this region to avoid reductions on
105  // dead nodes in the next iteration.
106  region.prune(false);
107  }
108  } while (reductionPerformed);
109 
110  Statistics_->AddIteration(region, numIterations);
111 }
112 
113 bool
115 {
116  bool reductionPerformed = false;
117 
118  // Reduce structural nodes
119  if (dynamic_cast<const rvsdg::GammaNode *>(&structuralNode))
120  {
121  reductionPerformed |= ReduceGammaNode(structuralNode);
122  }
123 
124  if (reductionPerformed)
125  {
126  // We can not go through the subregions as the structural node might already have been removed.
127  return true;
128  }
129 
130  // Reduce all nodes in the subregions
131  for (size_t n = 0; n < structuralNode.nsubregions(); n++)
132  {
133  const auto subregion = structuralNode.subregion(n);
134  ReduceNodesInRegion(*subregion);
135  }
136 
137  return false;
138 }
139 
140 bool
142 {
143  JLM_ASSERT(dynamic_cast<const rvsdg::GammaNode *>(&gammaNode));
144 
145  // FIXME: We can not apply the reduction below due to a bug. See github issue #303
146  // rvsdg::ReduceGammaControlConstant
147 
148  return ReduceGammaWithStaticallyKnownPredicate(gammaNode);
149 }
150 
151 bool
153 {
154  if (is<LoadNonVolatileOperation>(&simpleNode))
155  {
156  return ReduceLoadNode(simpleNode);
157  }
158  if (is<StoreNonVolatileOperation>(&simpleNode))
159  {
160  return ReduceStoreNode(simpleNode);
161  }
162  if (is<MemoryStateMergeOperation>(&simpleNode))
163  {
164  return ReduceMemoryStateMergeNode(simpleNode);
165  }
166  if (is<MemoryStateJoinOperation>(&simpleNode))
167  {
168  return rvsdg::ReduceNode<MemoryStateJoinOperation>(NormalizeMemoryStateJoinNode, simpleNode);
169  }
170  if (is<MemoryStateSplitOperation>(&simpleNode))
171  {
172  return ReduceMemoryStateSplitNode(simpleNode);
173  }
174  if (is<LambdaExitMemoryStateMergeOperation>(&simpleNode))
175  {
176  return ReduceLambdaExitMemoryStateMergeNode(simpleNode);
177  }
178  if (is<rvsdg::UnaryOperation>(&simpleNode))
179  {
180  // FIXME: handle the unary node
181  // See github issue #304
182  return false;
183  }
184  if (is<rvsdg::BinaryOperation>(&simpleNode))
185  {
186  return ReduceBinaryNode(simpleNode);
187  }
188 
189  return false;
190 }
191 
192 bool
194 {
195  JLM_ASSERT(is<LoadNonVolatileOperation>(&simpleNode));
196 
197  return rvsdg::ReduceNode<LoadNonVolatileOperation>(NormalizeLoadNode, simpleNode);
198 }
199 
200 bool
202 {
203  JLM_ASSERT(is<StoreNonVolatileOperation>(&simpleNode));
204 
205  return rvsdg::ReduceNode<StoreNonVolatileOperation>(NormalizeStoreNode, simpleNode);
206 }
207 
208 bool
210 {
211  JLM_ASSERT(is<rvsdg::BinaryOperation>(&simpleNode));
212 
213  return rvsdg::ReduceNode<rvsdg::BinaryOperation>(rvsdg::NormalizeBinaryOperation, simpleNode);
214 }
215 
216 bool
218 {
219  JLM_ASSERT(is<MemoryStateMergeOperation>(&simpleNode));
220 
221  return rvsdg::ReduceNode<MemoryStateMergeOperation>(NormalizeMemoryStateMergeNode, simpleNode);
222 }
223 
224 bool
226 {
227  JLM_ASSERT(is<MemoryStateSplitOperation>(&simpleNode));
228 
229  return rvsdg::ReduceNode<MemoryStateSplitOperation>(NormalizeMemoryStateSplitNode, simpleNode);
230 }
231 
232 bool
234 {
235  JLM_ASSERT(is<LambdaExitMemoryStateMergeOperation>(&simpleNode));
236  return rvsdg::ReduceNode<LambdaExitMemoryStateMergeOperation>(
238  simpleNode);
239 }
240 
241 std::optional<std::vector<rvsdg::Output *>>
243  const LoadNonVolatileOperation & operation,
244  const std::vector<rvsdg::Output *> & operands)
245 {
246  static std::vector<rvsdg::NodeNormalization<LoadNonVolatileOperation>> loadNodeNormalizations(
252 
253  return rvsdg::NormalizeSequence<LoadNonVolatileOperation>(
254  loadNodeNormalizations,
255  operation,
256  operands);
257 }
258 
259 std::optional<std::vector<rvsdg::Output *>>
261  const StoreNonVolatileOperation & operation,
262  const std::vector<rvsdg::Output *> & operands)
263 {
264  static std::vector<rvsdg::NodeNormalization<StoreNonVolatileOperation>> storeNodeNormalizations(
271 
272  return rvsdg::NormalizeSequence<StoreNonVolatileOperation>(
273  storeNodeNormalizations,
274  operation,
275  operands);
276 }
277 
278 std::optional<std::vector<rvsdg::Output *>>
280  const MemoryStateMergeOperation & operation,
281  const std::vector<rvsdg::Output *> & operands)
282 {
283  static std::vector<rvsdg::NodeNormalization<MemoryStateMergeOperation>> normalizations(
288 
289  return rvsdg::NormalizeSequence<MemoryStateMergeOperation>(normalizations, operation, operands);
290 }
291 
292 std::optional<std::vector<rvsdg::Output *>>
294  const MemoryStateJoinOperation & operation,
295  const std::vector<rvsdg::Output *> & operands)
296 {
297  static std::vector<rvsdg::NodeNormalization<MemoryStateJoinOperation>> normalizations(
300 
301  return rvsdg::NormalizeSequence<MemoryStateJoinOperation>(normalizations, operation, operands);
302 }
303 
304 std::optional<std::vector<rvsdg::Output *>>
306  const MemoryStateSplitOperation & operation,
307  const std::vector<rvsdg::Output *> & operands)
308 {
309  static std::vector<rvsdg::NodeNormalization<MemoryStateSplitOperation>> normalizations(
313 
314  return rvsdg::NormalizeSequence<MemoryStateSplitOperation>(normalizations, operation, operands);
315 }
316 
317 std::optional<std::vector<rvsdg::Output *>>
319  const LambdaExitMemoryStateMergeOperation & operation,
320  const std::vector<rvsdg::Output *> & operands)
321 {
322  static std::vector<rvsdg::NodeNormalization<LambdaExitMemoryStateMergeOperation>> normalizations(
326 
327  return rvsdg::NormalizeSequence<LambdaExitMemoryStateMergeOperation>(
328  normalizations,
329  operation,
330  operands);
331 }
332 
333 }
static jlm::util::StatisticsCollector statisticsCollector
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:338
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:316
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Load.cpp:327
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:305
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:38
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition: reduction.hpp:161
void End(const rvsdg::Graph &graph) noexcept
Definition: reduction.cpp:30
std::unordered_map< const rvsdg::Region *, size_t > NumIterations_
Definition: reduction.hpp:167
void Start(const rvsdg::Graph &graph) noexcept
Definition: reduction.cpp:22
std::optional< size_t > GetNumIterations(const rvsdg::Region &region) const noexcept
Definition: reduction.cpp:46
static bool ReduceLambdaExitMemoryStateMergeNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:233
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateMergeNode(const MemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:279
void ReduceNodesInRegion(rvsdg::Region &region)
Definition: reduction.cpp:79
static bool ReduceMemoryStateSplitNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:225
std::unique_ptr< Statistics > Statistics_
Definition: reduction.hpp:133
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Definition: reduction.cpp:63
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadNode(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:242
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateSplitNode(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:305
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreNode(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:260
bool ReduceStructuralNode(rvsdg::StructuralNode &structuralNode)
Definition: reduction.cpp:114
static std::optional< std::vector< rvsdg::Output * > > NormalizeMemoryStateJoinNode(const MemoryStateJoinOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:293
static bool ReduceSimpleNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:152
static bool ReduceBinaryNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:209
static bool ReduceStoreNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:201
static bool ReduceMemoryStateMergeNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:217
~NodeReduction() noexcept override
static bool ReduceGammaNode(rvsdg::StructuralNode &gammaNode)
Definition: reduction.cpp:141
static bool ReduceLoadNode(rvsdg::SimpleNode &simpleNode)
Definition: reduction.cpp:193
static std::optional< std::vector< rvsdg::Output * > > NormalizeLambdaExitMemoryStateMergeNode(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: reduction.cpp:318
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:254
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:210
static std::optional< std::vector< rvsdg::Output * > > normalizeStoreAllocaSingleUser(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.cpp:282
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:232
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:221
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Store.cpp:243
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:574
#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:785
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:838