Jlm
node.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2010 2011 2012 2014 2015 Helge Bahmann <hcb@chaoticmind.net>
3  * Copyright 2012 2013 2014 2015 2016 Nico Reißmann <nico.reissmann@gmail.com>
4  * See COPYING for terms of redistribution.
5  */
6 
7 #include <jlm/rvsdg/delta.hpp>
8 #include <jlm/rvsdg/gamma.hpp>
9 #include <jlm/rvsdg/lambda.hpp>
10 #include <jlm/rvsdg/MatchType.hpp>
11 #include <jlm/rvsdg/Phi.hpp>
12 #include <jlm/rvsdg/region.hpp>
14 #include <jlm/rvsdg/theta.hpp>
15 
16 namespace jlm::rvsdg
17 {
18 
19 Input::~Input() noexcept
20 {
21  origin()->remove_user(this);
22 }
23 
24 void
26  const Region & region,
27  const Output & origin,
28  const std::shared_ptr<const rvsdg::Type> & type)
29 {
30  if (&region != origin.region())
31  throw util::Error("Invalid operand region.");
32 
33  if (*type != *origin.Type())
34  throw util::TypeError(type->debug_string(), origin.Type()->debug_string());
35 }
36 
37 Input::Input(rvsdg::Node & owner, rvsdg::Output & origin, std::shared_ptr<const rvsdg::Type> type)
38  : index_(0),
39  Owner_(&owner),
40  Type_(std::move(type)),
41  UsersList_()
42 {
43  CheckTypes(*owner.region(), origin, Type_);
44  origin.add_user(this);
45 }
46 
47 Input::Input(rvsdg::Region & owner, rvsdg::Output & origin, std::shared_ptr<const rvsdg::Type> type)
48  : index_(0),
49  Owner_(&owner),
50  Type_(std::move(type)),
51  UsersList_()
52 {
53  CheckTypes(owner, origin, Type_);
54  origin.add_user(this);
55 }
56 
57 std::string
59 {
60  return jlm::util::strfmt("i", index());
61 }
62 
63 void
65 {
66  if (origin() == new_origin)
67  return;
68 
69  if (*Type() != *new_origin->Type())
70  throw jlm::util::TypeError(Type()->debug_string(), new_origin->Type()->debug_string());
71 
72  if (region() != new_origin->region())
73  throw util::Error("Invalid operand region.");
74 
75  auto old_origin = origin();
76  old_origin->remove_user(this);
77  new_origin->add_user(this);
78 
79  region()->notifyInputChange(this, old_origin, new_origin);
80 }
81 
82 [[nodiscard]] rvsdg::Region *
83 Input::region() const noexcept
84 {
85  if (auto node = std::get_if<Node *>(&Owner_))
86  {
87  return (*node)->region();
88  }
89  else if (auto region = std::get_if<Region *>(&Owner_))
90  {
91  return *region;
92  }
93  else
94  {
95  JLM_UNREACHABLE("Unhandled owner case.");
96  }
97 }
98 
99 static Input *
100 ComputeNextInput(const Input * input)
101 {
102  if (input == nullptr)
103  return nullptr;
104 
105  const auto index = input->index();
106  auto owner = input->GetOwner();
107 
108  if (auto node = std::get_if<Node *>(&owner))
109  {
110  return index + 1 < (*node)->ninputs() ? (*node)->input(index + 1) : nullptr;
111  }
112 
113  if (auto region = std::get_if<Region *>(&owner))
114  {
115  return index + 1 < (*region)->nresults() ? (*region)->result(index + 1) : nullptr;
116  }
117 
118  JLM_UNREACHABLE("Unhandled owner case.");
119 }
120 
121 Input *
123 {
124  return ComputeNextInput(Input_);
125 }
126 
127 Input *
129 {
130  return ComputeNextInput(Input_);
131 }
132 
133 Output::~Output() noexcept
134 {
135  JLM_ASSERT(NumUsers_ == 0);
136 }
137 
138 Output::Output(Node & owner, std::shared_ptr<const rvsdg::Type> type)
139  : index_(0),
140  Owner_(&owner),
141  Type_(std::move(type))
142 {}
143 
144 Output::Output(rvsdg::Region * owner, std::shared_ptr<const rvsdg::Type> type)
145  : index_(0),
146  Owner_(owner),
147  Type_(std::move(type))
148 {}
149 
150 [[nodiscard]] rvsdg::Region *
151 Output::region() const noexcept
152 {
153  if (auto node = std::get_if<Node *>(&Owner_))
154  {
155  return (*node)->region();
156  }
157  else if (auto region = std::get_if<Region *>(&Owner_))
158  {
159  return *region;
160  }
161  else
162  {
163  JLM_UNREACHABLE("Unhandled owner case.");
164  }
165 }
166 
167 std::string
169 {
170  return jlm::util::strfmt("o", index());
171 }
172 
173 void
175 {
176  JLM_ASSERT(user->origin_ == this);
177  user->origin_ = nullptr;
178 
179  Users_.erase(user);
180  NumUsers_ -= 1;
181 
182  if (auto node = TryGetOwnerNode<Node>(*this))
183  {
184  node->numSuccessors_ -= 1;
185  if (node->IsDead())
186  {
187  region()->onBottomNodeAdded(*node);
188  }
189  }
190 }
191 
192 void
194 {
195  JLM_ASSERT(user->origin_ == nullptr);
196  user->origin_ = this;
197 
198  if (auto node = TryGetOwnerNode<Node>(*this))
199  {
200  if (node->IsDead())
201  {
202  region()->onBottomNodeRemoved(*node);
203  }
204  node->numSuccessors_ += 1;
205  }
206 
207  Users_.push_back(user);
208  NumUsers_ += 1;
209 }
210 
211 static Output *
212 ComputeNextOutput(const Output * output)
213 {
214  if (output == nullptr)
215  return nullptr;
216 
217  const auto index = output->index();
218  const auto owner = output->GetOwner();
219 
220  if (const auto node = std::get_if<Node *>(&owner))
221  {
222  return index + 1 < (*node)->noutputs() ? (*node)->output(index + 1) : nullptr;
223  }
224 
225  if (const auto region = std::get_if<Region *>(&owner))
226  {
227  return index + 1 < (*region)->narguments() ? (*region)->argument(index + 1) : nullptr;
228  }
229 
230  JLM_UNREACHABLE("Unhandled owner case.");
231 }
232 
233 Output *
235 {
236  return ComputeNextOutput(Output_);
237 }
238 
239 Output *
241 {
242  return ComputeNextOutput(Output_);
243 }
244 
246  jlm::rvsdg::Output * origin,
247  Node * node,
248  std::shared_ptr<const rvsdg::Type> type)
249  : jlm::rvsdg::Input(*node, *origin, std::move(type))
250 {}
251 
252 NodeOutput::NodeOutput(Node * node, std::shared_ptr<const rvsdg::Type> type)
253  : Output(*node, std::move(type))
254 {}
255 
257  : Id_(region->generateNodeId()),
258  region_(region)
259 {
260  region->onBottomNodeAdded(*this);
261  region->onTopNodeAdded(*this);
262  region->onNodeAdded(*this);
263 }
264 
266 {
267  // Nodes should always be dead before they are removed
268  JLM_ASSERT(IsDead());
269  outputs_.clear();
270  region()->onBottomNodeRemoved(*this);
271 
272  if (ninputs() == 0)
273  {
274  region()->onTopNodeRemoved(*this);
275  }
276  inputs_.clear();
277 
278  region()->onNodeRemoved(*this);
279 }
280 
281 Graph *
282 Node::graph() const noexcept
283 {
284  return region_->graph();
285 }
286 
287 NodeInput *
288 Node::addInput(std::unique_ptr<NodeInput> input, bool notifyRegion)
289 {
290  // If we used to be a top node, we no longer are
291  if (ninputs() == 0)
292  {
293  region()->onTopNodeRemoved(*this);
294  }
295 
296  input->index_ = ninputs();
297  inputs_.push_back(std::move(input));
298  const auto inputPtr = inputs_.back().get();
299  if (notifyRegion)
300  region()->notifyInputCreate(inputPtr);
301 
302  return inputPtr;
303 }
304 
305 size_t
307 {
308  if (indices.IsEmpty())
309  {
310  return 0;
311  }
312 
313  size_t numLiveInputs = 0;
314  size_t numRemovedInputs = 0;
315  for (size_t n = 0; n < ninputs(); n++)
316  {
317  auto & input = inputs_[n];
318  if (indices.Contains(input->index()))
319  {
320  region()->notifyInputDestroy(input.get());
321  input.reset();
322  numRemovedInputs++;
323  }
324  else
325  {
326  input->index_ = numLiveInputs;
327  inputs_[numLiveInputs++] = std::move(input);
328  }
329  }
330  inputs_.resize(numLiveInputs);
331 
332  // If we no longer have any inputs we are now a top node
333  if (numLiveInputs == 0)
334  {
335  region()->onTopNodeAdded(*this);
336  }
337 
338  return numRemovedInputs;
339 }
340 
341 size_t
343 {
344  if (indices.IsEmpty())
345  return 0;
346 
347  size_t numLiveOutputs = 0;
348  size_t numRemovedOutputs = 0;
349  for (size_t n = 0; n < noutputs(); n++)
350  {
351  auto & output = outputs_[n];
352  if (output->IsDead() && indices.Contains(output->index()))
353  {
354  output.reset();
355  numRemovedOutputs++;
356  }
357  else
358  {
359  output->index_ = numLiveOutputs;
360  outputs_[numLiveOutputs++] = std::move(output);
361  }
362  }
363  outputs_.resize(numLiveOutputs);
364 
365  return numRemovedOutputs;
366 }
367 
368 Node *
369 Node::copy(rvsdg::Region * region, const std::vector<jlm::rvsdg::Output *> & operands) const
370 {
371  SubstitutionMap smap;
372 
373  size_t noperands = std::min(operands.size(), ninputs());
374  for (size_t n = 0; n < noperands; n++)
375  smap.insert(input(n)->origin(), operands[n]);
376 
377  return copy(region, smap);
378 }
379 
380 Output &
381 RouteToRegion(Output & output, Region & region)
382 {
383  if (&region == output.region())
384  return output;
385 
386  if (&region == &region.graph()->GetRootRegion())
387  {
388  // We reached the root region and have not found the outputs' region yet.
389  // This means that the output comes from a region in the region tree that
390  // is not an ancestor of "region".
391  throw std::logic_error("Output is not in an ancestor of region.");
392  }
393 
394  auto & origin = RouteToRegion(output, *region.node()->region());
395 
396  const auto newOrigin = MatchTypeOrFail(
397  *region.node(),
398  [&origin, &region](GammaNode & gammaNode)
399  {
400  auto [input, branchArgument] = gammaNode.AddEntryVar(&origin);
401  return branchArgument[region.index()];
402  },
403  [&origin](ThetaNode & thetaNode)
404  {
405  return thetaNode.AddLoopVar(&origin).pre;
406  },
407  [&origin](LambdaNode & lambdaNode)
408  {
409  return lambdaNode.AddContextVar(origin).inner;
410  },
411  [&origin](PhiNode & phiNode)
412  {
413  return phiNode.AddContextVar(origin).inner;
414  },
415  [&origin](DeltaNode & deltaNode)
416  {
417  return deltaNode.AddContextVar(origin).inner;
418  });
419 
420  return *newOrigin;
421 }
422 
490 }
Delta node.
Definition: delta.hpp:129
Conditional operator / pattern matching.
Definition: gamma.hpp:99
Region & GetRootRegion() const noexcept
Definition: graph.hpp:99
Input * ComputeNext() const
Definition: node.cpp:128
Input * ComputeNext() const
Definition: node.cpp:122
std::variant< Node *, Region * > Owner_
Definition: node.hpp:225
void divert_to(Output *new_origin)
Definition: node.cpp:64
Output * origin() const noexcept
Definition: node.hpp:58
std::shared_ptr< const rvsdg::Type > Type_
Definition: node.hpp:226
size_t index() const noexcept
Definition: node.hpp:52
Input(Node &owner, Output &origin, std::shared_ptr< const rvsdg::Type > type)
Definition: node.cpp:37
jlm::rvsdg::Output * origin_
Definition: node.hpp:224
virtual std::string debug_string() const
Definition: node.cpp:58
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:67
std::variant< Node *, Region * > GetOwner() const noexcept
Definition: node.hpp:79
Region * region() const noexcept
Definition: node.cpp:83
virtual ~Input() noexcept
Definition: node.cpp:19
static void CheckTypes(const Region &region, const Output &origin, const std::shared_ptr< const rvsdg::Type > &type)
Definition: node.cpp:25
size_t index_
Definition: node.hpp:223
Lambda node.
Definition: lambda.hpp:83
NodeInput(Output *origin, Node *node, std::shared_ptr< const rvsdg::Type > type)
Definition: node.cpp:245
NodeOutput(Node *node, std::shared_ptr< const rvsdg::Type > type)
Definition: node.cpp:252
Graph * graph() const noexcept
Definition: node.cpp:282
Node(Region *region)
Definition: node.cpp:256
Region * region_
Definition: node.hpp:807
virtual ~Node()
Definition: node.cpp:265
bool IsDead() const noexcept
Determines whether the node is dead.
Definition: node.hpp:688
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
std::vector< std::unique_ptr< NodeInput > > inputs_
Definition: node.hpp:808
std::vector< std::unique_ptr< NodeOutput > > outputs_
Definition: node.hpp:809
size_t RemoveInputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:306
NodeInput * input(size_t index) const noexcept
Definition: node.hpp:615
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
NodeInput * addInput(std::unique_ptr< NodeInput > input, bool notifyRegion)
Definition: node.cpp:288
size_t RemoveOutputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:342
virtual Node * copy(rvsdg::Region *region, const std::vector< jlm::rvsdg::Output * > &operands) const
Definition: node.cpp:369
Output * ComputeNext() const
Definition: node.cpp:240
Output * ComputeNext() const
Definition: node.cpp:234
std::size_t NumUsers_
Definition: node.hpp:526
void add_user(jlm::rvsdg::Input *user)
Definition: node.cpp:193
rvsdg::Region * region() const noexcept
Definition: node.cpp:151
std::variant< Node *, Region * > GetOwner() const noexcept
Definition: node.hpp:378
virtual ~Output() noexcept
Definition: node.cpp:133
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
virtual std::string debug_string() const
Definition: node.cpp:168
size_t index() const noexcept
Definition: node.hpp:274
void remove_user(jlm::rvsdg::Input *user)
Definition: node.cpp:174
std::variant< Node *, Region * > Owner_
Definition: node.hpp:523
UsersList Users_
Definition: node.hpp:525
bool IsDead() const noexcept
Definition: node.hpp:295
Output(Node &owner, std::shared_ptr< const rvsdg::Type > type)
Definition: node.cpp:138
A phi node represents the fixpoint of mutually recursive definitions.
Definition: Phi.hpp:46
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
void onNodeRemoved(Node &node)
Definition: region.cpp:400
rvsdg::StructuralNode * node() const noexcept
Definition: region.hpp:369
void onNodeAdded(Node &node)
Adds node to the list of nodes in the region.
Definition: region.cpp:392
Graph * graph() const noexcept
Definition: region.hpp:363
void notifyInputChange(Input *input, Output *old_origin, Output *new_origin)
Definition: region.cpp:435
void onBottomNodeRemoved(Node &node)
Definition: region.cpp:384
void onTopNodeRemoved(Node &node)
Definition: region.cpp:367
void onTopNodeAdded(Node &node)
Adds node to the top nodes of the region.
Definition: region.cpp:358
void notifyInputDestroy(Input *input)
Definition: region.cpp:444
void notifyInputCreate(Input *input)
Definition: region.cpp:426
void onBottomNodeAdded(Node &node)
Adds node to the set of bottom nodes in the region.
Definition: region.cpp:375
void insert(const Output *original, Output *substitute)
bool Contains(const ItemType &item) const noexcept
Definition: HashSet.hpp:150
bool IsEmpty() const noexcept
Definition: HashSet.hpp:198
void erase(ElementType *element) noexcept
void push_back(ElementType *element) noexcept
#define JLM_ASSERT(x)
Definition: common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition: common.hpp:43
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
static std::string type(const Node *n)
Definition: view.cpp:255
Output & RouteToRegion(Output &output, Region &region)
Definition: node.cpp:381
static Input * ComputeNextInput(const Input *input)
Definition: node.cpp:100
static Output * ComputeNextOutput(const Output *output)
Definition: node.cpp:212
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35