Jlm
LoopUnswitching.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
9 #include <jlm/llvm/opt/pull.hpp>
10 #include <jlm/rvsdg/gamma.hpp>
11 #include <jlm/rvsdg/theta.hpp>
12 #include <jlm/rvsdg/traverser.hpp>
13 #include <jlm/util/Statistics.hpp>
14 #include <jlm/util/time.hpp>
15 
16 namespace jlm::llvm
17 {
18 
20 {
21 public:
22  ~Statistics() override = default;
23 
24  explicit Statistics(const util::FilePath & sourceFile)
25  : util::Statistics(Id::LoopUnswitching, sourceFile)
26  {}
27 
28  void
29  start(const rvsdg::Graph & graph) noexcept
30  {
34  }
35 
36  void
37  end(const rvsdg::Graph & graph) noexcept
38  {
39  AddMeasurement(Label::NumRvsdgNodesAfter, rvsdg::nnodes(&graph.GetRootRegion()));
42  }
43 
44  static std::unique_ptr<Statistics>
45  Create(const util::FilePath & sourceFile)
46  {
47  return std::make_unique<Statistics>(sourceFile);
48  }
49 };
50 
53 {
54  auto [matchNode, matchOperation] =
55  rvsdg::TryGetSimpleNodeAndOptionalOp<rvsdg::MatchOperation>(*theta.predicate()->origin());
56  if (!matchOperation)
57  return nullptr;
58 
59  // The output of the match node should only be connected to the theta and gamma node
60  if (matchNode->output(0)->nusers() != 2)
61  return nullptr;
62 
63  rvsdg::GammaNode * gammaNode = nullptr;
64  for (const auto & user : matchNode->output(0)->Users())
65  {
66  if (&user == theta.predicate())
67  continue;
68 
69  gammaNode = rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(user);
70  if (!gammaNode)
71  return nullptr;
72  }
73 
74  // Only apply loop unswitching if the theta node is a converted for loop, i.e., everything but the
75  // predicate is contained in the gamma
76  for (const auto & loopVar : theta.GetLoopVars())
77  {
78  const auto origin = loopVar.post->origin();
79  if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(*origin))
80  {
81  // origin is a theta subregion argument
82  }
83  else if (rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(*origin) == gammaNode)
84  {
85  // origin is an output of gamma node
86  }
87  else
88  {
89  // we don't want to invert this
90  return nullptr;
91  }
92  }
93 
94  return gammaNode;
95 }
96 
97 void
99  rvsdg::GammaNode & gammaNode,
100  const rvsdg::ThetaNode & thetaNode)
101 {
103 
104  // Ensure all loop variables are routed through the gamma node
105  for (const auto & loopVar : thetaNode.GetLoopVars())
106  {
107  if (rvsdg::TryGetOwnerNode<rvsdg::Node>(*loopVar.post->origin()) != &gammaNode)
108  {
109  auto [input, branchArgument] = gammaNode.AddEntryVar(loopVar.post->origin());
110  JLM_ASSERT(branchArgument.size() == 2);
111  auto [_, output] = gammaNode.AddExitVar({ branchArgument[0], branchArgument[1] });
112  loopVar.post->divert_to(output);
113  }
114  }
115 
116  pullin_top(&gammaNode);
117 }
118 
119 std::vector<std::vector<rvsdg::Node *>>
121  const rvsdg::ThetaNode & thetaNode,
122  const rvsdg::GammaNode & gammaNode)
123 {
124  JLM_ASSERT(gammaNode.region()->node() == &thetaNode);
125 
126  auto depthMap = rvsdg::computeDepthMap(*thetaNode.subregion());
127 
128  std::vector<std::vector<rvsdg::Node *>> nodes;
129  for (auto & node : thetaNode.subregion()->Nodes())
130  {
131  if (&node == &gammaNode)
132  continue;
133 
134  const auto depth = depthMap[&node];
135  if (depth >= nodes.size())
136  nodes.resize(depth + 1);
137  nodes[depth].push_back(&node);
138  }
139 
140  return nodes;
141 }
142 
143 void
145  rvsdg::Region & target,
146  rvsdg::SubstitutionMap & substitutionMap,
147  const std::vector<std::vector<rvsdg::Node *>> & nodes)
148 {
149  for (auto & sameDepthNodes : nodes)
150  {
151  for (const auto & node : sameDepthNodes)
152  node->copy(&target, substitutionMap);
153  }
154 }
155 
156 bool
158 {
159  auto oldGammaNode = IsUnswitchable(oldThetaNode);
160  if (!oldGammaNode)
161  return false;
162 
163  SinkNodesIntoGamma(*oldGammaNode, oldThetaNode);
164 
165  // Copy condition nodes for new gamma node
166  rvsdg::SubstitutionMap substitutionMap;
167  auto conditionNodes = CollectPredicateNodes(oldThetaNode, *oldGammaNode);
168  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
169  substitutionMap.insert(oldLoopVar.pre, oldLoopVar.input->origin());
170  CopyPredicateNodes(*oldThetaNode.region(), substitutionMap, conditionNodes);
171 
172  auto newGammaNode = rvsdg::GammaNode::create(
173  &substitutionMap.lookup(*oldGammaNode->predicate()->origin()),
174  oldGammaNode->nsubregions());
175 
176  // Handle subregion 0
177  rvsdg::SubstitutionMap subregion0Map;
178  {
179  // Setup substitution map for exit region copying
180  auto oldSubregion0 = oldGammaNode->subregion(0);
181  for (const auto & [oldInput, oldBranchArgument] : oldGammaNode->GetEntryVars())
182  {
183  if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(*oldInput->origin()))
184  {
185  auto oldLoopVar = oldThetaNode.MapPreLoopVar(*oldInput->origin());
186  auto [_, branchArgument] = newGammaNode->AddEntryVar(oldLoopVar.input->origin());
187  subregion0Map.insert(oldBranchArgument[0], branchArgument[0]);
188  }
189  else
190  {
191  auto substitute = &substitutionMap.lookup(*oldInput->origin());
192  auto [_, branchArgument] = newGammaNode->AddEntryVar(substitute);
193  subregion0Map.insert(oldBranchArgument[0], branchArgument[0]);
194  }
195  }
196 
197  // Copy exit region
198  oldSubregion0->copy(newGammaNode->subregion(0), subregion0Map);
199 
200  // Update substitution map for insertion of exit variables
201  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
202  {
203  auto output = oldLoopVar.post->origin();
204  auto substitute = &subregion0Map.lookup(*oldSubregion0->result(output->index())->origin());
205  subregion0Map.insert(oldLoopVar.post->origin(), substitute);
206  }
207  }
208 
209  // Handle subregion 1
210  rvsdg::SubstitutionMap subregion1Map;
211  {
212  auto newThetaNode = rvsdg::ThetaNode::create(newGammaNode->subregion(1));
213 
214  // Add loop variables to new theta node and setup substitution map
215  auto oldSubregion0 = oldGammaNode->subregion(0);
216  auto oldSubregion1 = oldGammaNode->subregion(1);
217 
218  std::unordered_map<rvsdg::Input *, rvsdg::ThetaNode::LoopVar> newLoopVars;
219  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
220  {
221  auto [_, branchArgument] = newGammaNode->AddEntryVar(oldLoopVar.input->origin());
222  auto newLoopVar = newThetaNode->AddLoopVar(branchArgument[1]);
223  subregion1Map.insert(oldLoopVar.pre, newLoopVar.pre);
224  newLoopVars[oldLoopVar.input] = newLoopVar;
225  }
226  for (const auto & [oldInput, oldBranchArgument] : oldGammaNode->GetEntryVars())
227  {
228  if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(*oldInput->origin()))
229  {
230  auto oldLoopVar = oldThetaNode.MapPreLoopVar(*oldInput->origin());
231  subregion1Map.insert(oldBranchArgument[1], newLoopVars[oldLoopVar.input].pre);
232  }
233  else
234  {
235  auto [_, newBranchArgument] =
236  newGammaNode->AddEntryVar(&substitutionMap.lookup(*oldInput->origin()));
237  auto newLoopVar = newThetaNode->AddLoopVar(newBranchArgument[1]);
238  subregion1Map.insert(oldBranchArgument[1], newLoopVar.pre);
239  newLoopVars[oldInput] = newLoopVar;
240  }
241  }
242 
243  // Copy repetition region
244  oldSubregion1->copy(newThetaNode->subregion(), subregion1Map);
245 
246  // Adjust values in substitution map for condition node copying
247  for (const auto & oldLopVar : oldThetaNode.GetLoopVars())
248  {
249  auto output = oldLopVar.post->origin();
250  auto substitute = &subregion1Map.lookup(*oldSubregion1->result(output->index())->origin());
251  subregion1Map.insert(oldLopVar.pre, substitute);
252  }
253 
254  // Copy condition nodes
255  CopyPredicateNodes(*newThetaNode->subregion(), subregion1Map, conditionNodes);
256  auto predicate = &subregion1Map.lookup(*oldGammaNode->predicate()->origin());
257 
258  // Redirect results of loop variables and adjust substitution map for exit region copying
259  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
260  {
261  auto output = oldLoopVar.post->origin();
262  auto substitute = &subregion1Map.lookup(*oldSubregion1->result(output->index())->origin());
263  newLoopVars[oldLoopVar.input].post->divert_to(substitute);
264  subregion1Map.insert(oldLoopVar.post->origin(), newLoopVars[oldLoopVar.input].output);
265  }
266  for (const auto & [input, branchArgument] : oldGammaNode->GetEntryVars())
267  {
268  if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(*input->origin()))
269  {
270  auto oldLoopVar = oldThetaNode.MapPreLoopVar(*input->origin());
271  subregion1Map.insert(branchArgument[0], newLoopVars[oldLoopVar.input].output);
272  }
273  else
274  {
275  auto substitute = &subregion1Map.lookup(*input->origin());
276  newLoopVars[input].post->divert_to(substitute);
277  subregion1Map.insert(branchArgument[0], newLoopVars[input].output);
278  }
279  }
280 
281  newThetaNode->set_predicate(predicate);
282 
283  // Copy exit region
284  oldSubregion0->copy(newGammaNode->subregion(1), subregion1Map);
285 
286  // Adjust values in substitution map for exit variable creation
287  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
288  {
289  auto output = oldLoopVar.post->origin();
290  auto substitute = &subregion1Map.lookup(*oldSubregion0->result(output->index())->origin());
291  subregion1Map.insert(oldLoopVar.post->origin(), substitute);
292  }
293  }
294 
295  // Add exit variables to new gamma
296  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
297  {
298  auto o0 = &subregion0Map.lookup(*oldLoopVar.post->origin());
299  auto o1 = &subregion1Map.lookup(*oldLoopVar.post->origin());
300  auto [_, output] = newGammaNode->AddExitVar({ o0, o1 });
301  substitutionMap.insert(oldLoopVar.output, output);
302  }
303 
304  // Replace outputs
305  for (const auto & oldLoopVar : oldThetaNode.GetLoopVars())
306  oldLoopVar.output->divert_users(&substitutionMap.lookup(*oldLoopVar.output));
307 
308  return true;
309 }
310 
311 void
313 {
314  bool unswitchedLoop = false;
315  for (auto & node : region.Nodes())
316  {
317  if (const auto structuralNode = dynamic_cast<rvsdg::StructuralNode *>(&node))
318  {
319  // Handle innermost theta nodes first
320  for (auto & subregion : structuralNode->Subregions())
321  HandleRegion(subregion);
322 
323  if (const auto thetaNode = dynamic_cast<rvsdg::ThetaNode *>(structuralNode))
324  {
325  unswitchedLoop |= UnswitchLoop(*thetaNode);
326  }
327  }
328  }
329 
330  // If we successfully unswitched a loop, ensure the old nodes are pruned.
331  if (unswitchedLoop)
332  {
333  region.prune(false);
334  }
335 }
336 
337 LoopUnswitching::~LoopUnswitching() noexcept = default;
338 
339 void
341  rvsdg::RvsdgModule & rvsdgModule,
342  util::StatisticsCollector & statisticsCollector)
343 {
344  auto statistics = Statistics::Create(rvsdgModule.SourceFilePath().value());
345 
346  statistics->start(rvsdgModule.Rvsdg());
347  HandleRegion(rvsdgModule.Rvsdg().GetRootRegion());
348  statistics->end(rvsdgModule.Rvsdg());
349 
350  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
351 }
352 
353 void
355  rvsdg::RvsdgModule & rvsdgModule,
356  util::StatisticsCollector & statisticsCollector)
357 {
358  LoopUnswitching loopUnswitching;
359  loopUnswitching.Run(rvsdgModule, statisticsCollector);
360 }
361 
362 }
Statistics(const util::FilePath &sourceFile)
void end(const rvsdg::Graph &graph) noexcept
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
void start(const rvsdg::Graph &graph) noexcept
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static rvsdg::GammaNode * IsUnswitchable(const rvsdg::ThetaNode &thetaNode)
static void HandleRegion(rvsdg::Region &region)
static void SinkNodesIntoGamma(rvsdg::GammaNode &gammaNode, const rvsdg::ThetaNode &thetaNode)
static void CreateAndRun(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector)
static void CopyPredicateNodes(rvsdg::Region &target, rvsdg::SubstitutionMap &substitutionMap, const std::vector< std::vector< rvsdg::Node * >> &nodes)
static std::vector< std::vector< rvsdg::Node * > > CollectPredicateNodes(const rvsdg::ThetaNode &thetaNode, const rvsdg::GammaNode &gammaNode)
~LoopUnswitching() noexcept override
static bool UnswitchLoop(rvsdg::ThetaNode &thetaNode)
static size_t sinkDependentNodesIntoGamma(rvsdg::GammaNode &gammaNode)
Definition: pull.cpp:215
Conditional operator / pattern matching.
Definition: gamma.hpp:99
EntryVar AddEntryVar(rvsdg::Output *origin)
Routes a variable into the gamma branches.
Definition: gamma.cpp:260
static GammaNode * create(jlm::rvsdg::Output *predicate, size_t nalternatives)
Definition: gamma.hpp:161
ExitVar AddExitVar(std::vector< rvsdg::Output * > values)
Routes per-branch result of gamma to output.
Definition: gamma.cpp:342
Output * origin() const noexcept
Definition: node.hpp:58
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
rvsdg::StructuralNode * node() const noexcept
Definition: region.hpp:369
void prune(bool recursive)
Definition: region.cpp:323
NodeRange Nodes() noexcept
Definition: region.hpp:328
void insert(const Output *original, Output *substitute)
Output & lookup(const Output &original) const
RegionResult * predicate() const noexcept
Definition: theta.hpp:85
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
rvsdg::Region * subregion() const noexcept
Definition: theta.hpp:79
static ThetaNode * create(rvsdg::Region *parent)
Definition: theta.hpp:73
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.
void pullin_top(rvsdg::GammaNode *gamma)
Definition: pull.cpp:134
std::unordered_map< const Node *, size_t > computeDepthMap(const Region &region)
Definition: region.cpp:609
size_t nnodes(const jlm::rvsdg::Region *region) noexcept
Definition: region.cpp:629
size_t ninputs(const rvsdg::Region *region) noexcept
Definition: region.cpp:682
static const char * NumRvsdgNodesBefore
Definition: Statistics.hpp:211
static const char * NumRvsdgNodesAfter
Definition: Statistics.hpp:212
static const char * Timer
Definition: Statistics.hpp:240
static const char * NumRvsdgInputsAfter
Definition: Statistics.hpp:215
static const char * NumRvsdgInputsBefore
Definition: Statistics.hpp:214