Jlm
hls.cpp
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 #include <cmath>
7 #include <jlm/hls/ir/hls.hpp>
8 #include <jlm/util/Hash.hpp>
9 
10 namespace jlm::hls
11 {
12 
13 BranchOperation::~BranchOperation() noexcept = default;
14 
15 ForkOperation::~ForkOperation() noexcept = default;
16 
17 MuxOperation::~MuxOperation() noexcept = default;
18 
19 SinkOperation::~SinkOperation() noexcept = default;
20 
21 PredicateBufferOperation::~PredicateBufferOperation() noexcept = default;
22 
23 LoopConstantBufferOperation::~LoopConstantBufferOperation() noexcept = default;
24 
25 BundleType::~BundleType() noexcept = default;
26 
27 LoopOperation::~LoopOperation() noexcept = default;
28 
29 PrintOperation::~PrintOperation() noexcept = default;
30 
31 BufferOperation::~BufferOperation() noexcept = default;
32 
33 TriggerOperation::~TriggerOperation() noexcept = default;
34 
35 TriggerType::~TriggerType() noexcept = default;
36 
37 StateGateOperation::~StateGateOperation() noexcept = default;
38 
39 LoadOperation::~LoadOperation() noexcept = default;
40 
41 DecoupledLoadOperation::~DecoupledLoadOperation() noexcept = default;
42 
43 AddressQueueOperation::~AddressQueueOperation() noexcept = default;
44 
45 MemoryResponseOperation::~MemoryResponseOperation() noexcept = default;
46 
47 LocalLoadOperation::~LocalLoadOperation() noexcept = default;
48 
49 LocalMemoryOperation::~LocalMemoryOperation() noexcept = default;
50 
51 LocalMemoryRequestOperation::~LocalMemoryRequestOperation() noexcept = default;
52 
53 LocalMemoryResponseOperation::~LocalMemoryResponseOperation() noexcept = default;
54 
55 LocalStoreOperation::~LocalStoreOperation() noexcept = default;
56 
57 StoreOperation::~StoreOperation() noexcept = default;
58 
59 std::size_t
60 TriggerType::ComputeHash() const noexcept
61 {
62  return typeid(TriggerType).hash_code();
63 }
64 
66 TriggerType::Kind() const noexcept
67 {
69 }
70 
71 std::shared_ptr<const TriggerType>
73 {
74  static const TriggerType instance;
75  return std::shared_ptr<const TriggerType>(std::shared_ptr<void>(), &instance);
76 }
77 
78 std::size_t
79 BundleType::ComputeHash() const noexcept
80 {
81  std::size_t seed = typeid(BundleType).hash_code();
82  for (auto & element : elements_)
83  {
84  auto firstHash = std::hash<std::string>()(element.first);
85  util::combineHashesWithSeed(seed, firstHash, element.second->ComputeHash());
86  }
87 
88  return seed;
89 }
90 
92 BundleType::Kind() const noexcept
93 {
95 }
96 
97 EntryArgument::~EntryArgument() noexcept = default;
98 
100 EntryArgument::Copy(rvsdg::Region & region, rvsdg::StructuralInput * input) const
101 {
102  return Create(region, *input, Type());
103 }
104 
107 {
108  JLM_ASSERT(input == nullptr);
109  return create(&region, Type());
110 }
111 
114 {
115  JLM_ASSERT(output == nullptr);
116  return create(&origin);
117 }
118 
119 ExitResult::~ExitResult() noexcept = default;
120 
121 ExitResult::ExitResult(rvsdg::Output & origin, rvsdg::StructuralOutput & output)
122  : rvsdg::RegionResult(origin.region(), &origin, &output, origin.Type())
123 {
124  JLM_ASSERT(dynamic_cast<const LoopNode *>(origin.region()->node()));
125 }
126 
127 ExitResult &
129 {
130  return Create(origin, *output);
131 }
132 
135 {
136  // Create StructuralInput and EntryArgument
137  const auto input =
138  addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
139  auto & argument_in = EntryArgument::Create(*subregion(), *input, origin->Type());
140 
141  // Create back-edge
142  auto backedge_argument = add_backedge(origin->Type());
143  auto backedge_result = backedge_argument->result();
144 
145  // Create Mux to pick between EntryArgument and BackEdgeArgument
146  auto mux = MuxOperation::create(
148  { &argument_in, backedge_argument },
149  false,
150  true)[0];
151  // Give the caller a
152  if (buffer != nullptr)
153  *buffer = mux;
154 
155  // Create Branch to send the result to either an ExitResult or a BackEdgeResult
156  // We need to give it a value, so use the output of the mux as the result for now
157  auto branch = BranchOperation::create(*predicate()->origin(), *mux, true);
158 
159  // Create an ExitResult + StructuralOutput for when the loop is finished
160  const auto output = addOutput(std::make_unique<rvsdg::StructuralOutput>(this, origin->Type()));
161  ExitResult::Create(*branch[0], *output);
162 
163  // If the loop is not done, send the value to the BackEdgeResult, with a small buffer in between.
164  auto buf = BufferOperation::create(*branch[1], 2)[0];
165  backedge_result->divert_to(buf);
166  return output;
167 }
168 
171 {
172  auto input =
173  addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
174 
175  auto & argument_in = EntryArgument::Create(*subregion(), *input, origin->Type());
176  auto buffer = LoopConstantBufferOperation::create(GetPredicateBuffer(), argument_in)[0];
177  return buffer;
178 }
179 
182 {
183  const auto input =
184  addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
185  return &EntryArgument::Create(*subregion(), *input, origin->Type());
186 }
187 
190 {
191  const auto output = addOutput(std::make_unique<rvsdg::StructuralOutput>(this, origin->Type()));
192  ExitResult::Create(*origin, *output);
193  return output;
194 }
195 
196 void
198 {
199  JLM_ASSERT(output->node() == this);
201  JLM_ASSERT(output->results.size() == 1);
202  auto result = output->results.begin();
203 
204  subregion()->RemoveResults({ result->index() });
205  RemoveOutputs({ output->index() });
206 }
207 
208 void
210 {
211  JLM_ASSERT(input->node() == this);
212  JLM_ASSERT(input->arguments.size() == 1);
213  auto argument = input->arguments.begin();
214  JLM_ASSERT(argument->IsDead());
215 
216  subregion()->RemoveArguments({ argument->index() });
217  RemoveInputs({ input->index() });
218 }
219 
220 [[nodiscard]] const rvsdg::Operation &
221 LoopNode::GetOperation() const noexcept
222 {
223  static const LoopOperation singleton;
224  return singleton;
225 }
226 
227 LoopNode *
229 {
230  auto loop = create(region, false);
231 
232  for (size_t i = 0; i < ninputs(); ++i)
233  {
234  auto in_origin = &smap.lookup(*input(i)->origin());
235  auto inp = loop->addInput(
236  std::make_unique<rvsdg::StructuralInput>(loop, in_origin, in_origin->Type()),
237  true);
238 
239  auto oarg = input(i)->arguments.begin().ptr();
240  auto & narg = EntryArgument::Create(*loop->subregion(), *inp, oarg->Type());
241  smap.insert(oarg, &narg);
242  }
243  for (size_t i = 0; i < noutputs(); ++i)
244  {
245  auto out = loop->addOutput(std::make_unique<rvsdg::StructuralOutput>(loop, output(i)->Type()));
246 
247  smap.insert(output(i), out);
248  smap.insert(output(i), out);
249  }
250  for (size_t i = 0; i < subregion()->narguments(); ++i)
251  {
252  auto arg = subregion()->argument(i);
253  if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
254  {
255  auto na = loop->add_backedge(arg->Type());
256  smap.insert(ba, na);
257  }
258  }
259 
260  subregion()->copy(loop->subregion(), smap);
261  loop->PredicateBuffer_ = &smap.lookup(*PredicateBuffer_);
262  // redirect backedges
263  for (size_t i = 0; i < subregion()->narguments(); ++i)
264  {
265  auto arg = subregion()->argument(i);
266  if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
267  {
268  auto na = dynamic_cast<BackEdgeArgument *>(&smap.lookup(*ba));
269  na->result()->divert_to(&smap.lookup(*ba->result()->origin()));
270  }
271  }
272  for (size_t i = 0; i < noutputs(); ++i)
273  {
274  auto outp = output(i);
275  auto res = outp->results.begin().ptr();
276  auto origin = &smap.lookup(*res->origin());
277  ExitResult::Create(*origin, *loop->output(i));
278  }
279 
280  return loop;
281 }
282 
284 LoopNode::add_backedge(std::shared_ptr<const jlm::rvsdg::Type> type)
285 {
286  auto & argument_loop = BackEdgeArgument::create(subregion(), std::move(type));
287  auto & result_loop = BackEdgeResult::create(&argument_loop);
288  argument_loop.result_ = &result_loop;
289  result_loop.argument_ = &argument_loop;
290  return &argument_loop;
291 }
292 
293 LoopNode *
294 LoopNode::create(rvsdg::Region * parent, bool init)
295 {
296  auto ln = new LoopNode(parent);
297  if (init)
298  {
300  auto pred_arg = ln->add_backedge(rvsdg::ControlType::Create(2));
301  pred_arg->result()->divert_to(predicate);
302  // we need a buffer without pass-through behavior to avoid a combinatorial cycle of ready
303  // signals
304  auto pre_buffer = BufferOperation::create(*pred_arg, 2)[0];
305  ln->PredicateBuffer_ = PredicateBufferOperation::create(*pre_buffer)[0];
306  }
307  return ln;
308 }
309 
310 void
312 {
313  auto node = rvsdg::TryGetOwnerNode<Node>(*predicate()->origin());
314  predicate()->origin()->divert_users(p);
315  if (node && node->IsDead())
316  remove(node);
317 }
318 
319 std::shared_ptr<const BundleType>
320 get_mem_req_type(std::shared_ptr<const rvsdg::Type> elementType, bool write)
321 {
322  std::vector<std::pair<std::string, std::shared_ptr<const jlm::rvsdg::Type>>> elements;
323  elements.emplace_back("addr", llvm::PointerType::Create());
324  elements.emplace_back("size", jlm::rvsdg::BitType::Create(4));
325  elements.emplace_back("id", jlm::rvsdg::BitType::Create(8));
326  if (write)
327  {
328  elements.emplace_back("data", std::move(elementType));
329  elements.emplace_back("write", jlm::rvsdg::BitType::Create(1));
330  }
331  return std::make_shared<BundleType>(std::move(elements));
332 }
333 
334 std::shared_ptr<const BundleType>
335 get_mem_res_type(std::shared_ptr<const jlm::rvsdg::Type> dataType)
336 {
337  std::vector<std::pair<std::string, std::shared_ptr<const jlm::rvsdg::Type>>> elements;
338  elements.emplace_back("data", std::move(dataType));
339  elements.emplace_back("id", jlm::rvsdg::BitType::Create(8));
340  return std::make_shared<BundleType>(std::move(elements));
341 }
342 
343 int
345 {
346  if (auto bt = dynamic_cast<const jlm::rvsdg::BitType *>(type))
347  {
348  return bt->nbits();
349  }
350  else if (auto at = dynamic_cast<const llvm::ArrayType *>(type))
351  {
352  return JlmSize(&at->element_type()) * at->nelements();
353  }
354  else if (auto vt = dynamic_cast<const llvm::VectorType *>(type))
355  {
356  return JlmSize(&vt->type()) * vt->size();
357  }
358  else if (dynamic_cast<const llvm::PointerType *>(type))
359  {
360  return GetPointerSizeInBits();
361  }
362  else if (auto ct = dynamic_cast<const rvsdg::ControlType *>(type))
363  {
364  return ceil(log2(ct->nalternatives()));
365  }
366  else if (type->Kind() == rvsdg::TypeKind::State)
367  {
368  return 1;
369  }
370  else if (rvsdg::is<BundleType>(*type))
371  {
372  // TODO: fix this ugly hack needed for get_node_name
373  return 0;
374  }
375  else if (auto ft = dynamic_cast<const llvm::FloatingPointType *>(type))
376  {
377  switch (ft->size())
378  {
379  case llvm::fpsize::half:
380  return 16;
381  case llvm::fpsize::flt:
382  return 32;
383  case llvm::fpsize::dbl:
384  return 64;
385  default:
386  throw std::logic_error("Size of '" + type->debug_string() + "' is not implemented!");
387  }
388  }
389  else
390  {
391  throw std::logic_error("Size of '" + type->debug_string() + "' is not implemented!");
392  }
393 }
394 
395 size_t
397 {
398  return 64;
399 }
400 }
static BackEdgeArgument & create(rvsdg::Region *region, std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: hls.hpp:641
BackEdgeResult * result()
Definition: hls.hpp:632
BackEdgeArgument & Copy(rvsdg::Region &region, rvsdg::StructuralInput *input) const override
Definition: hls.cpp:106
BackEdgeResult & Copy(rvsdg::Output &origin, rvsdg::StructuralOutput *output) const override
Definition: hls.cpp:113
static BackEdgeResult & create(jlm::rvsdg::Output *origin)
Definition: hls.hpp:673
~BranchOperation() noexcept override
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, jlm::rvsdg::Output &value, bool loop=false)
Definition: hls.hpp:68
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &value, size_t capacity, bool pass_through=false)
Definition: hls.hpp:438
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: hls.cpp:92
const std::vector< std::pair< std::string, std::shared_ptr< const jlm::rvsdg::Type > > > elements_
Definition: hls.hpp:886
std::size_t ComputeHash() const noexcept override
Definition: hls.cpp:79
BundleType(const std::vector< std::pair< std::string, std::shared_ptr< const Type >>> elements)
Definition: hls.hpp:827
~EntryArgument() noexcept override
static EntryArgument & Create(rvsdg::Region &region, rvsdg::StructuralInput &input, const std::shared_ptr< const rvsdg::Type > type)
Definition: hls.hpp:608
ExitResult & Copy(rvsdg::Output &origin, rvsdg::StructuralOutput *output) const override
Definition: hls.cpp:128
~ExitResult() noexcept override
static ExitResult & Create(rvsdg::Output &origin, rvsdg::StructuralOutput &output)
Definition: hls.hpp:700
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, jlm::rvsdg::Output &value)
Definition: hls.hpp:378
LoopNode * copy(rvsdg::Region *region, rvsdg::SubstitutionMap &smap) const override
Copy a node with substitutions.
Definition: hls.cpp:228
LoopNode(rvsdg::Region *parent)
Definition: hls.hpp:713
rvsdg::Region * subregion() const noexcept
Definition: hls.hpp:725
rvsdg::Output * addRequestOutput(rvsdg::Output *origin)
Definition: hls.cpp:189
void removeLoopOutput(rvsdg::StructuralOutput *output)
Definition: hls.cpp:197
static LoopNode * create(rvsdg::Region *parent, bool init=true)
Definition: hls.cpp:294
rvsdg::Output * PredicateBuffer_
Definition: hls.hpp:819
rvsdg::StructuralOutput * AddLoopVar(rvsdg::Output *origin, rvsdg::Output **buffer=nullptr)
Definition: hls.cpp:134
rvsdg::Output & GetPredicateBuffer() const noexcept
Definition: hls.hpp:739
const rvsdg::Operation & GetOperation() const noexcept override
Definition: hls.cpp:221
void set_predicate(jlm::rvsdg::Output *p)
Definition: hls.cpp:311
rvsdg::Output * addLoopConstant(rvsdg::Output *origin)
Definition: hls.cpp:170
rvsdg::Output * addResponseInput(rvsdg::Output *origin)
Definition: hls.cpp:181
rvsdg::RegionResult * predicate() const noexcept
Definition: hls.hpp:731
BackEdgeArgument * add_backedge(std::shared_ptr< const jlm::rvsdg::Type > type)
Definition: hls.cpp:284
void removeLoopInput(rvsdg::StructuralInput *input)
Definition: hls.cpp:209
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
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate)
Definition: hls.hpp:337
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
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 Output & createFalse(Region &region)
Definition: control.hpp:134
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition: control.cpp:50
void divert_to(Output *new_origin)
Definition: node.cpp:64
Output * origin() const noexcept
Definition: node.hpp:58
size_t index() const noexcept
Definition: node.hpp:52
rvsdg::Region * region() const noexcept
Definition: node.hpp:761
size_t RemoveInputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:306
size_t ninputs() const noexcept
Definition: node.hpp:609
size_t noutputs() const noexcept
Definition: node.hpp:644
size_t RemoveOutputs(const util::HashSet< size_t > &indices)
Definition: node.cpp:342
rvsdg::Region * region() const noexcept
Definition: node.cpp:151
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: node.hpp:366
size_t index() const noexcept
Definition: node.hpp:274
void divert_users(jlm::rvsdg::Output *new_origin)
Definition: node.hpp:301
bool IsDead() const noexcept
Definition: node.hpp:295
StructuralInput * input() const noexcept
Definition: region.hpp:69
Represents the result of a region.
Definition: region.hpp:120
StructuralOutput * output() const noexcept
Definition: region.hpp:149
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
size_t RemoveResults(const util::HashSet< size_t > &indices)
Definition: region.cpp:278
void copy(Region *target, SubstitutionMap &smap) const
Copy a region with substitutions.
Definition: region.cpp:314
RegionArgument * argument(size_t index) const noexcept
Definition: region.hpp:437
size_t RemoveArguments(const util::HashSet< size_t > &indices)
Definition: region.cpp:210
size_t narguments() const noexcept
Definition: region.hpp:431
StructuralNode * node() const noexcept
StructuralInput * addInput(std::unique_ptr< StructuralInput > input, bool notifyRegion)
StructuralOutput * addOutput(std::unique_ptr< StructuralOutput > input)
StructuralOutput * output(size_t index) const noexcept
StructuralInput * input(size_t index) const noexcept
StructuralNode * node() const noexcept
void insert(const Output *original, Output *substitute)
Output & lookup(const Output &original) const
size_type size() const noexcept
Iterator begin() noexcept
#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 void remove(Node *node)
Definition: region.hpp:932
static std::string type(const Node *n)
Definition: view.cpp:255
TypeKind
The kinds of types supported in rvsdg.
Definition: type.hpp:22
@ State
Designate a state type.
@ Value
Designate a value type.
void combineHashesWithSeed(std::size_t &seed, std::size_t hash, Args... args)
Definition: Hash.hpp:45