Jlm
Store.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_STORE_HPP
7 #define JLM_LLVM_IR_OPERATORS_STORE_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  size_t numMemoryStates,
32  size_t alignment)
33  : SimpleOperation(operandTypes, resultTypes),
34  NumMemoryStates_(numMemoryStates),
35  Alignment_(alignment)
36  {
37  JLM_ASSERT(operandTypes.size() >= 2);
38 
39  auto & addressType = *operandTypes[0];
40  JLM_ASSERT(is<PointerType>(addressType));
41 
42  auto & storedType = *operandTypes[1];
43  JLM_ASSERT(storedType.Kind() == rvsdg::TypeKind::Value);
44 
45  JLM_ASSERT(operandTypes.size() == resultTypes.size() + 2);
46  for (size_t n = 0; n < resultTypes.size(); n++)
47  {
48  auto & operandType = *operandTypes[n + 2];
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]] const rvsdg::Type &
63  GetStoredType() const noexcept
64  {
65  return *argument(1).get();
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<StoreOperation>(&node));
78  auto & input = *node.input(0);
79  JLM_ASSERT(is<PointerType>(input.Type()));
80  return input;
81  }
82 
83  [[nodiscard]] static rvsdg::Input &
84  StoredValueInput(const rvsdg::Node & node) noexcept
85  {
86  JLM_ASSERT(is<StoreOperation>(&node));
87  auto & input = *node.input(1);
88  JLM_ASSERT(input.Type()->Kind() == rvsdg::TypeKind::Value);
89  return input;
90  }
91 
92  [[nodiscard]] static rvsdg::Node::OutputIteratorRange
93  MemoryStateOutputs(const rvsdg::Node & node) noexcept
94  {
95  const auto storeOperation = util::assertedCast<const StoreOperation>(&node.GetOperation());
96  if (storeOperation->NumMemoryStates_ == 0)
97  {
98  return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
99  }
100 
101  const auto firstMemoryStateOutput =
102  node.output(storeOperation->nresults() - storeOperation->NumMemoryStates_);
103  JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
104  return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
105  }
106 
115  [[nodiscard]] static rvsdg::Node::InputIteratorRange
116  getMemoryStateInputs(const rvsdg::Node & node) noexcept
117  {
118  const auto storeOperation = util::assertedCast<const StoreOperation>(&node.GetOperation());
119  if (storeOperation->NumMemoryStates_ == 0)
120  {
121  return { rvsdg::Input::Iterator(nullptr), rvsdg::Input::Iterator(nullptr) };
122  }
123 
124  const auto firstMemoryStateInput =
125  node.input(storeOperation->narguments() - storeOperation->NumMemoryStates_);
126  JLM_ASSERT(is<MemoryStateType>(firstMemoryStateInput->Type()));
127  return { rvsdg::Input::Iterator(firstMemoryStateInput), rvsdg::Input::Iterator(nullptr) };
128  }
129 
133  [[nodiscard]] static rvsdg::Input &
135  {
136  JLM_ASSERT(is<MemoryStateType>(output.Type()));
137  auto [storeNode, storeOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<StoreOperation>(output);
138  JLM_ASSERT(storeOperation);
139  JLM_ASSERT(storeNode->ninputs() - 2 == storeNode->noutputs());
140  const auto input = storeNode->input(output.index() + 2);
141  JLM_ASSERT(is<MemoryStateType>(input->Type()));
142  return *input;
143  }
144 
145 private:
147  size_t Alignment_;
148 };
149 
156 {
157 public:
158  ~StoreNonVolatileOperation() noexcept override;
159 
161  std::shared_ptr<const rvsdg::Type> storedType,
162  const size_t numMemoryStates,
163  const size_t alignment)
164  : StoreOperation(
165  CreateOperandTypes(std::move(storedType), numMemoryStates),
166  { numMemoryStates, MemoryStateType::Create() },
167  numMemoryStates,
168  alignment)
169  {}
170 
171  bool
172  operator==(const Operation & other) const noexcept override;
173 
174  [[nodiscard]] std::string
175  debug_string() const override;
176 
177  [[nodiscard]] std::unique_ptr<Operation>
178  copy() const override;
179 
198  static std::optional<std::vector<rvsdg::Output *>>
200  const StoreNonVolatileOperation & operation,
201  const std::vector<rvsdg::Output *> & operands);
202 
217  static std::optional<std::vector<rvsdg::Output *>>
219  const StoreNonVolatileOperation & operation,
220  const std::vector<rvsdg::Output *> & operands);
221 
240  static std::optional<std::vector<rvsdg::Output *>>
242  const StoreNonVolatileOperation & operation,
243  const std::vector<rvsdg::Output *> & operands);
244 
258  static std::optional<std::vector<rvsdg::Output *>>
260  const StoreNonVolatileOperation & operation,
261  const std::vector<rvsdg::Output *> & operands);
262 
281  static std::optional<std::vector<rvsdg::Output *>>
283  const StoreNonVolatileOperation & operation,
284  const std::vector<rvsdg::Output *> & operands);
285 
298  static std::optional<std::vector<rvsdg::Output *>>
300  const StoreNonVolatileOperation & operation,
301  const std::vector<rvsdg::Output *> & operands);
302 
303  static std::unique_ptr<llvm::ThreeAddressCode>
304  Create(const Variable * address, const Variable * value, const Variable * state, size_t alignment)
305  {
306  auto storedType = CheckAndExtractStoredType(value->Type());
307 
308  auto op = std::make_unique<StoreNonVolatileOperation>(storedType, 1, alignment);
309  return ThreeAddressCode::create(std::move(op), { address, value, state });
310  }
311 
312  static std::vector<rvsdg::Output *>
314  rvsdg::Output * address,
315  rvsdg::Output * value,
316  const std::vector<rvsdg::Output *> & memoryStates,
317  size_t alignment)
318  {
319  return outputs(&CreateNode(*address, *value, memoryStates, alignment));
320  }
321 
322  static rvsdg::SimpleNode &
324  rvsdg::Output & address,
325  rvsdg::Output & value,
326  const std::vector<rvsdg::Output *> & memoryStates,
327  size_t alignment)
328  {
329  auto storedType = CheckAndExtractStoredType(value.Type());
330 
331  std::vector operands({ &address, &value });
332  operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
333 
334  auto operation = std::make_unique<StoreNonVolatileOperation>(
335  std::move(storedType),
336  memoryStates.size(),
337  alignment);
338  return CreateNode(*address.region(), std::move(operation), operands);
339  }
340 
341  static std::vector<rvsdg::Output *>
343  rvsdg::Region & region,
344  std::unique_ptr<StoreNonVolatileOperation> storeOperation,
345  const std::vector<rvsdg::Output *> & operands)
346  {
347  return outputs(&CreateNode(region, std::move(storeOperation), operands));
348  }
349 
350  static rvsdg::SimpleNode &
352  rvsdg::Region & region,
353  std::unique_ptr<StoreNonVolatileOperation> storeOperation,
354  const std::vector<rvsdg::Output *> & operands)
355  {
356  return rvsdg::SimpleNode::Create(region, std::move(storeOperation), operands);
357  }
358 
359 private:
360  static const std::shared_ptr<const jlm::rvsdg::Type>
361  CheckAndExtractStoredType(const std::shared_ptr<const rvsdg::Type> & type)
362  {
363  if (type->Kind() == rvsdg::TypeKind::Value)
364  {
365  return type;
366  }
367 
368  throw util::Error("Expected value type");
369  }
370 
371  static std::vector<std::shared_ptr<const rvsdg::Type>>
372  CreateOperandTypes(std::shared_ptr<const rvsdg::Type> storedType, size_t numMemoryStates)
373  {
374  std::vector<std::shared_ptr<const rvsdg::Type>> types(
375  { PointerType::Create(), std::move(storedType) });
376  std::vector<std::shared_ptr<const rvsdg::Type>> states(
377  numMemoryStates,
379  types.insert(types.end(), states.begin(), states.end());
380  return types;
381  }
382 };
383 
396 {
397 public:
398  ~StoreVolatileOperation() noexcept override;
399 
401  std::shared_ptr<const rvsdg::Type> storedType,
402  const size_t numMemoryStates,
403  const size_t alignment)
404  : StoreOperation(
405  CreateOperandTypes(std::move(storedType), numMemoryStates),
406  CreateResultTypes(numMemoryStates),
407  numMemoryStates,
408  alignment)
409  {}
410 
411  bool
412  operator==(const Operation & other) const noexcept override;
413 
414  [[nodiscard]] std::string
415  debug_string() const override;
416 
417  [[nodiscard]] std::unique_ptr<Operation>
418  copy() const override;
419 
420  [[nodiscard]] static rvsdg::Input &
421  IOStateInput(const rvsdg::Node & node) noexcept
422  {
423  JLM_ASSERT(is<StoreOperation>(&node));
424  auto & input = *node.input(2);
425  JLM_ASSERT(is<IOStateType>(input.Type()));
426  return input;
427  }
428 
429  [[nodiscard]] static rvsdg::Output &
430  IOStateOutput(const rvsdg::Node & node) noexcept
431  {
432  JLM_ASSERT(is<StoreOperation>(&node));
433  auto & output = *node.output(0);
434  JLM_ASSERT(is<IOStateType>(output.Type()));
435  return output;
436  }
437 
438  static std::unique_ptr<llvm::ThreeAddressCode>
440  const Variable * address,
441  const Variable * value,
442  const Variable * ioState,
443  const Variable * memoryState,
444  size_t alignment)
445  {
446  auto storedType = CheckAndExtractStoredType(value->Type());
447 
448  auto op = std::make_unique<StoreVolatileOperation>(storedType, 1, alignment);
449  return ThreeAddressCode::create(std::move(op), { address, value, ioState, memoryState });
450  }
451 
452  static rvsdg::SimpleNode &
454  rvsdg::Region & region,
455  std::unique_ptr<StoreVolatileOperation> storeOperation,
456  const std::vector<rvsdg::Output *> & operands)
457  {
458  return rvsdg::SimpleNode::Create(region, std::move(storeOperation), operands);
459  }
460 
461  static rvsdg::SimpleNode &
463  rvsdg::Output & address,
464  rvsdg::Output & value,
465  rvsdg::Output & ioState,
466  const std::vector<rvsdg::Output *> & memoryStates,
467  size_t alignment)
468  {
469  auto storedType = CheckAndExtractStoredType(value.Type());
470 
471  std::vector<rvsdg::Output *> operands({ &address, &value, &ioState });
472  operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
473 
474  auto operation =
475  std::make_unique<StoreVolatileOperation>(storedType, memoryStates.size(), alignment);
476  return CreateNode(*address.region(), std::move(operation), operands);
477  }
478 
479  static std::vector<rvsdg::Output *>
481  rvsdg::Region & region,
482  std::unique_ptr<StoreVolatileOperation> storeOperation,
483  const std::vector<rvsdg::Output *> & operands)
484  {
485  return rvsdg::outputs(&CreateNode(region, std::move(storeOperation), operands));
486  }
487 
488 private:
489  static std::shared_ptr<const rvsdg::Type>
490  CheckAndExtractStoredType(const std::shared_ptr<const rvsdg::Type> & type)
491  {
492  if (type->Kind() == rvsdg::TypeKind::Value)
493  return type;
494 
495  throw util::Error("Expected value type");
496  }
497 
498  static std::vector<std::shared_ptr<const rvsdg::Type>>
499  CreateOperandTypes(std::shared_ptr<const rvsdg::Type> storedType, size_t numMemoryStates)
500  {
501  std::vector<std::shared_ptr<const rvsdg::Type>> types(
502  { PointerType::Create(), std::move(storedType), IOStateType::Create() });
503  std::vector<std::shared_ptr<const rvsdg::Type>> states(
504  numMemoryStates,
506  types.insert(types.end(), states.begin(), states.end());
507  return types;
508  }
509 
510  static std::vector<std::shared_ptr<const rvsdg::Type>>
511  CreateResultTypes(size_t numMemoryStates)
512  {
513  std::vector<std::shared_ptr<const rvsdg::Type>> types({ IOStateType::Create() });
514  std::vector<std::shared_ptr<const rvsdg::Type>> memoryStates(
515  numMemoryStates,
517  types.insert(types.end(), memoryStates.begin(), memoryStates.end());
518  return types;
519  }
520 };
521 
522 }
523 
524 #endif
static std::shared_ptr< const IOStateType > Create()
Definition: types.cpp:343
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< StoreNonVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.hpp:351
static const std::shared_ptr< const jlm::rvsdg::Type > CheckAndExtractStoredType(const std::shared_ptr< const rvsdg::Type > &type)
Definition: Store.hpp:361
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *value, const Variable *state, size_t alignment)
Definition: Store.hpp:304
static std::vector< rvsdg::Output * > Create(rvsdg::Output *address, rvsdg::Output *value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:313
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< StoreNonVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.hpp:342
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:323
static std::optional< std::vector< rvsdg::Output * > > NormalizeIOBarrierAllocaAddress(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the StoreNonVolatileOperation from an IOBarrierOperation when it can ...
Definition: Store.cpp:254
StoreNonVolatileOperation(std::shared_ptr< const rvsdg::Type > storedType, const size_t numMemoryStates, const size_t alignment)
Definition: Store.hpp:160
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > storedType, size_t numMemoryStates)
Definition: Store.hpp:372
std::string debug_string() const override
Definition: Store.cpp:30
bool operator==(const Operation &other) const noexcept override
Definition: Store.cpp:21
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreMux(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Swaps a memory state merge operation and a store operation.
Definition: Store.cpp:210
~StoreNonVolatileOperation() noexcept override
static std::optional< std::vector< rvsdg::Output * > > normalizeStoreAllocaSingleUser(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.cpp:282
std::unique_ptr< Operation > copy() const override
Definition: Store.cpp:36
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreAlloca(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes unnecessary state from a store node when its address originates directly from an alloca node.
Definition: Store.cpp:232
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreStore(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes a duplicated store to the same address.
Definition: Store.cpp:221
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Store.cpp:243
const rvsdg::Type & GetStoredType() const noexcept
Definition: Store.hpp:63
StoreOperation(const std::vector< std::shared_ptr< const rvsdg::Type >> &operandTypes, const std::vector< std::shared_ptr< const rvsdg::Type >> &resultTypes, size_t numMemoryStates, size_t alignment)
Definition: Store.hpp:28
static rvsdg::Input & StoredValueInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:84
size_t GetAlignment() const noexcept
Definition: Store.hpp:57
static rvsdg::Node::OutputIteratorRange MemoryStateOutputs(const rvsdg::Node &node) noexcept
Definition: Store.hpp:93
static rvsdg::Node::InputIteratorRange getMemoryStateInputs(const rvsdg::Node &node) noexcept
Definition: Store.hpp:116
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition: Store.hpp:134
size_t NumMemoryStates() const noexcept
Definition: Store.hpp:69
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:75
bool operator==(const Operation &other) const noexcept override
Definition: Store.cpp:307
static rvsdg::Output & IOStateOutput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:430
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< StoreVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.hpp:453
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *value, const Variable *ioState, const Variable *memoryState, size_t alignment)
Definition: Store.hpp:439
std::string debug_string() const override
Definition: Store.cpp:316
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > storedType, size_t numMemoryStates)
Definition: Store.hpp:499
~StoreVolatileOperation() noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(size_t numMemoryStates)
Definition: Store.hpp:511
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, rvsdg::Output &ioState, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:462
static std::shared_ptr< const rvsdg::Type > CheckAndExtractStoredType(const std::shared_ptr< const rvsdg::Type > &type)
Definition: Store.hpp:490
std::unique_ptr< Operation > copy() const override
Definition: Store.cpp:322
static rvsdg::Input & IOStateInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:421
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< StoreVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.hpp:480
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition: tac.hpp:135
const std::shared_ptr< const jlm::rvsdg::Type > Type() const noexcept
Definition: variable.hpp:62
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 > & argument(size_t index) const noexcept
Definition: operation.cpp:23
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