Jlm
MemoryStateOperations.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2021 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
11 #include <jlm/util/HashSet.hpp>
12 
13 namespace jlm::llvm
14 {
15 
17 
18 bool
19 MemoryStateMergeOperation::operator==(const Operation & other) const noexcept
20 {
21  auto operation = dynamic_cast<const MemoryStateMergeOperation *>(&other);
22  return operation && operation->narguments() == narguments();
23 }
24 
25 std::string
27 {
28  return "MemoryStateMerge";
29 }
30 
31 std::unique_ptr<rvsdg::Operation>
33 {
34  return std::make_unique<MemoryStateMergeOperation>(*this);
35 }
36 
37 std::optional<std::vector<rvsdg::Output *>>
40  const std::vector<rvsdg::Output *> & operands)
41 {
42  if (operands.size() == 1)
43  return operands;
44 
45  return std::nullopt;
46 }
47 
48 std::optional<std::vector<rvsdg::Output *>>
51  const std::vector<rvsdg::Output *> & operands)
52 {
53  const util::HashSet<rvsdg::Output *> uniqueOperands(operands.begin(), operands.end());
54 
55  if (uniqueOperands.Size() == operands.size())
56  return std::nullopt;
57 
58  auto result = Create(std::vector(uniqueOperands.Items().begin(), uniqueOperands.Items().end()));
59  return { { result } };
60 }
61 
62 template<class TMemoryStateMergeOrJoinOperation>
63 std::vector<rvsdg::Output *>
64 CollectNestedMemoryStateMergeOrJoinOperands(const std::vector<rvsdg::Output *> & operands)
65 {
66  static_assert(
67  std::is_same_v<TMemoryStateMergeOrJoinOperation, MemoryStateMergeOperation>
68  || std::is_same_v<TMemoryStateMergeOrJoinOperation, MemoryStateJoinOperation>,
69  "Template parameter T must be a MemoryStateMergeOperation or a MemoryStateJoinOperation!");
70 
71  std::vector<rvsdg::Output *> newOperands;
72  for (auto operand : operands)
73  {
74  auto [node, operation] =
75  rvsdg::TryGetSimpleNodeAndOptionalOp<TMemoryStateMergeOrJoinOperation>(*operand);
76  if (operation)
77  {
78  auto nodeOperands =
79  CollectNestedMemoryStateMergeOrJoinOperands<TMemoryStateMergeOrJoinOperation>(
80  rvsdg::operands(node));
81  newOperands.insert(newOperands.end(), nodeOperands.begin(), nodeOperands.end());
82  }
83  else
84  {
85  newOperands.emplace_back(operand);
86  }
87  }
88 
89  return newOperands;
90 }
91 
92 std::optional<std::vector<rvsdg::Output *>>
95  const std::vector<rvsdg::Output *> & operands)
96 {
97  auto newOperands =
98  CollectNestedMemoryStateMergeOrJoinOperands<MemoryStateMergeOperation>(operands);
99 
100  if (operands == newOperands)
101  return std::nullopt;
102 
103  auto result = Create(std::move(newOperands));
104  return { { result } };
105 }
106 
107 std::optional<std::vector<rvsdg::Output *>>
110  const std::vector<rvsdg::Output *> & operands)
111 {
112  std::vector<rvsdg::Output *> newOperands;
113  for (const auto operand : operands)
114  {
115  auto [splitNode, splitOperation] =
116  rvsdg::TryGetSimpleNodeAndOptionalOp<MemoryStateSplitOperation>(*operand);
117  if (splitOperation)
118  {
119  newOperands.emplace_back(splitNode->input(0)->origin());
120  }
121  else
122  {
123  newOperands.emplace_back(operand);
124  }
125  }
126 
127  if (operands == newOperands)
128  return std::nullopt;
129 
130  auto result = Create(std::move(newOperands));
131  return { { result } };
132 }
133 
135 
136 bool
137 MemoryStateJoinOperation::operator==(const Operation & other) const noexcept
138 {
139  const auto operation = dynamic_cast<const MemoryStateMergeOperation *>(&other);
140  return operation && operation->narguments() == narguments();
141 }
142 
143 std::string
145 {
146  return "MemoryStateJoin";
147 }
148 
149 std::unique_ptr<rvsdg::Operation>
151 {
152  return std::make_unique<MemoryStateJoinOperation>(*this);
153 }
154 
155 std::optional<std::vector<rvsdg::Output *>>
157  const MemoryStateJoinOperation &,
158  const std::vector<rvsdg::Output *> & operands)
159 {
160  if (operands.size() == 1)
161  return operands;
162 
163  return std::nullopt;
164 }
165 
166 std::optional<std::vector<rvsdg::Output *>>
168  const MemoryStateJoinOperation &,
169  const std::vector<rvsdg::Output *> & operands)
170 {
171  std::vector<rvsdg::Output *> newOperands;
172  util::HashSet<rvsdg::Output *> seenOperands;
173  for (auto operand : operands)
174  {
175  if (seenOperands.Contains(operand))
176  continue;
177 
178  seenOperands.insert(operand);
179  newOperands.emplace_back(operand);
180  }
181 
182  if (newOperands.size() == operands.size())
183  return std::nullopt;
184 
185  if (newOperands.size() == 1)
186  {
187  // There is no need to create a join node if there is only a single operand.
188  return newOperands;
189  }
190 
191  return { { CreateNode(newOperands).output(0) } };
192 }
193 
194 std::optional<std::vector<rvsdg::Output *>>
196  const MemoryStateJoinOperation &,
197  const std::vector<rvsdg::Output *> & operands)
198 {
199  auto newOperands =
200  CollectNestedMemoryStateMergeOrJoinOperands<MemoryStateJoinOperation>(operands);
201 
202  if (operands == newOperands)
203  return std::nullopt;
204 
205  const auto & memoryStateJoinNode = CreateNode(std::move(newOperands));
206  return { { memoryStateJoinNode.output(0) } };
207 }
208 
210 
211 bool
212 MemoryStateSplitOperation::operator==(const Operation & other) const noexcept
213 {
214  auto operation = dynamic_cast<const MemoryStateSplitOperation *>(&other);
215  return operation && operation->nresults() == nresults();
216 }
217 
218 std::string
220 {
221  return "MemoryStateSplit";
222 }
223 
224 std::unique_ptr<rvsdg::Operation>
226 {
227  return std::make_unique<MemoryStateSplitOperation>(*this);
228 }
229 
230 std::optional<std::vector<rvsdg::Output *>>
232  const MemoryStateSplitOperation & operation,
233  const std::vector<rvsdg::Output *> & operands)
234 {
235  JLM_ASSERT(operands.size() == 1);
236 
237  if (operation.nresults() == 1)
238  return operands;
239 
240  return std::nullopt;
241 }
242 
243 std::optional<std::vector<rvsdg::Output *>>
245  const MemoryStateSplitOperation & operation,
246  const std::vector<rvsdg::Output *> & operands)
247 {
248  JLM_ASSERT(operands.size() == 1);
249  const auto operand = operands[0];
250 
251  auto [splitNode, splitOperation] =
252  rvsdg::TryGetSimpleNodeAndOptionalOp<MemoryStateSplitOperation>(*operand);
253  if (!splitOperation)
254  return std::nullopt;
255 
256  const auto numResults = splitOperation->nresults() + operation.nresults();
257  auto & newOperand = *splitNode->input(0)->origin();
258  auto results = Create(newOperand, numResults);
259 
260  for (size_t n = 0; n < splitNode->noutputs(); n++)
261  {
262  const auto output = splitNode->output(n);
263  output->divert_users(results[n]);
264  }
265 
266  return { { std::next(results.begin(), splitNode->noutputs()), results.end() } };
267 }
268 
269 std::optional<std::vector<rvsdg::Output *>>
271  const MemoryStateSplitOperation & operation,
272  const std::vector<rvsdg::Output *> & operands)
273 {
274  JLM_ASSERT(operands.size() == 1);
275  const auto operand = operands[0];
276 
277  auto [mergeNode, mergeOperation] =
278  rvsdg::TryGetSimpleNodeAndOptionalOp<MemoryStateMergeOperation>(*operand);
279  if (!mergeOperation || mergeOperation->narguments() != operation.nresults())
280  return std::nullopt;
281 
282  return { rvsdg::operands(mergeNode) };
283 }
284 
285 static void
286 CheckMemoryNodeIds(const std::vector<MemoryNodeId> & memoryNodeIds)
287 {
288  const util::HashSet<MemoryNodeId> memoryNodeIdsSet(
289  { memoryNodeIds.begin(), memoryNodeIds.end() });
290 
291  if (memoryNodeIdsSet.Size() != memoryNodeIds.size())
292  throw std::logic_error("Found duplicated memory node identifiers.");
293 }
294 
295 static std::string
296 ToString(const std::vector<MemoryNodeId> & memoryNodeIds)
297 {
298  std::string str;
299  for (size_t n = 0; n < memoryNodeIds.size(); n++)
300  {
301  str.append(util::strfmt(memoryNodeIds[n]));
302  if (n != memoryNodeIds.size() - 1)
303  str.append(", ");
304  }
305 
306  return str;
307 }
308 
310  const std::vector<MemoryNodeId> & memoryNodeIds)
311  : MemoryStateOperation(1, memoryNodeIds.size())
312 {
313  CheckMemoryNodeIds(memoryNodeIds);
314  for (size_t n = 0; n < memoryNodeIds.size(); n++)
315  {
316  memoryNodeIdToIndexMap_.Insert(memoryNodeIds[n], n);
317  }
318 }
319 
321 
322 bool
323 LambdaEntryMemoryStateSplitOperation::operator==(const Operation & other) const noexcept
324 {
325  const auto operation = dynamic_cast<const LambdaEntryMemoryStateSplitOperation *>(&other);
326  return operation && operation->nresults() == nresults()
327  && operation->memoryNodeIdToIndexMap_ == memoryNodeIdToIndexMap_;
328 }
329 
330 std::string
332 {
333  return util::strfmt("LambdaEntryMemoryStateSplit[", ToString(getMemoryNodeIds()), "]");
334 }
335 
336 std::unique_ptr<rvsdg::Operation>
338 {
339  return std::make_unique<LambdaEntryMemoryStateSplitOperation>(*this);
340 }
341 
344  const rvsdg::SimpleNode & node,
345  const MemoryNodeId memoryNodeId)
346 {
347  const auto operation =
348  dynamic_cast<const LambdaEntryMemoryStateSplitOperation *>(&node.GetOperation());
349  if (!operation)
350  {
351  return nullptr;
352  }
353 
354  if (!operation->memoryNodeIdToIndexMap_.HasKey(memoryNodeId))
355  {
356  return nullptr;
357  }
358 
359  const auto index = operation->memoryNodeIdToIndexMap_.LookupKey(memoryNodeId);
360  return node.output(index);
361 }
362 
365 {
366  auto [_, operation] =
367  rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaEntryMemoryStateSplitOperation>(output);
368  JLM_ASSERT(operation != nullptr);
369 
370  return operation->memoryNodeIdToIndexMap_.LookupValue(output.index());
371 }
372 
373 std::optional<std::vector<rvsdg::Output *>>
375  const LambdaEntryMemoryStateSplitOperation & lambdaEntrySplitOperation,
376  const std::vector<rvsdg::Output *> & operands)
377 {
378  JLM_ASSERT(operands.size() == 1);
379 
380  auto [callEntryMergeNode, callEntryMergeOperation] =
381  rvsdg::TryGetSimpleNodeAndOptionalOp<CallEntryMemoryStateMergeOperation>(*operands[0]);
382  if (!callEntryMergeOperation)
383  return std::nullopt;
384 
385  if (callEntryMergeOperation->narguments() != lambdaEntrySplitOperation.nresults())
386  {
387  // We only perform this reduction if the number of memory region IDs (and therefore the
388  // inputs/outputs) of the call entry merge operation as well as the lambda entry split operation
389  // are the same. The reason for this is that we might at the moment otherwise loose information,
390  // leading to incorrect RVSDGs. The problem arises from examples where the lambda entry/exit
391  // operations feature fewer memory region IDs than the call entry/exit operations. If we
392  // perform the lambda/call entry reduction here, then we would drop memory state edges (and
393  // potentially nodes) and would in the case of the equivalent call/lambda exit reduction not
394  // know where to route to.
395  //
396  // Example:
397  // s1 = StoreNonVolatileOperation ...
398  // s2 = CallEntryMemoryStateMerge[1, 2, 3] s1 x y
399  // x, y = LambdaEntryMemoryStateSplit[2, 3] s2
400  // ...
401  // s3 = LambdaExitMemoryStateMerge[2, 3] x, y
402  // z, x, y = CallExitMemoryStateSplit[1, 2, 3] s3
403  //
404  // In the above example, if we perform the call entry/exit normalization, then we would drop
405  // s1 (and potentially the store), and we would not have it available any longer when we would
406  // perform the lambda/call exit normalization.
407  return std::nullopt;
408  }
409 
410  std::vector<rvsdg::Output *> newOperands;
411  for (const auto & memoryNodeId : lambdaEntrySplitOperation.getMemoryNodeIds())
412  {
414  *callEntryMergeNode,
415  memoryNodeId);
416  JLM_ASSERT(input != nullptr);
417  newOperands.push_back(input->origin());
418  }
419 
420  return newOperands;
421 }
422 
424  const std::vector<MemoryNodeId> & memoryNodeIds)
425  : MemoryStateOperation(memoryNodeIds.size(), 1)
426 {
427  CheckMemoryNodeIds(memoryNodeIds);
428  for (size_t n = 0; n < memoryNodeIds.size(); n++)
429  {
430  MemoryNodeIdToIndex_.Insert(memoryNodeIds[n], n);
431  }
432 }
433 
435 
436 bool
437 LambdaExitMemoryStateMergeOperation::operator==(const Operation & other) const noexcept
438 {
439  const auto operation = dynamic_cast<const LambdaExitMemoryStateMergeOperation *>(&other);
440  return operation && operation->MemoryNodeIdToIndex_ == MemoryNodeIdToIndex_;
441 }
442 
443 std::string
445 {
446  return util::strfmt("LambdaExitMemoryStateMerge[", ToString(getMemoryNodeIds()), "]");
447 }
448 
449 std::unique_ptr<rvsdg::Operation>
451 {
452  return std::make_unique<LambdaExitMemoryStateMergeOperation>(*this);
453 }
454 
455 rvsdg::Input *
457  const rvsdg::SimpleNode & node,
458  const MemoryNodeId memoryNodeId)
459 {
460  const auto operation =
461  dynamic_cast<const LambdaExitMemoryStateMergeOperation *>(&node.GetOperation());
462  if (!operation)
463  {
464  return nullptr;
465  }
466 
467  if (!operation->MemoryNodeIdToIndex_.HasKey(memoryNodeId))
468  {
469  return nullptr;
470  }
471 
472  const auto index = operation->MemoryNodeIdToIndex_.LookupKey(memoryNodeId);
473  JLM_ASSERT(index < node.ninputs());
474  return node.input(index);
475 }
476 
479 {
480  auto [_, operation] =
481  rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaExitMemoryStateMergeOperation>(input);
482  JLM_ASSERT(operation != nullptr);
483 
484  return operation->MemoryNodeIdToIndex_.LookupValue(input.index());
485 }
486 
487 std::optional<std::vector<rvsdg::Output *>>
489  const LambdaExitMemoryStateMergeOperation & operation,
490  const std::vector<rvsdg::Output *> & operands)
491 {
492  if (operands.empty())
493  return std::nullopt;
494 
495  bool replacedOperands = false;
496  std::vector<rvsdg::Output *> newOperands;
497  for (auto operand : operands)
498  {
499  auto [loadNode, loadOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<LoadOperation>(*operand);
500  if (!loadOperation)
501  {
502  newOperands.push_back(operand);
503  continue;
504  }
505 
506  auto loadAddress = LoadOperation::AddressInput(*loadNode).origin();
507  if (!rvsdg::IsOwnerNodeOperation<AllocaOperation>(*loadAddress))
508  {
509  newOperands.push_back(operand);
510  continue;
511  }
512 
513  auto newOperand = LoadOperation::MapMemoryStateOutputToInput(*operand).origin();
514  newOperands.push_back(newOperand);
515  replacedOperands = true;
516  }
517 
518  if (!replacedOperands)
519  return std::nullopt;
520 
521  return {
522  { CreateNode(*operands[0]->region(), newOperands, operation.getMemoryNodeIds()).output(0) }
523  };
524 }
525 
526 std::optional<std::vector<rvsdg::Output *>>
528  const LambdaExitMemoryStateMergeOperation & operation,
529  const std::vector<rvsdg::Output *> & operands)
530 {
531  if (operands.empty())
532  return std::nullopt;
533 
534  bool replacedOperands = false;
535  std::vector<rvsdg::Output *> newOperands;
536  for (auto operand : operands)
537  {
538  auto [storeNode, storeOperation] =
539  rvsdg::TryGetSimpleNodeAndOptionalOp<StoreOperation>(*operand);
540  if (!storeOperation)
541  {
542  newOperands.push_back(operand);
543  continue;
544  }
545 
546  auto storeAddress = StoreOperation::AddressInput(*storeNode).origin();
547  if (!rvsdg::IsOwnerNodeOperation<AllocaOperation>(*storeAddress))
548  {
549  newOperands.push_back(operand);
550  continue;
551  }
552 
553  auto newOperand = StoreOperation::MapMemoryStateOutputToInput(*operand).origin();
554  newOperands.push_back(newOperand);
555  replacedOperands = true;
556  }
557 
558  if (!replacedOperands)
559  return std::nullopt;
560 
561  return {
562  { CreateNode(*operands[0]->region(), newOperands, operation.getMemoryNodeIds()).output(0) }
563  };
564 }
565 
566 std::optional<std::vector<rvsdg::Output *>>
568  const LambdaExitMemoryStateMergeOperation & operation,
569  const std::vector<rvsdg::Output *> & operands)
570 {
571  if (operands.empty())
572  return std::nullopt;
573 
574  bool replacedOperands = false;
575  std::vector<rvsdg::Output *> newOperands;
576  for (auto operand : operands)
577  {
578  auto [allocaNode, allocaOperation] =
579  rvsdg::TryGetSimpleNodeAndOptionalOp<AllocaOperation>(*operand);
580  if (allocaOperation)
581  {
582  auto newOperand =
583  UndefValueOperation::Create(*allocaNode->region(), MemoryStateType::Create());
584  newOperands.push_back(newOperand);
585  replacedOperands = true;
586  }
587  else
588  {
589  newOperands.push_back(operand);
590  }
591  }
592 
593  if (!replacedOperands)
594  return std::nullopt;
595 
596  return {
597  { CreateNode(*operands[0]->region(), newOperands, operation.getMemoryNodeIds()).output(0) }
598  };
599 }
600 
602 
604  const std::vector<MemoryNodeId> & memoryNodeIds)
605  : MemoryStateOperation(memoryNodeIds.size(), 1)
606 {
607  CheckMemoryNodeIds(memoryNodeIds);
608  for (size_t n = 0; n < memoryNodeIds.size(); n++)
609  {
610  MemoryNodeIdToIndex_.Insert(memoryNodeIds[n], n);
611  }
612 }
613 
614 bool
615 CallEntryMemoryStateMergeOperation::operator==(const Operation & other) const noexcept
616 {
617  const auto operation = dynamic_cast<const CallEntryMemoryStateMergeOperation *>(&other);
618  return operation && operation->MemoryNodeIdToIndex_ == MemoryNodeIdToIndex_;
619 }
620 
621 std::string
623 {
624  return util::strfmt("CallEntryMemoryStateMerge[", ToString(getMemoryNodeIds()), "]");
625 }
626 
627 std::unique_ptr<rvsdg::Operation>
629 {
630  return std::make_unique<CallEntryMemoryStateMergeOperation>(*this);
631 }
632 
633 rvsdg::Input *
635  const rvsdg::SimpleNode & node,
636  const MemoryNodeId memoryNodeId)
637 {
638  const auto operation =
639  dynamic_cast<const CallEntryMemoryStateMergeOperation *>(&node.GetOperation());
640  if (!operation)
641  {
642  return nullptr;
643  }
644 
645  if (!operation->MemoryNodeIdToIndex_.HasKey(memoryNodeId))
646  {
647  return nullptr;
648  }
649 
650  const auto index = operation->MemoryNodeIdToIndex_.LookupKey(memoryNodeId);
651  JLM_ASSERT(index < node.ninputs());
652  return node.input(index);
653 }
654 
656 
658  const std::vector<MemoryNodeId> & memoryNodeIds)
659  : MemoryStateOperation(1, memoryNodeIds.size())
660 {
661  CheckMemoryNodeIds(memoryNodeIds);
662  for (size_t n = 0; n < memoryNodeIds.size(); n++)
663  {
664  memoryNodeIdToIndexMap_.Insert(memoryNodeIds[n], n);
665  }
666 }
667 
668 bool
669 CallExitMemoryStateSplitOperation::operator==(const Operation & other) const noexcept
670 {
671  const auto operation = dynamic_cast<const CallExitMemoryStateSplitOperation *>(&other);
672  return operation && operation->memoryNodeIdToIndexMap_ == memoryNodeIdToIndexMap_;
673 }
674 
675 std::string
677 {
678  return util::strfmt("CallExitMemoryStateSplit[", ToString(getMemoryNodeIds()), "]");
679 }
680 
681 std::unique_ptr<rvsdg::Operation>
683 {
684  return std::make_unique<CallExitMemoryStateSplitOperation>(*this);
685 }
686 
689  const rvsdg::SimpleNode & node,
690  const MemoryNodeId memoryNodeId)
691 {
692  const auto operation =
693  dynamic_cast<const CallExitMemoryStateSplitOperation *>(&node.GetOperation());
694  if (!operation)
695  {
696  return nullptr;
697  }
698 
699  if (!operation->memoryNodeIdToIndexMap_.HasKey(memoryNodeId))
700  {
701  return nullptr;
702  }
703 
704  const auto index = operation->memoryNodeIdToIndexMap_.LookupKey(memoryNodeId);
705  JLM_ASSERT(index < node.noutputs());
706  return node.output(index);
707 }
708 
711 {
712  auto [_, operation] =
713  rvsdg::TryGetSimpleNodeAndOptionalOp<CallExitMemoryStateSplitOperation>(output);
714  JLM_ASSERT(operation != nullptr);
715 
716  return operation->memoryNodeIdToIndexMap_.LookupValue(output.index());
717 }
718 
719 std::optional<std::vector<rvsdg::Output *>>
721  const CallExitMemoryStateSplitOperation & callExitSplitOperation,
722  const std::vector<rvsdg::Output *> & operands)
723 {
724  JLM_ASSERT(operands.size() == 1);
725 
726  auto [lambdaExitMergeNode, lambdaExitMergeOperation] =
727  rvsdg::TryGetSimpleNodeAndOptionalOp<LambdaExitMemoryStateMergeOperation>(*operands[0]);
728  if (!lambdaExitMergeOperation)
729  return std::nullopt;
730 
731  if (lambdaExitMergeNode->ninputs() != callExitSplitOperation.nresults())
732  {
733  // We only perform this transformation if the number of memory region IDs (and therefore
734  // inputs/outputs) are the same. See
735  // LambdaEntryMemoryStateSplitOperation::NormalizeCallEntryMemoryStateMerge() for a detailed
736  // explanation.
737  return std::nullopt;
738  }
739 
740  std::vector<rvsdg::Output *> newOperands;
741  for (const auto & memoryNodeId : callExitSplitOperation.getMemoryNodeIds())
742  {
744  *lambdaExitMergeNode,
745  memoryNodeId);
746  JLM_ASSERT(input != nullptr);
747  newOperands.push_back(input->origin());
748  }
749 
750  return newOperands;
751 }
752 
753 bool
755 {
756  for (auto & input : node.Inputs())
757  {
758  if (is<MemoryStateType>(input.Type()))
759  {
760  return true;
761  }
762  }
763 
764  for (auto & output : node.Outputs())
765  {
766  if (is<MemoryStateType>(output.Type()))
767  {
768  return true;
769  }
770  }
771 
772  return false;
773 }
774 
775 }
bool operator==(const Operation &other) const noexcept override
util::BijectiveMap< MemoryNodeId, size_t > MemoryNodeIdToIndex_
std::unique_ptr< Operation > copy() const override
~CallEntryMemoryStateMergeOperation() noexcept override
std::vector< MemoryNodeId > getMemoryNodeIds() const noexcept
static rvsdg::Input * tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
~CallExitMemoryStateSplitOperation() noexcept override
static MemoryNodeId mapOutputToMemoryNodeId(const rvsdg::Output &output)
static std::optional< std::vector< rvsdg::Output * > > NormalizeLambdaExitMemoryStateMerge(const CallExitMemoryStateSplitOperation &callExitSplitOperation, const std::vector< rvsdg::Output * > &operands)
util::BijectiveMap< MemoryNodeId, size_t > memoryNodeIdToIndexMap_
std::unique_ptr< Operation > copy() const override
static rvsdg::Output * tryMapMemoryNodeIdToOutput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
bool operator==(const Operation &other) const noexcept override
std::vector< MemoryNodeId > getMemoryNodeIds() const noexcept
util::BijectiveMap< MemoryNodeId, size_t > memoryNodeIdToIndexMap_
static rvsdg::Output * tryMapMemoryNodeIdToOutput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
std::vector< MemoryNodeId > getMemoryNodeIds() const noexcept
LambdaEntryMemoryStateSplitOperation(const std::vector< MemoryNodeId > &memoryNodeIds)
static std::optional< std::vector< rvsdg::Output * > > NormalizeCallEntryMemoryStateMerge(const LambdaEntryMemoryStateSplitOperation &lambdaEntrySplitOperation, const std::vector< rvsdg::Output * > &operands)
std::unique_ptr< Operation > copy() const override
static MemoryNodeId mapOutputToMemoryNodeId(const rvsdg::Output &output)
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadFromAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
std::vector< MemoryNodeId > getMemoryNodeIds() const noexcept
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreToAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
util::BijectiveMap< MemoryNodeId, size_t > MemoryNodeIdToIndex_
static rvsdg::Input * tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
static rvsdg::Node & CreateNode(rvsdg::Region &region, const std::vector< rvsdg::Output * > &operands, const std::vector< MemoryNodeId > &memoryNodeIds)
LambdaExitMemoryStateMergeOperation(const std::vector< MemoryNodeId > &memoryNodeIds)
std::unique_ptr< Operation > copy() const override
static std::optional< std::vector< rvsdg::Output * > > NormalizeAlloca(const LambdaExitMemoryStateMergeOperation &operation, const std::vector< rvsdg::Output * > &operands)
~LambdaExitMemoryStateMergeOperation() noexcept override
static MemoryNodeId mapInputToMemoryNodeId(const rvsdg::Input &input)
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Load.hpp:75
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition: Load.hpp:142
std::unique_ptr< Operation > copy() const override
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 * > > NormalizeNestedJoins(const MemoryStateJoinOperation &operation, const std::vector< rvsdg::Output * > &operands)
Fuses nested MemoryStateJoinOperation nodes into a single node.
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.
std::string debug_string() const override
static rvsdg::SimpleNode & CreateNode(const std::vector< rvsdg::Output * > &operands)
~MemoryStateJoinOperation() noexcept override
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 rvsdg::Output * Create(const std::vector< rvsdg::Output * > &operands)
~MemoryStateMergeOperation() noexcept override
std::unique_ptr< Operation > copy() const override
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.
std::string debug_string() const override
static std::optional< std::vector< rvsdg::Output * > > NormalizeNestedSplits(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Fuses nested splits into a single split.
~MemoryStateSplitOperation() noexcept override
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::vector< rvsdg::Output * > Create(rvsdg::Output &operand, const size_t numResults)
std::unique_ptr< Operation > copy() const override
std::string debug_string() const override
static std::optional< std::vector< rvsdg::Output * > > NormalizeSplitMerge(const MemoryStateSplitOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes an idempotent split-merge pair.
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition: Store.hpp:111
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:75
static jlm::rvsdg::Output * Create(rvsdg::Region &region, std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: operators.hpp:1024
Output * origin() const noexcept
Definition: node.hpp:58
size_t index() const noexcept
Definition: node.hpp:52
OutputIteratorRange Outputs() noexcept
Definition: node.hpp:657
InputIteratorRange Inputs() noexcept
Definition: node.hpp:622
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 index() const noexcept
Definition: node.hpp:274
const SimpleOperation & GetOperation() const noexcept override
Definition: simple-node.cpp:48
NodeInput * input(size_t index) const noexcept
Definition: simple-node.hpp:82
NodeOutput * output(size_t index) const noexcept
Definition: simple-node.hpp:88
const std::shared_ptr< const rvsdg::Type > & result(size_t index) const noexcept
Definition: operation.cpp:36
size_t nresults() const noexcept
Definition: operation.cpp:30
size_t narguments() const noexcept
Definition: operation.cpp:17
const V & LookupKey(const K &key) const
bool Insert(const K &key, const V &value)
bool insert(ItemType item)
Definition: HashSet.hpp:210
std::size_t Size() const noexcept
Definition: HashSet.hpp:187
bool Contains(const ItemType &item) const noexcept
Definition: HashSet.hpp:150
IteratorRange< ItemConstIterator > Items() const noexcept
Definition: HashSet.hpp:223
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
std::size_t MemoryNodeId
bool hasMemoryState(const rvsdg::Node &node)
std::vector< rvsdg::Output * > CollectNestedMemoryStateMergeOrJoinOperands(const std::vector< rvsdg::Output * > &operands)
static std::string ToString(const std::vector< MemoryNodeId > &memoryNodeIds)
static void CheckMemoryNodeIds(const std::vector< MemoryNodeId > &memoryNodeIds)
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35