Jlm
gamma.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2010 2011 2012 2013 2014 Helge Bahmann <hcb@chaoticmind.net>
3  * Copyright 2013 2014 2015 2016 Nico Reißmann <nico.reissmann@gmail.com>
4  * See COPYING for terms of redistribution.
5  */
6 
7 #include <algorithm>
8 
9 #include <jlm/rvsdg/control.hpp>
10 #include <jlm/rvsdg/gamma.hpp>
12 #include <jlm/rvsdg/UnitType.hpp>
13 
14 namespace jlm::rvsdg
15 {
16 
17 /* gamma normal form */
18 
19 static bool
21 {
22  auto constant = rvsdg::TryGetOwnerNode<SimpleNode>(*gamma->predicate()->origin());
23  return constant && is<ControlConstantOperation>(constant->GetOperation());
24 }
25 
26 static void
28 {
29  auto origin = gamma->predicate()->origin();
30  auto & constant = AssertGetOwnerNode<SimpleNode>(*origin);
31  auto cop = static_cast<const ControlConstantOperation *>(&constant.GetOperation());
32  auto alternative = cop->value().alternative();
33 
35  for (const auto & ev : gamma->GetEntryVars())
36  smap.insert(ev.branchArgument[alternative], ev.input->origin());
37 
38  gamma->subregion(alternative)->copy(gamma->region(), smap);
39 
40  for (auto exitvar : gamma->GetExitVars())
41  exitvar.output->divert_users(&smap.lookup(*exitvar.branchResult[alternative]->origin()));
42 
43  remove(gamma);
44 }
45 
46 static bool
48 {
49  bool was_normalized = true;
50  for (auto exitvar : gamma->GetExitVars())
51  {
52  auto argument = dynamic_cast<const rvsdg::RegionArgument *>(exitvar.branchResult[0]->origin());
53  if (!argument)
54  continue;
55 
56  size_t n = 0;
57  auto input = argument->input();
58  for (n = 1; n < exitvar.branchResult.size(); n++)
59  {
60  auto argument =
61  dynamic_cast<const rvsdg::RegionArgument *>(exitvar.branchResult[n]->origin());
62  if (!argument && argument->input() != input)
63  break;
64  }
65 
66  if (n == exitvar.branchResult.size())
67  {
68  exitvar.output->divert_users(argument->input()->origin());
69  was_normalized = false;
70  }
71  }
72 
73  return was_normalized;
74 }
75 
76 static std::unordered_set<jlm::rvsdg::Output *>
78 {
79  // check gamma predicate
80  auto match = rvsdg::TryGetOwnerNode<SimpleNode>(*gamma->predicate()->origin());
81  if (!match || !is<MatchOperation>(match->GetOperation()))
82  return {};
83 
84  /* check number of alternatives */
85  auto match_op = static_cast<const MatchOperation *>(&match->GetOperation());
86  std::unordered_set<uint64_t> set({ match_op->default_alternative() });
87  for (const auto & pair : *match_op)
88  set.insert(pair.second);
89 
90  if (set.size() != gamma->nsubregions())
91  return {};
92 
93  /* check for constants */
94  std::unordered_set<jlm::rvsdg::Output *> outputs;
95  for (const auto & exitvar : gamma->GetExitVars())
96  {
97  if (!is<ControlType>(*exitvar.output->Type()))
98  continue;
99 
100  size_t n = 0;
101  for (n = 0; n < exitvar.branchResult.size(); n++)
102  {
103  auto node = rvsdg::TryGetOwnerNode<SimpleNode>(*exitvar.branchResult[n]->origin());
104  if (!node || !is<ControlConstantOperation>(node->GetOperation()))
105  break;
106 
107  auto op = static_cast<const ControlConstantOperation *>(&node->GetOperation());
108  if (op->value().nalternatives() != 2)
109  break;
110  }
111  if (n == exitvar.branchResult.size())
112  outputs.insert(exitvar.output);
113  }
114 
115  return outputs;
116 }
117 
118 static void
119 perform_control_constant_reduction(std::unordered_set<jlm::rvsdg::Output *> & outputs)
120 {
121  auto & gamma = rvsdg::AssertGetOwnerNode<GammaNode>(**outputs.begin());
122  auto origin = gamma.predicate()->origin();
123  auto [matchNode, matchOperation] = TryGetSimpleNodeAndOptionalOp<MatchOperation>(*origin);
124  JLM_ASSERT(matchNode && matchOperation);
125 
126  std::unordered_map<uint64_t, uint64_t> map;
127  for (const auto & pair : *matchOperation)
128  map[pair.second] = pair.first;
129 
130  for (const auto & xv : gamma.GetExitVars())
131  {
132  if (outputs.find(xv.output) == outputs.end())
133  continue;
134 
135  size_t defalt = 0;
136  size_t nalternatives = 0;
137  std::unordered_map<uint64_t, uint64_t> new_mapping;
138  for (size_t n = 0; n < xv.branchResult.size(); n++)
139  {
140  auto origin = xv.branchResult[n]->origin();
141  auto [ctlConstantNode, ctlConstantOperation] =
142  TryGetSimpleNodeAndOptionalOp<ControlConstantOperation>(*origin);
143  auto & value = ctlConstantOperation->value();
144  nalternatives = value.nalternatives();
145  if (map.find(n) != map.end())
146  new_mapping[map[n]] = value.alternative();
147  else
148  defalt = value.alternative();
149  }
150 
151  auto origin = matchNode->input(0)->origin();
152  auto m = match(matchOperation->nbits(), new_mapping, defalt, nalternatives, origin);
153  xv.output->divert_users(m);
154  }
155 }
156 
157 bool
159 {
160  auto gammaNode = dynamic_cast<GammaNode *>(&node);
161  if (gammaNode && is_predicate_reducible(gammaNode))
162  {
163  perform_predicate_reduction(gammaNode);
164  return true;
165  }
166 
167  return false;
168 }
169 
170 bool
172 {
173  auto gammaNode = dynamic_cast<GammaNode *>(&node);
174  if (gammaNode == nullptr)
175  return false;
176 
177  auto outputs = is_control_constant_reducible(gammaNode);
178  if (outputs.empty())
179  return false;
180 
182  return true;
183 }
184 
185 bool
187 {
188  const auto gammaNode = dynamic_cast<GammaNode *>(&node);
189  if (gammaNode == nullptr)
190  return false;
191 
192  return !perform_invariant_reduction(gammaNode);
193 }
194 
196 {}
197 
198 std::string
200 {
201  return "GAMMA";
202 }
203 
204 std::unique_ptr<Operation>
206 {
207  return std::make_unique<GammaOperation>(*this);
208 }
209 
210 bool
211 GammaOperation::operator==(const Operation & other) const noexcept
212 {
213  auto op = dynamic_cast<const GammaOperation *>(&other);
214  return op && op->numAlternatives_ == numAlternatives_;
215 }
216 
217 std::shared_ptr<const Type>
218 GammaOperation::GetMatchContentType(std::size_t alternative) const
219 {
220  return alternative < MatchContentTypes_.size() ? MatchContentTypes_[alternative]
221  : UnitType::Create();
222 }
223 
224 /* gamma node */
225 
226 GammaNode::~GammaNode() noexcept = default;
227 
228 GammaNode::GammaNode(rvsdg::Output & predicate, std::unique_ptr<GammaOperation> op)
229  : StructuralNode(predicate.region(), op->nalternatives()),
230  Operation_(std::move(op))
231 {
232  addInput(
233  std::make_unique<StructuralInput>(
234  this,
235  &predicate,
236  ControlType::Create(Operation_->nalternatives())),
237  false);
238  for (std::size_t n = 0; n < Operation_->nalternatives(); ++n)
239  {
240  RegionArgument::Create(*subregion(n), nullptr, Operation_->GetMatchContentType(n));
241  }
242 }
243 
245  rvsdg::Output & predicate,
246  size_t nalternatives,
247  std::vector<std::shared_ptr<const Type>> match_content_types)
248  : GammaNode(
249  predicate,
250  std::make_unique<GammaOperation>(nalternatives, std::move(match_content_types)))
251 {}
252 
253 [[nodiscard]] const GammaOperation &
254 GammaNode::GetOperation() const noexcept
255 {
256  return *Operation_;
257 }
258 
261 {
262  auto gammaInput = new StructuralInput(this, origin, origin->Type());
263  addInput(std::unique_ptr<StructuralInput>(gammaInput), true);
264 
265  EntryVar ev;
266  ev.input = gammaInput;
267 
268  for (size_t n = 0; n < nsubregions(); n++)
269  {
270  ev.branchArgument.push_back(
271  &RegionArgument::Create(*subregion(n), gammaInput, gammaInput->Type()));
272  }
273 
274  return ev;
275 }
276 
278 GammaNode::GetEntryVar(std::size_t index) const
279 {
280  JLM_ASSERT(index <= ninputs() - 1);
281  EntryVar ev;
282  ev.input = input(index + 1);
283  for (size_t n = 0; n < nsubregions(); ++n)
284  {
285  ev.branchArgument.push_back(subregion(n)->argument(index + 1));
286  }
287  return ev;
288 }
289 
292 {
293  MatchVar mv;
294  mv.input = input(0);
295  for (size_t n = 0; n < nsubregions(); ++n)
296  {
297  mv.matchContent.push_back(subregion(n)->argument(0));
298  }
299  return mv;
300 }
301 
302 std::vector<GammaNode::EntryVar>
304 {
305  std::vector<GammaNode::EntryVar> vars;
306  for (size_t n = 0; n < ninputs() - 1; ++n)
307  {
308  vars.push_back(GetEntryVar(n));
309  }
310  return vars;
311 }
312 
313 std::variant<GammaNode::MatchVar, GammaNode::EntryVar>
314 GammaNode::MapInput(const rvsdg::Input & input) const
315 {
316  JLM_ASSERT(rvsdg::TryGetOwnerNode<GammaNode>(input) == this);
317  if (input.index() == 0)
318  {
319  return GetMatchVar();
320  }
321  else
322  {
323  return GetEntryVar(input.index() - 1);
324  }
325 }
326 
327 std::variant<GammaNode::MatchVar, GammaNode::EntryVar>
329 {
330  JLM_ASSERT(rvsdg::TryGetRegionParentNode<GammaNode>(output) == this);
331  if (output.index() == 0)
332  {
333  return GetMatchVar();
334  }
335  else
336  {
337  return GetEntryVar(output.index() - 1);
338  }
339 }
340 
342 GammaNode::AddExitVar(std::vector<jlm::rvsdg::Output *> values)
343 {
344  if (values.size() != nsubregions())
345  throw util::Error("Incorrect number of values.");
346 
347  const auto & type = values[0]->Type();
348  auto output = addOutput(std::make_unique<StructuralOutput>(this, type));
349 
350  std::vector<rvsdg::Input *> branchResults;
351  for (size_t n = 0; n < nsubregions(); n++)
352  {
353  branchResults.push_back(
354  &rvsdg::RegionResult::Create(*subregion(n), *values[n], output, output->Type()));
355  }
356 
357  return ExitVar{ std::move(branchResults), std::move(output) };
358 }
359 
360 std::vector<GammaNode::ExitVar>
362 {
363  std::vector<GammaNode::ExitVar> vars;
364  for (size_t n = 0; n < noutputs(); ++n)
365  {
366  std::vector<rvsdg::Input *> branchResults;
367  for (size_t k = 0; k < nsubregions(); ++k)
368  {
369  branchResults.push_back(subregion(k)->result(n));
370  }
371  vars.push_back(ExitVar{ std::move(branchResults), output(n) });
372  }
373  return vars;
374 }
375 
378 {
379  JLM_ASSERT(TryGetOwnerNode<GammaNode>(output) == this);
380  std::vector<rvsdg::Input *> branchResults;
381  for (size_t k = 0; k < nsubregions(); ++k)
382  {
383  branchResults.push_back(subregion(k)->result(output.index()));
384  }
385  return ExitVar{ std::move(branchResults), Node::output(output.index()) };
386 }
387 
390 {
391  JLM_ASSERT(TryGetRegionParentNode<GammaNode>(input) == this);
392  std::vector<rvsdg::Input *> branchResults;
393  for (size_t k = 0; k < nsubregions(); ++k)
394  {
395  branchResults.push_back(subregion(k)->result(input.index()));
396  }
397  return ExitVar{ std::move(branchResults), Node::output(input.index()) };
398 }
399 
400 void
401 GammaNode::RemoveExitVars(const std::vector<ExitVar> & exitVars)
402 {
403  util::HashSet<size_t> indices;
404  for (const auto & [_, output] : exitVars)
405  {
406  JLM_ASSERT(TryGetOwnerNode<GammaNode>(*output) == this);
407  indices.insert(output->index());
408  }
409 
410  for (auto & subregion : Subregions())
411  {
412  [[maybe_unused]] const auto numRemovedResults = subregion.RemoveResults(indices);
413  JLM_ASSERT(numRemovedResults == indices.Size());
414  }
415  [[maybe_unused]] const auto numRemovedOutputs = RemoveOutputs(indices);
416  JLM_ASSERT(numRemovedOutputs == indices.Size());
417 }
418 
419 void
420 GammaNode::RemoveEntryVars(const std::vector<EntryVar> & entryVars)
421 {
422  util::HashSet<size_t> indices;
423  for (const auto & [input, _] : entryVars)
424  {
425  JLM_ASSERT(TryGetOwnerNode<GammaNode>(*input) == this);
426  indices.insert(input->index());
427  }
428 
429  for (auto & subregion : Subregions())
430  {
431  [[maybe_unused]] const auto numRemovedArguments = subregion.RemoveArguments(indices);
432  JLM_ASSERT(numRemovedArguments == indices.Size());
433  }
434  [[maybe_unused]] const auto numRemovedInputs = RemoveInputs(indices);
435  JLM_ASSERT(numRemovedInputs == indices.Size());
436 }
437 
438 GammaNode *
440 {
441  auto gamma = create(&smap.lookup(*predicate()->origin()), nsubregions());
442 
443  /* add entry variables to new gamma */
444  std::vector<SubstitutionMap> rmap(nsubregions());
445  for (const auto & oev : GetEntryVars())
446  {
447  auto nev = gamma->AddEntryVar(&smap.lookup(*oev.input->origin()));
448  for (size_t n = 0; n < nsubregions(); n++)
449  rmap[n].insert(oev.branchArgument[n], nev.branchArgument[n]);
450  }
451 
452  /* copy subregions */
453  for (size_t r = 0; r < nsubregions(); r++)
454  subregion(r)->copy(gamma->subregion(r), rmap[r]);
455 
456  /* add exit variables to new gamma */
457  for (const auto & oex : GetExitVars())
458  {
459  std::vector<jlm::rvsdg::Output *> operands;
460  for (size_t n = 0; n < oex.branchResult.size(); n++)
461  operands.push_back(&rmap[n].lookup(*oex.branchResult[n]->origin()));
462  auto nex = gamma->AddExitVar(std::move(operands));
463  smap.insert(oex.output, nex.output);
464  }
465 
466  return gamma;
467 }
468 
469 std::optional<rvsdg::Output *>
470 GetGammaInvariantOrigin(const GammaNode & gamma, const GammaNode::ExitVar & exitvar)
471 {
472  // For any region result, check if it directly maps to a
473  // gamma entry variable, and returns the origin of its
474  // corresponding value (the def site preceding the gamma node).
475  auto GetExternalOriginOf = [&gamma](rvsdg::Input * use) -> std::optional<rvsdg::Output *>
476  {
477  // Test whether origin of this is a region entry argument of
478  // this gamma node.
479  auto def = use->origin();
480  if (rvsdg::TryGetRegionParentNode<GammaNode>(*def) != &gamma)
481  {
482  return std::nullopt;
483  }
484  auto rolevar = gamma.MapBranchArgument(*def);
485  if (auto entryvar = std::get_if<GammaNode::EntryVar>(&rolevar))
486  {
487  return entryvar->input->origin();
488  }
489  else
490  {
491  return std::nullopt;
492  }
493  };
494 
495  auto firstOrigin = GetExternalOriginOf(exitvar.branchResult[0]);
496  if (!firstOrigin)
497  {
498  return std::nullopt;
499  }
500 
501  for (size_t n = 1; n < exitvar.branchResult.size(); ++n)
502  {
503  auto currentOrigin = GetExternalOriginOf(exitvar.branchResult[n]);
504  if (!currentOrigin || *firstOrigin != *currentOrigin)
505  {
506  return std::nullopt;
507  }
508  }
509 
510  return firstOrigin;
511 }
512 
513 }
const ControlValueRepresentation & value() const noexcept
Definition: control.hpp:116
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition: control.cpp:50
size_t alternative() const noexcept
Definition: control.hpp:83
Conditional operator / pattern matching.
Definition: gamma.hpp:99
void RemoveEntryVars(const std::vector< EntryVar > &entryVars)
Removes the given entry variables.
Definition: gamma.cpp:420
const GammaOperation & GetOperation() const noexcept override
Definition: gamma.cpp:254
ExitVar MapOutputExitVar(const rvsdg::Output &output) const
Maps gamma output to exit variable description.
Definition: gamma.cpp:377
std::unique_ptr< GammaOperation > Operation_
Definition: gamma.hpp:370
std::variant< MatchVar, EntryVar > MapInput(const rvsdg::Input &input) const
Maps gamma input to its role (match variable or entry variable).
Definition: gamma.cpp:314
GammaNode * copy(jlm::rvsdg::Region *region, SubstitutionMap &smap) const override
Copy a node with substitutions.
Definition: gamma.cpp:439
EntryVar AddEntryVar(rvsdg::Output *origin)
Routes a variable into the gamma branches.
Definition: gamma.cpp:260
EntryVar GetEntryVar(std::size_t index) const
Gets entry variable by index.
Definition: gamma.cpp:278
GammaNode(rvsdg::Output &predicate, std::unique_ptr< GammaOperation > op)
Definition: gamma.cpp:228
void RemoveExitVars(const std::vector< ExitVar > &exitVars)
Removes the given exit variables.
Definition: gamma.cpp:401
std::variant< MatchVar, EntryVar > MapBranchArgument(const rvsdg::Output &output) const
Maps branch subregion entry argument to its role (pattern match or entry variable).
Definition: gamma.cpp:328
std::vector< ExitVar > GetExitVars() const
Gets all exit variables for this gamma.
Definition: gamma.cpp:361
std::vector< EntryVar > GetEntryVars() const
Gets all entry variables for this gamma.
Definition: gamma.cpp:303
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
ExitVar MapBranchResultExitVar(const rvsdg::Input &input) const
Maps gamma region exit result to exit variable description.
Definition: gamma.cpp:389
rvsdg::Input * predicate() const noexcept
Definition: gamma.hpp:398
~GammaNode() noexcept override
MatchVar GetMatchVar() const
Definition: gamma.cpp:291
std::shared_ptr< const Type > GetMatchContentType(std::size_t alternative) const
Returns the type of pattern matching content produced in a branch.
Definition: gamma.cpp:218
std::unique_ptr< Operation > copy() const override
Definition: gamma.cpp:205
bool operator==(const Operation &other) const noexcept override
Definition: gamma.cpp:211
std::vector< std::shared_ptr< const Type > > MatchContentTypes_
Definition: gamma.hpp:61
std::string debug_string() const override
Definition: gamma.cpp:199
~GammaOperation() noexcept override
Definition: gamma.cpp:195
Output * origin() const noexcept
Definition: node.hpp:58
size_t index() const noexcept
Definition: node.hpp:52
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
size_t RemoveInputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:306
NodeOutput * output(size_t index) const noexcept
Definition: node.hpp:650
size_t ninputs() const noexcept
Definition: node.hpp:609
size_t noutputs() const noexcept
Definition: node.hpp:644
size_t RemoveOutputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:342
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
size_t index() const noexcept
Definition: node.hpp:274
void divert_users(jlm::rvsdg::Output *new_origin)
Definition: node.hpp:301
Represents the argument of a region.
Definition: region.hpp:41
StructuralInput * input() const noexcept
Definition: region.hpp:69
static RegionArgument & Create(rvsdg::Region &region, StructuralInput *input, std::shared_ptr< const rvsdg::Type > type)
Creates region entry argument.
Definition: region.cpp:62
static RegionResult & Create(rvsdg::Region &region, rvsdg::Output &origin, StructuralOutput *output, std::shared_ptr< const rvsdg::Type > type)
Create region exit result.
Definition: region.cpp:111
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
RegionResult * result(size_t index) const noexcept
Definition: region.hpp:471
size_t RemoveResults(const util::HashSet< size_t > &indices)
Definition: region.cpp:278
void copy(Region *target, SubstitutionMap &smap) const
Copy a region with substitutions.
Definition: region.cpp:314
RegionArgument * argument(size_t index) const noexcept
Definition: region.hpp:437
size_t RemoveArguments(const util::HashSet< size_t > &indices)
Definition: region.cpp:210
StructuralInput * addInput(std::unique_ptr< StructuralInput > input, bool notifyRegion)
StructuralOutput * addOutput(std::unique_ptr< StructuralOutput > input)
SubregionIteratorRange Subregions()
size_t nsubregions() const noexcept
StructuralOutput * output(size_t index) const noexcept
StructuralInput * input(size_t index) const noexcept
rvsdg::Region * subregion(size_t index) const noexcept
void insert(const Output *original, Output *substitute)
Output & lookup(const Output &original) const
static std::shared_ptr< const UnitType > Create()
Definition: UnitType.cpp:33
bool insert(ItemType item)
Definition: HashSet.hpp:210
std::size_t Size() const noexcept
Definition: HashSet.hpp:187
#define JLM_ASSERT(x)
Definition: common.hpp:16
static bool perform_invariant_reduction(GammaNode *gamma)
Definition: gamma.cpp:47
static std::unordered_set< jlm::rvsdg::Output * > is_control_constant_reducible(GammaNode *gamma)
Definition: gamma.cpp:77
bool ReduceGammaControlConstant(Node &node)
Definition: gamma.cpp:171
static void perform_predicate_reduction(GammaNode *gamma)
Definition: gamma.cpp:27
bool ReduceGammaWithStaticallyKnownPredicate(Node &node)
Definition: gamma.cpp:158
static bool is_predicate_reducible(const GammaNode *gamma)
Definition: gamma.cpp:20
static void remove(Node *node)
Definition: region.hpp:932
static void perform_control_constant_reduction(std::unordered_set< jlm::rvsdg::Output * > &outputs)
Definition: gamma.cpp:119
static std::string type(const Node *n)
Definition: view.cpp:255
jlm::rvsdg::Output * match(size_t nbits, const std::unordered_map< uint64_t, uint64_t > &mapping, uint64_t default_alternative, size_t nalternatives, jlm::rvsdg::Output *operand)
Definition: control.cpp:179
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition: node.hpp:1058
std::optional< rvsdg::Output * > GetGammaInvariantOrigin(const GammaNode &gamma, const GammaNode::ExitVar &exitvar)
Determines whether a gamma exit var is path-invariant.
Definition: gamma.cpp:470
bool ReduceGammaInvariantVariables(Node &node)
Definition: gamma.cpp:186
A variable routed into all gamma regions.
Definition: gamma.hpp:131
rvsdg::Input * input
Variable at entry point (input to gamma node).
Definition: gamma.hpp:135
std::vector< rvsdg::Output * > branchArgument
Variable inside each of the branch regions (argument per subregion).
Definition: gamma.hpp:139
A variable routed out of all gamma regions as result.
Definition: gamma.hpp:146
std::vector< rvsdg::Input * > branchResult
Variable exit points (results per subregion).
Definition: gamma.hpp:150
The match/discriminator variable of this gamma node.
Definition: gamma.hpp:116
rvsdg::Input * input
The variable matched over (i.e. the "selector" of the gamma branch).
Definition: gamma.hpp:120
std::vector< rvsdg::Output * > matchContent
The content of the match per branch.
Definition: gamma.hpp:124