Jlm
Loading...
Searching...
No Matches
StdLibIntrinsicOperations.hpp
Go to the documentation of this file.
1/*
2 * Copyright 2024 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#ifndef JLM_LLVM_IR_OPERATORS_STDLIBINTRINSICOPERATIONS_HPP
7#define JLM_LLVM_IR_OPERATORS_STDLIBINTRINSICOPERATIONS_HPP
8
9#include <jlm/llvm/ir/tac.hpp>
10#include <jlm/llvm/ir/types.hpp>
13
14namespace jlm::llvm
15{
16
24{
25protected:
27 const std::vector<std::shared_ptr<const rvsdg::Type>> & operandTypes,
28 const std::vector<std::shared_ptr<const rvsdg::Type>> & resultTypes)
29 : SimpleOperation(operandTypes, resultTypes)
30 {
31 JLM_ASSERT(operandTypes.size() >= 3);
32
33 auto & dstAddressType = *operandTypes[0];
34 JLM_ASSERT(is<PointerType>(dstAddressType));
35
36 auto & srcAddressType = *operandTypes[1];
37 JLM_ASSERT(is<PointerType>(srcAddressType));
38
39 auto & lengthType = *operandTypes[2];
40 if (lengthType != *rvsdg::BitType::Create(32) && lengthType != *rvsdg::BitType::Create(64))
41 {
42 throw util::Error("Expected 32 bit or 64 bit integer type.");
43 }
44 }
45
46public:
47 [[nodiscard]] const rvsdg::BitType &
48 LengthType() const noexcept
49 {
50 auto type = std::dynamic_pointer_cast<const rvsdg::BitType>(argument(2));
51 JLM_ASSERT(type != nullptr);
52 return *type;
53 }
54
55 [[nodiscard]] virtual size_t
56 NumMemoryStates() const noexcept = 0;
57
62 [[nodiscard]] static rvsdg::Input &
63 destinationInput(const rvsdg::Node & node) noexcept
64 {
65 JLM_ASSERT(is<MemCpyOperation>(&node));
66 const auto input = node.input(0);
67 JLM_ASSERT(is<PointerType>(input->Type()));
68 return *input;
69 }
70
75 [[nodiscard]] static rvsdg::Input &
76 sourceInput(const rvsdg::Node & node) noexcept
77 {
78 JLM_ASSERT(is<MemCpyOperation>(&node));
79 const auto input = node.input(1);
80 JLM_ASSERT(is<PointerType>(input->Type()));
81 return *input;
82 }
83
88 [[nodiscard]] static rvsdg::Input &
89 countInput(const rvsdg::Node & node) noexcept
90 {
91 JLM_ASSERT(is<MemCpyOperation>(&node));
92 const auto input = node.input(2);
93 JLM_ASSERT(is<rvsdg::BitType>(input->Type()));
94 return *input;
95 }
96
100 [[nodiscard]] static rvsdg::Input &
102 {
103 JLM_ASSERT(is<MemoryStateType>(output.Type()));
104 auto [memCpyNode, memCpyOperation] =
106 JLM_ASSERT(memCpyOperation);
107 const auto numNonMemoryStateOutputs =
108 memCpyNode->noutputs() - memCpyOperation->NumMemoryStates();
109 JLM_ASSERT(output.index() >= numNonMemoryStateOutputs);
110 const auto numNonMemoryStateInputs = memCpyNode->ninputs() - memCpyOperation->NumMemoryStates();
111 const auto inputIndex = numNonMemoryStateInputs + (output.index() - numNonMemoryStateOutputs);
112 const auto input = memCpyNode->input(inputIndex);
113 JLM_ASSERT(is<MemoryStateType>(input->Type()));
114 return *input;
115 }
116
120 [[nodiscard]] static rvsdg::Output &
122 {
123 JLM_ASSERT(is<MemoryStateType>(input.Type()));
124 auto [memCpyNode, memCpyOperation] =
126 JLM_ASSERT(memCpyOperation);
127 const auto numNonMemoryStateInputs = memCpyNode->ninputs() - memCpyOperation->NumMemoryStates();
128 JLM_ASSERT(input.index() >= numNonMemoryStateInputs);
129 const auto numNonMemoryStateOutputs =
130 memCpyNode->noutputs() - memCpyOperation->NumMemoryStates();
131 const auto outputIndex = numNonMemoryStateOutputs + (input.index() - numNonMemoryStateInputs);
132 const auto output = memCpyNode->output(outputIndex);
133 JLM_ASSERT(is<MemoryStateType>(output->Type()));
134 return *output;
135 }
136
145 [[nodiscard]] static rvsdg::Node::OutputIteratorRange
146 memoryStateOutputs(const rvsdg::Node & node) noexcept
147 {
148 const auto memCpyOperation = util::assertedCast<const MemCpyOperation>(&node.GetOperation());
149 if (memCpyOperation->NumMemoryStates() == 0)
150 {
151 return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
152 }
153
154 const auto firstMemoryStateOutput =
155 node.output(memCpyOperation->nresults() - memCpyOperation->NumMemoryStates());
156 JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
157 return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
158 }
159};
160
167{
168public:
170
171 MemCpyNonVolatileOperation(std::shared_ptr<const rvsdg::Type> lengthType, size_t numMemoryStates)
173 CreateOperandTypes(std::move(lengthType), numMemoryStates),
174 CreateResultTypes(numMemoryStates))
175 {}
176
177 bool
178 operator==(const Operation & other) const noexcept override;
179
180 [[nodiscard]] std::string
181 debug_string() const override;
182
183 [[nodiscard]] std::unique_ptr<Operation>
184 copy() const override;
185
186 [[nodiscard]] size_t
187 NumMemoryStates() const noexcept override;
188
189 static std::unique_ptr<llvm::ThreeAddressCode>
191 const Variable * destination,
192 const Variable * source,
193 const Variable * length,
194 const std::vector<const Variable *> & memoryStates)
195 {
196 std::vector<const Variable *> operands = { destination, source, length };
197 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
198
199 auto operation =
200 std::make_unique<MemCpyNonVolatileOperation>(length->Type(), memoryStates.size());
201 return ThreeAddressCode::create(std::move(operation), operands);
202 }
203
204 static rvsdg::SimpleNode &
206 rvsdg::Output & destination,
207 rvsdg::Output & source,
208 rvsdg::Output & length,
209 const std::vector<rvsdg::Output *> & memoryStates)
210 {
211 std::vector operands = { &destination, &source, &length };
212 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
213
215 operands,
216 length.Type(),
217 memoryStates.size());
218 }
219
220 static std::vector<rvsdg::Output *>
222 rvsdg::Output * destination,
223 rvsdg::Output * source,
224 rvsdg::Output * length,
225 const std::vector<rvsdg::Output *> & memoryStates)
226 {
227 std::vector operands = { destination, source, length };
228 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
229
231 operands,
232 length->Type(),
233 memoryStates.size()));
234 }
235
236private:
237 static std::vector<std::shared_ptr<const rvsdg::Type>>
238 CreateOperandTypes(std::shared_ptr<const rvsdg::Type> length, size_t numMemoryStates)
239 {
240 auto pointerType = PointerType::Create();
241 std::vector<std::shared_ptr<const rvsdg::Type>> types = { pointerType, pointerType, length };
242 types.insert(types.end(), numMemoryStates, MemoryStateType::Create());
243 return types;
244 }
245
246 static std::vector<std::shared_ptr<const rvsdg::Type>>
247 CreateResultTypes(size_t numMemoryStates)
248 {
249 // The memcpy() standard C library call has as return type void*, but LLVM models the
250 // function call nevertheless as:
251 // call void @llvm.memcpy.p0.p0.i64(ptr align 4 %7, ptr align 4 %8, i64 %9, i1 false)
252 //
253 // LLVM simply hands in register %7 (dest pointer) to all users of the return type of memcpy().
254 // Thus, we only need state types as result types of the operation here.
255 return { numMemoryStates, MemoryStateType::Create() };
256 }
257};
258
271{
272public:
273 ~MemCpyVolatileOperation() noexcept override;
274
275 MemCpyVolatileOperation(std::shared_ptr<const rvsdg::Type> lengthType, size_t numMemoryStates)
277 CreateOperandTypes(std::move(lengthType), numMemoryStates),
278 CreateResultTypes(numMemoryStates))
279 {}
280
281 bool
282 operator==(const Operation & other) const noexcept override;
283
284 [[nodiscard]] std::string
285 debug_string() const override;
286
287 [[nodiscard]] std::unique_ptr<Operation>
288 copy() const override;
289
290 [[nodiscard]] size_t
291 NumMemoryStates() const noexcept override;
292
293 [[nodiscard]] static rvsdg::Input &
294 getIOStateInput(const rvsdg::Node & node) noexcept
295 {
296 JLM_ASSERT(is<MemCpyVolatileOperation>(&node));
297 const auto input = node.input(3);
298 JLM_ASSERT(is<IOStateType>(input->Type()));
299 return *input;
300 }
301
302 [[nodiscard]] static rvsdg::Output &
304 {
305 JLM_ASSERT(is<MemCpyVolatileOperation>(&node));
306 const auto output = node.output(0);
307 JLM_ASSERT(is<IOStateType>(output->Type()));
308 return *output;
309 }
310
311 static std::unique_ptr<llvm::ThreeAddressCode>
313 const Variable & destination,
314 const Variable & source,
315 const Variable & length,
316 const Variable & ioState,
317 const std::vector<const Variable *> & memoryStates)
318 {
319 std::vector<const Variable *> operands = { &destination, &source, &length, &ioState };
320 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
321
322 auto operation = std::make_unique<MemCpyVolatileOperation>(length.Type(), memoryStates.size());
323 return ThreeAddressCode::create(std::move(operation), operands);
324 }
325
326 static rvsdg::SimpleNode &
328 rvsdg::Output & destination,
329 rvsdg::Output & source,
330 rvsdg::Output & length,
331 rvsdg::Output & ioState,
332 const std::vector<rvsdg::Output *> & memoryStates)
333 {
334 std::vector operands = { &destination, &source, &length, &ioState };
335 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
336
338 operands,
339 length.Type(),
340 memoryStates.size());
341 }
342
343private:
344 static std::vector<std::shared_ptr<const rvsdg::Type>>
345 CreateOperandTypes(std::shared_ptr<const rvsdg::Type> lengthType, size_t numMemoryStates)
346 {
347 auto pointerType = PointerType::Create();
348 std::vector<std::shared_ptr<const rvsdg::Type>> types = { pointerType,
349 pointerType,
350 std::move(lengthType),
352 types.insert(types.end(), numMemoryStates, MemoryStateType::Create());
353 return types;
354 }
355
356 static std::vector<std::shared_ptr<const rvsdg::Type>>
357 CreateResultTypes(size_t numMemoryStates)
358 {
359 // The memcpy() standard C library call has as return type void*, but LLVM models the
360 // function call nevertheless as:
361 // call void @llvm.memcpy.p0.p0.i64(ptr align 4 %7, ptr align 4 %8, i64 %9, i1 false)
362 //
363 // LLVM simply hands in register %7 (dest pointer) to all users of the return type of memcpy().
364 // Thus, we only need state types as result types of the operation here.
365 std::vector<std::shared_ptr<const rvsdg::Type>> types(1, IOStateType::Create());
366 types.insert(types.end(), numMemoryStates, MemoryStateType::Create());
367 return types;
368 }
369};
370
377{
378protected:
380 const std::vector<std::shared_ptr<const rvsdg::Type>> & operandTypes,
381 const std::vector<std::shared_ptr<const rvsdg::Type>> & resultTypes)
382 : SimpleOperation(operandTypes, resultTypes)
383 {
384 JLM_ASSERT(operandTypes.size() >= 3);
385
386 auto & dstAddressType = *operandTypes[0];
387 JLM_ASSERT(is<PointerType>(dstAddressType));
388
389 if (auto & valueType = *operandTypes[1]; valueType != *rvsdg::BitType::Create(8))
390 {
391 throw std::runtime_error("Expected 8 bit integer type.");
392 }
393
394 if (auto & lengthType = *operandTypes[2];
396 {
397 throw util::Error("Expected 32 bit or 64 bit integer type.");
398 }
399 }
400
401public:
405 [[nodiscard]] const rvsdg::BitType &
406 lengthType() const noexcept
407 {
408 const auto type = std::dynamic_pointer_cast<const rvsdg::BitType>(argument(2));
409 JLM_ASSERT(type != nullptr);
410 JLM_ASSERT(type->nbits() == 32 || type->nbits() == 64);
411 return *type;
412 }
413
417 [[nodiscard]] virtual size_t
418 numMemoryStates() const noexcept = 0;
419
424 [[nodiscard]] static rvsdg::Input &
425 destinationInput(const rvsdg::Node & node) noexcept
426 {
427 JLM_ASSERT(is<MemSetOperation>(&node));
428 const auto input = node.input(0);
429 JLM_ASSERT(is<PointerType>(input->Type()));
430 return *input;
431 }
432
437 [[nodiscard]] static rvsdg::Input &
438 valueInput(const rvsdg::Node & node) noexcept
439 {
440 JLM_ASSERT(is<MemSetOperation>(&node));
441 const auto input = node.input(1);
442 JLM_ASSERT(*input->Type() == *rvsdg::BitType::Create(8));
443 return *input;
444 }
445
450 [[nodiscard]] static rvsdg::Input &
451 lengthInput(const rvsdg::Node & node) noexcept
452 {
453 JLM_ASSERT(is<MemSetOperation>(&node));
454 const auto input = node.input(2);
456 *input->Type() == *rvsdg::BitType::Create(32)
457 || *input->Type() == *rvsdg::BitType::Create(64));
458 return *input;
459 }
460
464 [[nodiscard]] static rvsdg::Input &
466 {
467 JLM_ASSERT(is<MemoryStateType>(output.Type()));
468 auto [memsetNode, memsetOperation] =
470 JLM_ASSERT(memsetOperation);
471 const auto numNonMemoryStateOutputs =
472 memsetNode->noutputs() - memsetOperation->numMemoryStates();
473 JLM_ASSERT(output.index() >= numNonMemoryStateOutputs);
474 const auto numNonMemoryStateInputs = memsetNode->ninputs() - memsetOperation->numMemoryStates();
475 const auto inputIndex = numNonMemoryStateInputs + (output.index() - numNonMemoryStateOutputs);
476 const auto input = memsetNode->input(inputIndex);
477 JLM_ASSERT(is<MemoryStateType>(input->Type()));
478 return *input;
479 }
480
484 [[nodiscard]] static rvsdg::Output &
486 {
487 JLM_ASSERT(is<MemoryStateType>(input.Type()));
488 auto [memsetNode, memsetOperation] =
490 JLM_ASSERT(memsetOperation);
491 const auto numNonMemoryStateInputs = memsetNode->ninputs() - memsetOperation->numMemoryStates();
492 JLM_ASSERT(input.index() >= numNonMemoryStateInputs);
493 const auto numNonMemoryStateOutputs =
494 memsetNode->noutputs() - memsetOperation->numMemoryStates();
495 const auto outputIndex = numNonMemoryStateOutputs + (input.index() - numNonMemoryStateInputs);
496 const auto output = memsetNode->output(outputIndex);
497 JLM_ASSERT(is<MemoryStateType>(output->Type()));
498 return *output;
499 }
500
509 [[nodiscard]] static rvsdg::Node::OutputIteratorRange
510 memoryStateOutputs(const rvsdg::Node & node) noexcept
511 {
512 const auto memSetOperation = util::assertedCast<const MemSetOperation>(&node.GetOperation());
513 if (memSetOperation->numMemoryStates() == 0)
514 {
515 return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
516 }
517
518 const auto firstMemoryStateOutput =
519 node.output(memSetOperation->nresults() - memSetOperation->numMemoryStates());
520 JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
521 return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
522 }
523};
524
532{
533public:
534 ~MemSetNonVolatileOperation() noexcept override;
535
541
542 bool
543 operator==(const Operation & other) const noexcept override;
544
545 [[nodiscard]] std::string
546 debug_string() const override;
547
548 [[nodiscard]] std::unique_ptr<Operation>
549 copy() const override;
550
551 [[nodiscard]] size_t
552 numMemoryStates() const noexcept override;
553
554 static std::unique_ptr<ThreeAddressCode>
556 const Variable & destination,
557 const Variable & value,
558 const Variable & length,
559 const std::vector<const Variable *> & memoryStates)
560 {
561 std::vector operands = { &destination, &value, &length };
562 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
563
564 auto operation =
565 std::make_unique<MemSetNonVolatileOperation>(length.Type(), memoryStates.size());
566 return ThreeAddressCode::create(std::move(operation), operands);
567 }
568
569 static rvsdg::SimpleNode &
571 rvsdg::Output & destination,
572 rvsdg::Output & value,
573 rvsdg::Output & length,
574 const std::vector<rvsdg::Output *> & memoryStates)
575 {
576 std::vector operands = { &destination, &value, &length };
577 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
578
580 operands,
581 length.Type(),
582 memoryStates.size());
583 }
584
585private:
586 static std::vector<std::shared_ptr<const rvsdg::Type>>
588 const std::shared_ptr<const rvsdg::Type> & lengthType,
589 const size_t numMemoryStates)
590 {
591 const auto pointerType = PointerType::Create();
592 const auto & valueType = rvsdg::BitType::Create(8);
593 std::vector<std::shared_ptr<const rvsdg::Type>> types = { pointerType, valueType, lengthType };
594 types.insert(types.end(), numMemoryStates, MemoryStateType::Create());
595 return types;
596 }
597
598 static std::vector<std::shared_ptr<const rvsdg::Type>>
600 {
601 // The memset() standard C library call has as return type void*, but LLVM models the
602 // function call nevertheless as:
603 // call void @llvm.memset.p0.i64(ptr %0, i8 0, i64 8, i1 false)
604 //
605 // LLVM simply hands in register %7 (dest pointer) to all users of the return type of memset().
606 // Thus, we only need state types as result types of the operation here.
608 }
609};
610
620{
621protected:
623 const std::vector<std::shared_ptr<const rvsdg::Type>> & operandTypes,
624 const std::vector<std::shared_ptr<const rvsdg::Type>> & resultTypes)
625 : SimpleOperation(operandTypes, resultTypes)
626 {
627 JLM_ASSERT(operandTypes.size() >= 3);
628
629 auto & destAddressType = *operandTypes[0];
630 JLM_ASSERT(is<PointerType>(destAddressType));
631
632 auto & srcAddressType = *operandTypes[1];
633 JLM_ASSERT(is<PointerType>(srcAddressType));
634
635 auto & lengthType = *operandTypes[2];
638 }
639
640public:
644 [[nodiscard]] const rvsdg::BitType &
645 lengthType() const noexcept
646 {
647 const auto type = std::dynamic_pointer_cast<const rvsdg::BitType>(argument(2));
648 JLM_ASSERT(type != nullptr);
649 JLM_ASSERT(type->nbits() == 32 || type->nbits() == 64);
650 return *type;
651 }
652
656 [[nodiscard]] virtual size_t
657 numMemoryStates() const noexcept = 0;
658
663 [[nodiscard]] static rvsdg::Input &
664 destinationInput(const rvsdg::Node & node) noexcept
665 {
666 JLM_ASSERT(is<MemMoveOperation>(&node));
667 const auto input = node.input(0);
668 JLM_ASSERT(is<PointerType>(input->Type()));
669 return *input;
670 }
671
676 [[nodiscard]] static rvsdg::Input &
677 sourceInput(const rvsdg::Node & node) noexcept
678 {
679 JLM_ASSERT(is<MemMoveOperation>(&node));
680 const auto input = node.input(1);
681 JLM_ASSERT(is<PointerType>(input->Type()));
682 return *input;
683 }
684
689 [[nodiscard]] static rvsdg::Input &
690 lengthInput(const rvsdg::Node & node) noexcept
691 {
692 JLM_ASSERT(is<MemMoveOperation>(&node));
693 const auto input = node.input(2);
694 const auto type = std::dynamic_pointer_cast<const rvsdg::BitType>(node.input(2)->Type());
695 JLM_ASSERT(type != nullptr);
696 JLM_ASSERT(type->nbits() == 32 || type->nbits() == 64);
697 return *input;
698 }
699
703 [[nodiscard]] static rvsdg::Input &
705 {
706 JLM_ASSERT(is<MemoryStateType>(output.Type()));
707 auto [memmoveNode, memmoveOperation] =
709 JLM_ASSERT(memmoveOperation);
710 const auto numNonMemoryStateOutputs =
711 memmoveNode->noutputs() - memmoveOperation->numMemoryStates();
712 JLM_ASSERT(output.index() >= numNonMemoryStateOutputs);
713 const auto numNonMemoryStateInputs =
714 memmoveNode->ninputs() - memmoveOperation->numMemoryStates();
715 const auto inputIndex = numNonMemoryStateInputs + (output.index() - numNonMemoryStateOutputs);
716 const auto input = memmoveNode->input(inputIndex);
717 JLM_ASSERT(is<MemoryStateType>(input->Type()));
718 return *input;
719 }
720
724 [[nodiscard]] static rvsdg::Output &
726 {
727 JLM_ASSERT(is<MemoryStateType>(input.Type()));
728 auto [memmoveNode, memmoveOperation] =
730 JLM_ASSERT(memmoveOperation);
731 const auto numNonMemoryStateInputs =
732 memmoveNode->ninputs() - memmoveOperation->numMemoryStates();
733 JLM_ASSERT(input.index() >= numNonMemoryStateInputs);
734 const auto numNonMemoryStateOutputs =
735 memmoveNode->noutputs() - memmoveOperation->numMemoryStates();
736 const auto outputIndex = numNonMemoryStateOutputs + (input.index() - numNonMemoryStateInputs);
737 const auto output = memmoveNode->output(outputIndex);
738 JLM_ASSERT(is<MemoryStateType>(output->Type()));
739 return *output;
740 }
741
750 [[nodiscard]] static rvsdg::Node::OutputIteratorRange
751 memoryStateOutputs(const rvsdg::Node & node) noexcept
752 {
753 const auto memMoveOperation = util::assertedCast<const MemMoveOperation>(&node.GetOperation());
754 if (memMoveOperation->numMemoryStates() == 0)
755 {
756 return { rvsdg::Output::Iterator(nullptr), rvsdg::Output::Iterator(nullptr) };
757 }
758
759 const auto firstMemoryStateOutput =
760 node.output(memMoveOperation->nresults() - memMoveOperation->numMemoryStates());
761 JLM_ASSERT(is<MemoryStateType>(firstMemoryStateOutput->Type()));
762 return { rvsdg::Output::Iterator(firstMemoryStateOutput), rvsdg::Output::Iterator(nullptr) };
763 }
764};
765
773{
774public:
775 ~MemMoveNonVolatileOperation() noexcept override;
776
782
783 bool
784 operator==(const Operation & other) const noexcept override;
785
786 [[nodiscard]] std::string
787 debug_string() const override;
788
789 [[nodiscard]] std::unique_ptr<Operation>
790 copy() const override;
791
792 [[nodiscard]] size_t
793 numMemoryStates() const noexcept override;
794
795 static std::unique_ptr<ThreeAddressCode>
797 const Variable & dest,
798 const Variable & src,
799 const Variable & length,
800 const std::vector<const Variable *> & memoryStates)
801 {
802 std::vector operands = { &dest, &src, &length };
803 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
804
805 auto operation =
806 std::make_unique<MemMoveNonVolatileOperation>(length.Type(), memoryStates.size());
807 return ThreeAddressCode::create(std::move(operation), operands);
808 }
809
810 static rvsdg::SimpleNode &
812 rvsdg::Output & dest,
813 rvsdg::Output & src,
814 rvsdg::Output & length,
815 const std::vector<rvsdg::Output *> & memoryStates)
816 {
817 std::vector operands = { &dest, &src, &length };
818 operands.insert(operands.end(), memoryStates.begin(), memoryStates.end());
819
821 operands,
822 length.Type(),
823 memoryStates.size());
824 }
825
826private:
827 static std::vector<std::shared_ptr<const rvsdg::Type>>
829 const std::shared_ptr<const rvsdg::Type> & lengthType,
830 const size_t numMemoryStates)
831 {
832 const auto bitType = std::dynamic_pointer_cast<const rvsdg::BitType>(lengthType);
833 if (!bitType || (bitType->nbits() != 32 && bitType->nbits() != 64))
834 {
835 throw std::runtime_error("lengthType must be a 32-bit or 64-bit integer type");
836 }
837
838 std::vector<std::shared_ptr<const rvsdg::Type>> types = { PointerType::Create(),
840 lengthType };
841 types.insert(types.end(), numMemoryStates, MemoryStateType::Create());
842 return types;
843 }
844
845 static std::vector<std::shared_ptr<const rvsdg::Type>>
847 {
848 // The memmove() standard C library call has as return type void*, but LLVM models the
849 // function call nevertheless as:
850 // call void @llvm.memmove.p0.p0.i64(ptr %0, i8 0, i64 8, i1 false)
851 //
852 // LLVM simply hands in register %7 (dest pointer) to all users of the return type of memmove().
853 // Thus, we only need state types as result types of the operation here.
855 }
856};
857
865{
866public:
867 ~UMaxOperation() noexcept override;
868
869 explicit UMaxOperation(const std::shared_ptr<const rvsdg::Type> & type)
870 : SimpleOperation({ type, type }, { type })
871 {
872 checkType(type);
873 }
874
875 bool
876 operator==(const Operation & other) const noexcept override;
877
878 std::string
879 debug_string() const override;
880
881 [[nodiscard]] std::unique_ptr<Operation>
882 copy() const override;
883
884 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
885 getType() const noexcept
886 {
887 return result(0);
888 }
889
890 static std::unique_ptr<ThreeAddressCode>
891 createTac(const Variable & operand1, const Variable & operand2)
892 {
893 auto operation = std::make_unique<UMaxOperation>(operand1.Type());
894 return ThreeAddressCode::create(std::move(operation), { &operand1, &operand2 });
895 }
896
897 static rvsdg::SimpleNode &
898 createNode(rvsdg::Output & operand1, rvsdg::Output & operand2)
899 {
901 {
902 &operand1,
903 &operand2,
904 },
905 operand1.Type());
906 }
907
908private:
909 static void
910 checkType(const std::shared_ptr<const rvsdg::Type> & type);
911};
912
920{
921public:
922 ~SMinOperation() noexcept override;
923
924 explicit SMinOperation(const std::shared_ptr<const rvsdg::Type> & type)
925 : SimpleOperation({ type, type }, { type })
926 {
927 checkType(type);
928 }
929
930 bool
931 operator==(const Operation & other) const noexcept override;
932
933 std::string
934 debug_string() const override;
935
936 [[nodiscard]] std::unique_ptr<Operation>
937 copy() const override;
938
939 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
940 getType() const noexcept
941 {
942 return result(0);
943 }
944
945 static std::unique_ptr<ThreeAddressCode>
946 createTac(const Variable & operand1, const Variable & operand2)
947 {
948 auto operation = std::make_unique<SMinOperation>(operand1.Type());
949 return ThreeAddressCode::create(std::move(operation), { &operand1, &operand2 });
950 }
951
952 static rvsdg::SimpleNode &
953 createNode(rvsdg::Output & operand1, rvsdg::Output & operand2)
954 {
956 {
957 &operand1,
958 &operand2,
959 },
960 operand1.Type());
961 }
962
963private:
964 static void
965 checkType(const std::shared_ptr<const rvsdg::Type> & type);
966};
967
975{
976public:
977 ~UMinOperation() noexcept override;
978
979 explicit UMinOperation(const std::shared_ptr<const rvsdg::Type> & type)
980 : SimpleOperation({ type, type }, { type })
981 {
982 checkType(type);
983 }
984
985 bool
986 operator==(const Operation & other) const noexcept override;
987
988 std::string
989 debug_string() const override;
990
991 [[nodiscard]] std::unique_ptr<Operation>
992 copy() const override;
993
994 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
995 getType() const noexcept
996 {
997 return result(0);
998 }
999
1000 static std::unique_ptr<ThreeAddressCode>
1001 createTac(const Variable & operand1, const Variable & operand2)
1002 {
1003 auto operation = std::make_unique<UMinOperation>(operand1.Type());
1004 return ThreeAddressCode::create(std::move(operation), { &operand1, &operand2 });
1005 }
1006
1007 static rvsdg::SimpleNode &
1009 {
1011 {
1012 &operand1,
1013 &operand2,
1014 },
1015 operand1.Type());
1016 }
1017
1018private:
1019 static void
1020 checkType(const std::shared_ptr<const rvsdg::Type> & type);
1021};
1022
1030{
1031public:
1032 ~FAbsOperation() noexcept override;
1033
1034 explicit FAbsOperation(const std::shared_ptr<const rvsdg::Type> & type)
1035 : UnaryOperation(type, type)
1036 {
1037 checkType(type);
1038 }
1039
1040 bool
1041 operator==(const Operation & other) const noexcept override;
1042
1043 std::string
1044 debug_string() const override;
1045
1046 [[nodiscard]] std::unique_ptr<Operation>
1047 copy() const override;
1048
1049 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
1050 getType() const noexcept
1051 {
1052 return result(0);
1053 }
1054
1055 static std::unique_ptr<ThreeAddressCode>
1056 createTac(const Variable & operand)
1057 {
1058 auto operation = std::make_unique<FAbsOperation>(operand.Type());
1059 return ThreeAddressCode::create(std::move(operation), { &operand });
1060 }
1061
1062 static rvsdg::SimpleNode &
1064 {
1066 {
1067 &operand,
1068 },
1069 operand.Type());
1070 }
1071
1072private:
1073 static void
1074 checkType(const std::shared_ptr<const rvsdg::Type> & type);
1075};
1076
1084{
1085public:
1086 ~AbsOperation() noexcept override;
1087
1088 explicit AbsOperation(const std::shared_ptr<const rvsdg::Type> & type)
1089 : SimpleOperation({ type, rvsdg::BitType::Create(1) }, { type })
1090 {
1091 checkType(type);
1092 }
1093
1094 bool
1095 operator==(const Operation & other) const noexcept override;
1096
1097 std::string
1098 debug_string() const override;
1099
1100 [[nodiscard]] std::unique_ptr<Operation>
1101 copy() const override;
1102
1103 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
1104 getType() const noexcept
1105 {
1106 return result(0);
1107 }
1108
1109 static std::unique_ptr<ThreeAddressCode>
1110 createTac(const Variable & operand1, const Variable & operand2)
1111 {
1112 auto operation = std::make_unique<AbsOperation>(operand1.Type());
1113 return ThreeAddressCode::create(std::move(operation), { &operand1, &operand2 });
1114 }
1115
1116 static rvsdg::SimpleNode &
1118 {
1120 {
1121 &operand1,
1122 &operand2,
1123 },
1124 operand1.Type());
1125 }
1126
1127private:
1128 static void
1129 checkType(const std::shared_ptr<const rvsdg::Type> & type);
1130};
1131
1139{
1140public:
1141 ~SMaxOperation() noexcept override;
1142
1143 explicit SMaxOperation(const std::shared_ptr<const rvsdg::Type> & type)
1144 : SimpleOperation({ type, type }, { type })
1145 {
1146 checkType(type);
1147 }
1148
1149 bool
1150 operator==(const Operation & other) const noexcept override;
1151
1152 std::string
1153 debug_string() const override;
1154
1155 [[nodiscard]] std::unique_ptr<Operation>
1156 copy() const override;
1157
1158 [[nodiscard]] std::shared_ptr<const rvsdg::Type>
1159 getType() const noexcept
1160 {
1161 return result(0);
1162 }
1163
1164 static std::unique_ptr<ThreeAddressCode>
1165 createTac(const Variable & operand1, const Variable & operand2)
1166 {
1167 auto operation = std::make_unique<SMaxOperation>(operand1.Type());
1168 return ThreeAddressCode::create(std::move(operation), { &operand1, &operand2 });
1169 }
1170
1171 static rvsdg::SimpleNode &
1173 {
1175 {
1176 &operand1,
1177 &operand2,
1178 },
1179 operand1.Type());
1180 }
1181
1182private:
1183 static void
1184 checkType(const std::shared_ptr<const rvsdg::Type> & type);
1185};
1186
1187}
1188
1189#endif
~AbsOperation() noexcept override
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand1, rvsdg::Output &operand2)
bool operator==(const Operation &other) const noexcept override
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand1, const Variable &operand2)
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
std::unique_ptr< Operation > copy() const override
std::string debug_string() const override
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand)
std::string debug_string() const override
bool operator==(const Operation &other) const noexcept override
~FAbsOperation() noexcept override
std::unique_ptr< Operation > copy() const override
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand)
static std::shared_ptr< const IOStateType > Create()
Definition types.cpp:343
std::unique_ptr< Operation > copy() const override
MemCpyNonVolatileOperation(std::shared_ptr< const rvsdg::Type > lengthType, size_t numMemoryStates)
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(size_t numMemoryStates)
size_t NumMemoryStates() const noexcept override
static std::vector< rvsdg::Output * > create(rvsdg::Output *destination, rvsdg::Output *source, rvsdg::Output *length, const std::vector< rvsdg::Output * > &memoryStates)
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > length, size_t numMemoryStates)
bool operator==(const Operation &other) const noexcept override
static rvsdg::SimpleNode & createNode(rvsdg::Output &destination, rvsdg::Output &source, rvsdg::Output &length, const std::vector< rvsdg::Output * > &memoryStates)
static std::unique_ptr< llvm::ThreeAddressCode > create(const Variable *destination, const Variable *source, const Variable *length, const std::vector< const Variable * > &memoryStates)
MemCpyOperation(const std::vector< std::shared_ptr< const rvsdg::Type > > &operandTypes, const std::vector< std::shared_ptr< const rvsdg::Type > > &resultTypes)
const rvsdg::BitType & LengthType() const noexcept
virtual size_t NumMemoryStates() const noexcept=0
static rvsdg::Input & sourceInput(const rvsdg::Node &node) noexcept
static rvsdg::Output & mapMemoryStateInputToOutput(const rvsdg::Input &input)
static rvsdg::Node::OutputIteratorRange memoryStateOutputs(const rvsdg::Node &node) noexcept
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & countInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & mapMemoryStateOutputToInput(const rvsdg::Output &output)
static rvsdg::Input & getIOStateInput(const rvsdg::Node &node) noexcept
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateResultTypes(size_t numMemoryStates)
std::unique_ptr< Operation > copy() const override
static rvsdg::Output & getIOStateOutput(const rvsdg::Node &node)
~MemCpyVolatileOperation() noexcept override
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &destination, rvsdg::Output &source, rvsdg::Output &length, rvsdg::Output &ioState, const std::vector< rvsdg::Output * > &memoryStates)
static std::unique_ptr< llvm::ThreeAddressCode > CreateThreeAddressCode(const Variable &destination, const Variable &source, const Variable &length, const Variable &ioState, const std::vector< const Variable * > &memoryStates)
bool operator==(const Operation &other) const noexcept override
size_t NumMemoryStates() const noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > CreateOperandTypes(std::shared_ptr< const rvsdg::Type > lengthType, size_t numMemoryStates)
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &dest, const Variable &src, const Variable &length, const std::vector< const Variable * > &memoryStates)
static std::vector< std::shared_ptr< const rvsdg::Type > > createResultTypes(size_t numMemoryStates)
size_t numMemoryStates() const noexcept override
std::unique_ptr< Operation > copy() const override
~MemMoveNonVolatileOperation() noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > checkAndCreateOperandTypes(const std::shared_ptr< const rvsdg::Type > &lengthType, const size_t numMemoryStates)
static rvsdg::SimpleNode & createNode(rvsdg::Output &dest, rvsdg::Output &src, rvsdg::Output &length, const std::vector< rvsdg::Output * > &memoryStates)
bool operator==(const Operation &other) const noexcept override
virtual size_t numMemoryStates() const noexcept=0
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Output & mapMemoryStateInputToOutput(const rvsdg::Input &input)
static rvsdg::Input & lengthInput(const rvsdg::Node &node) noexcept
static rvsdg::Node::OutputIteratorRange memoryStateOutputs(const rvsdg::Node &node) noexcept
const rvsdg::BitType & lengthType() const noexcept
static rvsdg::Input & mapMemoryStateOutputToInput(const rvsdg::Output &output)
MemMoveOperation(const std::vector< std::shared_ptr< const rvsdg::Type > > &operandTypes, const std::vector< std::shared_ptr< const rvsdg::Type > > &resultTypes)
static rvsdg::Input & sourceInput(const rvsdg::Node &node) noexcept
size_t numMemoryStates() const noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > createOperandTypes(const std::shared_ptr< const rvsdg::Type > &lengthType, const size_t numMemoryStates)
~MemSetNonVolatileOperation() noexcept override
static std::vector< std::shared_ptr< const rvsdg::Type > > createResultTypes(size_t numMemoryStates)
static rvsdg::SimpleNode & createNode(rvsdg::Output &destination, rvsdg::Output &value, rvsdg::Output &length, const std::vector< rvsdg::Output * > &memoryStates)
bool operator==(const Operation &other) const noexcept override
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &destination, const Variable &value, const Variable &length, const std::vector< const Variable * > &memoryStates)
std::unique_ptr< Operation > copy() const override
MemSetOperation(const std::vector< std::shared_ptr< const rvsdg::Type > > &operandTypes, const std::vector< std::shared_ptr< const rvsdg::Type > > &resultTypes)
static rvsdg::Input & valueInput(const rvsdg::Node &node) noexcept
virtual size_t numMemoryStates() const noexcept=0
static rvsdg::Input & lengthInput(const rvsdg::Node &node) noexcept
const rvsdg::BitType & lengthType() const noexcept
static rvsdg::Output & mapMemoryStateInputToOutput(const rvsdg::Input &input)
static rvsdg::Node::OutputIteratorRange memoryStateOutputs(const rvsdg::Node &node) noexcept
static rvsdg::Input & mapMemoryStateOutputToInput(const rvsdg::Output &output)
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static std::shared_ptr< const MemoryStateType > Create()
Definition types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition types.cpp:45
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
std::string debug_string() const override
~SMaxOperation() noexcept override
std::unique_ptr< Operation > copy() const override
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand1, rvsdg::Output &operand2)
bool operator==(const Operation &other) const noexcept override
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand1, const Variable &operand2)
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand1, const Variable &operand2)
~SMinOperation() noexcept override
bool operator==(const Operation &other) const noexcept override
std::string debug_string() const override
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand1, rvsdg::Output &operand2)
std::unique_ptr< Operation > copy() const override
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition tac.hpp:155
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand1, const Variable &operand2)
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
std::unique_ptr< Operation > copy() const override
std::string debug_string() const override
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand1, rvsdg::Output &operand2)
bool operator==(const Operation &other) const noexcept override
~UMaxOperation() noexcept override
static std::unique_ptr< ThreeAddressCode > createTac(const Variable &operand1, const Variable &operand2)
std::unique_ptr< Operation > copy() const override
~UMinOperation() noexcept override
static rvsdg::SimpleNode & createNode(rvsdg::Output &operand1, rvsdg::Output &operand2)
std::string debug_string() const override
std::shared_ptr< const rvsdg::Type > getType() const noexcept
static void checkType(const std::shared_ptr< const rvsdg::Type > &type)
bool operator==(const Operation &other) const noexcept override
const std::shared_ptr< const jlm::rvsdg::Type > Type() const noexcept
Definition variable.hpp:62
static std::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
Definition type.cpp:45
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
size_t index() const noexcept
Definition node.hpp:274
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
const std::shared_ptr< const rvsdg::Type > & argument(size_t index) const noexcept
Definition operation.cpp:23
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
Unary operator.
Definition unary.hpp:24
UnaryOperation(std::shared_ptr< const Type > operand, std::shared_ptr< const Type > result)
Definition unary.hpp:28
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872