Jlm
MemoryStateOperations.hpp
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 
6 #ifndef JLM_LLVM_IR_OPERATORS_MEMORYSTATEOPERATIONS_HPP
7 #define JLM_LLVM_IR_OPERATORS_MEMORYSTATEOPERATIONS_HPP
8 
9 #include <jlm/llvm/ir/tac.hpp>
10 #include <jlm/llvm/ir/types.hpp>
13 
14 namespace jlm::llvm
15 {
16 
17 using MemoryNodeId = std::size_t;
18 
23 {
24 protected:
25  MemoryStateOperation(size_t numOperands, size_t numResults)
27  { numOperands, MemoryStateType::Create() },
28  { numResults, MemoryStateType::Create() })
29  {}
30 };
31 
39 {
40 public:
41  ~MemoryStateMergeOperation() noexcept override;
42 
43  explicit MemoryStateMergeOperation(size_t numOperands)
44  : MemoryStateOperation(numOperands, 1)
45  {
46  if (numOperands == 0)
47  throw util::Error("Insufficient number of operands.");
48  }
49 
50  bool
51  operator==(const Operation & other) const noexcept override;
52 
53  [[nodiscard]] std::string
54  debug_string() const override;
55 
56  [[nodiscard]] std::unique_ptr<Operation>
57  copy() const override;
58 
67  static std::optional<std::vector<rvsdg::Output *>>
69  const MemoryStateMergeOperation & operation,
70  const std::vector<rvsdg::Output *> & operands);
71 
78  static std::optional<std::vector<rvsdg::Output *>>
80  const MemoryStateMergeOperation & operation,
81  const std::vector<rvsdg::Output *> & operands);
82 
90  static std::optional<std::vector<rvsdg::Output *>>
92  const MemoryStateMergeOperation & operation,
93  const std::vector<rvsdg::Output *> & operands);
94 
102  static std::optional<std::vector<rvsdg::Output *>>
104  const MemoryStateMergeOperation & operation,
105  const std::vector<rvsdg::Output *> & operands);
106 
107  static rvsdg::SimpleNode &
108  CreateNode(const std::vector<rvsdg::Output *> & operands)
109  {
110  return rvsdg::CreateOpNode<MemoryStateMergeOperation>(operands, operands.size());
111  }
112 
113  static rvsdg::Output *
114  Create(const std::vector<rvsdg::Output *> & operands)
115  {
116  return CreateNode(operands).output(0);
117  }
118 
119  static std::unique_ptr<ThreeAddressCode>
120  Create(const std::vector<const Variable *> & operands)
121  {
122  if (operands.empty())
123  throw util::Error("Insufficient number of operands.");
124 
125  auto operation = std::make_unique<MemoryStateMergeOperation>(operands.size());
126  return ThreeAddressCode::create(std::move(operation), operands);
127  }
128 };
129 
137 {
138 public:
139  ~MemoryStateJoinOperation() noexcept override;
140 
141  explicit MemoryStateJoinOperation(const size_t numOperands)
142  : MemoryStateOperation(numOperands, 1)
143  {
144  if (numOperands == 0)
145  throw std::logic_error("Insufficient number of operands.");
146  }
147 
148  bool
149  operator==(const Operation & other) const noexcept override;
150 
151  [[nodiscard]] std::string
152  debug_string() const override;
153 
154  [[nodiscard]] std::unique_ptr<Operation>
155  copy() const override;
156 
165  static std::optional<std::vector<rvsdg::Output *>>
167  const MemoryStateJoinOperation & operation,
168  const std::vector<rvsdg::Output *> & operands);
169 
176  static std::optional<std::vector<rvsdg::Output *>>
178  const MemoryStateJoinOperation & operation,
179  const std::vector<rvsdg::Output *> & operands);
180 
188  static std::optional<std::vector<rvsdg::Output *>>
190  const MemoryStateJoinOperation & operation,
191  const std::vector<rvsdg::Output *> & operands);
192 
193  static rvsdg::SimpleNode &
194  CreateNode(const std::vector<rvsdg::Output *> & operands)
195  {
196  return rvsdg::CreateOpNode<MemoryStateJoinOperation>(operands, operands.size());
197  }
198 };
199 
207 {
208 public:
209  ~MemoryStateSplitOperation() noexcept override;
210 
211  explicit MemoryStateSplitOperation(const size_t numResults)
212  : MemoryStateOperation(1, numResults)
213  {
214  if (numResults == 0)
215  throw util::Error("Insufficient number of results.");
216  }
217 
218  bool
219  operator==(const Operation & other) const noexcept override;
220 
221  [[nodiscard]] std::string
222  debug_string() const override;
223 
224  [[nodiscard]] std::unique_ptr<Operation>
225  copy() const override;
226 
235  static std::optional<std::vector<rvsdg::Output *>>
237  const MemoryStateSplitOperation & operation,
238  const std::vector<rvsdg::Output *> & operands);
239 
247  static std::optional<std::vector<rvsdg::Output *>>
249  const MemoryStateSplitOperation & operation,
250  const std::vector<rvsdg::Output *> & operands);
251 
260  static std::optional<std::vector<rvsdg::Output *>>
262  const MemoryStateSplitOperation & operation,
263  const std::vector<rvsdg::Output *> & operands);
264 
265  static rvsdg::SimpleNode &
266  CreateNode(rvsdg::Output & operand, const size_t numResults)
267  {
268  return rvsdg::CreateOpNode<MemoryStateSplitOperation>({ &operand }, numResults);
269  }
270 
271  static std::vector<rvsdg::Output *>
272  Create(rvsdg::Output & operand, const size_t numResults)
273  {
274  return outputs(&CreateNode(operand, numResults));
275  }
276 };
277 
289 {
290 public:
292 
293  explicit LambdaEntryMemoryStateSplitOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
294 
295  bool
296  operator==(const Operation & other) const noexcept override;
297 
298  [[nodiscard]] std::string
299  debug_string() const override;
300 
301  [[nodiscard]] std::unique_ptr<Operation>
302  copy() const override;
303 
307  [[nodiscard]] std::vector<MemoryNodeId>
308  getMemoryNodeIds() const noexcept
309  {
310  std::vector<MemoryNodeId> memoryNodeIds(nresults());
311  for (auto [memoryNodeId, index] : memoryNodeIdToIndexMap_)
312  {
313  JLM_ASSERT(index < nresults());
314  memoryNodeIds[index] = memoryNodeId;
315  }
316 
317  return memoryNodeIds;
318  }
319 
328  [[nodiscard]] static rvsdg::Output *
329  tryMapMemoryNodeIdToOutput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
330 
340  [[nodiscard]] static MemoryNodeId
341  mapOutputToMemoryNodeId(const rvsdg::Output & output);
342 
355  static std::optional<std::vector<rvsdg::Output *>>
357  const LambdaEntryMemoryStateSplitOperation & lambdaEntrySplitOperation,
358  const std::vector<rvsdg::Output *> & operands);
359 
360  static rvsdg::SimpleNode &
361  CreateNode(rvsdg::Output & operand, std::vector<MemoryNodeId> memoryNodeIds)
362  {
363  return rvsdg::CreateOpNode<LambdaEntryMemoryStateSplitOperation>(
364  { &operand },
365  std::move(memoryNodeIds));
366  }
367 
368 private:
370 };
371 
383 {
384 public:
386 
387  explicit LambdaExitMemoryStateMergeOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
388 
389  bool
390  operator==(const Operation & other) const noexcept override;
391 
392  [[nodiscard]] std::string
393  debug_string() const override;
394 
395  [[nodiscard]] std::unique_ptr<Operation>
396  copy() const override;
397 
401  [[nodiscard]] std::vector<MemoryNodeId>
402  getMemoryNodeIds() const noexcept
403  {
404  std::vector<MemoryNodeId> memoryNodeIds(narguments());
405  for (auto [memoryNodeId, index] : MemoryNodeIdToIndex_)
406  {
407  JLM_ASSERT(index < narguments());
408  memoryNodeIds[index] = memoryNodeId;
409  }
410 
411  return memoryNodeIds;
412  }
413 
422  [[nodiscard]] static rvsdg::Input *
423  tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
424 
434  [[nodiscard]] static MemoryNodeId
435  mapInputToMemoryNodeId(const rvsdg::Input & input);
436 
448  static std::optional<std::vector<rvsdg::Output *>>
450  const LambdaExitMemoryStateMergeOperation & operation,
451  const std::vector<rvsdg::Output *> & operands);
452 
464  static std::optional<std::vector<rvsdg::Output *>>
466  const LambdaExitMemoryStateMergeOperation & operation,
467  const std::vector<rvsdg::Output *> & operands);
468 
479  static std::optional<std::vector<rvsdg::Output *>>
481  const LambdaExitMemoryStateMergeOperation & operation,
482  const std::vector<rvsdg::Output *> & operands);
483 
484  static rvsdg::Node &
486  rvsdg::Region & region,
487  const std::vector<rvsdg::Output *> & operands,
488  const std::vector<MemoryNodeId> & memoryNodeIds)
489  {
490  return operands.empty()
491  ? rvsdg::CreateOpNode<LambdaExitMemoryStateMergeOperation>(region, memoryNodeIds)
492  : rvsdg::CreateOpNode<LambdaExitMemoryStateMergeOperation>(operands, memoryNodeIds);
493  }
494 
495 private:
497 };
498 
510 {
511 public:
513 
514  explicit CallEntryMemoryStateMergeOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
515 
516  bool
517  operator==(const Operation & other) const noexcept override;
518 
519  [[nodiscard]] std::string
520  debug_string() const override;
521 
522  [[nodiscard]] std::unique_ptr<Operation>
523  copy() const override;
524 
528  [[nodiscard]] std::vector<MemoryNodeId>
529  getMemoryNodeIds() const noexcept
530  {
531  std::vector<MemoryNodeId> memoryNodeIds(narguments());
532  for (auto [memoryNodeId, index] : MemoryNodeIdToIndex_)
533  {
534  JLM_ASSERT(index < narguments());
535  memoryNodeIds[index] = memoryNodeId;
536  }
537 
538  return memoryNodeIds;
539  }
540 
549  [[nodiscard]] static rvsdg::Input *
550  tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
551 
552  static rvsdg::SimpleNode &
554  rvsdg::Region & region,
555  const std::vector<rvsdg::Output *> & operands,
556  std::vector<MemoryNodeId> memoryNodeIds)
557  {
558  return operands.empty() ? rvsdg::CreateOpNode<CallEntryMemoryStateMergeOperation>(
559  region,
560  std::move(memoryNodeIds))
561  : rvsdg::CreateOpNode<CallEntryMemoryStateMergeOperation>(
562  operands,
563  std::move(memoryNodeIds));
564  }
565 
566 private:
568 };
569 
581 {
582 public:
584 
585  explicit CallExitMemoryStateSplitOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
586 
587  bool
588  operator==(const Operation & other) const noexcept override;
589 
590  [[nodiscard]] std::string
591  debug_string() const override;
592 
593  [[nodiscard]] std::unique_ptr<Operation>
594  copy() const override;
595 
599  [[nodiscard]] std::vector<MemoryNodeId>
600  getMemoryNodeIds() const noexcept
601  {
602  std::vector<MemoryNodeId> memoryNodeIds(nresults());
603  for (auto [memoryNodeId, index] : memoryNodeIdToIndexMap_)
604  {
605  JLM_ASSERT(index < nresults());
606  memoryNodeIds[index] = memoryNodeId;
607  }
608 
609  return memoryNodeIds;
610  }
611 
620  [[nodiscard]] static rvsdg::Output *
621  tryMapMemoryNodeIdToOutput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
622 
632  [[nodiscard]] static MemoryNodeId
633  mapOutputToMemoryNodeId(const rvsdg::Output & output);
634 
647  static std::optional<std::vector<rvsdg::Output *>>
649  const CallExitMemoryStateSplitOperation & callExitSplitOperation,
650  const std::vector<rvsdg::Output *> & operands);
651 
652  static rvsdg::SimpleNode &
653  CreateNode(rvsdg::Output & operand, std::vector<MemoryNodeId> memoryNodeIds)
654  {
655  return rvsdg::CreateOpNode<CallExitMemoryStateSplitOperation>(
656  { &operand },
657  std::move(memoryNodeIds));
658  }
659 
660 private:
662 };
663 
670 [[nodiscard]] bool
671 hasMemoryState(const rvsdg::Node & node);
672 
673 }
674 
675 #endif
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, const std::vector< rvsdg::Output * > &operands, std::vector< MemoryNodeId > memoryNodeIds)
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 rvsdg::SimpleNode & CreateNode(rvsdg::Output &operand, std::vector< MemoryNodeId > memoryNodeIds)
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)
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
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &operand, 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)
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)
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.
bool operator==(const Operation &other) const noexcept override
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.
bool operator==(const Operation &other) const noexcept override
static rvsdg::SimpleNode & CreateNode(const std::vector< rvsdg::Output * > &operands)
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::unique_ptr< ThreeAddressCode > Create(const std::vector< const Variable * > &operands)
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
MemoryStateOperation(size_t numOperands, size_t numResults)
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
bool operator==(const Operation &other) const 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 rvsdg::SimpleNode & CreateNode(rvsdg::Output &operand, const size_t numResults)
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition: tac.hpp:135
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
NodeOutput * output(size_t index) const noexcept
Definition: simple-node.hpp:88
size_t nresults() const noexcept
Definition: operation.cpp:30
SimpleOperation(std::vector< std::shared_ptr< const jlm::rvsdg::Type >> operands, std::vector< std::shared_ptr< const jlm::rvsdg::Type >> results)
Definition: operation.hpp:61
size_t narguments() const noexcept
Definition: operation.cpp:17
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
std::size_t MemoryNodeId
bool hasMemoryState(const rvsdg::Node &node)
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