Jlm
Loading...
Searching...
No Matches
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
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 const size_t numMemoryStates,
32 const size_t alignment)
33 : SimpleOperation(operandTypes, resultTypes),
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
55public:
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 size_t
101 numMemoryStates(const rvsdg::SimpleNode & node) noexcept
102 {
103 const auto loadOperation = util::assertedCast<const LoadOperation>(&node.GetOperation());
104 return loadOperation->NumMemoryStates_;
105 }
106
115 [[nodiscard]] static rvsdg::Node::OutputIteratorRange
116 MemoryStateOutputs(const rvsdg::Node & node) noexcept
117 {
118 const auto loadOperation = util::assertedCast<const LoadOperation>(&node.GetOperation());
119 if (loadOperation->NumMemoryStates_ == 0)
120 {
121 return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
122 }
123
124 const auto firstMemoryStateOutput =
125 node.output(loadOperation->nresults() - loadOperation->NumMemoryStates_);
126 JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
127 return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
128 }
129
138 [[nodiscard]] static rvsdg::Node::InputIteratorRange
139 MemoryStateInputs(const rvsdg::Node & node) noexcept
140 {
141 const auto loadOperation = util::assertedCast<const LoadOperation>(&node.GetOperation());
142 if (loadOperation->NumMemoryStates_ == 0)
143 {
144 return { rvsdg::Input::Iterator(nullptr), rvsdg::Input::Iterator(nullptr) };
145 }
146
147 const auto firstMemoryStateOutput =
148 node.input(loadOperation->narguments() - loadOperation->NumMemoryStates_);
149 JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
150 return { rvsdg::Input::Iterator(firstMemoryStateOutput), rvsdg::Input::Iterator(nullptr) };
151 }
152
156 [[nodiscard]] static rvsdg::Input &
158 {
159 JLM_ASSERT(is<MemoryStateType>(output.Type()));
160 auto [loadNode, loadOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<LoadOperation>(output);
161 JLM_ASSERT(loadOperation);
162 JLM_ASSERT(loadNode->ninputs() == loadNode->noutputs());
163 const auto input = loadNode->input(output.index());
164 JLM_ASSERT(is<MemoryStateType>(input->Type()));
165 return *input;
166 }
167
171 [[nodiscard]] static rvsdg::Output &
173 {
174 JLM_ASSERT(is<MemoryStateType>(input.Type()));
175 auto [loadNode, loadOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<LoadOperation>(input);
176 JLM_ASSERT(loadOperation);
177 JLM_ASSERT(loadNode->ninputs() == loadNode->noutputs());
178 const auto output = loadNode->output(input.index());
179 JLM_ASSERT(is<MemoryStateType>(output->Type()));
180 return *output;
181 }
182
183private:
186};
187
200{
201public:
202 ~LoadVolatileOperation() noexcept override;
203
205 std::shared_ptr<const rvsdg::Type> loadedType,
206 size_t numMemoryStates,
207 size_t alignment)
210 CreateResultTypes(std::move(loadedType), numMemoryStates),
212 alignment)
213 {}
214
215 bool
216 operator==(const Operation & other) const noexcept override;
217
218 [[nodiscard]] std::string
219 debug_string() const override;
220
221 [[nodiscard]] std::unique_ptr<Operation>
222 copy() const override;
223
224 [[nodiscard]] static rvsdg::Input &
225 IOStateInput(const rvsdg::Node & node) noexcept
226 {
227 JLM_ASSERT(is<LoadVolatileOperation>(&node));
228 const auto input = node.input(1);
229 JLM_ASSERT(is<IOStateType>(input->Type()));
230 return *input;
231 }
232
233 [[nodiscard]] static rvsdg::Output &
235 {
236 JLM_ASSERT(is<LoadVolatileOperation>(&node));
237 const auto output = node.output(1);
238 JLM_ASSERT(is<IOStateType>(output->Type()));
239 return *output;
240 }
241
242 static std::unique_ptr<llvm::ThreeAddressCode>
244 const Variable * address,
245 const Variable * iOState,
246 const Variable * memoryState,
247 std::shared_ptr<const rvsdg::Type> loadedType,
248 size_t alignment)
249 {
250 auto operation = std::make_unique<LoadVolatileOperation>(std::move(loadedType), 1, alignment);
251 return ThreeAddressCode::create(std::move(operation), { address, iOState, memoryState });
252 }
253
254 static rvsdg::SimpleNode &
256 rvsdg::Region & region,
257 std::unique_ptr<LoadVolatileOperation> loadOperation,
258 const std::vector<rvsdg::Output *> & operands);
259
260 static rvsdg::SimpleNode &
262 rvsdg::Output & address,
263 rvsdg::Output & iOState,
264 const std::vector<rvsdg::Output *> & memoryStates,
265 std::shared_ptr<const rvsdg::Type> loadedType,
266 size_t alignment)
267 {
268 std::vector operands({ &address, &iOState });
269 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
270
271 auto operation = std::make_unique<LoadVolatileOperation>(
272 std::move(loadedType),
273 memoryStates.size(),
274 alignment);
275 return CreateNode(*address.region(), std::move(operation), operands);
276 }
277
278private:
279 static std::vector<std::shared_ptr<const rvsdg::Type>>
281 {
282 std::vector<std::shared_ptr<const rvsdg::Type>> types(
284 std::vector<std::shared_ptr<const rvsdg::Type>> states(
287 types.insert(types.end(), states.begin(), states.end());
288 return types;
289 }
290
291 static std::vector<std::shared_ptr<const rvsdg::Type>>
292 CreateResultTypes(std::shared_ptr<const rvsdg::Type> loadedType, size_t numMemoryStates)
293 {
294 std::vector<std::shared_ptr<const rvsdg::Type>> types(
295 { std::move(loadedType), IOStateType::Create() });
296 std::vector<std::shared_ptr<const rvsdg::Type>> states(
299 types.insert(types.end(), states.begin(), states.end());
300 return types;
301 }
302};
303
310{
311public:
312 ~LoadNonVolatileOperation() noexcept override;
313
315 std::shared_ptr<const rvsdg::Type> loadedType,
316 size_t numMemoryStates,
317 size_t alignment)
320 CreateResultTypes(std::move(loadedType), numMemoryStates),
322 alignment)
323 {}
324
325 bool
326 operator==(const Operation & other) const noexcept override;
327
328 [[nodiscard]] std::string
329 debug_string() const override;
330
331 [[nodiscard]] std::unique_ptr<Operation>
332 copy() const override;
333
352 static std::optional<std::vector<rvsdg::Output *>>
354 const LoadNonVolatileOperation & operation,
355 const std::vector<rvsdg::Output *> & operands);
356
373 static std::optional<std::vector<rvsdg::Output *>>
375 const LoadNonVolatileOperation & operation,
376 const std::vector<rvsdg::Output *> & operands);
377
397 static std::optional<std::vector<rvsdg::Output *>>
399 const LoadNonVolatileOperation & operation,
400 const std::vector<rvsdg::Output *> & operands);
401
415 static std::optional<std::vector<rvsdg::Output *>>
417 const LoadNonVolatileOperation & operation,
418 const std::vector<rvsdg::Output *> & operands);
419
441 static std::optional<std::vector<rvsdg::Output *>>
443 const LoadNonVolatileOperation & loadOperation,
444 const std::vector<rvsdg::Output *> & operands);
445
446 static std::unique_ptr<llvm::ThreeAddressCode>
448 const Variable * address,
449 const Variable * state,
450 std::shared_ptr<const rvsdg::Type> loadedType,
451 size_t alignment)
452 {
453 auto operation =
454 std::make_unique<LoadNonVolatileOperation>(std::move(loadedType), 1, alignment);
455 return ThreeAddressCode::create(std::move(operation), { address, state });
456 }
457
458 static std::vector<rvsdg::Output *>
460 rvsdg::Output * address,
461 const std::vector<rvsdg::Output *> & memoryStates,
462 std::shared_ptr<const rvsdg::Type> loadedType,
463 const size_t alignment)
464 {
465 return rvsdg::outputs(&CreateNode(*address, memoryStates, std::move(loadedType), alignment));
466 }
467
468 static rvsdg::SimpleNode &
470 rvsdg::Region & region,
471 std::unique_ptr<LoadNonVolatileOperation> loadOperation,
472 const std::vector<rvsdg::Output *> & operands)
473 {
474 return rvsdg::SimpleNode::Create(region, std::move(loadOperation), operands);
475 }
476
477 static std::vector<rvsdg::Output *>
479 rvsdg::Region & region,
480 std::unique_ptr<LoadNonVolatileOperation> loadOperation,
481 const std::vector<rvsdg::Output *> & operands)
482 {
483 return outputs(&CreateNode(region, std::move(loadOperation), operands));
484 }
485
486 static rvsdg::SimpleNode &
488 rvsdg::Output & address,
489 const std::vector<rvsdg::Output *> & memoryStates,
490 std::shared_ptr<const rvsdg::Type> loadedType,
491 size_t alignment)
492 {
493 std::vector operands({ &address });
494 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
495
496 auto operation = std::make_unique<LoadNonVolatileOperation>(
497 std::move(loadedType),
498 memoryStates.size(),
499 alignment);
500 return CreateNode(*address.region(), std::move(operation), operands);
501 }
502
503private:
504 static std::vector<std::shared_ptr<const rvsdg::Type>>
506 {
507 std::vector<std::shared_ptr<const rvsdg::Type>> types(1, PointerType::Create());
508 std::vector<std::shared_ptr<const rvsdg::Type>> states(
511 types.insert(types.end(), states.begin(), states.end());
512 return types;
513 }
514
515 static std::vector<std::shared_ptr<const rvsdg::Type>>
516 CreateResultTypes(std::shared_ptr<const rvsdg::Type> loadedType, size_t numMemoryStates)
517 {
518 std::vector<std::shared_ptr<const rvsdg::Type>> types(1, std::move(loadedType));
519 std::vector<std::shared_ptr<const rvsdg::Type>> states(
522 types.insert(types.end(), states.begin(), states.end());
523 return types;
524 }
525};
526
527}
528
529#endif
static std::shared_ptr< const IOStateType > Create()
Definition types.cpp:343
static std::optional< std::vector< rvsdg::Output * > > normalizeIOBarrierAddress(const LoadNonVolatileOperation &loadOperation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the LoadNonVolatileOperation node from an IOBarrierOperation node whe...
Definition Load.cpp:367
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(size_t numMemoryStates)
Definition Load.hpp:505
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:459
static std::vector< rvsdg::Output * > Create(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition Load.hpp:478
std::unique_ptr< Operation > copy() const override
Definition Load.cpp:39
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:447
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:320
~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:331
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition Load.hpp:469
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(std::shared_ptr< const rvsdg::Type > loadedType, size_t numMemoryStates)
Definition Load.hpp:516
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:309
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:487
std::string debug_string() const override
Definition Load.cpp:33
bool operator==(const Operation &other) const noexcept override
Definition Load.cpp:24
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:248
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
size_t NumMemoryStates() const noexcept
Definition Load.hpp:69
static rvsdg::Output & mapMemoryStateInputToOutput(const rvsdg::Input &input)
Definition Load.hpp:172
static size_t numMemoryStates(const rvsdg::SimpleNode &node) noexcept
Definition Load.hpp:101
static rvsdg::Node::OutputIteratorRange MemoryStateOutputs(const rvsdg::Node &node) noexcept
Definition Load.hpp:116
static rvsdg::Output & LoadedValueOutput(const rvsdg::Node &node)
Definition Load.hpp:84
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::Node::InputIteratorRange MemoryStateInputs(const rvsdg::Node &node) noexcept
Definition Load.hpp:139
static rvsdg::Input & MapMemoryStateOutputToInput(const rvsdg::Output &output)
Definition Load.hpp:157
size_t GetAlignment() const noexcept
Definition Load.hpp:57
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:243
std::string debug_string() const override
Definition Load.cpp:418
static rvsdg::Input & IOStateInput(const rvsdg::Node &node) noexcept
Definition Load.hpp:225
~LoadVolatileOperation() noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(std::shared_ptr< const rvsdg::Type > loadedType, size_t numMemoryStates)
Definition Load.hpp:292
bool operator==(const Operation &other) const noexcept override
Definition Load.cpp:409
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition Load.cpp:430
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:261
static rvsdg::Output & IOStateOutput(const rvsdg::Node &node)
Definition Load.hpp:234
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(size_t numMemoryStates)
Definition Load.hpp:280
std::unique_ptr< Operation > copy() const override
Definition Load.cpp:424
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
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 > & 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.
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