83d4da1b77516732c562772ea9b78dbb4af6a077
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / src / compiler / gcompiler.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2018 Intel Corporation
6
7
8 #include "precomp.hpp"
9
10 #include <vector>
11 #include <stack>
12 #include <unordered_map>
13
14 #include <ade/util/algorithm.hpp>      // any_of
15 #include <ade/util/zip_range.hpp>      // zip_range, indexed
16
17 #include <ade/graph.hpp>
18 #include <ade/passes/check_cycles.hpp>
19
20 #include "api/gcomputation_priv.hpp"
21 #include "api/gnode_priv.hpp"   // FIXME: why it is here?
22 #include "api/gproto_priv.hpp"  // FIXME: why it is here?
23 #include "api/gcall_priv.hpp"   // FIXME: why it is here?
24
25 #include "api/gbackend_priv.hpp" // Backend basic API (newInstance, etc)
26
27 #include "compiler/gmodel.hpp"
28 #include "compiler/gmodelbuilder.hpp"
29 #include "compiler/gcompiler.hpp"
30 #include "compiler/gcompiled_priv.hpp"
31 #include "compiler/passes/passes.hpp"
32
33 #include "executor/gexecutor.hpp"
34 #include "backends/common/gbackend.hpp"
35
36 // <FIXME:>
37 #if !defined(GAPI_STANDALONE)
38 #include "opencv2/gapi/cpu/core.hpp"    // Also directly refer to Core
39 #include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations
40 #endif // !defined(GAPI_STANDALONE)
41 // </FIXME:>
42
43 #include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
44
45 #include "logger.hpp"
46
47 namespace
48 {
49     cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args)
50     {
51         static auto ocv_pkg =
52 #if !defined(GAPI_STANDALONE)
53             combine(cv::gapi::core::cpu::kernels(),
54                     cv::gapi::imgproc::cpu::kernels(),
55                     cv::unite_policy::KEEP);
56 #else
57             cv::gapi::GKernelPackage();
58 #endif // !defined(GAPI_STANDALONE)
59         auto user_pkg = cv::gimpl::getCompileArg<cv::gapi::GKernelPackage>(args);
60         return combine(ocv_pkg, user_pkg.value_or(cv::gapi::GKernelPackage{}), cv::unite_policy::REPLACE);
61     }
62
63     cv::util::optional<std::string> getGraphDumpDirectory(cv::GCompileArgs& args)
64     {
65         auto dump_info = cv::gimpl::getCompileArg<cv::graph_dump_path>(args);
66         if (!dump_info.has_value())
67         {
68             const char* path = std::getenv("GRAPH_DUMP_PATH");
69             return path
70                 ? cv::util::make_optional(std::string(path))
71                 : cv::util::optional<std::string>();
72         }
73         else
74         {
75             return cv::util::make_optional(dump_info.value().m_dump_path);
76         }
77     }
78 } // anonymous namespace
79
80
81 // GCompiler implementation ////////////////////////////////////////////////////
82
83 cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c,
84                                 GMetaArgs              &&metas,
85                                 GCompileArgs           &&args)
86     : m_c(c), m_metas(std::move(metas)), m_args(std::move(args))
87 {
88     using namespace std::placeholders;
89     m_all_kernels       = getKernelPackage(m_args);
90     auto lookup_order   = getCompileArg<gapi::GLookupOrder>(m_args).value_or(gapi::GLookupOrder());
91     auto dump_path      = getGraphDumpDirectory(m_args);
92
93     m_e.addPassStage("init");
94     m_e.addPass("init", "check_cycles",  ade::passes::CheckCycles());
95     m_e.addPass("init", "expand_kernels",  std::bind(passes::expandKernels, _1,
96                                                      m_all_kernels)); // NB: package is copied
97     m_e.addPass("init", "topo_sort",     ade::passes::TopologicalSort());
98     m_e.addPass("init", "init_islands",  passes::initIslands);
99     m_e.addPass("init", "check_islands", passes::checkIslands);
100     // TODO:
101     // - Check basic graph validity (i.e., all inputs are connected)
102     // - Complex dependencies (i.e. parent-child) unrolling
103     // - etc, etc, etc
104
105     // Remove GCompoundBackend to avoid calling setupBackend() with it in the list
106     m_all_kernels.remove(cv::gapi::compound::backend());
107
108     m_e.addPassStage("kernels");
109     m_e.addPass("kernels", "resolve_kernels", std::bind(passes::resolveKernels, _1,
110                                                      std::ref(m_all_kernels), // NB: and not copied here
111                                                      lookup_order));
112     m_e.addPass("kernels", "check_islands_content", passes::checkIslandsContent);
113
114     m_e.addPassStage("meta");
115     m_e.addPass("meta", "initialize",   std::bind(passes::initMeta, _1, std::ref(m_metas)));
116     m_e.addPass("meta", "propagate",    std::bind(passes::inferMeta, _1, false));
117     m_e.addPass("meta", "finalize",     passes::storeResultingMeta);
118     // moved to another stage, FIXME: two dumps?
119     //    m_e.addPass("meta", "dump_dot",     passes::dumpDotStdout);
120
121     // Special stage for backend-specific transformations
122     // FIXME: document passes hierarchy and order for backend developers
123     m_e.addPassStage("transform");
124
125     m_e.addPassStage("exec");
126     m_e.addPass("exec", "fuse_islands",     passes::fuseIslands);
127     m_e.addPass("exec", "sync_islands",     passes::syncIslandTags);
128
129     if (dump_path.has_value())
130     {
131         m_e.addPass("exec", "dump_dot", std::bind(passes::dumpGraph, _1,
132                                                   dump_path.value()));
133     }
134
135     // Process backends at the last moment (after all G-API passes are added).
136     ade::ExecutionEngineSetupContext ectx(m_e);
137     auto backends = m_all_kernels.backends();
138     for (auto &b : backends)
139     {
140         b.priv().addBackendPasses(ectx);
141     }
142 }
143
144 void cv::gimpl::GCompiler::validateInputMeta()
145 {
146     if (m_metas.size() != m_c.priv().m_ins.size())
147     {
148         util::throw_error(std::logic_error
149                     ("COMPILE: GComputation interface / metadata mismatch! "
150                      "(expected " + std::to_string(m_c.priv().m_ins.size()) + ", "
151                      "got " + std::to_string(m_metas.size()) + " meta arguments)"));
152     }
153
154     const auto meta_matches = [](const GMetaArg &meta, const GProtoArg &proto) {
155         switch (proto.index())
156         {
157         // FIXME: Auto-generate methods like this from traits:
158         case GProtoArg::index_of<cv::GMat>():
159         case GProtoArg::index_of<cv::GMatP>():
160             return util::holds_alternative<cv::GMatDesc>(meta);
161
162         case GProtoArg::index_of<cv::GScalar>():
163             return util::holds_alternative<cv::GScalarDesc>(meta);
164
165         case GProtoArg::index_of<cv::detail::GArrayU>():
166             return util::holds_alternative<cv::GArrayDesc>(meta);
167
168         default:
169             GAPI_Assert(false);
170         }
171         return false; // should never happen
172     };
173
174     for (const auto &meta_arg_idx : ade::util::indexed(ade::util::zip(m_metas, m_c.priv().m_ins)))
175     {
176         const auto &meta  = std::get<0>(ade::util::value(meta_arg_idx));
177         const auto &proto = std::get<1>(ade::util::value(meta_arg_idx));
178
179         if (!meta_matches(meta, proto))
180         {
181             const auto index  = ade::util::index(meta_arg_idx);
182             util::throw_error(std::logic_error
183                         ("GComputation object type / metadata descriptor mismatch "
184                          "(argument " + std::to_string(index) + ")"));
185             // FIXME: report what we've got and what we've expected
186         }
187     }
188     // All checks are ok
189 }
190
191 void cv::gimpl::GCompiler::validateOutProtoArgs()
192 {
193     for (const auto &out_pos : ade::util::indexed(m_c.priv().m_outs))
194     {
195         const auto &node = proto::origin_of(ade::util::value(out_pos)).node;
196         if (node.shape() != cv::GNode::NodeShape::CALL)
197         {
198             auto pos = ade::util::index(out_pos);
199             util::throw_error(std::logic_error
200                         ("Computation output " + std::to_string(pos) +
201                          " is not a result of any operation"));
202         }
203     }
204 }
205
206 cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::generateGraph()
207 {
208     validateInputMeta();
209     validateOutProtoArgs();
210
211     // Generate ADE graph from expression-based computation
212     std::unique_ptr<ade::Graph> pG(new ade::Graph);
213     ade::Graph& g = *pG;
214
215     GModel::Graph gm(g);
216     cv::gimpl::GModel::init(gm);
217     cv::gimpl::GModelBuilder builder(g);
218     auto proto_slots = builder.put(m_c.priv().m_ins, m_c.priv().m_outs);
219     GAPI_LOG_INFO(NULL, "Generated graph: " << g.nodes().size() << " nodes" << std::endl);
220
221     // Store Computation's protocol in metadata
222     Protocol p;
223     std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
224     gm.metadata().set(p);
225
226     return pG;
227 }
228
229 void cv::gimpl::GCompiler::runPasses(ade::Graph &g)
230 {
231     m_e.runPasses(g);
232     GAPI_LOG_INFO(NULL, "All compiler passes are successful");
233 }
234
235 void cv::gimpl::GCompiler::compileIslands(ade::Graph &g)
236 {
237     GModel::Graph gm(g);
238     std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model);
239     GIslandModel::Graph gim(*gptr);
240
241     // Run topological sort on GIslandModel first
242     auto pass_ctx = ade::passes::PassContext{*gptr};
243     ade::passes::TopologicalSort{}(pass_ctx);
244
245     // Now compile islands
246     GIslandModel::compileIslands(gim, g, m_args);
247 }
248
249 cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg)
250 {
251     // This is the final compilation step. Here:
252     // - An instance of GExecutor is created. Depening on the platform,
253     //   build configuration, etc, a GExecutor may be:
254     //   - a naive single-thread graph interpreter;
255     //   - a std::thread-based thing
256     //   - a TBB-based thing, etc.
257     // - All this stuff is wrapped into a GCompiled object and returned
258     //   to user.
259
260     // Note: this happens in the last pass ("compile_islands"):
261     // - Each GIsland of GIslandModel instantiates its own,
262     //   backend-specific executable object
263     //   - Every backend gets a subgraph to execute, and builds
264     //     an execution plan for it (backend-specific execution)
265     // ...before call to produceCompiled();
266
267     const auto &outMetas = GModel::ConstGraph(*pg).metadata()
268         .get<OutputMeta>().outMeta;
269     std::unique_ptr<GExecutor> pE(new GExecutor(std::move(pg)));
270     // FIXME: select which executor will be actually used,
271     // make GExecutor abstract.
272
273     GCompiled compiled;
274     compiled.priv().setup(m_metas, outMetas, std::move(pE));
275     return compiled;
276 }
277
278 cv::GCompiled cv::gimpl::GCompiler::compile()
279 {
280     std::unique_ptr<ade::Graph> pG = generateGraph();
281     runPasses(*pG);
282     compileIslands(*pG);
283     return produceCompiled(std::move(pG));
284 }