Jlm
Loading...
Searching...
No Matches
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 <algorithm>
7#include <cmath>
8#include <jlm/hls/ir/hls.hpp>
9#include <jlm/util/Hash.hpp>
10
11namespace jlm::hls
12{
13
15
16ForkOperation::~ForkOperation() noexcept = default;
17
18MuxOperation::~MuxOperation() noexcept = default;
19
20SinkOperation::~SinkOperation() noexcept = default;
21
22PredicateBufferOperation::~PredicateBufferOperation() noexcept = default;
23
24LoopConstantBufferOperation::~LoopConstantBufferOperation() noexcept = default;
25
26BundleType::~BundleType() noexcept = default;
27
28LoopOperation::~LoopOperation() noexcept = default;
29
30PrintOperation::~PrintOperation() noexcept = default;
31
32BufferOperation::~BufferOperation() noexcept = default;
33
34TriggerOperation::~TriggerOperation() noexcept = default;
35
36TriggerType::~TriggerType() noexcept = default;
37
38StateGateOperation::~StateGateOperation() noexcept = default;
39
40LoadOperation::~LoadOperation() noexcept = default;
41
42DecoupledLoadOperation::~DecoupledLoadOperation() noexcept = default;
43
44AddressQueueOperation::~AddressQueueOperation() noexcept = default;
45
46MemoryResponseOperation::~MemoryResponseOperation() noexcept = default;
47
48LocalLoadOperation::~LocalLoadOperation() noexcept = default;
49
50LocalMemoryOperation::~LocalMemoryOperation() noexcept = default;
51
52LocalMemoryRequestOperation::~LocalMemoryRequestOperation() noexcept = default;
53
54LocalMemoryResponseOperation::~LocalMemoryResponseOperation() noexcept = default;
55
56LocalStoreOperation::~LocalStoreOperation() noexcept = default;
57
58StoreOperation::~StoreOperation() noexcept = default;
59
60std::size_t
62{
63 return typeid(TriggerType).hash_code();
64}
65
71
72std::shared_ptr<const TriggerType>
74{
75 static const TriggerType instance;
76 return std::shared_ptr<const TriggerType>(std::shared_ptr<void>(), &instance);
77}
78
79std::size_t
81{
82 std::size_t seed = typeid(BundleType).hash_code();
83 for (auto & element : elements_)
84 {
85 auto firstHash = std::hash<std::string>()(element.first);
86 util::combineHashesWithSeed(seed, firstHash, element.second->ComputeHash());
87 }
88
89 return seed;
90}
91
97
99
101EntryArgument::Copy(rvsdg::Region & region, rvsdg::StructuralInput * input) const
102{
103 return Create(region, *input, Type());
104}
105
108{
109 JLM_ASSERT(input == nullptr);
110 return create(&region, Type());
111}
112
115{
116 JLM_ASSERT(output == nullptr);
117 return create(&origin);
118}
119
121
122ExitResult::ExitResult(rvsdg::Output & origin, rvsdg::StructuralOutput & output)
123 : rvsdg::RegionResult(origin.region(), &origin, &output, origin.Type())
124{
125 JLM_ASSERT(dynamic_cast<const LoopNode *>(origin.region()->node()));
126}
127
130{
131 return Create(origin, *output);
132}
133
136{
137 // Create StructuralInput and EntryArgument
138 const auto input =
139 addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
140 auto & argument_in = EntryArgument::Create(*subregion(), *input, origin->Type());
141
142 // Create back-edge
143 auto backedge_argument = add_backedge(origin->Type());
144 auto backedge_result = backedge_argument->result();
145
146 // Create Mux to pick between EntryArgument and BackEdgeArgument
150 false,
151 true)[0];
152 // Give the caller a
153 if (buffer != nullptr)
154 *buffer = mux;
155
156 // Create Branch to send the result to either an ExitResult or a BackEdgeResult
157 // We need to give it a value, so use the output of the mux as the result for now
158 auto branch = BranchOperation::create(*predicate()->origin(), *mux, true);
159
160 // Create an ExitResult + StructuralOutput for when the loop is finished
161 const auto output = addOutput(std::make_unique<rvsdg::StructuralOutput>(this, origin->Type()));
163
164 // If the loop is not done, send the value to the BackEdgeResult, with a small buffer in between.
165 auto buf = BufferOperation::create(*branch[1], 2)[0];
166 backedge_result->divert_to(buf);
167 return output;
168}
169
172{
173 auto input =
174 addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
175
176 auto & argument_in = EntryArgument::Create(*subregion(), *input, origin->Type());
178 return buffer;
179}
180
183{
184 const auto input =
185 addInput(std::make_unique<rvsdg::StructuralInput>(this, origin, origin->Type()), true);
186 return &EntryArgument::Create(*subregion(), *input, origin->Type());
187}
188
191{
192 const auto output = addOutput(std::make_unique<rvsdg::StructuralOutput>(this, origin->Type()));
193 ExitResult::Create(*origin, *output);
194 return output;
195}
196
197[[nodiscard]] const rvsdg::Operation &
199{
200 static const LoopOperation singleton;
201 return singleton;
202}
203
206{
207 return EntryVar{ const_cast<rvsdg::Input *>(&input),
208 const_cast<rvsdg::RegionArgument *>(
209 &*static_cast<const rvsdg::StructuralInput &>(input).arguments.begin()) };
210}
211
214{
215 return ExitVar{ const_cast<rvsdg::RegionResult *>(
216 &*static_cast<const rvsdg::StructuralOutput &>(output).results.begin()),
217 const_cast<rvsdg::Output *>(&output) };
218}
219
220std::variant<LoopNode::EntryVar, LoopNode::BackEdgeVar>
222{
223 if (auto backedge = dynamic_cast<const BackEdgeArgument *>(&argument))
224 {
225 return BackEdgeVar{ const_cast<BackEdgeArgument *>(backedge),
226 const_cast<BackEdgeArgument *>(backedge)->result() };
227 }
228 else if (auto entry = dynamic_cast<const EntryArgument *>(&argument))
229 {
230 return EntryVar{ entry->input(), const_cast<EntryArgument *>(entry) };
231 }
232 else
233 {
234 throw std::logic_error("LoopNode argument is neither entry nor backedge");
235 }
236}
237
238std::variant<LoopNode::ExitVar, LoopNode::BackEdgeVar>
240{
241 if (auto backedge = dynamic_cast<const BackEdgeResult *>(&result))
242 {
243 return BackEdgeVar{ const_cast<BackEdgeResult *>(backedge)->argument(),
244 const_cast<BackEdgeResult *>(backedge) };
245 }
246 else if (auto exit = dynamic_cast<const ExitResult *>(&result))
247 {
248 return ExitVar{ const_cast<ExitResult *>(exit), exit->output() };
249 }
250 else
251 {
252 throw std::logic_error("LoopNode result is neither exit nor backedge");
253 }
254}
255
256std::vector<LoopNode::EntryVar>
258{
259 std::vector<EntryVar> entryvars;
260 for (const auto & input : Inputs())
261 {
262 entryvars.push_back(mapInput(input));
263 }
264 return entryvars;
265}
266
267std::vector<LoopNode::ExitVar>
269{
270 std::vector<ExitVar> exitvars;
271 for (const auto & output : Outputs())
272 {
273 exitvars.push_back(mapOutput(output));
274 }
275 return exitvars;
276}
277
278std::vector<LoopNode::BackEdgeVar>
280{
281 std::vector<BackEdgeVar> backedges;
282 for (const auto & argument : subregion()->Arguments())
283 {
284 auto var = mapArgument(*argument);
285 if (auto backedge = std::get_if<BackEdgeVar>(&var))
286 {
287 backedges.push_back(*backedge);
288 }
289 }
290 return backedges;
291}
292
293void
294LoopNode::removeEntryVars(std::vector<EntryVar> vars)
295{
298 for (const auto & var : vars)
299 {
300 JLM_ASSERT(dynamic_cast<rvsdg::StructuralInput *>(var.input)->node() == this);
301 JLM_ASSERT(dynamic_cast<EntryArgument *>(var.inner)->input() == var.input);
302 JLM_ASSERT(var.inner->IsDead());
303 inputs.insert(var.input->index());
304 arguments.insert(var.inner->index());
305 }
308}
309
310void
311LoopNode::removeExitVars(std::vector<ExitVar> vars)
312{
315 for (const auto & var : vars)
316 {
317 JLM_ASSERT(dynamic_cast<rvsdg::StructuralOutput *>(var.output)->node() == this);
318 JLM_ASSERT(dynamic_cast<ExitResult *>(var.inner)->output() == var.output);
319 JLM_ASSERT(var.output->IsDead());
320 results.insert(var.inner->index());
321 outputs.insert(var.output->index());
322 }
323 subregion()->RemoveResults(results);
325}
326
327void
328LoopNode::removeBackEdgeVars(std::vector<BackEdgeVar> vars)
329{
332 for (const auto & var : vars)
333 {
334 JLM_ASSERT(dynamic_cast<BackEdgeArgument *>(var.pre)->region() == subregion());
335 JLM_ASSERT(dynamic_cast<BackEdgeResult *>(var.post)->region() == subregion());
336 arguments.insert(var.pre->index());
337 results.insert(var.post->index());
338 }
339 subregion()->RemoveResults(results);
341}
342
343LoopNode *
345{
346 auto loop = new LoopNode(region);
347
348 for (size_t i = 0; i < ninputs(); ++i)
349 {
350 auto in_origin = &smap.lookup(*input(i)->origin());
351 auto inp = loop->addInput(
352 std::make_unique<rvsdg::StructuralInput>(loop, in_origin, in_origin->Type()),
353 true);
354
355 auto oarg = input(i)->arguments.begin().ptr();
356 auto & narg = EntryArgument::Create(*loop->subregion(), *inp, oarg->Type());
357 smap.insert(oarg, &narg);
358 }
359 for (size_t i = 0; i < noutputs(); ++i)
360 {
361 auto out = loop->addOutput(std::make_unique<rvsdg::StructuralOutput>(loop, output(i)->Type()));
362
363 smap.insert(output(i), out);
364 smap.insert(output(i), out);
365 }
366 for (size_t i = 0; i < subregion()->narguments(); ++i)
367 {
368 auto arg = subregion()->argument(i);
369 if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
370 {
371 auto na = loop->add_backedge(arg->Type());
372 smap.insert(ba, na);
373 }
374 }
375
376 subregion()->copy(loop->subregion(), smap);
377 loop->PredicateBuffer_ = &smap.lookup(*PredicateBuffer_);
378 // redirect backedges
379 for (size_t i = 0; i < subregion()->narguments(); ++i)
380 {
381 auto arg = subregion()->argument(i);
382 if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
383 {
384 auto na = dynamic_cast<BackEdgeArgument *>(&smap.lookup(*ba));
385 na->result()->divert_to(&smap.lookup(*ba->result()->origin()));
386 }
387 }
388 for (size_t i = 0; i < noutputs(); ++i)
389 {
390 auto outp = output(i);
391 auto res = outp->results.begin().ptr();
392 auto origin = &smap.lookup(*res->origin());
393 ExitResult::Create(*origin, *loop->output(i));
394 }
395
396 return loop;
397}
398
400LoopNode::add_backedge(std::shared_ptr<const jlm::rvsdg::Type> type)
401{
402 auto & argument_loop = BackEdgeArgument::create(subregion(), std::move(type));
404 argument_loop.result_ = &result_loop;
405 result_loop.argument_ = &argument_loop;
406 return &argument_loop;
407}
408
409LoopNode *
411{
412 auto ln = new LoopNode(parent);
414 auto pred_arg = ln->add_backedge(rvsdg::ControlType::Create(2));
415 pred_arg->result()->divert_to(predicate);
416 // we need a buffer without pass-through behavior to avoid a combinatorial cycle of ready
417 // signals
419 ln->PredicateBuffer_ = PredicateBufferOperation::create(*pre_buffer)[0];
420 return ln;
421}
422
423void
425{
426 auto node = rvsdg::TryGetOwnerNode<Node>(*predicate()->origin());
428 if (node && node->IsDead())
429 remove(node);
430}
431
432std::shared_ptr<const BundleType>
433get_mem_req_type(std::shared_ptr<const rvsdg::Type> elementType, bool write)
434{
435 std::vector<std::pair<std::string, std::shared_ptr<const jlm::rvsdg::Type>>> elements;
436 elements.emplace_back("addr", llvm::PointerType::Create());
437 elements.emplace_back("size", jlm::rvsdg::BitType::Create(4));
438 elements.emplace_back("id", jlm::rvsdg::BitType::Create(8));
439 if (write)
440 {
441 elements.emplace_back("data", std::move(elementType));
442 elements.emplace_back("write", jlm::rvsdg::BitType::Create(1));
443 }
444 return std::make_shared<BundleType>(std::move(elements));
445}
446
447std::shared_ptr<const BundleType>
448get_mem_res_type(std::shared_ptr<const jlm::rvsdg::Type> dataType)
449{
450 std::vector<std::pair<std::string, std::shared_ptr<const jlm::rvsdg::Type>>> elements;
451 elements.emplace_back("data", std::move(dataType));
452 elements.emplace_back("id", jlm::rvsdg::BitType::Create(8));
453 return std::make_shared<BundleType>(std::move(elements));
454}
455
456int
458{
459 if (auto bt = dynamic_cast<const jlm::rvsdg::BitType *>(type))
460 {
461 return bt->nbits();
462 }
463 else if (auto at = dynamic_cast<const llvm::ArrayType *>(type))
464 {
465 return JlmSize(&at->element_type()) * at->nelements();
466 }
467 else if (auto vt = dynamic_cast<const llvm::VectorType *>(type))
468 {
469 return JlmSize(&vt->type()) * vt->size();
470 }
471 else if (dynamic_cast<const llvm::PointerType *>(type))
472 {
473 return GetPointerSizeInBits();
474 }
475 else if (auto ct = dynamic_cast<const rvsdg::ControlType *>(type))
476 {
477 return ceil(log2(ct->nalternatives()));
478 }
479 else if (type->Kind() == rvsdg::TypeKind::State)
480 {
481 return 1;
482 }
483 else if (rvsdg::is<BundleType>(*type))
484 {
485 // TODO: fix this ugly hack needed for get_node_name
486 return 0;
487 }
488 else if (auto ft = dynamic_cast<const llvm::FloatingPointType *>(type))
489 {
490 switch (ft->size())
491 {
493 return 16;
495 return 32;
497 return 64;
498 default:
499 throw std::logic_error("Size of '" + type->debug_string() + "' is not implemented!");
500 }
501 }
502 else
503 {
504 throw std::logic_error("Size of '" + type->debug_string() + "' is not implemented!");
505 }
506}
507
508size_t
510{
511 return 64;
512}
513}
static const auto vt
Definition PullTests.cpp:16
util::HashSet< rvsdg::Output * > arguments
BackEdgeResult * result()
Definition hls.hpp:632
static BackEdgeArgument & create(rvsdg::Region *region, std::shared_ptr< const jlm::rvsdg::Type > type)
Definition hls.hpp:641
BackEdgeArgument & Copy(rvsdg::Region &region, rvsdg::StructuralInput *input) const override
Definition hls.cpp:107
BackEdgeResult & Copy(rvsdg::Output &origin, rvsdg::StructuralOutput *output) const override
Definition hls.cpp:114
static BackEdgeResult & create(jlm::rvsdg::Output *origin)
Definition hls.hpp:673
static std::vector< jlm::rvsdg::Output * > create(jlm::rvsdg::Output &predicate, jlm::rvsdg::Output &value, bool loop=false)
Definition hls.hpp:68
~BranchOperation() noexcept override
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:93
const std::vector< std::pair< std::string, std::shared_ptr< const jlm::rvsdg::Type > > > elements_
Definition hls.hpp:951
std::size_t ComputeHash() const noexcept override
Definition hls.cpp:80
~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:129
static ExitResult & Create(rvsdg::Output &origin, rvsdg::StructuralOutput &output)
Definition hls.hpp:700
~ExitResult() noexcept override
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:344
static LoopNode * create(rvsdg::Region *parent)
Definition hls.cpp:410
void removeEntryVars(std::vector< EntryVar > vars)
Definition hls.cpp:294
rvsdg::Output * addRequestOutput(rvsdg::Output *origin)
Definition hls.cpp:190
std::variant< ExitVar, BackEdgeVar > mapResult(const rvsdg::Input &result)
Definition hls.cpp:239
std::vector< BackEdgeVar > getBackEdgeVars()
Definition hls.cpp:279
rvsdg::RegionResult * predicate() const noexcept
Definition hls.hpp:806
rvsdg::Output * PredicateBuffer_
Definition hls.hpp:880
rvsdg::Output & GetPredicateBuffer() const noexcept
Definition hls.hpp:814
rvsdg::StructuralOutput * AddLoopVar(rvsdg::Output *origin, rvsdg::Output **buffer=nullptr)
Definition hls.cpp:135
std::variant< EntryVar, BackEdgeVar > mapArgument(const rvsdg::Output &argument)
Definition hls.cpp:221
void removeExitVars(std::vector< ExitVar > vars)
Definition hls.cpp:311
const rvsdg::Operation & GetOperation() const noexcept override
Definition hls.cpp:198
void set_predicate(jlm::rvsdg::Output *p)
Definition hls.cpp:424
void removeBackEdgeVars(std::vector< BackEdgeVar > vars)
Definition hls.cpp:328
rvsdg::Region * subregion() const noexcept
Definition hls.hpp:800
rvsdg::Output * addLoopConstant(rvsdg::Output *origin)
Definition hls.cpp:171
rvsdg::Output * addResponseInput(rvsdg::Output *origin)
Definition hls.cpp:182
ExitVar mapOutput(const rvsdg::Output &output)
Definition hls.cpp:213
BackEdgeArgument * add_backedge(std::shared_ptr< const jlm::rvsdg::Type > type)
Definition hls.cpp:400
EntryVar mapInput(const rvsdg::Input &input)
Definition hls.cpp:205
std::vector< ExitVar > getExitVars()
Definition hls.cpp:268
std::vector< EntryVar > getEntryVars()
Definition hls.cpp:257
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:67
static std::shared_ptr< const TriggerType > Create()
Definition hls.cpp:73
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
Region * region() const noexcept
Definition node.cpp:83
OutputIteratorRange Outputs() noexcept
Definition node.hpp:657
size_t RemoveInputs(const util::HashSet< size_t > &indices)
Definition node.cpp:306
rvsdg::Region * region() const noexcept
Definition node.hpp:761
InputIteratorRange Inputs() noexcept
Definition node.hpp:622
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
void divert_users(jlm::rvsdg::Output *new_origin)
Definition node.hpp:301
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
Represents the argument of a region.
Definition region.hpp:41
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:281
RegionArgument * argument(size_t index) const noexcept
Definition region.hpp:466
RegionArgumentRange Arguments() noexcept
Definition region.hpp:319
void copy(Region *target, SubstitutionMap &smap) const
Copy a region with substitutions.
Definition region.cpp:317
size_t RemoveArguments(const util::HashSet< size_t > &indices)
Definition region.cpp:213
size_t narguments() const noexcept
Definition region.hpp:460
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
virtual std::string debug_string() const =0
virtual TypeKind Kind() const noexcept=0
Return the kind of this type.
bool insert(ItemType item)
Definition HashSet.hpp:210
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:448
std::shared_ptr< const BundleType > get_mem_req_type(std::shared_ptr< const rvsdg::Type > elementType, bool write)
Definition hls.cpp:433
int JlmSize(const jlm::rvsdg::Type *type)
Definition hls.cpp:457
size_t GetPointerSizeInBits()
Definition hls.cpp:509
static void remove(Node *node)
Definition region.hpp:1035
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition node.hpp:1058
TypeKind
The kinds of types supported in rvsdg.
Definition type.hpp:22
@ State
Designate a state type.
@ Value
Designate a value type.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
void combineHashesWithSeed(std::size_t &seed, std::size_t hash, Args... args)
Definition Hash.hpp:45
Variable passed between hls loop iterations.
Definition hls.hpp:752
Variable entering the hls loop.
Definition hls.hpp:722
rvsdg::Input * input
Variable at loop entry.
Definition hls.hpp:726
Variable exiting the hls loop.
Definition hls.hpp:737