summaryrefslogtreecommitdiff
path: root/compiler/enco/core/src/CppGen/Host.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/enco/core/src/CppGen/Host.cpp')
-rw-r--r--compiler/enco/core/src/CppGen/Host.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/compiler/enco/core/src/CppGen/Host.cpp b/compiler/enco/core/src/CppGen/Host.cpp
new file mode 100644
index 000000000..37e0583d7
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/Host.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Host.h"
+
+#include <pp/EnclosedDocument.h>
+
+#include <stdex/Memory.h>
+
+#include <map>
+#include <string>
+
+namespace
+{
+
+/**
+ * @brief Data transfer between flat arrays
+ *
+ * Transfer(from, into) denotes the following C code:
+ * dst[into] = src[from];
+ */
+class Transfer
+{
+public:
+ Transfer(uint32_t from, uint32_t into) : _from{from}, _into{into}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t from(void) const { return _from; }
+ uint32_t into(void) const { return _into; }
+
+private:
+ uint32_t _from;
+ uint32_t _into;
+};
+
+using TransferSequence = std::vector<Transfer>;
+
+/**
+ * @brief Convert Shuffle instruction as a sequence of data transfer
+ */
+TransferSequence as_transfer_sequence(const coco::Shuffle *shuffle)
+{
+ TransferSequence seq;
+
+ for (const auto &dst : shuffle->range())
+ {
+ const auto src = shuffle->at(dst);
+ seq.emplace_back(src.value(), dst.value());
+ }
+
+ return seq;
+}
+
+/**
+ * Given a sequence of N data transfers,
+ * find_loop tries to compute count, src_step, dst_step that satisfies
+ * the following properties:
+ *
+ * First, N should be a multiple of count.
+ * Below we refer to that multiplier as 'window' (= N / count)
+ *
+ * Second,
+ * for all n in [0, count),
+ * for all k in [0, window),
+ * from[n * window + k] == from[k] + src_step, and
+ * into[n * window + k] == into[k] + dst_step
+ */
+bool find_loop(TransferSequence::const_iterator beg, TransferSequence::const_iterator end,
+ uint32_t *p_count, uint32_t *p_src_step, uint32_t *p_dst_step)
+{
+ assert(p_count != nullptr);
+ assert(p_src_step != nullptr);
+ assert(p_dst_step != nullptr);
+
+ const uint32_t size = end - beg;
+
+ for (uint32_t window = 1; window <= size; ++window)
+ {
+ if (size % window != 0)
+ {
+ continue;
+ }
+
+ auto src_step_at = [&beg, window](uint32_t n) {
+ return (beg + n)->from() - (beg + n - window)->from();
+ };
+
+ auto dst_step_at = [&beg, window](uint32_t n) {
+ return (beg + n)->into() - (beg + n - window)->into();
+ };
+
+ const uint32_t count = size / window;
+ const uint32_t src_step = src_step_at(window);
+ const uint32_t dst_step = dst_step_at(window);
+
+ bool consistent = true;
+
+ for (uint32_t n = window + 1; n < size; ++n)
+ {
+ if ((src_step_at(n) != src_step) || (dst_step_at(n) != dst_step))
+ {
+ consistent = false;
+ break;
+ }
+ }
+
+ if (consistent)
+ {
+ *p_count = count;
+ *p_src_step = src_step;
+ *p_dst_step = dst_step;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief Single transfer loop (a triple of count, source step, detination step)
+ */
+class TransferLoop
+{
+public:
+ class Step
+ {
+ public:
+ Step(uint32_t src, uint32_t dst) : _src{src}, _dst{dst}
+ {
+ // DO NOTHING
+ }
+
+ public:
+ uint32_t src(void) const { return _src; }
+ uint32_t dst(void) const { return _dst; }
+
+ private:
+ uint32_t _src;
+ uint32_t _dst;
+ };
+
+public:
+ TransferLoop(uint32_t count, uint32_t src_step, uint32_t dst_step)
+ : _count{count}, _step{src_step, dst_step}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t count(void) const { return _count; }
+ const Step &step(void) const { return _step; }
+
+private:
+ uint32_t _count;
+ Step _step;
+};
+
+/**
+ * @brief Nested transfer loops
+ */
+using TransferNest = std::vector<TransferLoop>;
+
+/**
+ * @brief Construct nested transfer loop-nest that correponds to a given Shuffle instruction
+ */
+TransferNest as_nest(const TransferSequence &seq)
+{
+ TransferNest nest;
+
+ auto beg = seq.begin();
+ auto end = seq.end();
+
+ uint32_t window = end - beg;
+ uint32_t count = 0;
+ uint32_t src_step = 0;
+ uint32_t dst_step = 0;
+
+ while ((window > 1) && find_loop(beg, end, &count, &src_step, &dst_step))
+ {
+ assert(window % count == 0);
+
+ window /= count;
+ end = beg + window;
+
+ nest.emplace_back(count, src_step, dst_step);
+ }
+
+ return nest;
+};
+
+uint32_t loop_count(const TransferNest &nest)
+{
+ uint32_t count = 1;
+
+ for (const auto &loop : nest)
+ {
+ count *= loop.count();
+ }
+
+ return count;
+};
+
+class InstrPrinter : public coco::Instr::Visitor<pp::LinearDocument>
+{
+public:
+ InstrPrinter(const enco::MemoryContext &mem) : _mem(mem)
+ {
+ // DO NOTHING
+ }
+
+private:
+ pp::LinearDocument visit(const coco::Shuffle *shuffle) override
+ {
+ auto from = shuffle->from();
+ auto into = shuffle->into();
+
+ //
+ // Analyze 'Shuffle' pattern, and convert it as nested loops
+ //
+ auto tseq = as_transfer_sequence(shuffle);
+ auto nest = as_nest(tseq);
+ assert(tseq.size() % loop_count(nest) == 0);
+ uint32_t window = tseq.size() / loop_count(nest);
+
+ //
+ // Generate loop body
+ //
+ pp::EnclosedDocument loop_body;
+
+ auto var_at = [](uint32_t lv) { return pp::fmt("_", lv); };
+
+ for (uint32_t lv = 0; lv < nest.size(); ++lv)
+ {
+ auto var = var_at(lv);
+
+ loop_body.front().append("for (uint32_t ", var, " = 0; ", var, " < ", nest.at(lv).count(),
+ "; ++", var, ") {");
+ loop_body.front().indent();
+
+ loop_body.back().append("}");
+ loop_body.back().indent();
+ }
+
+ std::string src_index = "0";
+ std::string dst_index = "0";
+
+ for (uint32_t lv = 0; lv < nest.size(); ++lv)
+ {
+ src_index += pp::fmt(" + ", nest.at(lv).step().src(), " * ", var_at(lv));
+ dst_index += pp::fmt(" + ", nest.at(lv).step().dst(), " * ", var_at(lv));
+ }
+
+ for (uint32_t n = 0; n < window; ++n)
+ {
+ const auto src_base = pp::fmt("reinterpret_cast<const float *>(", _mem.base(from), ")");
+ const auto dst_base = pp::fmt("reinterpret_cast<float *>(", _mem.base(into), ")");
+
+ loop_body.front().append(dst_base, "[", dst_index, " + ", tseq.at(n).into(), "] = ", src_base,
+ "[", src_index, " + ", tseq.at(n).from(), "];");
+ }
+
+ pp::LinearDocument res;
+ res.append(loop_body);
+ return res;
+ }
+
+private:
+ const enco::MemoryContext &_mem;
+};
+
+} // namespace
+
+namespace enco
+{
+
+std::unique_ptr<pp::MultiLineText> HostBlockCompiler::compile(const coco::Block *blk) const
+{
+ InstrPrinter prn{_mem};
+
+ auto res = stdex::make_unique<pp::LinearDocument>();
+
+ for (auto ins = blk->instr()->head(); ins; ins = ins->next())
+ {
+ res->append(ins->accept(prn));
+ }
+
+ return std::move(res);
+}
+
+} // namespace enco