Jlm
Phi.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2012 2013 2014 2015 Nico Reißmann <nico.reissmann@gmail.com>
3  * Copyright 2012 2013 2014 2025 Helge Bahmann <hcb@chaoticmind.net>
4  * See COPYING for terms of redistribution.
5  */
6 
7 #include <algorithm>
8 #include <functional>
9 
10 #include <jlm/rvsdg/Phi.hpp>
11 
12 namespace jlm::rvsdg
13 {
14 
15 PhiOperation::~PhiOperation() = default;
16 
17 std::string
19 {
20  return "Phi";
21 }
22 
23 std::unique_ptr<Operation>
25 {
26  return std::make_unique<PhiOperation>(*this);
27 }
28 
29 PhiNode::~PhiNode() = default;
30 
31 [[nodiscard]] const PhiOperation &
32 PhiNode::GetOperation() const noexcept
33 {
34  // Phi nodes are not parameterized, so we can return operation singleton.
35  static const PhiOperation singleton;
36  return singleton;
37 }
38 
41 {
42  auto input = addInput(std::make_unique<StructuralInput>(this, &origin, origin.Type()), true);
43  auto & argument =
44  subregion()->addArgument(std::make_unique<RegionArgument>(subregion(), input, origin.Type()));
45 
46  return ContextVar{ input, &argument };
47 }
48 
49 [[nodiscard]] std::vector<PhiNode::ContextVar>
50 PhiNode::GetContextVars() const noexcept
51 {
52  std::vector<PhiNode::ContextVar> vars;
53 
54  for (size_t n = 0; n < ninputs(); ++n)
55  {
56  vars.push_back(ContextVar{ input(n), subregion()->argument(n + subregion()->nresults()) });
57  }
58 
59  return vars;
60 }
61 
62 [[nodiscard]] std::vector<PhiNode::FixVar>
63 PhiNode::GetFixVars() const noexcept
64 {
65  std::vector<PhiNode::FixVar> vars;
66 
67  for (std::size_t n = 0; n < noutputs(); ++n)
68  {
69  vars.push_back(FixVar{ subregion()->argument(n), subregion()->result(n), output(n) });
70  }
71 
72  return vars;
73 }
74 
75 [[nodiscard]] std::optional<PhiNode::FixVar>
76 PhiNode::MapArgumentFixVar(const rvsdg::Output & argument) const noexcept
77 {
78  JLM_ASSERT(rvsdg::TryGetRegionParentNode<PhiNode>(argument) == this);
79  if (argument.index() < subregion()->nresults())
80  {
81  size_t n = argument.index();
82  return FixVar{ subregion()->argument(n), subregion()->result(n), output(n) };
83  }
84  else
85  {
86  return std::nullopt;
87  }
88 }
89 
90 [[nodiscard]] PhiNode::FixVar
91 PhiNode::MapResultFixVar(const rvsdg::Input & result) const noexcept
92 {
93  JLM_ASSERT(rvsdg::TryGetRegionParentNode<PhiNode>(result) == this);
94  return FixVar{ subregion()->argument(result.index()),
95  subregion()->result(result.index()),
96  output(result.index()) };
97 }
98 
99 [[nodiscard]] PhiNode::FixVar
100 PhiNode::MapOutputFixVar(const rvsdg::Output & output) const noexcept
101 {
102  JLM_ASSERT(rvsdg::TryGetOwnerNode<PhiNode>(output) == this);
103  return FixVar{ subregion()->argument(output.index()),
104  subregion()->result(output.index()),
105  PhiNode::output(output.index()) };
106 }
107 
108 [[nodiscard]] PhiNode::ContextVar
109 PhiNode::MapInputContextVar(const rvsdg::Input & input) const noexcept
110 {
111  JLM_ASSERT(rvsdg::TryGetOwnerNode<PhiNode>(input) == this);
112  return ContextVar{ PhiNode::input(input.index()),
113  subregion()->argument(input.index() + subregion()->nresults()) };
114 }
115 
116 [[nodiscard]] std::optional<PhiNode::ContextVar>
117 PhiNode::MapArgumentContextVar(const rvsdg::Output & argument) const noexcept
118 {
119  JLM_ASSERT(rvsdg::TryGetRegionParentNode<PhiNode>(argument) == this);
120  if (argument.index() >= subregion()->nresults())
121  {
122  size_t n = argument.index();
123  return ContextVar{ input(n - subregion()->nresults()), subregion()->argument(n) };
124  }
125  else
126  {
127  return std::nullopt;
128  }
129 }
130 
131 [[nodiscard]] std::variant<PhiNode::FixVar, PhiNode::ContextVar>
132 PhiNode::MapArgument(const rvsdg::Output & argument) const noexcept
133 {
134  JLM_ASSERT(rvsdg::TryGetRegionParentNode<PhiNode>(argument) == this);
135  if (auto ctxvar = MapArgumentContextVar(argument))
136  {
137  return *ctxvar;
138  }
139  else if (auto fixvar = MapArgumentFixVar(argument))
140  {
141  return *fixvar;
142  }
143  else
144  {
145  JLM_UNREACHABLE("phi binder is neither context nor fixpoint variable");
146  }
147 }
148 
149 void
150 PhiNode::RemoveContextVars(std::vector<ContextVar> vars)
151 {
152  util::HashSet<size_t> inputIndices;
153  util::HashSet<size_t> argumentIndices;
154  for (const auto [input, argument] : vars)
155  {
156  argumentIndices.insert(argument->index());
157  inputIndices.insert(input->index());
158  }
159 
160  [[maybe_unused]] const auto numRemovedArguments = subregion()->RemoveArguments(argumentIndices);
161  JLM_ASSERT(numRemovedArguments == argumentIndices.Size());
162 
163  [[maybe_unused]] const auto numRemovedInputs = RemoveInputs(inputIndices);
164  JLM_ASSERT(numRemovedInputs == inputIndices.Size());
165 }
166 
167 void
168 PhiNode::RemoveFixVars(std::vector<FixVar> vars)
169 {
170  util::HashSet<size_t> resultIndices;
171  util::HashSet<size_t> argumentIndices;
172  util::HashSet<size_t> outputIndices;
173  for (const auto & [argument, result, output] : vars)
174  {
175  resultIndices.insert(result->index());
176  argumentIndices.insert(argument->index());
177  outputIndices.insert(output->index());
178  }
179 
180  [[maybe_unused]] const auto numRemovedResults = subregion()->RemoveResults(resultIndices);
181  JLM_ASSERT(numRemovedResults == resultIndices.Size());
182 
183  [[maybe_unused]] const auto numRemovedArguments = subregion()->RemoveArguments(argumentIndices);
184  JLM_ASSERT(numRemovedArguments == argumentIndices.Size());
185 
186  [[maybe_unused]] const auto numRemovedOutputs = RemoveOutputs(outputIndices);
187  JLM_ASSERT(numRemovedOutputs == outputIndices.Size());
188 }
189 
190 PhiNode *
192 {
193  PhiBuilder pb;
194  pb.begin(region);
195 
196  // add context variables
197  rvsdg::SubstitutionMap subregionmap;
198  for (const auto & var : GetContextVars())
199  {
200  auto & origin = smap.lookup(*var.input->origin());
201  auto newcv = pb.AddContextVar(origin);
202  subregionmap.insert(var.inner, newcv.inner);
203  }
204 
205  // add recursion variables
206  for (auto var : GetFixVars())
207  {
208  auto newrv = pb.AddFixVar(var.recref->Type());
209  subregionmap.insert(var.recref, newrv.recref);
210  }
211 
212  // copy subregion
213  subregion()->copy(pb.subregion(), subregionmap);
214 
215  // finalize phi
216  for (auto var : GetFixVars())
217  {
218  auto neworigin = &subregionmap.lookup(*var.result->origin());
219  var.result->divert_to(neworigin);
220  }
221 
222  return pb.end();
223 }
224 
225 std::vector<rvsdg::LambdaNode *>
227 {
228  std::function<void(const PhiNode &, std::vector<rvsdg::LambdaNode *> &)> extractLambdaNodes =
229  [&](auto & phiNode, auto & lambdaNodes)
230  {
231  for (auto & node : phiNode.subregion()->Nodes())
232  {
233  if (auto lambdaNode = dynamic_cast<rvsdg::LambdaNode *>(&node))
234  {
235  lambdaNodes.push_back(lambdaNode);
236  }
237  else if (auto innerPhiNode = dynamic_cast<const PhiNode *>(&node))
238  {
239  extractLambdaNodes(*innerPhiNode, lambdaNodes);
240  }
241  }
242  };
243 
244  std::vector<rvsdg::LambdaNode *> lambdaNodes;
245  extractLambdaNodes(phiNode, lambdaNodes);
246 
247  return lambdaNodes;
248 }
249 
252 {
253  return node_->AddContextVar(origin);
254 }
255 
257 PhiBuilder::AddFixVar(std::shared_ptr<const jlm::rvsdg::Type> type)
258 {
259  auto output = node_->addOutput(std::make_unique<StructuralOutput>(node_, type));
260  auto & argument = subregion()->insertArgument(
261  subregion()->nresults(),
262  std::make_unique<RegionArgument>(subregion(), nullptr, type));
263  auto & result =
264  subregion()->addResult(std::make_unique<RegionResult>(subregion(), &argument, output, type));
265 
266  return PhiNode::FixVar{ &argument, &result, output };
267 }
268 
269 PhiNode *
271 {
272  if (!node_)
273  return nullptr;
274 
275  for (auto var : node_->GetFixVars())
276  {
277  if (var.result->origin() == var.recref)
278  throw util::Error("Recursion variable not properly set.");
279  }
280 
281  auto node = node_;
282  node_ = nullptr;
283 
284  return node;
285 }
286 
287 }
size_t index() const noexcept
Definition: node.hpp:52
Lambda node.
Definition: lambda.hpp:83
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
size_t RemoveInputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:306
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
rvsdg::Region * subregion() const noexcept
Definition: Phi.hpp:349
PhiNode * end()
Definition: Phi.cpp:270
PhiNode::ContextVar AddContextVar(jlm::rvsdg::Output &origin)
Definition: Phi.cpp:251
PhiNode::FixVar AddFixVar(std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: Phi.cpp:257
void begin(rvsdg::Region *parent)
Definition: Phi.hpp:355
PhiNode * node_
Definition: Phi.hpp:373
A phi node represents the fixpoint of mutually recursive definitions.
Definition: Phi.hpp:46
rvsdg::Region * subregion() const noexcept
Definition: Phi.hpp:320
FixVar MapResultFixVar(const rvsdg::Input &result) const noexcept
Maps region result to fixpoint variable.
Definition: Phi.cpp:91
PhiNode * copy(rvsdg::Region *region, rvsdg::SubstitutionMap &smap) const override
Copy a node with substitutions.
Definition: Phi.cpp:191
ContextVar MapInputContextVar(const rvsdg::Input &input) const noexcept
Maps input to context variable.
Definition: Phi.cpp:109
std::optional< ContextVar > MapArgumentContextVar(const rvsdg::Output &argument) const noexcept
Attempts to map bound variable reference to context variable.
Definition: Phi.cpp:117
static std::vector< rvsdg::LambdaNode * > ExtractLambdaNodes(const PhiNode &phiNode)
Definition: Phi.cpp:226
std::vector< FixVar > GetFixVars() const noexcept
Gets all fixpoint variables.
Definition: Phi.cpp:63
void RemoveContextVars(std::vector< ContextVar > vars)
Removes context variables from phi node.
Definition: Phi.cpp:150
FixVar MapOutputFixVar(const rvsdg::Output &output) const noexcept
Maps output to fixpoint variable.
Definition: Phi.cpp:100
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
Definition: Phi.cpp:50
std::optional< FixVar > MapArgumentFixVar(const rvsdg::Output &argument) const noexcept
Tries to map region argument to fixpoint variable.
Definition: Phi.cpp:76
std::variant< FixVar, ContextVar > MapArgument(const rvsdg::Output &argument) const noexcept
Maps region argument to its function.
Definition: Phi.cpp:132
ContextVar AddContextVar(jlm::rvsdg::Output &origin)
Adds a context variable to the phi node.
Definition: Phi.cpp:40
void RemoveFixVars(std::vector< FixVar > vars)
Removes fixpoint variables from the phi node.
Definition: Phi.cpp:168
const PhiOperation & GetOperation() const noexcept override
Definition: Phi.cpp:32
std::string debug_string() const override
Definition: Phi.cpp:18
std::unique_ptr< Operation > copy() const override
Definition: Phi.cpp:24
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
RegionArgument & addArgument(std::unique_ptr< RegionArgument > argument)
Definition: region.cpp:176
void copy(Region *target, SubstitutionMap &smap) const
Copy a region with substitutions.
Definition: region.cpp:314
RegionResult & addResult(std::unique_ptr< RegionResult > result)
Definition: region.cpp:262
RegionArgument * argument(size_t index) const noexcept
Definition: region.hpp:437
size_t RemoveArguments(const util::HashSet< size_t > &indices)
Definition: region.cpp:210
NodeRange Nodes() noexcept
Definition: region.hpp:328
RegionArgument & insertArgument(size_t index, std::unique_ptr< RegionArgument > argument)
Definition: region.cpp:187
StructuralInput * addInput(std::unique_ptr< StructuralInput > input, bool notifyRegion)
StructuralOutput * addOutput(std::unique_ptr< StructuralOutput > input)
StructuralOutput * output(size_t index) const noexcept
StructuralInput * input(size_t index) const noexcept
void insert(const Output *original, Output *substitute)
Output & lookup(const Output &original) const
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
#define JLM_UNREACHABLE(msg)
Definition: common.hpp:43
static std::string type(const Node *n)
Definition: view.cpp:255
Bound context variable.
Definition: Phi.hpp:73
Description of a recursively defined variable.
Definition: Phi.hpp:100
rvsdg::Input * result
Definition result of a variable within the phi region.
Definition: Phi.hpp:118