Jlm
Loading...
Searching...
No Matches
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
16namespace jlm::llvm
17{
18
26{
27protected:
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
55public:
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
138 [[nodiscard]] static std::vector<rvsdg::Output *>
139 getMemoryStateOperands(const rvsdg::Node & node) noexcept
140 {
141 std::vector<rvsdg::Output *> operands;
142 for (auto & input : getMemoryStateInputs(node))
143 operands.push_back(input.origin());
144
145 return operands;
146 }
147
151 [[nodiscard]] static rvsdg::Input &
153 {
154 JLM_ASSERT(is<MemoryStateType>(output.Type()));
155 auto [storeNode, storeOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<StoreOperation>(output);
156 JLM_ASSERT(storeOperation);
157 JLM_ASSERT(storeNode->ninputs() - 2 == storeNode->noutputs());
158 const auto input = storeNode->input(output.index() + 2);
159 JLM_ASSERT(is<MemoryStateType>(input->Type()));
160 return *input;
161 }
162
163private:
166};
167
174{
175public:
176 ~StoreNonVolatileOperation() noexcept override;
177
179 std::shared_ptr<const rvsdg::Type> storedType,
180 const size_t numMemoryStates,
181 const size_t alignment)
183 CreateOperandTypes(std::move(storedType), numMemoryStates),
184 { numMemoryStates, MemoryStateType::Create() },
185 numMemoryStates,
186 alignment)
187 {}
188
189 bool
190 operator==(const Operation & other) const noexcept override;
191
192 [[nodiscard]] std::string
193 debug_string() const override;
194
195 [[nodiscard]] std::unique_ptr<Operation>
196 copy() const override;
197
216 static std::optional<std::vector<rvsdg::Output *>>
218 const StoreNonVolatileOperation & operation,
219 const std::vector<rvsdg::Output *> & operands);
220
235 static std::optional<std::vector<rvsdg::Output *>>
237 const StoreNonVolatileOperation & store2Op,
238 const std::vector<rvsdg::Output *> & operands);
239
258 static std::optional<std::vector<rvsdg::Output *>>
260 const StoreNonVolatileOperation & operation,
261 const std::vector<rvsdg::Output *> & operands);
262
276 static std::optional<std::vector<rvsdg::Output *>>
278 const StoreNonVolatileOperation & operation,
279 const std::vector<rvsdg::Output *> & operands);
280
302 static std::optional<std::vector<rvsdg::Output *>>
304 const StoreNonVolatileOperation & storeOperation,
305 const std::vector<rvsdg::Output *> & operands);
306
319 static std::optional<std::vector<rvsdg::Output *>>
321 const StoreNonVolatileOperation & operation,
322 const std::vector<rvsdg::Output *> & operands);
323
324 static std::unique_ptr<llvm::ThreeAddressCode>
325 Create(const Variable * address, const Variable * value, const Variable * state, size_t alignment)
326 {
327 auto storedType = CheckAndExtractStoredType(value->Type());
328
329 auto op = std::make_unique<StoreNonVolatileOperation>(storedType, 1, alignment);
330 return ThreeAddressCode::create(std::move(op), { address, value, state });
331 }
332
333 static std::vector<rvsdg::Output *>
335 rvsdg::Output * address,
336 rvsdg::Output * value,
337 const std::vector<rvsdg::Output *> & memoryStates,
338 size_t alignment)
339 {
340 return outputs(&CreateNode(*address, *value, memoryStates, alignment));
341 }
342
343 static rvsdg::SimpleNode &
345 rvsdg::Output & address,
346 rvsdg::Output & value,
347 const std::vector<rvsdg::Output *> & memoryStates,
348 size_t alignment)
349 {
350 auto storedType = CheckAndExtractStoredType(value.Type());
351
352 std::vector operands({ &address, &value });
353 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
354
355 auto operation = std::make_unique<StoreNonVolatileOperation>(
356 std::move(storedType),
357 memoryStates.size(),
358 alignment);
359 return CreateNode(*address.region(), std::move(operation), operands);
360 }
361
362 static std::vector<rvsdg::Output *>
364 rvsdg::Region & region,
365 std::unique_ptr<StoreNonVolatileOperation> storeOperation,
366 const std::vector<rvsdg::Output *> & operands)
367 {
368 return outputs(&CreateNode(region, std::move(storeOperation), operands));
369 }
370
371 static rvsdg::SimpleNode &
373 rvsdg::Region & region,
374 std::unique_ptr<StoreNonVolatileOperation> storeOperation,
375 const std::vector<rvsdg::Output *> & operands)
376 {
377 return rvsdg::SimpleNode::Create(region, std::move(storeOperation), operands);
378 }
379
380private:
381 static const std::shared_ptr<const jlm::rvsdg::Type>
382 CheckAndExtractStoredType(const std::shared_ptr<const rvsdg::Type> & type)
383 {
384 if (type->Kind() == rvsdg::TypeKind::Value)
385 {
386 return type;
387 }
388
389 throw util::Error("Expected value type");
390 }
391
392 static std::vector<std::shared_ptr<const rvsdg::Type>>
393 CreateOperandTypes(std::shared_ptr<const rvsdg::Type> storedType, size_t numMemoryStates)
394 {
395 std::vector<std::shared_ptr<const rvsdg::Type>> types(
396 { PointerType::Create(), std::move(storedType) });
397 std::vector<std::shared_ptr<const rvsdg::Type>> states(
398 numMemoryStates,
400 types.insert(types.end(), states.begin(), states.end());
401 return types;
402 }
403};
404
417{
418public:
419 ~StoreVolatileOperation() noexcept override;
420
422 std::shared_ptr<const rvsdg::Type> storedType,
423 const size_t numMemoryStates,
424 const size_t alignment)
426 CreateOperandTypes(std::move(storedType), numMemoryStates),
427 CreateResultTypes(numMemoryStates),
428 numMemoryStates,
429 alignment)
430 {}
431
432 bool
433 operator==(const Operation & other) const noexcept override;
434
435 [[nodiscard]] std::string
436 debug_string() const override;
437
438 [[nodiscard]] std::unique_ptr<Operation>
439 copy() const override;
440
441 [[nodiscard]] static rvsdg::Input &
442 IOStateInput(const rvsdg::Node & node) noexcept
443 {
444 JLM_ASSERT(is<StoreOperation>(&node));
445 auto & input = *node.input(2);
446 JLM_ASSERT(is<IOStateType>(input.Type()));
447 return input;
448 }
449
450 [[nodiscard]] static rvsdg::Output &
451 IOStateOutput(const rvsdg::Node & node) noexcept
452 {
453 JLM_ASSERT(is<StoreOperation>(&node));
454 auto & output = *node.output(0);
455 JLM_ASSERT(is<IOStateType>(output.Type()));
456 return output;
457 }
458
459 static std::unique_ptr<llvm::ThreeAddressCode>
461 const Variable * address,
462 const Variable * value,
463 const Variable * ioState,
464 const Variable * memoryState,
465 size_t alignment)
466 {
467 auto storedType = CheckAndExtractStoredType(value->Type());
468
469 auto op = std::make_unique<StoreVolatileOperation>(storedType, 1, alignment);
470 return ThreeAddressCode::create(std::move(op), { address, value, ioState, memoryState });
471 }
472
473 static rvsdg::SimpleNode &
475 rvsdg::Region & region,
476 std::unique_ptr<StoreVolatileOperation> storeOperation,
477 const std::vector<rvsdg::Output *> & operands)
478 {
479 return rvsdg::SimpleNode::Create(region, std::move(storeOperation), operands);
480 }
481
482 static rvsdg::SimpleNode &
484 rvsdg::Output & address,
485 rvsdg::Output & value,
486 rvsdg::Output & ioState,
487 const std::vector<rvsdg::Output *> & memoryStates,
488 size_t alignment)
489 {
490 auto storedType = CheckAndExtractStoredType(value.Type());
491
492 std::vector<rvsdg::Output *> operands({ &address, &value, &ioState });
493 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
494
495 auto operation =
496 std::make_unique<StoreVolatileOperation>(storedType, memoryStates.size(), alignment);
497 return CreateNode(*address.region(), std::move(operation), operands);
498 }
499
500 static std::vector<rvsdg::Output *>
502 rvsdg::Region & region,
503 std::unique_ptr<StoreVolatileOperation> storeOperation,
504 const std::vector<rvsdg::Output *> & operands)
505 {
506 return rvsdg::outputs(&CreateNode(region, std::move(storeOperation), operands));
507 }
508
509private:
510 static std::shared_ptr<const rvsdg::Type>
511 CheckAndExtractStoredType(const std::shared_ptr<const rvsdg::Type> & type)
512 {
513 if (type->Kind() == rvsdg::TypeKind::Value)
514 return type;
515
516 throw util::Error("Expected value type");
517 }
518
519 static std::vector<std::shared_ptr<const rvsdg::Type>>
520 CreateOperandTypes(std::shared_ptr<const rvsdg::Type> storedType, size_t numMemoryStates)
521 {
522 std::vector<std::shared_ptr<const rvsdg::Type>> types(
523 { PointerType::Create(), std::move(storedType), IOStateType::Create() });
524 std::vector<std::shared_ptr<const rvsdg::Type>> states(
525 numMemoryStates,
527 types.insert(types.end(), states.begin(), states.end());
528 return types;
529 }
530
531 static std::vector<std::shared_ptr<const rvsdg::Type>>
532 CreateResultTypes(size_t numMemoryStates)
533 {
534 std::vector<std::shared_ptr<const rvsdg::Type>> types({ IOStateType::Create() });
535 std::vector<std::shared_ptr<const rvsdg::Type>> memoryStates(
536 numMemoryStates,
538 types.insert(types.end(), memoryStates.begin(), memoryStates.end());
539 return types;
540 }
541};
542
543}
544
545#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 std::optional< std::vector< rvsdg::Output * > > normalizeStoreStore(const StoreNonVolatileOperation &store2Op, const std::vector< rvsdg::Output * > &operands)
Removes a duplicated store to the same address.
Definition Store.cpp:174
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< StoreNonVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition Store.hpp:372
static const std::shared_ptr< const jlm::rvsdg::Type > CheckAndExtractStoredType(const std::shared_ptr< const rvsdg::Type > &type)
Definition Store.hpp:382
static std::optional< std::vector< rvsdg::Output * > > normalizeIOBarrierAddress(const StoreNonVolatileOperation &storeOperation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the StoreNonVolatileOperation node from an IOBarrierOperation node wh...
Definition Store.cpp:279
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *value, const Variable *state, size_t alignment)
Definition Store.hpp:325
std::string debug_string() const override
Definition Store.cpp:33
bool operator==(const Operation &other) const noexcept override
Definition Store.cpp:24
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:163
~StoreNonVolatileOperation() noexcept override
static std::vector< rvsdg::Output * > Create(rvsdg::Output *address, rvsdg::Output *value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition Store.hpp:334
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > storedType, size_t numMemoryStates)
Definition Store.hpp:393
static std::optional< std::vector< rvsdg::Output * > > normalizeStoreAllocaSingleUser(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition Store.cpp:320
std::unique_ptr< Operation > copy() const override
Definition Store.cpp:39
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< StoreNonVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition Store.hpp:363
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:230
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition Store.hpp:344
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition Store.cpp:241
static std::vector< rvsdg::Output * > getMemoryStateOperands(const rvsdg::Node &node) noexcept
Definition Store.hpp:139
static rvsdg::Input & StoredValueInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:84
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition Store.hpp:152
size_t GetAlignment() const noexcept
Definition Store.hpp:57
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:75
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::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
const rvsdg::Type & GetStoredType() const noexcept
Definition Store.hpp:63
size_t NumMemoryStates() const noexcept
Definition Store.hpp:69
bool operator==(const Operation &other) const noexcept override
Definition Store.cpp:345
std::string debug_string() const override
Definition Store.cpp:354
~StoreVolatileOperation() noexcept override
static rvsdg::Output & IOStateOutput(const rvsdg::Node &node) noexcept
Definition Store.hpp:451
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(size_t numMemoryStates)
Definition Store.hpp:532
std::unique_ptr< Operation > copy() const override
Definition Store.cpp:360
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > storedType, size_t numMemoryStates)
Definition Store.hpp:520
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< StoreVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition Store.hpp:501
static rvsdg::Input & IOStateInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:442
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:460
static std::shared_ptr< const rvsdg::Type > CheckAndExtractStoredType(const std::shared_ptr< const rvsdg::Type > &type)
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:483
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< StoreVolatileOperation > storeOperation, const std::vector< rvsdg::Output * > &operands)
Definition Store.hpp:474
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
size_t index() const noexcept
Definition node.hpp:274
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
static SimpleNode & Create(Region &region, std::unique_ptr< Operation > operation, const std::vector< rvsdg::Output * > &operands)
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.
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition node.hpp:1058
@ State
Designate a state type.
@ Value
Designate a value type.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872