be496b4934d16a53f71557b394ea000d280ce4b7
[platform/core/ml/nnfw.git] / contrib / enco / core / src / Transforms / DataLayoutConversion.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "DataLayoutConversion.h"
18 #include "Session.h"
19
20 #include <coco/IR/FeatureLayouts.h>
21 #include <coco/IR/KernelLayouts.h>
22
23 #include <nncc/core/ADT/feature/Layout.h>
24 #include <nncc/core/ADT/kernel/Layout.h>
25
26 #include <nncc/core/ADT/feature/HWCLayout.h>
27 #include <nncc/core/ADT/kernel/NHWCLayout.h>
28
29 #include <nncc/foundation/Memory.h>
30
31 #include <set>
32
33 using nncc::foundation::make_unique;
34
35 namespace
36 {
37
38 coco::Copy *make_copy(coco::FeatureObject *from, coco::FeatureObject *into)
39 {
40   auto m = from->module();
41   assert(m != nullptr);
42   assert(from->module() == into->module());
43
44   auto copy = m->entity()->instr()->create<coco::Copy>();
45
46   copy->from(from);
47   copy->into(into);
48
49   return copy;
50 }
51
52 coco::FeatureObject *clone_feature(const coco::FeatureObject *oldobj)
53 {
54   auto module = oldobj->module();
55   auto newobj = module->entity()->object()->create(oldobj->shape());
56   newobj->layout(coco::FeatureLayouts::BHWC::create(oldobj->shape()));
57
58   if (auto oldbag = oldobj->bag())
59   {
60     assert(oldbag->module() == module);
61     // Clone bag only when there is a backing bag for a given feature object
62     auto newbag = module->entity()->bag()->create(oldbag->size());
63     newobj->bag(newbag);
64   }
65
66   return newobj;
67 }
68
69 /**
70  * @brief Insert Copy before Load if necessary
71  */
72 void insert_copy_before_load(coco::Load *load)
73 {
74   if (auto obj = load->object())
75   {
76     if (auto ifm = obj->asFeature())
77     {
78       if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
79       {
80         auto oldobj = ifm;
81         auto newobj = clone_feature(oldobj);
82
83         load->object(newobj);
84
85         auto copy = make_copy(oldobj, newobj);
86         copy->insertBefore(load->parent());
87       }
88     }
89   }
90 }
91
92 /**
93  * @brief Insert Copy after Eval if necessary
94  */
95 void insert_copy_after_eval(coco::Eval *eval)
96 {
97   if (auto out = eval->out())
98   {
99     if (auto ofm = out->asFeature())
100     {
101       if (ofm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
102       {
103         auto oldobj = ofm;
104         auto newobj = clone_feature(oldobj);
105
106         eval->out(newobj);
107
108         auto copy = make_copy(newobj, oldobj);
109         copy->insertAfter(eval);
110       }
111     }
112   }
113 }
114
115 /**
116  * @brief Update convolution kernel data layout
117  */
118 void change_conv2d_kernel_layout(coco::Conv2D *conv)
119 {
120   auto m = conv->module();
121   assert(m != nullptr);
122   auto d = enco::data(enco::session(m));
123   assert(d != nullptr);
124
125   auto old_obj = conv->ker();
126   assert(old_obj != nullptr);
127   auto old_bag = old_obj->bag();
128   assert(old_bag != nullptr);
129
130   if (old_obj->layout()->id() == coco::KernelLayouts::NHWC::uid())
131   {
132     // Skip if kernel already uses NHWC layout
133     return;
134   }
135
136   const auto &ker_shape = old_obj->shape();
137
138   assert(d->allocated(old_bag));
139
140   auto new_bag = m->entity()->bag()->create(old_bag->size());
141   auto new_obj = m->entity()->object()->create<coco::KernelObject>();
142
143   new_obj->bag(new_bag);
144   new_obj->layout(coco::KernelLayouts::NHWC::create(ker_shape));
145
146   d->f32()->allocate(new_obj);
147
148   auto src = d->f32()->read(old_obj);
149   auto dst = d->f32()->access(new_obj);
150
151   const auto ker_N = ker_shape.count();
152   const auto ker_C = ker_shape.depth();
153   const auto ker_H = ker_shape.height();
154   const auto ker_W = ker_shape.width();
155
156   for (uint32_t n = 0; n < ker_N; ++n)
157   {
158     for (uint32_t ch = 0; ch < ker_C; ++ch)
159     {
160       for (uint32_t row = 0; row < ker_H; ++row)
161       {
162         for (uint32_t col = 0; col < ker_W; ++col)
163         {
164           dst->at(n, ch, row, col) = src->at(n, ch, row, col);
165         }
166       }
167     }
168   }
169
170   conv->ker(new_obj);
171   d->release(old_bag);
172 }
173
174 } // namespace
175
176 namespace
177 {
178
179 /**
180  * @brief Return the set of all of allocated Load Op(s) in a given module
181  */
182 std::set<coco::Load *> loads(coco::Module *m)
183 {
184   std::set<coco::Load *> res;
185
186   for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
187   {
188     if (auto load = m->entity()->op()->at(n)->asLoad())
189     {
190       res.insert(load);
191     }
192   }
193
194   return res;
195 }
196
197 /**
198  * @brief Return the set of every (allocated) Eval instruction in a given module
199  */
200 std::set<coco::Eval *> evals(coco::Module *m)
201 {
202   std::set<coco::Eval *> res;
203
204   for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
205   {
206     if (auto eval = m->entity()->instr()->at(n)->asEval())
207     {
208       res.insert(eval);
209     }
210   }
211
212   return res;
213 }
214
215 /**
216  * @brief Return the set of allocated Conv2D op in a given module
217  */
218 std::set<coco::Conv2D *> convs(coco::Module *m)
219 {
220   std::set<coco::Conv2D *> res;
221
222   for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
223   {
224     if (auto op = m->entity()->op()->at(n)->asConv2D())
225     {
226       res.insert(op);
227     }
228   }
229
230   return res;
231 }
232
233 class NormalizePass
234 {
235 private:
236   void runOnModule(coco::Module *m) const;
237
238 public:
239   void runOnCode(enco::Code *) const;
240 };
241
242 void NormalizePass::runOnModule(coco::Module *m) const
243 {
244   // Insert Copy before all Load Op (if necessary)
245   for (auto load : loads(m))
246   {
247     insert_copy_before_load(load);
248   }
249
250   // Insert Copy after all Eval Instr (if necessary)
251   for (auto eval : evals(m))
252   {
253     insert_copy_after_eval(eval);
254   }
255
256   // Change Kernel Layout of Conv2D opertion (if necessary)
257   for (auto conv : convs(m))
258   {
259     change_conv2d_kernel_layout(conv);
260   }
261 }
262
263 void NormalizePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
264
265 } // namespace
266
267 namespace enco
268 {
269
270 void convert_data_layout(enco::Code *code)
271 {
272   NormalizePass pass;
273   pass.runOnCode(code);
274 }
275
276 } // namespace enco