Jlm
Load.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #ifndef JLM_LLVM_IR_OPERATORS_LOAD_HPP
7 #define JLM_LLVM_IR_OPERATORS_LOAD_HPP
8 
9 #include <jlm/llvm/ir/tac.hpp>
10 #include <jlm/llvm/ir/types.hpp>
11 #include <jlm/rvsdg/graph.hpp>
13 
14 #include <optional>
15 
16 namespace jlm::llvm
17 {
18 
26 {
27 protected:
29  const std::vector<std::shared_ptr<const rvsdg::Type>> & operandTypes,
30  const std::vector<std::shared_ptr<const rvsdg::Type>> & resultTypes,
31  const size_t numMemoryStates,
32  const size_t alignment)
33  : SimpleOperation(operandTypes, resultTypes),
34  NumMemoryStates_(numMemoryStates),
35  Alignment_(alignment)
36  {
37  JLM_ASSERT(!operandTypes.empty() && !resultTypes.empty());
38 
39  auto & addressType = *operandTypes[0];
40  JLM_ASSERT(is<PointerType>(addressType));
41 
42  auto & loadedType = *resultTypes[0];
43  JLM_ASSERT(loadedType.Kind() == rvsdg::TypeKind::Value);
44 
45  JLM_ASSERT(operandTypes.size() == resultTypes.size());
46  for (size_t n = 1; n < operandTypes.size(); n++)
47  {
48  auto & operandType = *operandTypes[n];
49  auto & resultType = *resultTypes[n];
50  JLM_ASSERT(operandType == resultType);
51  JLM_ASSERT(operandType.Kind() == rvsdg::TypeKind::State);
52  }
53  }
54 
55 public:
56  [[nodiscard]] size_t
57  GetAlignment() const noexcept
58  {
59  return Alignment_;
60  }
61 
62  [[nodiscard]] std::shared_ptr<const rvsdg::Type>
63  GetLoadedType() const noexcept
64  {
65  return result(0);
66  }
67 
68  [[nodiscard]] size_t
69  NumMemoryStates() const noexcept
70  {
71  return NumMemoryStates_;
72  }
73 
74  [[nodiscard]] static rvsdg::Input &
75  AddressInput(const rvsdg::Node & node) noexcept
76  {
77  JLM_ASSERT(is<LoadOperation>(&node));
78  const auto input = node.input(0);
79  JLM_ASSERT(is<PointerType>(input->Type()));
80  return *input;
81  }
82 
83  [[nodiscard]] static rvsdg::Output &
85  {
86  JLM_ASSERT(is<LoadOperation>(&node));
87  const auto output = node.output(0);
88  JLM_ASSERT(output->Type()->Kind() == rvsdg::TypeKind::Value);
89  return *output;
90  }
91 
100  [[nodiscard]] static rvsdg::Node::OutputIteratorRange
101  MemoryStateOutputs(const rvsdg::Node & node) noexcept
102  {
103  const auto loadOperation = util::assertedCast<const LoadOperation>(&node.GetOperation());
104  if (loadOperation->NumMemoryStates_ == 0)
105  {
106  return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
107  }
108 
109  const auto firstMemoryStateOutput =
110  node.output(loadOperation->nresults() - loadOperation->NumMemoryStates_);
111  JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
112  return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
113  }
114 
123  [[nodiscard]] static rvsdg::Node::InputIteratorRange
124  MemoryStateInputs(const rvsdg::Node & node) noexcept
125  {
126  const auto loadOperation = util::assertedCast<const LoadOperation>(&node.GetOperation());
127  if (loadOperation->NumMemoryStates_ == 0)
128  {
129  return { rvsdg::Input::Iterator(nullptr), rvsdg::Input::Iterator(nullptr) };
130  }
131 
132  const auto firstMemoryStateOutput =
133  node.input(loadOperation->narguments() - loadOperation->NumMemoryStates_);
134  JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
135  return { rvsdg::Input::Iterator(firstMemoryStateOutput), rvsdg::Input::Iterator(nullptr) };
136  }
137 
141  [[nodiscard]] static rvsdg::Input &
143  {
144  JLM_ASSERT(is<MemoryStateType>(output.Type()));
145  auto [loadNode, loadOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<LoadOperation>(output);
146  JLM_ASSERT(loadOperation);
147  JLM_ASSERT(loadNode->ninputs() == loadNode->noutputs());
148  const auto input = loadNode->input(output.index());
149  JLM_ASSERT(is<MemoryStateType>(input->Type()));
150  return *input;
151  }
152 
156  [[nodiscard]] static rvsdg::Output &
158  {
159  JLM_ASSERT(is<MemoryStateType>(input.Type()));
160  auto [loadNode, loadOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<LoadOperation>(input);
161  JLM_ASSERT(loadOperation);
162  JLM_ASSERT(loadNode->ninputs() == loadNode->noutputs());
163  const auto output = loadNode->output(input.index());
164  JLM_ASSERT(is<MemoryStateType>(output->Type()));
165  return *output;
166  }
167 
168 private:
170  size_t Alignment_;
171 };
172 
185 {
186 public:
187  ~LoadVolatileOperation() noexcept override;
188 
190  std::shared_ptr<const rvsdg::Type> loadedType,
191  size_t numMemoryStates,
192  size_t alignment)
193  : LoadOperation(
194  CreateOperandTypes(numMemoryStates),
195  CreateResultTypes(std::move(loadedType), numMemoryStates),
196  numMemoryStates,
197  alignment)
198  {}
199 
200  bool
201  operator==(const Operation & other) const noexcept override;
202 
203  [[nodiscard]] std::string
204  debug_string() const override;
205 
206  [[nodiscard]] std::unique_ptr<Operation>
207  copy() const override;
208 
209  [[nodiscard]] static rvsdg::Input &
210  IOStateInput(const rvsdg::Node & node) noexcept
211  {
212  JLM_ASSERT(is<LoadVolatileOperation>(&node));
213  const auto input = node.input(1);
214  JLM_ASSERT(is<IOStateType>(input->Type()));
215  return *input;
216  }
217 
218  [[nodiscard]] static rvsdg::Output &
220  {
221  JLM_ASSERT(is<LoadVolatileOperation>(&node));
222  const auto output = node.output(1);
223  JLM_ASSERT(is<IOStateType>(output->Type()));
224  return *output;
225  }
226 
227  static std::unique_ptr<llvm::ThreeAddressCode>
229  const Variable * address,
230  const Variable * iOState,
231  const Variable * memoryState,
232  std::shared_ptr<const rvsdg::Type> loadedType,
233  size_t alignment)
234  {
235  auto operation = std::make_unique<LoadVolatileOperation>(std::move(loadedType), 1, alignment);
236  return ThreeAddressCode::create(std::move(operation), { address, iOState, memoryState });
237  }
238 
239  static rvsdg::SimpleNode &
240  CreateNode(
241  rvsdg::Region & region,
242  std::unique_ptr<LoadVolatileOperation> loadOperation,
243  const std::vector<rvsdg::Output *> & operands);
244 
245  static rvsdg::SimpleNode &
247  rvsdg::Output & address,
248  rvsdg::Output & iOState,
249  const std::vector<rvsdg::Output *> & memoryStates,
250  std::shared_ptr<const rvsdg::Type> loadedType,
251  size_t alignment)
252  {
253  std::vector operands({ &address, &iOState });
254  operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
255 
256  auto operation = std::make_unique<LoadVolatileOperation>(
257  std::move(loadedType),
258  memoryStates.size(),
259  alignment);
260  return CreateNode(*address.region(), std::move(operation), operands);
261  }
262 
263 private:
264  static std::vector<std::shared_ptr<const rvsdg::Type>>
265  CreateOperandTypes(size_t numMemoryStates)
266  {
267  std::vector<std::shared_ptr<const rvsdg::Type>> types(
269  std::vector<std::shared_ptr<const rvsdg::Type>> states(
270  numMemoryStates,
272  types.insert(types.end(), states.begin(), states.end());
273  return types;
274  }
275 
276  static std::vector<std::shared_ptr<const rvsdg::Type>>
277  CreateResultTypes(std::shared_ptr<const rvsdg::Type> loadedType, size_t numMemoryStates)
278  {
279  std::vector<std::shared_ptr<const rvsdg::Type>> types(
280  { std::move(loadedType), IOStateType::Create() });
281  std::vector<std::shared_ptr<const rvsdg::Type>> states(
282  numMemoryStates,
284  types.insert(types.end(), states.begin(), states.end());
285  return types;
286  }
287 };
288 
295 {
296 public:
297  ~LoadNonVolatileOperation() noexcept override;
298 
300  std::shared_ptr<const rvsdg::Type> loadedType,
301  size_t numMemoryStates,
302  size_t alignment)
303  : LoadOperation(
304  CreateOperandTypes(numMemoryStates),
305  CreateResultTypes(std::move(loadedType), numMemoryStates),
306  numMemoryStates,
307  alignment)
308  {}
309 
310  bool
311  operator==(const Operation & other) const noexcept override;
312 
313  [[nodiscard]] std::string
314  debug_string() const override;
315 
316  [[nodiscard]] std::unique_ptr<Operation>
317  copy() const override;
318 
337  static std::optional<std::vector<rvsdg::Output *>>
339  const LoadNonVolatileOperation & operation,
340  const std::vector<rvsdg::Output *> & operands);
341 
358  static std::optional<std::vector<rvsdg::Output *>>
360  const LoadNonVolatileOperation & operation,
361  const std::vector<rvsdg::Output *> & operands);
362 
382  static std::optional<std::vector<rvsdg::Output *>>
384  const LoadNonVolatileOperation & operation,
385  const std::vector<rvsdg::Output *> & operands);
386 
400  static std::optional<std::vector<rvsdg::Output *>>
402  const LoadNonVolatileOperation & operation,
403  const std::vector<rvsdg::Output *> & operands);
404 
423  static std::optional<std::vector<rvsdg::Output *>>
425  const LoadNonVolatileOperation & operation,
426  const std::vector<rvsdg::Output *> & operands);
427 
428  static std::unique_ptr<llvm::ThreeAddressCode>
430  const Variable * address,
431  const Variable * state,
432  std::shared_ptr<const rvsdg::Type> loadedType,
433  size_t alignment)
434  {
435  auto operation =
436  std::make_unique<LoadNonVolatileOperation>(std::move(loadedType), 1, alignment);
437  return ThreeAddressCode::create(std::move(operation), { address, state });
438  }
439 
440  static std::vector<rvsdg::Output *>
442  rvsdg::Output * address,
443  const std::vector<rvsdg::Output *> & memoryStates,
444  std::shared_ptr<const rvsdg::Type> loadedType,
445  const size_t alignment)
446  {
447  return rvsdg::outputs(&CreateNode(*address, memoryStates, std::move(loadedType), alignment));
448  }
449 
450  static rvsdg::SimpleNode &
452  rvsdg::Region & region,
453  std::unique_ptr<LoadNonVolatileOperation> loadOperation,
454  const std::vector<rvsdg::Output *> & operands)
455  {
456  return rvsdg::SimpleNode::Create(region, std::move(loadOperation), operands);
457  }
458 
459  static std::vector<rvsdg::Output *>
461  rvsdg::Region & region,
462  std::unique_ptr<LoadNonVolatileOperation> loadOperation,
463  const std::vector<rvsdg::Output *> & operands)
464  {
465  return outputs(&CreateNode(region, std::move(loadOperation), operands));
466  }
467 
468  static rvsdg::SimpleNode &
470  rvsdg::Output & address,
471  const std::vector<rvsdg::Output *> & memoryStates,
472  std::shared_ptr<const rvsdg::Type> loadedType,
473  size_t alignment)
474  {
475  std::vector operands({ &address });
476  operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
477 
478  auto operation = std::make_unique<LoadNonVolatileOperation>(
479  std::move(loadedType),
480  memoryStates.size(),
481  alignment);
482  return CreateNode(*address.region(), std::move(operation), operands);
483  }
484 
485 private:
486  static std::vector<std::shared_ptr<const rvsdg::Type>>
487  CreateOperandTypes(size_t numMemoryStates)
488  {
489  std::vector<std::shared_ptr<const rvsdg::Type>> types(1, PointerType::Create());
490  std::vector<std::shared_ptr<const rvsdg::Type>> states(
491  numMemoryStates,
493  types.insert(types.end(), states.begin(), states.end());
494  return types;
495  }
496 
497  static std::vector<std::shared_ptr<const rvsdg::Type>>
498  CreateResultTypes(std::shared_ptr<const rvsdg::Type> loadedType, size_t numMemoryStates)
499  {
500  std::vector<std::shared_ptr<const rvsdg::Type>> types(1, std::move(loadedType));
501  std::vector<std::shared_ptr<const rvsdg::Type>> states(
502  numMemoryStates,
504  types.insert(types.end(), states.begin(), states.end());
505  return types;
506  }
507 };
508 
509 }
510 
511 #endif
static std::shared_ptr< const IOStateType > Create()
Definition: types.cpp:343
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(size_t numMemoryStates)
Definition: Load.hpp:487
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Load.hpp:451
static std::optional< std::vector< rvsdg::Output * > > NormalizeIOBarrierAllocaAddress(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the LoadNonVolatileOperation from an IOBarrierOperation when it can b...
Definition: Load.cpp:340
static std::vector< rvsdg::Output * > Create(rvsdg::Output *address, const std::vector< rvsdg::Output * > &memoryStates, std::shared_ptr< const rvsdg::Type > loadedType, const size_t alignment)
Definition: Load.hpp:441
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, const std::vector< rvsdg::Output * > &memoryStates, std::shared_ptr< const rvsdg::Type > loadedType, size_t alignment)
Definition: Load.hpp:469
std::unique_ptr< Operation > copy() const override
Definition: Load.cpp:35
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadStoreState(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
If the producer of a load's address is an alloca operation, then we can remove all state edges origin...
Definition: Load.cpp:318
~LoadNonVolatileOperation() noexcept override
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Load.cpp:329
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(std::shared_ptr< const rvsdg::Type > loadedType, size_t numMemoryStates)
Definition: Load.hpp:498
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Load.hpp:460
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *state, std::shared_ptr< const rvsdg::Type > loadedType, size_t alignment)
Definition: Load.hpp:429
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadAlloca(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
If the producer of a load's address is an alloca operation, then we can remove all state edges origin...
Definition: Load.cpp:307
std::string debug_string() const override
Definition: Load.cpp:29
bool operator==(const Operation &other) const noexcept override
Definition: Load.cpp:20
static std::optional< std::vector< rvsdg::Output * > > NormalizeLoadStore(const LoadNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Forwards the value from a store operation.
Definition: Load.cpp:244
size_t NumMemoryStates() const noexcept
Definition: Load.hpp:69
std::shared_ptr< const rvsdg::Type > GetLoadedType() const noexcept
Definition: Load.hpp:63
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Load.hpp:75
static rvsdg::Output & LoadedValueOutput(const rvsdg::Node &node)
Definition: Load.hpp:84
LoadOperation(const std::vector< std::shared_ptr< const rvsdg::Type >> &operandTypes, const std::vector< std::shared_ptr< const rvsdg::Type >> &resultTypes, const size_t numMemoryStates, const size_t alignment)
Definition: Load.hpp:28
static rvsdg::Node::OutputIteratorRange MemoryStateOutputs(const rvsdg::Node &node) noexcept
Definition: Load.hpp:101
static rvsdg::Output & mapMemoryStateInputToOutput(const rvsdg::Input &input)
Definition: Load.hpp:157
static rvsdg::Node::InputIteratorRange MemoryStateInputs(const rvsdg::Node &node) noexcept
Definition: Load.hpp:124
size_t GetAlignment() const noexcept
Definition: Load.hpp:57
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition: Load.hpp:142
std::string debug_string() const override
Definition: Load.cpp:378
~LoadVolatileOperation() noexcept override
static rvsdg::Input & IOStateInput(const rvsdg::Node &node) noexcept
Definition: Load.hpp:210
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &iOState, const std::vector< rvsdg::Output * > &memoryStates, std::shared_ptr< const rvsdg::Type > loadedType, size_t alignment)
Definition: Load.hpp:246
bool operator==(const Operation &other) const noexcept override
Definition: Load.cpp:369
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(std::shared_ptr< const rvsdg::Type > loadedType, size_t numMemoryStates)
Definition: Load.hpp:277
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Load.cpp:390
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *iOState, const Variable *memoryState, std::shared_ptr< const rvsdg::Type > loadedType, size_t alignment)
Definition: Load.hpp:228
std::unique_ptr< Operation > copy() const override
Definition: Load.cpp:384
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(size_t numMemoryStates)
Definition: Load.hpp:265
static rvsdg::Output & IOStateOutput(const rvsdg::Node &node)
Definition: Load.hpp:219
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition: tac.hpp:135
size_t index() const noexcept
Definition: node.hpp:52
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:67
NodeOutput * output(size_t index) const noexcept
Definition: node.hpp:650
rvsdg::Region * region() const noexcept
Definition: node.cpp:151
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
size_t index() const noexcept
Definition: node.hpp:274
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
static SimpleNode & Create(Region &region, std::unique_ptr< Operation > operation, const std::vector< rvsdg::Output * > &operands)
Definition: simple-node.hpp:49
const std::shared_ptr< const rvsdg::Type > & result(size_t index) const noexcept
Definition: operation.cpp:36
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
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
@ State
Designate a state type.
@ Value
Designate a value type.
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