Jlm
hls.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2021 David Metz <david.c.metz@ntnu.no>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #ifndef JLM_HLS_IR_HLS_HPP
7 #define JLM_HLS_IR_HLS_HPP
8 
10 #include <jlm/llvm/ir/types.hpp>
11 #include <jlm/rvsdg/control.hpp>
12 #include <jlm/rvsdg/operation.hpp>
15 #include <jlm/util/common.hpp>
16 
17 #include <memory>
18 #include <utility>
19 
20 namespace jlm::hls
21 {
25 [[nodiscard]] size_t
27 
28 int
30 
32 {
33 public:
34  ~BranchOperation() noexcept override;
35 
37  size_t nalternatives,
38  const std::shared_ptr<const jlm::rvsdg::Type> & type,
39  bool loop)
41  { rvsdg::ControlType::Create(nalternatives), type },
42  { nalternatives, type }),
43  loop(loop)
44  {}
45 
46  bool
47  operator==(const Operation & other) const noexcept override
48  {
49  auto ot = dynamic_cast<const BranchOperation *>(&other);
50  // check predicate and value
51  return ot && ot->loop == loop && *ot->argument(0) == *argument(0)
52  && *ot->result(0) == *result(0);
53  }
54 
55  std::string
56  debug_string() const override
57  {
58  return "HLS_BRANCH";
59  }
60 
61  [[nodiscard]] std::unique_ptr<Operation>
62  copy() const override
63  {
64  return std::make_unique<BranchOperation>(*this);
65  }
66 
67  static std::vector<jlm::rvsdg::Output *>
68  create(jlm::rvsdg::Output & predicate, jlm::rvsdg::Output & value, bool loop = false)
69  {
70  auto ctl = std::dynamic_pointer_cast<const rvsdg::ControlType>(predicate.Type());
71  if (!ctl)
72  throw util::Error("Predicate needs to be a control type.");
73 
74  return outputs(&rvsdg::CreateOpNode<BranchOperation>(
75  { &predicate, &value },
76  ctl->nalternatives(),
77  value.Type(),
78  loop));
79  }
80 
81  bool loop; // only used for dot output
82 };
83 
97 {
98 public:
99  ~ForkOperation() noexcept override;
100 
107  ForkOperation(size_t nalternatives, const std::shared_ptr<const jlm::rvsdg::Type> & type)
108  : SimpleOperation({ type }, { nalternatives, type })
109  {}
110 
119  size_t nalternatives,
120  const std::shared_ptr<const jlm::rvsdg::Type> & type,
121  bool isConstant)
122  : SimpleOperation({ type }, { nalternatives, type }),
123  IsConstant_(isConstant)
124  {}
125 
126  bool
127  operator==(const Operation & other) const noexcept override
128  {
129  const auto forkOp = dynamic_cast<const ForkOperation *>(&other);
130  // check predicate and value
131  return forkOp && *forkOp->argument(0) == *argument(0) && forkOp->nresults() == nresults()
132  && forkOp->IsConstant() == IsConstant_;
133  }
134 
139  std::string
140  debug_string() const override
141  {
142  return IsConstant() ? "HLS_CFORK" : "HLS_FORK";
143  }
144 
145  [[nodiscard]] std::unique_ptr<Operation>
146  copy() const override
147  {
148  return std::make_unique<ForkOperation>(*this);
149  }
150 
160  static std::vector<jlm::rvsdg::Output *>
161  create(size_t nalternatives, jlm::rvsdg::Output & value, bool isConstant = false)
162  {
163  return outputs(
164  &rvsdg::CreateOpNode<ForkOperation>({ &value }, nalternatives, value.Type(), isConstant));
165  }
166 
176  static rvsdg::Node &
177  CreateNode(const size_t numResults, rvsdg::Output & operand, const bool isConstant = false)
178  {
179  return rvsdg::CreateOpNode<ForkOperation>({ &operand }, numResults, operand.Type(), isConstant);
180  }
181 
188  [[nodiscard]] bool
189  IsConstant() const noexcept
190  {
191  return IsConstant_;
192  }
193 
194 private:
195  bool IsConstant_ = false;
196 };
197 
199 {
200 public:
201  ~MuxOperation() noexcept override;
202 
204  size_t nalternatives,
205  const std::shared_ptr<const jlm::rvsdg::Type> & type,
206  bool discarding,
207  bool loop)
208  : SimpleOperation(create_typevector(nalternatives, type), { type }),
210  loop(loop)
211  {}
212 
213  bool
214  operator==(const Operation & other) const noexcept override
215  {
216  const auto ot = dynamic_cast<const MuxOperation *>(&other);
217  // check predicate and value
218  return ot && *ot->argument(0) == *argument(0) && *ot->result(0) == *result(0)
219  && ot->discarding == discarding;
220  }
221 
222  std::string
223  debug_string() const override
224  {
225  return discarding ? "HLS_DMUX" : "HLS_NDMUX";
226  }
227 
228  [[nodiscard]] std::unique_ptr<Operation>
229  copy() const override
230  {
231  return std::make_unique<MuxOperation>(*this);
232  }
233 
234  static std::vector<jlm::rvsdg::Output *>
236  jlm::rvsdg::Output & predicate,
237  const std::vector<jlm::rvsdg::Output *> & alternatives,
238  bool discarding,
239  bool loop = false)
240  {
241  if (alternatives.empty())
242  throw util::Error("Insufficient number of operands.");
243  auto ctl = std::dynamic_pointer_cast<const rvsdg::ControlType>(predicate.Type());
244  if (!ctl)
245  throw util::Error("Predicate needs to be a control type.");
246  if (alternatives.size() != ctl->nalternatives())
247  throw util::Error("Alternatives and predicate do not match.");
248 
249  auto operands = std::vector<jlm::rvsdg::Output *>();
250  operands.push_back(&predicate);
251  operands.insert(operands.end(), alternatives.begin(), alternatives.end());
252  return outputs(&rvsdg::CreateOpNode<MuxOperation>(
253  operands,
254  alternatives.size(),
255  alternatives.front()->Type(),
256  discarding,
257  loop));
258  }
259 
261  bool loop; // used only for dot output
262 private:
263  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
264  create_typevector(size_t nalternatives, std::shared_ptr<const jlm::rvsdg::Type> type)
265  {
266  auto vec =
267  std::vector<std::shared_ptr<const jlm::rvsdg::Type>>(nalternatives + 1, std::move(type));
268  vec[0] = rvsdg::ControlType::Create(nalternatives);
269  return vec;
270  }
271 };
272 
274 {
275 public:
276  ~SinkOperation() noexcept override;
277 
278  explicit SinkOperation(const std::shared_ptr<const jlm::rvsdg::Type> & type)
279  : SimpleOperation({ type }, {})
280  {}
281 
282  bool
283  operator==(const Operation & other) const noexcept override
284  {
285  const auto ot = dynamic_cast<const SinkOperation *>(&other);
286  return ot && *ot->argument(0) == *argument(0);
287  }
288 
289  std::string
290  debug_string() const override
291  {
292  return "HLS_SINK";
293  }
294 
295  [[nodiscard]] std::unique_ptr<Operation>
296  copy() const override
297  {
298  return std::make_unique<SinkOperation>(*this);
299  }
300 
301  static std::vector<jlm::rvsdg::Output *>
303  {
304  return outputs(&rvsdg::CreateOpNode<SinkOperation>({ &value }, value.Type()));
305  }
306 };
307 
309 {
310 public:
311  ~PredicateBufferOperation() noexcept override;
312 
313  explicit PredicateBufferOperation(const std::shared_ptr<const rvsdg::ControlType> & type)
314  : SimpleOperation({ type }, { type })
315  {}
316 
317  bool
318  operator==(const Operation & other) const noexcept override
319  {
320  const auto ot = dynamic_cast<const PredicateBufferOperation *>(&other);
321  return ot && *ot->result(0) == *result(0);
322  }
323 
324  std::string
325  debug_string() const override
326  {
327  return "HLS_PRED_BUF";
328  }
329 
330  [[nodiscard]] std::unique_ptr<Operation>
331  copy() const override
332  {
333  return std::make_unique<PredicateBufferOperation>(*this);
334  }
335 
336  static std::vector<jlm::rvsdg::Output *>
338  {
339  auto ctl = std::dynamic_pointer_cast<const rvsdg::ControlType>(predicate.Type());
340  if (!ctl)
341  throw util::Error("Predicate needs to be a control type.");
342 
343  return outputs(&rvsdg::CreateOpNode<PredicateBufferOperation>({ &predicate }, ctl));
344  }
345 };
346 
348 {
349 public:
350  ~LoopConstantBufferOperation() noexcept override;
351 
353  const std::shared_ptr<const rvsdg::ControlType> & ctltype,
354  const std::shared_ptr<const jlm::rvsdg::Type> & type)
355  : SimpleOperation({ ctltype, type }, { type })
356  {}
357 
358  bool
359  operator==(const Operation & other) const noexcept override
360  {
361  const auto ot = dynamic_cast<const LoopConstantBufferOperation *>(&other);
362  return ot && *ot->result(0) == *result(0) && *ot->argument(0) == *argument(0);
363  }
364 
365  std::string
366  debug_string() const override
367  {
368  return "HLS_LOOP_CONST_BUF";
369  }
370 
371  [[nodiscard]] std::unique_ptr<Operation>
372  copy() const override
373  {
374  return std::make_unique<LoopConstantBufferOperation>(*this);
375  }
376 
377  static std::vector<jlm::rvsdg::Output *>
379  {
380  auto ctl = std::dynamic_pointer_cast<const rvsdg::ControlType>(predicate.Type());
381  if (!ctl)
382  throw util::Error("Predicate needs to be a control type.");
383 
384  return outputs(&rvsdg::CreateOpNode<LoopConstantBufferOperation>(
385  { &predicate, &value },
386  ctl,
387  value.Type()));
388  }
389 };
390 
392 {
393 public:
394  ~BufferOperation() noexcept override;
395 
397  const std::shared_ptr<const jlm::rvsdg::Type> & type,
398  size_t capacity,
399  bool pass_through)
400  : SimpleOperation({ type }, { type }),
401  Capacity_(capacity),
402  IsPassThrough_(pass_through)
403  {}
404 
405  [[nodiscard]] std::size_t
406  Capacity() const noexcept
407  {
408  return Capacity_;
409  }
410 
411  [[nodiscard]] bool
412  IsPassThrough() const noexcept
413  {
414  return IsPassThrough_;
415  }
416 
417  bool
418  operator==(const Operation & other) const noexcept override
419  {
420  const auto ot = dynamic_cast<const BufferOperation *>(&other);
421  return ot && ot->Capacity() == Capacity() && ot->IsPassThrough() == IsPassThrough()
422  && *ot->result(0) == *result(0);
423  }
424 
425  [[nodiscard]] std::string
426  debug_string() const override
427  {
428  return util::strfmt("HLS_BUF_", (IsPassThrough() ? "P_" : ""), Capacity());
429  }
430 
431  [[nodiscard]] std::unique_ptr<Operation>
432  copy() const override
433  {
434  return std::make_unique<BufferOperation>(*this);
435  }
436 
437  static std::vector<jlm::rvsdg::Output *>
438  create(jlm::rvsdg::Output & value, size_t capacity, bool pass_through = false)
439  {
440  return outputs(
441  &rvsdg::CreateOpNode<BufferOperation>({ &value }, value.Type(), capacity, pass_through));
442  }
443 
444 private:
445  std::size_t Capacity_;
447 };
448 
449 class TriggerType final : public rvsdg::Type
450 {
451 public:
452  ~TriggerType() noexcept override;
453 
454  TriggerType() = default;
455 
456  std::string
457  debug_string() const override
458  {
459  return "trigger";
460  };
461 
462  bool
463  operator==(const Type & other) const noexcept override
464  {
465  return jlm::rvsdg::is<TriggerType>(other);
466  };
467 
468  [[nodiscard]] std::size_t
469  ComputeHash() const noexcept override;
470 
471  rvsdg::TypeKind
472  Kind() const noexcept override;
473 
474  static std::shared_ptr<const TriggerType>
475  Create();
476 };
477 
478 class TriggerOperation final : public rvsdg::SimpleOperation
479 {
480 public:
481  ~TriggerOperation() noexcept override;
482 
483  explicit TriggerOperation(const std::shared_ptr<const rvsdg::Type> & type)
485  {}
486 
487  bool
488  operator==(const Operation & other) const noexcept override
489  {
490  const auto ot = dynamic_cast<const TriggerOperation *>(&other);
491  // check predicate and value
492  return ot && *ot->argument(1) == *argument(1) && *ot->result(0) == *result(0);
493  }
494 
495  std::string
496  debug_string() const override
497  {
498  return "HLS_TRIGGER";
499  }
500 
501  [[nodiscard]] std::unique_ptr<Operation>
502  copy() const override
503  {
504  return std::make_unique<TriggerOperation>(*this);
505  }
506 
507  static std::vector<jlm::rvsdg::Output *>
509  {
510  if (!rvsdg::is<TriggerType>(tg.Type()))
511  throw util::Error("Trigger needs to be a TriggerType.");
512 
513  return outputs(&rvsdg::CreateOpNode<TriggerOperation>({ &tg, &value }, value.Type()));
514  }
515 };
516 
518 {
519  size_t _id;
520 
521 public:
522  ~PrintOperation() noexcept override;
523 
524  explicit PrintOperation(const std::shared_ptr<const rvsdg::Type> & type)
525  : SimpleOperation({ type }, { type })
526  {
527  static size_t common_id{ 0 };
528  _id = common_id++;
529  }
530 
531  bool
532  operator==(const Operation &) const noexcept override
533  {
534  // print nodes are intentionally distinct
535  return false;
536  }
537 
538  std::string
539  debug_string() const override
540  {
541  return util::strfmt("HLS_PRINT_", _id);
542  }
543 
544  size_t
545  id() const
546  {
547  return _id;
548  }
549 
550  [[nodiscard]] std::unique_ptr<Operation>
551  copy() const override
552  {
553  return std::make_unique<PrintOperation>(*this);
554  }
555 
556  static std::vector<jlm::rvsdg::Output *>
558  {
559  return outputs(&rvsdg::CreateOpNode<PrintOperation>({ &value }, value.Type()));
560  }
561 };
562 
564 {
565 public:
566  ~LoopOperation() noexcept override;
567 
568  std::string
569  debug_string() const override
570  {
571  return "HLS_LOOP";
572  }
573 
574  [[nodiscard]] std::unique_ptr<Operation>
575  copy() const override
576  {
577  return std::make_unique<LoopOperation>(*this);
578  }
579 };
580 
581 class BackEdgeArgument;
582 class BackEdgeResult;
583 class LoopNode;
584 
589 {
590  friend LoopNode;
591 
593  rvsdg::Region & region,
594  rvsdg::StructuralInput & input,
595  const std::shared_ptr<const rvsdg::Type> type)
596  : rvsdg::RegionArgument(&region, &input, std::move(type))
597  {}
598 
599 public:
600  ~EntryArgument() noexcept override;
601 
602  EntryArgument &
603  Copy(rvsdg::Region & region, rvsdg::StructuralInput * input) const override;
604 
605  // FIXME: This should not be public, but we currently still have some transformations that use
606  // this one. Make it eventually private.
607  static EntryArgument &
609  rvsdg::Region & region,
610  rvsdg::StructuralInput & input,
611  const std::shared_ptr<const rvsdg::Type> type)
612  {
613  std::unique_ptr<EntryArgument> argument(new EntryArgument(region, input, std::move(type)));
614  return static_cast<EntryArgument &>(region.addArgument(std::move(argument)));
615  }
616 };
617 
619 {
620  friend LoopNode;
622 
623  BackEdgeArgument(rvsdg::Region * region, const std::shared_ptr<const jlm::rvsdg::Type> & type)
624  : rvsdg::RegionArgument(region, nullptr, type),
625  result_(nullptr)
626  {}
627 
628 public:
629  ~BackEdgeArgument() noexcept override = default;
630 
632  result()
633  {
634  return result_;
635  }
636 
638  Copy(rvsdg::Region & region, rvsdg::StructuralInput * input) const override;
639 
640  static BackEdgeArgument &
641  create(rvsdg::Region * region, std::shared_ptr<const jlm::rvsdg::Type> type)
642  {
643  std::unique_ptr<BackEdgeArgument> argument(new BackEdgeArgument(region, std::move(type)));
644  return static_cast<BackEdgeArgument &>(region->addArgument(std::move(argument)));
645  }
646 
648 };
649 
651 {
652  friend LoopNode;
654 
656  : rvsdg::RegionResult(origin->region(), origin, nullptr, origin->Type()),
657  argument_(nullptr)
658  {}
659 
660 public:
661  ~BackEdgeResult() override = default;
662 
664  argument() const
665  {
666  return argument_;
667  }
668 
670  Copy(rvsdg::Output & origin, rvsdg::StructuralOutput * output) const override;
671 
672  static BackEdgeResult &
674  {
675  std::unique_ptr<BackEdgeResult> result(new BackEdgeResult(origin));
676  return static_cast<BackEdgeResult &>(origin->region()->addResult(std::move(result)));
677  }
678 
680 };
681 
685 class ExitResult final : public rvsdg::RegionResult
686 {
687  friend LoopNode;
688 
690 
691 public:
692  ~ExitResult() noexcept override;
693 
694  ExitResult &
695  Copy(rvsdg::Output & origin, rvsdg::StructuralOutput * output) const override;
696 
697  // FIXME: This should not be public, but we currently still have some transformations that use
698  // this one. Make it eventually private.
699  static ExitResult &
700  Create(rvsdg::Output & origin, rvsdg::StructuralOutput & output)
701  {
702  std::unique_ptr<RegionResult> result(new ExitResult(origin, output));
703  return static_cast<ExitResult &>(origin.region()->addResult(std::move(result)));
704  }
705 };
706 
707 class LoopNode final : public rvsdg::StructuralNode
708 {
709 public:
710  ~LoopNode() noexcept override = default;
711 
712 private:
713  explicit LoopNode(rvsdg::Region * parent)
714  : StructuralNode(parent, 1)
715  {}
716 
717 public:
718  [[nodiscard]] const rvsdg::Operation &
719  GetOperation() const noexcept override;
720 
721  static LoopNode *
722  create(rvsdg::Region * parent, bool init = true);
723 
724  rvsdg::Region *
725  subregion() const noexcept
726  {
727  return StructuralNode::subregion(0);
728  }
729 
730  [[nodiscard]] rvsdg::RegionResult *
731  predicate() const noexcept
732  {
733  auto result = subregion()->result(0);
734  JLM_ASSERT(rvsdg::is<const rvsdg::ControlType>(result->Type()));
735  return result;
736  }
737 
738  rvsdg::Output &
739  GetPredicateBuffer() const noexcept
740  {
741  return *PredicateBuffer_;
742  }
743 
744  void
745  set_predicate(jlm::rvsdg::Output * p);
746 
748  add_backedge(std::shared_ptr<const jlm::rvsdg::Type> type);
749 
770  AddLoopVar(rvsdg::Output * origin, rvsdg::Output ** buffer = nullptr);
771 
778  rvsdg::Output *
779  addLoopConstant(rvsdg::Output * origin);
780 
788  rvsdg::Output *
789  addResponseInput(rvsdg::Output * origin);
790 
798  rvsdg::Output *
799  addRequestOutput(rvsdg::Output * origin);
800 
805  void
806  removeLoopOutput(rvsdg::StructuralOutput * output);
807 
812  void
813  removeLoopInput(rvsdg::StructuralInput * input);
814 
815  LoopNode *
816  copy(rvsdg::Region * region, rvsdg::SubstitutionMap & smap) const override;
817 
818 private:
819  rvsdg::Output * PredicateBuffer_{};
820 };
821 
822 class BundleType final : public rvsdg::Type
823 {
824 public:
825  ~BundleType() noexcept override;
826 
827  explicit BundleType(
828  const std::vector<std::pair<std::string, std::shared_ptr<const Type>>> elements)
829  : elements_(std::move(elements))
830  {}
831 
832  BundleType(const BundleType &) = default;
833 
834  BundleType(BundleType &&) = delete;
835 
836  BundleType &
837  operator=(const BundleType &) = delete;
838 
839  BundleType &
840  operator=(BundleType &&) = delete;
841 
842  bool
843  operator==(const jlm::rvsdg::Type & other) const noexcept override
844  {
845  auto type = dynamic_cast<const BundleType *>(&other);
846  // TODO: better comparison?
847  if (!type || type->elements_.size() != elements_.size())
848  {
849  return false;
850  }
851  for (size_t i = 0; i < elements_.size(); ++i)
852  {
853  if (type->elements_.at(i).first != elements_.at(i).first
854  || *type->elements_.at(i).second != *elements_.at(i).second)
855  {
856  return false;
857  }
858  }
859  return true;
860  };
861 
862  [[nodiscard]] std::size_t
863  ComputeHash() const noexcept override;
864 
865  rvsdg::TypeKind
866  Kind() const noexcept override;
867 
868  std::shared_ptr<const jlm::rvsdg::Type>
869  get_element_type(std::string element) const
870  {
871  for (size_t i = 0; i < elements_.size(); ++i)
872  {
873  if (elements_.at(i).first == element)
874  {
875  return elements_.at(i).second;
876  }
877  }
878  // TODO: do something different?
879  return {};
880  }
881 
882  [[nodiscard]] std::string
883  debug_string() const override
884  {
885  return "bundle";
886  };
887 
888  // private:
889  // TODO: fix memory leak
890  const std::vector<std::pair<std::string, std::shared_ptr<const jlm::rvsdg::Type>>> elements_;
891 };
892 
893 std::shared_ptr<const BundleType>
894 get_mem_req_type(std::shared_ptr<const rvsdg::Type> elementType, bool write);
895 
896 std::shared_ptr<const BundleType>
897 get_mem_res_type(std::shared_ptr<const jlm::rvsdg::Type> dataType);
898 
900 {
901 public:
902  ~LoadOperation() noexcept override;
903 
904  LoadOperation(const std::shared_ptr<const rvsdg::Type> & pointeeType, size_t numStates)
905  : SimpleOperation(
906  CreateInTypes(pointeeType, numStates),
907  CreateOutTypes(pointeeType, numStates))
908  {}
909 
910  bool
911  operator==(const Operation & other) const noexcept override
912  {
913  auto ot = dynamic_cast<const LoadOperation *>(&other);
914  // check predicate and value
915  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
916  }
917 
918  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
919  CreateInTypes(std::shared_ptr<const rvsdg::Type> pointeeType, size_t numStates)
920  {
921  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(
922  1,
923  llvm::PointerType::Create()); // addr
924  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
925  numStates,
927  types.insert(types.end(), states.begin(), states.end());
928  types.emplace_back(std::move(pointeeType)); // result
929  return types;
930  }
931 
932  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
933  CreateOutTypes(std::shared_ptr<const rvsdg::Type> pointeeType, size_t numStates)
934  {
935  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(1, std::move(pointeeType));
936  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
937  numStates,
939  types.insert(types.end(), states.begin(), states.end());
940  types.emplace_back(llvm::PointerType::Create()); // addr
941  return types;
942  }
943 
944  std::string
945  debug_string() const override
946  {
947  return "HLS_LOAD_" + argument(narguments() - 1)->debug_string();
948  }
949 
950  [[nodiscard]] std::unique_ptr<Operation>
951  copy() const override
952  {
953  return std::make_unique<LoadOperation>(*this);
954  }
955 
956  static std::vector<jlm::rvsdg::Output *>
958  jlm::rvsdg::Output & addr,
959  const std::vector<jlm::rvsdg::Output *> & states,
960  jlm::rvsdg::Output & load_result)
961  {
962  std::vector<jlm::rvsdg::Output *> inputs;
963  inputs.push_back(&addr);
964  inputs.insert(inputs.end(), states.begin(), states.end());
965  inputs.push_back(&load_result);
966  return outputs(&rvsdg::CreateOpNode<LoadOperation>(inputs, load_result.Type(), states.size()));
967  }
968 
969  [[nodiscard]] const llvm::PointerType &
970  GetPointerType() const noexcept
971  {
972  return *util::assertedCast<const llvm::PointerType>(argument(0).get());
973  }
974 
975  [[nodiscard]] std::shared_ptr<const rvsdg::Type>
976  GetLoadedType() const noexcept
977  {
978  return result(0);
979  }
980 };
981 
983 {
984 public:
985  ~AddressQueueOperation() noexcept override;
986 
988  const std::shared_ptr<const llvm::PointerType> & pointerType,
989  size_t capacity,
990  bool combinatorial)
991  : SimpleOperation(CreateInTypes(pointerType), CreateOutTypes(pointerType)),
992  combinatorial(combinatorial),
993  capacity(capacity)
994  {}
995 
996  bool
997  operator==(const Operation & other) const noexcept override
998  {
999  auto ot = dynamic_cast<const AddressQueueOperation *>(&other);
1000  // check predicate and value
1001  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1002  }
1003 
1004  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1005  CreateInTypes(std::shared_ptr<const llvm::PointerType> pointerType)
1006  {
1007  // check, enq
1008  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(2, std::move(pointerType));
1009  types.emplace_back(llvm::MemoryStateType::Create()); // deq
1010  return types;
1011  }
1012 
1013  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1014  CreateOutTypes(std::shared_ptr<const llvm::PointerType> pointerType)
1015  {
1016  return { std::move(pointerType) };
1017  }
1018 
1019  std::string
1020  debug_string() const override
1021  {
1022  if (combinatorial)
1023  {
1024  return "HLS_ADDR_QUEUE_COMB_" + argument(narguments() - 1)->debug_string();
1025  }
1026  return "HLS_ADDR_QUEUE_" + argument(narguments() - 1)->debug_string();
1027  }
1028 
1029  [[nodiscard]] std::unique_ptr<Operation>
1030  copy() const override
1031  {
1032  return std::make_unique<AddressQueueOperation>(*this);
1033  }
1034 
1035  static jlm::rvsdg::Output *
1037  jlm::rvsdg::Output & check,
1038  jlm::rvsdg::Output & enq,
1039  jlm::rvsdg::Output & deq,
1040  bool combinatorial,
1041  size_t capacity = 10)
1042  {
1043  return rvsdg::CreateOpNode<AddressQueueOperation>(
1044  { &check, &enq, &deq },
1045  std::dynamic_pointer_cast<const llvm::PointerType>(check.Type()),
1046  capacity,
1047  combinatorial)
1048  .output(0);
1049  }
1050 
1052  size_t capacity;
1053 };
1054 
1056 {
1057 public:
1058  ~StateGateOperation() noexcept override;
1059 
1060  StateGateOperation(const std::shared_ptr<const rvsdg::Type> & type, const size_t numStates)
1061  : SimpleOperation(CreateInOutTypes(type, numStates), CreateInOutTypes(type, numStates))
1062  {}
1063 
1064  bool
1065  operator==(const Operation & other) const noexcept override
1066  {
1067  auto ot = dynamic_cast<const StateGateOperation *>(&other);
1068  // check predicate and value
1069  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1070  }
1071 
1072  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1073  CreateInOutTypes(const std::shared_ptr<const jlm::rvsdg::Type> & type, size_t numStates)
1074  {
1075  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(1, type);
1076  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
1077  numStates,
1079  types.insert(types.end(), states.begin(), states.end());
1080  return types;
1081  }
1082 
1083  std::string
1084  debug_string() const override
1085  {
1086  return "HLS_STATE_GATE_" + argument(narguments() - 1)->debug_string();
1087  }
1088 
1089  [[nodiscard]] std::unique_ptr<Operation>
1090  copy() const override
1091  {
1092  return std::make_unique<StateGateOperation>(*this);
1093  }
1094 
1095  static std::vector<jlm::rvsdg::Output *>
1096  create(jlm::rvsdg::Output & addr, const std::vector<jlm::rvsdg::Output *> & states)
1097  {
1098  std::vector<jlm::rvsdg::Output *> inputs;
1099  inputs.push_back(&addr);
1100  inputs.insert(inputs.end(), states.begin(), states.end());
1101  return outputs(&rvsdg::CreateOpNode<StateGateOperation>(inputs, addr.Type(), states.size()));
1102  }
1103 };
1104 
1106 {
1107 public:
1108  ~DecoupledLoadOperation() noexcept override;
1109 
1110  DecoupledLoadOperation(const std::shared_ptr<const rvsdg::Type> & pointeeType, size_t capacity)
1111  : SimpleOperation(CreateInTypes(pointeeType), CreateOutTypes(pointeeType)),
1112  capacity(capacity)
1113  {}
1114 
1115  bool
1116  operator==(const Operation & other) const noexcept override
1117  {
1118  auto ot = dynamic_cast<const DecoupledLoadOperation *>(&other);
1119  // check predicate and value
1120  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1121  }
1122 
1123  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1124  CreateInTypes(std::shared_ptr<const rvsdg::Type> pointeeType)
1125  {
1126  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types = {
1128  pointeeType // result
1129  };
1130  return types;
1131  }
1132 
1133  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1134  CreateOutTypes(std::shared_ptr<const rvsdg::Type> pointeeType)
1135  {
1136  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types = {
1137  pointeeType,
1138  llvm::PointerType::Create() // addr
1139  };
1140  return types;
1141  }
1142 
1143  std::string
1144  debug_string() const override
1145  {
1146  return "HLS_DEC_LOAD_" + std::to_string(capacity) + "_"
1147  + argument(narguments() - 1)->debug_string();
1148  }
1149 
1150  [[nodiscard]] std::unique_ptr<Operation>
1151  copy() const override
1152  {
1153  return std::make_unique<DecoupledLoadOperation>(*this);
1154  }
1155 
1156  static std::vector<jlm::rvsdg::Output *>
1157  create(jlm::rvsdg::Output & addr, jlm::rvsdg::Output & load_result, size_t capacity)
1158  {
1159  std::vector<jlm::rvsdg::Output *> inputs;
1160  inputs.push_back(&addr);
1161  inputs.push_back(&load_result);
1162  JLM_ASSERT(capacity >= 1);
1163  return outputs(
1164  &rvsdg::CreateOpNode<DecoupledLoadOperation>(inputs, load_result.Type(), capacity));
1165  }
1166 
1167  [[nodiscard]] const llvm::PointerType &
1168  GetPointerType() const noexcept
1169  {
1170  return *util::assertedCast<const llvm::PointerType>(argument(0).get());
1171  }
1172 
1173  [[nodiscard]] std::shared_ptr<const rvsdg::Type>
1174  GetLoadedType() const noexcept
1175  {
1176  return result(0);
1177  }
1178 
1179  size_t capacity;
1180 };
1181 
1183 {
1184 public:
1185  ~MemoryResponseOperation() noexcept override;
1186 
1188  const std::vector<std::shared_ptr<const rvsdg::Type>> & output_types,
1189  int in_width)
1190  : SimpleOperation(CreateInTypes(in_width), CreateOutTypes(output_types))
1191  {}
1192 
1193  bool
1194  operator==(const Operation & other) const noexcept override
1195  {
1196  auto ot = dynamic_cast<const MemoryResponseOperation *>(&other);
1197  // check predicate and value
1198  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1199  }
1200 
1201  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1202  CreateInTypes(int in_width)
1203  {
1204  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types;
1205  types.emplace_back(get_mem_res_type(jlm::rvsdg::BitType::Create(in_width)));
1206  return types;
1207  }
1208 
1209  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1210  CreateOutTypes(const std::vector<std::shared_ptr<const rvsdg::Type>> & output_types)
1211  {
1212  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types;
1213  types.reserve(output_types.size());
1214  for (auto outputType : output_types)
1215  {
1216  types.emplace_back(outputType);
1217  }
1218  return types;
1219  }
1220 
1221  std::string
1222  debug_string() const override
1223  {
1224  return "HLS_MEM_RESP";
1225  }
1226 
1227  [[nodiscard]] std::unique_ptr<Operation>
1228  copy() const override
1229  {
1230  return std::make_unique<MemoryResponseOperation>(*this);
1231  }
1232 
1233  static std::vector<jlm::rvsdg::Output *>
1235  rvsdg::Output & result,
1236  const std::vector<std::shared_ptr<const rvsdg::Type>> & output_types,
1237  int in_width)
1238  {
1239  return outputs(
1240  &rvsdg::CreateOpNode<MemoryResponseOperation>({ &result }, output_types, in_width));
1241  }
1242 };
1243 
1245 {
1246 public:
1247  ~MemoryRequestOperation() noexcept override = default;
1248 
1250  const std::vector<std::shared_ptr<const rvsdg::Type>> & load_types,
1251  const std::vector<std::shared_ptr<const rvsdg::Type>> & store_types)
1252  : SimpleOperation(
1253  CreateInTypes(load_types, store_types),
1254  CreateOutTypes(load_types, store_types))
1255  {
1256  for (auto loadType : load_types)
1257  {
1258  LoadTypes_.emplace_back(loadType);
1259  }
1260  for (auto storeType : store_types)
1261  {
1262  StoreTypes_.emplace_back(storeType);
1263  }
1264  }
1265 
1267 
1268  bool
1269  operator==(const Operation & other) const noexcept override
1270  {
1271  auto ot = dynamic_cast<const MemoryRequestOperation *>(&other);
1272  // check predicate and value
1273  return ot && ot->narguments() == narguments()
1274  && (ot->narguments() == 0 || (*ot->argument(1) == *argument(1)))
1275  && ot->narguments() == narguments();
1276  }
1277 
1278  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1280  const std::vector<std::shared_ptr<const rvsdg::Type>> & load_types,
1281  const std::vector<std::shared_ptr<const rvsdg::Type>> & store_types)
1282  {
1283  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types;
1284  for (size_t i = 0; i < load_types.size(); i++)
1285  {
1286  types.emplace_back(llvm::PointerType::Create()); // addr
1287  }
1288  for (auto storeType : store_types)
1289  {
1290  types.emplace_back(llvm::PointerType::Create()); // addr
1291  types.emplace_back(storeType); // data
1292  }
1293  return types;
1294  }
1295 
1296  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1298  const std::vector<std::shared_ptr<const rvsdg::Type>> & load_types,
1299  const std::vector<std::shared_ptr<const rvsdg::Type>> & store_types)
1300  {
1301  int max_width = 0;
1302  for (auto tp : load_types)
1303  {
1304  auto sz = JlmSize(tp.get());
1305  max_width = sz > max_width ? sz : max_width;
1306  }
1307  for (auto tp : store_types)
1308  {
1309  auto sz = JlmSize(tp.get());
1310  max_width = sz > max_width ? sz : max_width;
1311  }
1312  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types;
1313  types.emplace_back(
1314  get_mem_req_type(jlm::rvsdg::BitType::Create(max_width), !store_types.empty()));
1315  return types;
1316  }
1317 
1318  std::string
1319  debug_string() const override
1320  {
1321  return "HLS_MEM_REQ";
1322  }
1323 
1324  [[nodiscard]] std::unique_ptr<Operation>
1325  copy() const override
1326  {
1327  return std::make_unique<MemoryRequestOperation>(*this);
1328  }
1329 
1330  static std::vector<jlm::rvsdg::Output *>
1332  const std::vector<jlm::rvsdg::Output *> & load_operands,
1333  const std::vector<std::shared_ptr<const rvsdg::Type>> & loadTypes,
1334  const std::vector<jlm::rvsdg::Output *> & store_operands,
1335  rvsdg::Region *)
1336  {
1337  // Stores have both addr and data operand
1338  // But we are only interested in the data operand type
1339  JLM_ASSERT(store_operands.size() % 2 == 0);
1340  std::vector<std::shared_ptr<const rvsdg::Type>> storeTypes;
1341  for (size_t i = 1; i < store_operands.size(); i += 2)
1342  {
1343  storeTypes.push_back(store_operands[i]->Type());
1344  }
1345  std::vector operands(load_operands);
1346  operands.insert(operands.end(), store_operands.begin(), store_operands.end());
1347  return outputs(&rvsdg::CreateOpNode<MemoryRequestOperation>(operands, loadTypes, storeTypes));
1348  }
1349 
1350  size_t
1351  get_nloads() const
1352  {
1353  return LoadTypes_.size();
1354  }
1355 
1356  const std::vector<std::shared_ptr<const rvsdg::Type>> *
1358  {
1359  return &LoadTypes_;
1360  }
1361 
1362  const std::vector<std::shared_ptr<const rvsdg::Type>> *
1364  {
1365  return &StoreTypes_;
1366  }
1367 
1368 private:
1369  std::vector<std::shared_ptr<const rvsdg::Type>> LoadTypes_;
1370  std::vector<std::shared_ptr<const rvsdg::Type>> StoreTypes_;
1371 };
1372 
1374 {
1375 public:
1376  ~StoreOperation() noexcept override;
1377 
1378  StoreOperation(const std::shared_ptr<const rvsdg::Type> & pointeeType, size_t numStates)
1379  : SimpleOperation(
1380  CreateInTypes(pointeeType, numStates),
1381  CreateOutTypes(pointeeType, numStates))
1382  {}
1383 
1384  bool
1385  operator==(const Operation & other) const noexcept override
1386  {
1387  auto ot = dynamic_cast<const StoreOperation *>(&other);
1388  // check predicate and value
1389  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1390  }
1391 
1392  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1393  CreateInTypes(const std::shared_ptr<const rvsdg::Type> & pointeeType, size_t numStates)
1394  {
1395  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(
1396  { llvm::PointerType::Create(), pointeeType });
1397  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
1398  numStates + 1,
1400  types.insert(types.end(), states.begin(), states.end());
1401  return types;
1402  }
1403 
1404  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1405  CreateOutTypes(const std::shared_ptr<const rvsdg::Type> & pointeeType, size_t numStates)
1406  {
1407  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(
1408  numStates,
1410  types.emplace_back(llvm::PointerType::Create()); // addr
1411  types.emplace_back(pointeeType); // data
1412  return types;
1413  }
1414 
1415  std::string
1416  debug_string() const override
1417  {
1418  return "HLS_STORE_" + argument(narguments() - 1)->debug_string();
1419  }
1420 
1421  [[nodiscard]] std::unique_ptr<Operation>
1422  copy() const override
1423  {
1424  return std::make_unique<StoreOperation>(*this);
1425  }
1426 
1427  static std::vector<jlm::rvsdg::Output *>
1429  jlm::rvsdg::Output & addr,
1430  jlm::rvsdg::Output & value,
1431  const std::vector<jlm::rvsdg::Output *> & states,
1432  jlm::rvsdg::Output & resp)
1433  {
1434  std::vector<jlm::rvsdg::Output *> inputs;
1435  inputs.push_back(&addr);
1436  inputs.push_back(&value);
1437  inputs.insert(inputs.end(), states.begin(), states.end());
1438  inputs.push_back(&resp);
1439  return outputs(&rvsdg::CreateOpNode<StoreOperation>(inputs, value.Type(), states.size()));
1440  }
1441 
1442  [[nodiscard]] const llvm::PointerType &
1443  GetPointerType() const noexcept
1444  {
1445  return *util::assertedCast<const llvm::PointerType>(argument(0).get());
1446  }
1447 
1448  [[nodiscard]] const rvsdg::Type &
1449  GetStoredType() const noexcept
1450  {
1451  return *argument(1).get();
1452  }
1453 };
1454 
1456 {
1457 public:
1458  ~LocalMemoryOperation() noexcept override;
1459 
1460  explicit LocalMemoryOperation(std::shared_ptr<const llvm::ArrayType> at)
1461  : SimpleOperation({}, CreateOutTypes(std::move(at)))
1462  {}
1463 
1464  bool
1465  operator==(const Operation &) const noexcept override
1466  {
1467  return false;
1468  }
1469 
1470  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1471  CreateOutTypes(std::shared_ptr<const llvm::ArrayType> at)
1472  {
1473  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(2, std::move(at));
1474  return types;
1475  }
1476 
1477  std::string
1478  debug_string() const override
1479  {
1480  return "HLS_LOCAL_MEM_" + result(0)->debug_string();
1481  }
1482 
1483  [[nodiscard]] std::unique_ptr<Operation>
1484  copy() const override
1485  {
1486  return std::make_unique<LocalMemoryOperation>(*this);
1487  }
1488 
1489  static std::vector<jlm::rvsdg::Output *>
1490  create(std::shared_ptr<const llvm::ArrayType> at, rvsdg::Region * region)
1491  {
1492  return outputs(&rvsdg::CreateOpNode<LocalMemoryOperation>(*region, std::move(at)));
1493  }
1494 };
1495 
1497 {
1498 public:
1499  ~LocalMemoryResponseOperation() noexcept override;
1500 
1501  LocalMemoryResponseOperation(const std::shared_ptr<const llvm::ArrayType> & at, size_t resp_count)
1502  : SimpleOperation({ at }, CreateOutTypes(at, resp_count))
1503  {}
1504 
1505  bool
1506  operator==(const Operation & other) const noexcept override
1507  {
1508  auto ot = dynamic_cast<const LocalMemoryResponseOperation *>(&other);
1509  // check predicate and value
1510  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1511  }
1512 
1513  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1514  CreateOutTypes(const std::shared_ptr<const jlm::llvm::ArrayType> & at, size_t resp_count)
1515  {
1516  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(resp_count, at->GetElementType());
1517  return types;
1518  }
1519 
1520  std::string
1521  debug_string() const override
1522  {
1523  return "HLS_LOCAL_MEM_RESP";
1524  }
1525 
1526  [[nodiscard]] std::unique_ptr<Operation>
1527  copy() const override
1528  {
1529  return std::make_unique<LocalMemoryResponseOperation>(*this);
1530  }
1531 
1532  static std::vector<jlm::rvsdg::Output *>
1533  create(jlm::rvsdg::Output & mem, size_t resp_count)
1534  {
1535  return outputs(&rvsdg::CreateOpNode<LocalMemoryResponseOperation>(
1536  { &mem },
1537  std::dynamic_pointer_cast<const llvm::ArrayType>(mem.Type()),
1538  resp_count));
1539  }
1540 };
1541 
1543 {
1544 public:
1545  ~LocalLoadOperation() noexcept override;
1546 
1547  LocalLoadOperation(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1548  : SimpleOperation(CreateInTypes(valuetype, numStates), CreateOutTypes(valuetype, numStates))
1549  {}
1550 
1551  bool
1552  operator==(const Operation & other) const noexcept override
1553  {
1554  auto ot = dynamic_cast<const LocalLoadOperation *>(&other);
1555  // check predicate and value
1556  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1557  }
1558 
1559  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1560  CreateInTypes(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1561  {
1562  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(1, jlm::rvsdg::BitType::Create(64));
1563  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
1564  numStates,
1566  types.insert(types.end(), states.begin(), states.end());
1567  types.emplace_back(valuetype); // result
1568  return types;
1569  }
1570 
1571  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1572  CreateOutTypes(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1573  {
1574  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(1, valuetype);
1575  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
1576  numStates,
1578  types.insert(types.end(), states.begin(), states.end());
1579  types.emplace_back(jlm::rvsdg::BitType::Create(64)); // addr
1580  return types;
1581  }
1582 
1583  std::string
1584  debug_string() const override
1585  {
1586  return "HLS_LOCAL_LOAD_" + argument(narguments() - 1)->debug_string();
1587  }
1588 
1589  [[nodiscard]] std::unique_ptr<Operation>
1590  copy() const override
1591  {
1592  return std::make_unique<LocalLoadOperation>(*this);
1593  }
1594 
1595  static std::vector<jlm::rvsdg::Output *>
1597  jlm::rvsdg::Output & index,
1598  const std::vector<jlm::rvsdg::Output *> & states,
1599  jlm::rvsdg::Output & load_result)
1600  {
1601  std::vector<jlm::rvsdg::Output *> inputs;
1602  inputs.push_back(&index);
1603  inputs.insert(inputs.end(), states.begin(), states.end());
1604  inputs.push_back(&load_result);
1605  return outputs(
1606  &rvsdg::CreateOpNode<LocalLoadOperation>(inputs, load_result.Type(), states.size()));
1607  }
1608 
1609  [[nodiscard]] std::shared_ptr<const rvsdg::Type>
1610  GetLoadedType() const noexcept
1611  {
1612  return result(0);
1613  }
1614 };
1615 
1617 {
1618 public:
1619  ~LocalStoreOperation() noexcept override;
1620 
1621  LocalStoreOperation(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1622  : SimpleOperation(CreateInTypes(valuetype, numStates), CreateOutTypes(valuetype, numStates))
1623  {}
1624 
1625  bool
1626  operator==(const Operation & other) const noexcept override
1627  {
1628  auto ot = dynamic_cast<const LocalStoreOperation *>(&other);
1629  // check predicate and value
1630  return ot && *ot->argument(1) == *argument(1) && ot->narguments() == narguments();
1631  }
1632 
1633  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1634  CreateInTypes(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1635  {
1636  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(
1637  { jlm::rvsdg::BitType::Create(64), valuetype });
1638  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> states(
1639  numStates,
1641  types.insert(types.end(), states.begin(), states.end());
1642  return types;
1643  }
1644 
1645  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1646  CreateOutTypes(const std::shared_ptr<const jlm::rvsdg::Type> & valuetype, size_t numStates)
1647  {
1648  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(
1649  numStates,
1651  types.emplace_back(jlm::rvsdg::BitType::Create(64)); // addr
1652  types.emplace_back(valuetype); // data
1653  return types;
1654  }
1655 
1656  std::string
1657  debug_string() const override
1658  {
1659  return "HLS_LOCAL_STORE_" + argument(narguments() - 1)->debug_string();
1660  }
1661 
1662  [[nodiscard]] std::unique_ptr<Operation>
1663  copy() const override
1664  {
1665  return std::make_unique<LocalStoreOperation>(*this);
1666  }
1667 
1668  static std::vector<jlm::rvsdg::Output *>
1670  jlm::rvsdg::Output & index,
1671  jlm::rvsdg::Output & value,
1672  const std::vector<jlm::rvsdg::Output *> & states)
1673  {
1674  std::vector<jlm::rvsdg::Output *> inputs;
1675  inputs.push_back(&index);
1676  inputs.push_back(&value);
1677  inputs.insert(inputs.end(), states.begin(), states.end());
1678  return outputs(&rvsdg::CreateOpNode<LocalStoreOperation>(inputs, value.Type(), states.size()));
1679  }
1680 
1681  [[nodiscard]] const jlm::rvsdg::Type &
1682  GetStoredType() const noexcept
1683  {
1684  return *argument(1).get();
1685  }
1686 };
1687 
1689 {
1690 public:
1691  ~LocalMemoryRequestOperation() noexcept override;
1692 
1694  const std::shared_ptr<const llvm::ArrayType> & at,
1695  size_t load_cnt,
1696  size_t store_cnt)
1697  : SimpleOperation(CreateInTypes(at, load_cnt, store_cnt), {})
1698  {}
1699 
1700  bool
1701  operator==(const Operation & other) const noexcept override
1702  {
1703  auto ot = dynamic_cast<const LocalMemoryRequestOperation *>(&other);
1704  // check predicate and value
1705  return ot && ot->narguments() == narguments()
1706  && (ot->narguments() == 0 || (*ot->argument(1) == *argument(1)))
1707  && ot->narguments() == narguments();
1708  }
1709 
1710  static std::vector<std::shared_ptr<const jlm::rvsdg::Type>>
1712  const std::shared_ptr<const llvm::ArrayType> & at,
1713  size_t load_cnt,
1714  size_t store_cnt)
1715  {
1716  std::vector<std::shared_ptr<const jlm::rvsdg::Type>> types(1, at);
1717  for (size_t i = 0; i < load_cnt; ++i)
1718  {
1719  types.emplace_back(jlm::rvsdg::BitType::Create(64)); // addr
1720  }
1721  for (size_t i = 0; i < store_cnt; ++i)
1722  {
1723  types.emplace_back(jlm::rvsdg::BitType::Create(64)); // addr
1724  types.emplace_back(at->GetElementType()); // data
1725  }
1726  return types;
1727  }
1728 
1729  std::string
1730  debug_string() const override
1731  {
1732  return "HLS_LOCAL_MEM_REQ";
1733  }
1734 
1735  [[nodiscard]] std::unique_ptr<Operation>
1736  copy() const override
1737  {
1738  return std::make_unique<LocalMemoryRequestOperation>(*this);
1739  }
1740 
1741  static std::vector<jlm::rvsdg::Output *>
1743  jlm::rvsdg::Output & mem,
1744  const std::vector<jlm::rvsdg::Output *> & load_operands,
1745  const std::vector<jlm::rvsdg::Output *> & store_operands)
1746  {
1747  JLM_ASSERT(store_operands.size() % 2 == 0);
1748  std::vector operands(1, &mem);
1749  operands.insert(operands.end(), load_operands.begin(), load_operands.end());
1750  operands.insert(operands.end(), store_operands.begin(), store_operands.end());
1751  return outputs(&rvsdg::CreateOpNode<LocalMemoryRequestOperation>(
1752  operands,
1753  std::dynamic_pointer_cast<const llvm::ArrayType>(mem.Type()),
1754  load_operands.size(),
1755  store_operands.size() / 2));
1756  }
1757 };
1758 
1759 }
1760 #endif // JLM_HLS_IR_HLS_HPP
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1030
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:997
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(std::shared_ptr< const llvm::PointerType > pointerType)
Definition: hls.hpp:1014
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(std::shared_ptr< const llvm::PointerType > pointerType)
Definition: hls.hpp:1005
~AddressQueueOperation() noexcept override
static jlm::rvsdg::Output * create(jlm::rvsdg::Output &check, jlm::rvsdg::Output &enq, jlm::rvsdg::Output &deq, bool combinatorial, size_t capacity=10)
Definition: hls.hpp:1036
std::string debug_string() const override
Definition: hls.hpp:1020
BackEdgeResult * result_
Definition: hls.hpp:647
~BackEdgeArgument() noexcept override=default
BackEdgeArgument(rvsdg::Region *region, const std::shared_ptr< const jlm::rvsdg::Type > &type)
Definition: hls.hpp:623
static BackEdgeArgument & create(rvsdg::Region *region, std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: hls.hpp:641
BackEdgeResult(rvsdg::Output *origin)
Definition: hls.hpp:655
~BackEdgeResult() override=default
BackEdgeArgument * argument_
Definition: hls.hpp:679
static BackEdgeResult & create(jlm::rvsdg::Output *origin)
Definition: hls.hpp:673
BackEdgeArgument * argument() const
Definition: hls.hpp:664
std::string debug_string() const override
Definition: hls.hpp:56
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:62
~BranchOperation() noexcept override
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:47
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, jlm::rvsdg::Output &value, bool loop=false)
Definition: hls.hpp:68
~BufferOperation() noexcept override
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:418
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:432
std::string debug_string() const override
Definition: hls.hpp:426
std::size_t Capacity_
Definition: hls.hpp:445
std::size_t Capacity() const noexcept
Definition: hls.hpp:406
bool IsPassThrough() const noexcept
Definition: hls.hpp:412
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &value, size_t capacity, bool pass_through=false)
Definition: hls.hpp:438
std::string debug_string() const override
Definition: hls.hpp:883
BundleType(const BundleType &)=default
const std::vector< std::pair< std::string, std::shared_ptr< const jlm::rvsdg::Type > > > elements_
Definition: hls.hpp:886
~BundleType() noexcept override
BundleType(BundleType &&)=delete
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: hls.hpp:843
BundleType & operator=(const BundleType &)=delete
BundleType & operator=(BundleType &&)=delete
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(std::shared_ptr< const rvsdg::Type > pointeeType)
Definition: hls.hpp:1124
~DecoupledLoadOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &addr, jlm::rvsdg::Output &load_result, size_t capacity)
Definition: hls.hpp:1157
std::string debug_string() const override
Definition: hls.hpp:1144
std::shared_ptr< const rvsdg::Type > GetLoadedType() const noexcept
Definition: hls.hpp:1174
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1116
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1151
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(std::shared_ptr< const rvsdg::Type > pointeeType)
Definition: hls.hpp:1134
const llvm::PointerType & GetPointerType() const noexcept
Definition: hls.hpp:1168
~EntryArgument() noexcept override
EntryArgument(rvsdg::Region &region, rvsdg::StructuralInput &input, const std::shared_ptr< const rvsdg::Type > type)
Definition: hls.hpp:592
~ExitResult() noexcept override
ForkOperation(size_t nalternatives, const std::shared_ptr< const jlm::rvsdg::Type > &type, bool isConstant)
Definition: hls.hpp:118
static std::vector< jlm::rvsdg::Output * > create(size_t nalternatives, jlm::rvsdg::Output &value, bool isConstant=false)
Definition: hls.hpp:161
static rvsdg::Node & CreateNode(const size_t numResults, rvsdg::Output &operand, const bool isConstant=false)
Definition: hls.hpp:177
std::string debug_string() const override
Definition: hls.hpp:140
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:146
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:127
~ForkOperation() noexcept override
bool IsConstant() const noexcept
Definition: hls.hpp:189
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:951
const llvm::PointerType & GetPointerType() const noexcept
Definition: hls.hpp:970
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:911
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(std::shared_ptr< const rvsdg::Type > pointeeType, size_t numStates)
Definition: hls.hpp:919
std::shared_ptr< const rvsdg::Type > GetLoadedType() const noexcept
Definition: hls.hpp:976
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &addr, const std::vector< jlm::rvsdg::Output * > &states, jlm::rvsdg::Output &load_result)
Definition: hls.hpp:957
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(std::shared_ptr< const rvsdg::Type > pointeeType, size_t numStates)
Definition: hls.hpp:933
~LoadOperation() noexcept override
std::string debug_string() const override
Definition: hls.hpp:945
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1552
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1590
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(const std::shared_ptr< const jlm::rvsdg::Type > &valuetype, size_t numStates)
Definition: hls.hpp:1560
std::shared_ptr< const rvsdg::Type > GetLoadedType() const noexcept
Definition: hls.hpp:1610
std::string debug_string() const override
Definition: hls.hpp:1584
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::shared_ptr< const jlm::rvsdg::Type > &valuetype, size_t numStates)
Definition: hls.hpp:1572
~LocalLoadOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &index, const std::vector< jlm::rvsdg::Output * > &states, jlm::rvsdg::Output &load_result)
Definition: hls.hpp:1596
bool operator==(const Operation &) const noexcept override
Definition: hls.hpp:1465
static std::vector< jlm::rvsdg::Output * > create(std::shared_ptr< const llvm::ArrayType > at, rvsdg::Region *region)
Definition: hls.hpp:1490
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(std::shared_ptr< const llvm::ArrayType > at)
Definition: hls.hpp:1471
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1484
std::string debug_string() const override
Definition: hls.hpp:1478
~LocalMemoryOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &mem, const std::vector< jlm::rvsdg::Output * > &load_operands, const std::vector< jlm::rvsdg::Output * > &store_operands)
Definition: hls.hpp:1742
~LocalMemoryRequestOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1736
std::string debug_string() const override
Definition: hls.hpp:1730
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1701
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(const std::shared_ptr< const llvm::ArrayType > &at, size_t load_cnt, size_t store_cnt)
Definition: hls.hpp:1711
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &mem, size_t resp_count)
Definition: hls.hpp:1533
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1506
~LocalMemoryResponseOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1527
std::string debug_string() const override
Definition: hls.hpp:1521
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::shared_ptr< const jlm::llvm::ArrayType > &at, size_t resp_count)
Definition: hls.hpp:1514
~LocalStoreOperation() noexcept override
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(const std::shared_ptr< const jlm::rvsdg::Type > &valuetype, size_t numStates)
Definition: hls.hpp:1634
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1663
std::string debug_string() const override
Definition: hls.hpp:1657
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &index, jlm::rvsdg::Output &value, const std::vector< jlm::rvsdg::Output * > &states)
Definition: hls.hpp:1669
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1626
const jlm::rvsdg::Type & GetStoredType() const noexcept
Definition: hls.hpp:1682
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::shared_ptr< const jlm::rvsdg::Type > &valuetype, size_t numStates)
Definition: hls.hpp:1646
std::string debug_string() const override
Definition: hls.hpp:366
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:359
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, jlm::rvsdg::Output &value)
Definition: hls.hpp:378
~LoopConstantBufferOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:372
rvsdg::Output & GetPredicateBuffer() const noexcept
Definition: hls.hpp:739
rvsdg::RegionResult * predicate() const noexcept
Definition: hls.hpp:731
~LoopNode() noexcept override=default
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:575
~LoopOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1325
~MemoryRequestOperation() noexcept override=default
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(const std::vector< std::shared_ptr< const rvsdg::Type >> &load_types, const std::vector< std::shared_ptr< const rvsdg::Type >> &store_types)
Definition: hls.hpp:1279
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1269
MemoryRequestOperation(const MemoryRequestOperation &other)=default
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::vector< std::shared_ptr< const rvsdg::Type >> &load_types, const std::vector< std::shared_ptr< const rvsdg::Type >> &store_types)
Definition: hls.hpp:1297
std::vector< std::shared_ptr< const rvsdg::Type > > LoadTypes_
Definition: hls.hpp:1369
std::string debug_string() const override
Definition: hls.hpp:1319
std::vector< std::shared_ptr< const rvsdg::Type > > StoreTypes_
Definition: hls.hpp:1370
static std::vector< jlm::rvsdg::Output * > create(const std::vector< jlm::rvsdg::Output * > &load_operands, const std::vector< std::shared_ptr< const rvsdg::Type >> &loadTypes, const std::vector< jlm::rvsdg::Output * > &store_operands, rvsdg::Region *)
Definition: hls.hpp:1331
const std::vector< std::shared_ptr< const rvsdg::Type > > * GetStoreTypes() const
Definition: hls.hpp:1363
const std::vector< std::shared_ptr< const rvsdg::Type > > * GetLoadTypes() const
Definition: hls.hpp:1357
static std::vector< jlm::rvsdg::Output * > create(rvsdg::Output &result, const std::vector< std::shared_ptr< const rvsdg::Type >> &output_types, int in_width)
Definition: hls.hpp:1234
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(int in_width)
Definition: hls.hpp:1202
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::vector< std::shared_ptr< const rvsdg::Type >> &output_types)
Definition: hls.hpp:1210
std::string debug_string() const override
Definition: hls.hpp:1222
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1228
~MemoryResponseOperation() noexcept override
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1194
~MuxOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:229
std::string debug_string() const override
Definition: hls.hpp:223
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, const std::vector< jlm::rvsdg::Output * > &alternatives, bool discarding, bool loop=false)
Definition: hls.hpp:235
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:214
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > create_typevector(size_t nalternatives, std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: hls.hpp:264
~PredicateBufferOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate)
Definition: hls.hpp:337
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:331
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:318
std::string debug_string() const override
Definition: hls.hpp:325
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:551
bool operator==(const Operation &) const noexcept override
Definition: hls.hpp:532
~PrintOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &value)
Definition: hls.hpp:557
std::string debug_string() const override
Definition: hls.hpp:539
size_t id() const
Definition: hls.hpp:545
~SinkOperation() noexcept override
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:283
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:296
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &value)
Definition: hls.hpp:302
std::string debug_string() const override
Definition: hls.hpp:290
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1065
~StateGateOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &addr, const std::vector< jlm::rvsdg::Output * > &states)
Definition: hls.hpp:1096
std::string debug_string() const override
Definition: hls.hpp:1084
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1090
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInOutTypes(const std::shared_ptr< const jlm::rvsdg::Type > &type, size_t numStates)
Definition: hls.hpp:1073
~StoreOperation() noexcept override
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateOutTypes(const std::shared_ptr< const rvsdg::Type > &pointeeType, size_t numStates)
Definition: hls.hpp:1405
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &addr, jlm::rvsdg::Output &value, const std::vector< jlm::rvsdg::Output * > &states, jlm::rvsdg::Output &resp)
Definition: hls.hpp:1428
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:1385
static std::vector< std::shared_ptr< const jlm::rvsdg::Type > > CreateInTypes(const std::shared_ptr< const rvsdg::Type > &pointeeType, size_t numStates)
Definition: hls.hpp:1393
std::string debug_string() const override
Definition: hls.hpp:1416
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:1422
const llvm::PointerType & GetPointerType() const noexcept
Definition: hls.hpp:1443
const rvsdg::Type & GetStoredType() const noexcept
Definition: hls.hpp:1449
bool operator==(const Operation &other) const noexcept override
Definition: hls.hpp:488
std::string debug_string() const override
Definition: hls.hpp:496
~TriggerOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: hls.hpp:502
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &tg, jlm::rvsdg::Output &value)
Definition: hls.hpp:508
std::size_t ComputeHash() const noexcept override
Definition: hls.cpp:60
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: hls.cpp:66
static std::shared_ptr< const TriggerType > Create()
Definition: hls.cpp:72
std::string debug_string() const override
Definition: hls.hpp:457
~TriggerType() noexcept override
bool operator==(const Type &other) const noexcept override
Definition: hls.hpp:463
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
PointerType class.
Definition: types.hpp:25
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
static std::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
Definition: type.cpp:45
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition: control.cpp:50
rvsdg::Region * region() const noexcept
Definition: node.cpp:151
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
Represents the argument of a region.
Definition: region.hpp:41
Represents the result of a region.
Definition: region.hpp:120
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
RegionArgument & addArgument(std::unique_ptr< RegionArgument > argument)
Definition: region.cpp:176
RegionResult & addResult(std::unique_ptr< RegionResult > result)
Definition: region.cpp:262
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
size_t nresults() const noexcept
Definition: operation.cpp:30
size_t narguments() const noexcept
Definition: operation.cpp:17
rvsdg::Region * subregion(size_t index) const noexcept
constexpr Type() noexcept
Definition: type.hpp:46
#define JLM_ASSERT(x)
Definition: common.hpp:16
std::shared_ptr< const BundleType > get_mem_res_type(std::shared_ptr< const jlm::rvsdg::Type > dataType)
Definition: hls.cpp:335
std::shared_ptr< const BundleType > get_mem_req_type(std::shared_ptr< const rvsdg::Type > elementType, bool write)
Definition: hls.cpp:320
int JlmSize(const jlm::rvsdg::Type *type)
Definition: hls.cpp:344
size_t GetPointerSizeInBits()
Definition: hls.cpp:396
static std::string type(const Node *n)
Definition: view.cpp:255
TypeKind
The kinds of types supported in rvsdg.
Definition: type.hpp:22
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
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35