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 
343  static rvsdg::SimpleNode &
344  CreateNode(rvsdg::Output & operand, std::vector<MemoryNodeId> memoryNodeIds)
345  {
346  return rvsdg::CreateOpNode<LambdaEntryMemoryStateSplitOperation>(
347  { &operand },
348  std::move(memoryNodeIds));
349  }
350 
351 private:
353 };
354 
366 {
367 public:
369 
370  explicit LambdaExitMemoryStateMergeOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
371 
372  bool
373  operator==(const Operation & other) const noexcept override;
374 
375  [[nodiscard]] std::string
376  debug_string() const override;
377 
378  [[nodiscard]] std::unique_ptr<Operation>
379  copy() const override;
380 
384  [[nodiscard]] std::vector<MemoryNodeId>
385  getMemoryNodeIds() const noexcept
386  {
387  std::vector<MemoryNodeId> memoryNodeIds(narguments());
388  for (auto [memoryNodeId, index] : MemoryNodeIdToIndex_)
389  {
390  JLM_ASSERT(index < narguments());
391  memoryNodeIds[index] = memoryNodeId;
392  }
393 
394  return memoryNodeIds;
395  }
396 
405  [[nodiscard]] static rvsdg::Input *
406  tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
407 
417  [[nodiscard]] static MemoryNodeId
418  mapInputToMemoryNodeId(const rvsdg::Input & input);
419 
431  static std::optional<std::vector<rvsdg::Output *>>
433  const LambdaExitMemoryStateMergeOperation & operation,
434  const std::vector<rvsdg::Output *> & operands);
435 
447  static std::optional<std::vector<rvsdg::Output *>>
449  const LambdaExitMemoryStateMergeOperation & operation,
450  const std::vector<rvsdg::Output *> & operands);
451 
462  static std::optional<std::vector<rvsdg::Output *>>
464  const LambdaExitMemoryStateMergeOperation & operation,
465  const std::vector<rvsdg::Output *> & operands);
466 
467  static rvsdg::SimpleNode &
469  rvsdg::Region & region,
470  const std::vector<rvsdg::Output *> & operands,
471  const std::vector<MemoryNodeId> & memoryNodeIds)
472  {
473  return operands.empty()
474  ? rvsdg::CreateOpNode<LambdaExitMemoryStateMergeOperation>(region, memoryNodeIds)
475  : rvsdg::CreateOpNode<LambdaExitMemoryStateMergeOperation>(operands, memoryNodeIds);
476  }
477 
478 private:
480 };
481 
493 {
494 public:
496 
497  explicit CallEntryMemoryStateMergeOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
498 
499  bool
500  operator==(const Operation & other) const noexcept override;
501 
502  [[nodiscard]] std::string
503  debug_string() const override;
504 
505  [[nodiscard]] std::unique_ptr<Operation>
506  copy() const override;
507 
511  [[nodiscard]] std::vector<MemoryNodeId>
512  getMemoryNodeIds() const noexcept
513  {
514  std::vector<MemoryNodeId> memoryNodeIds(narguments());
515  for (auto [memoryNodeId, index] : MemoryNodeIdToIndex_)
516  {
517  JLM_ASSERT(index < narguments());
518  memoryNodeIds[index] = memoryNodeId;
519  }
520 
521  return memoryNodeIds;
522  }
523 
532  [[nodiscard]] static rvsdg::Input *
533  tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
534 
535  static rvsdg::SimpleNode &
537  rvsdg::Region & region,
538  const std::vector<rvsdg::Output *> & operands,
539  std::vector<MemoryNodeId> memoryNodeIds)
540  {
541  return operands.empty() ? rvsdg::CreateOpNode<CallEntryMemoryStateMergeOperation>(
542  region,
543  std::move(memoryNodeIds))
544  : rvsdg::CreateOpNode<CallEntryMemoryStateMergeOperation>(
545  operands,
546  std::move(memoryNodeIds));
547  }
548 
549 private:
551 };
552 
564 {
565 public:
567 
568  explicit CallExitMemoryStateSplitOperation(const std::vector<MemoryNodeId> & memoryNodeIds);
569 
570  bool
571  operator==(const Operation & other) const noexcept override;
572 
573  [[nodiscard]] std::string
574  debug_string() const override;
575 
576  [[nodiscard]] std::unique_ptr<Operation>
577  copy() const override;
578 
582  [[nodiscard]] std::vector<MemoryNodeId>
583  getMemoryNodeIds() const noexcept
584  {
585  std::vector<MemoryNodeId> memoryNodeIds(nresults());
586  for (auto [memoryNodeId, index] : memoryNodeIdToIndexMap_)
587  {
588  JLM_ASSERT(index < nresults());
589  memoryNodeIds[index] = memoryNodeId;
590  }
591 
592  return memoryNodeIds;
593  }
594 
603  [[nodiscard]] static rvsdg::Output *
604  tryMapMemoryNodeIdToOutput(const rvsdg::SimpleNode & node, MemoryNodeId memoryNodeId);
605 
615  [[nodiscard]] static MemoryNodeId
616  mapOutputToMemoryNodeId(const rvsdg::Output & output);
617 
618  static rvsdg::SimpleNode &
619  CreateNode(rvsdg::Output & operand, std::vector<MemoryNodeId> memoryNodeIds)
620  {
621  return rvsdg::CreateOpNode<CallExitMemoryStateSplitOperation>(
622  { &operand },
623  std::move(memoryNodeIds));
624  }
625 
626 private:
628 };
629 
636 [[nodiscard]] bool
637 hasMemoryState(const rvsdg::Node & node);
638 
639 }
640 
641 #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)
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)
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::SimpleNode & CreateNode(rvsdg::Region &region, const std::vector< rvsdg::Output * > &operands, const std::vector< MemoryNodeId > &memoryNodeIds)
static rvsdg::Input * tryMapMemoryNodeIdToInput(const rvsdg::SimpleNode &node, MemoryNodeId memoryNodeId)
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