• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2020-2021 Huawei Technologies Co., Ltd
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 "src/delegate/npu/pass/npu_pass_utils.h"
18 #include <algorithm>
19 #include "src/delegate/npu/op/scale_npu.h"
20 #include "src/delegate/npu/op/transpose_npu.h"
21 #include "src/delegate/npu/npu_converter_utils.h"
22 
23 namespace mindspore {
24 std::unordered_map<schema::PrimitiveType, std::set<int>> nodes2const_index{
25   {schema::PrimitiveType_Split, {1}},
26   {schema::PrimitiveType_PadFusion, {1}},
27   {schema::PrimitiveType_StridedSlice, {1, 2, 3}}};
28 
CreateNchw2NhwcOp(const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & out_tensors,const std::string & name)29 NPUOp *NPUPassUtils::CreateNchw2NhwcOp(const std::vector<mindspore::MSTensor> &in_tensors,
30                                        const std::vector<mindspore::MSTensor> &out_tensors, const std::string &name) {
31   std::vector<int> perm = {0, 2, 3, 1};
32   auto npu_op = new (std::nothrow) TransposeNPUOp(in_tensors, out_tensors, perm, name);
33   if (npu_op == nullptr) {
34     MS_LOG(ERROR) << "New Nchw2Nhwc NPUOp failed.";
35     return nullptr;
36   }
37   return npu_op;
38 }
39 
CreateNhwc2NchwOp(const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & out_tensors,const std::string & name)40 NPUOp *NPUPassUtils::CreateNhwc2NchwOp(const std::vector<mindspore::MSTensor> &in_tensors,
41                                        const std::vector<mindspore::MSTensor> &out_tensors, const std::string &name) {
42   std::vector<int> perm = {0, 3, 1, 2};
43   auto npu_op = new (std::nothrow) TransposeNPUOp(in_tensors, out_tensors, perm, name);
44   if (npu_op == nullptr) {
45     MS_LOG(ERROR) << "New Nhwc2Nchw NPUOp failed.";
46     return nullptr;
47   }
48   return npu_op;
49 }
50 
UpdateOp(NPUOp * op,const std::vector<NPUOp * > & in_ops,const std::vector<NPUOp * > & out_ops,const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & outputs)51 void NPUPassUtils::UpdateOp(NPUOp *op, const std::vector<NPUOp *> &in_ops, const std::vector<NPUOp *> &out_ops,
52                             const std::vector<mindspore::MSTensor> &in_tensors,
53                             const std::vector<mindspore::MSTensor> &outputs) {
54   op->set_inputs(in_tensors);
55   op->set_outputs(outputs);
56   op->set_in_ops(in_ops);
57   op->set_out_ops(out_ops);
58 }
59 
UpdateNH2NCTransNodePreOp(NPUOp * pre_op,NPUOp * trans_op,NPUOp * op)60 void NPUPassUtils::UpdateNH2NCTransNodePreOp(NPUOp *pre_op, NPUOp *trans_op, NPUOp *op) {
61   // For op before trans, update the out_ops; the output tensor of op is the input tensor of trans.
62   std::vector<NPUOp *> out_ops = pre_op->out_ops();
63   size_t i = 0;
64   for (; i < out_ops.size(); i++) {
65     if (out_ops[i] == op) {
66       out_ops[i] = trans_op;
67       break;
68     }
69   }
70   if (i == out_ops.size()) {
71     out_ops.push_back(trans_op);
72   }
73   pre_op->set_out_ops(out_ops);
74 }
75 
UpdateNC2NHTransNodePreOp(NPUOp * pre_op,const std::vector<NPUOp * > & trans_ops,const std::vector<NPUOp * > & ops)76 void NPUPassUtils::UpdateNC2NHTransNodePreOp(NPUOp *pre_op, const std::vector<NPUOp *> &trans_ops,
77                                              const std::vector<NPUOp *> &ops) {
78   // For op before trans, there may be multiple outputs.
79   auto cur_out_ops = pre_op->out_ops();
80   for (size_t i = 0; i < ops.size(); i++) {
81     auto itr = find(cur_out_ops.begin(), cur_out_ops.end(), ops[i]);
82     if (itr != cur_out_ops.end()) {
83       cur_out_ops.erase(itr);
84     }
85   }
86   std::copy(trans_ops.begin(), trans_ops.end(), std::back_inserter(cur_out_ops));
87   pre_op->set_out_ops(cur_out_ops);
88   // For op before trans, the output tensor is used for output tensor of trans, so replace the output tensor
89   // with the input tensor of trans.
90   pre_op->set_outputs({trans_ops.at(0)->inputs().at(0)});
91 }
92 
UpdateNH2NCTransNodePostOp(NPUOp * trans_op,NPUOp * post_op)93 void NPUPassUtils::UpdateNH2NCTransNodePostOp(NPUOp *trans_op, NPUOp *post_op) {
94   auto cur_in_tensors = post_op->inputs();
95   cur_in_tensors[0] = trans_op->outputs()[0];
96   post_op->set_inputs(cur_in_tensors);
97   post_op->set_in_ops({trans_op});
98 }
99 
UpdateNC2NHPostOpInTensors(NPUOp * op,NPUOp * trans_op,NPUOp * post_op)100 void NPUPassUtils::UpdateNC2NHPostOpInTensors(NPUOp *op, NPUOp *trans_op, NPUOp *post_op) {
101   // For post_op that doesn't require insert trans op, because the output tensor of op(input tensor of
102   // trans_op) is updated, replace the input tensor of post_op.
103   auto post_in_tensors = post_op->inputs();
104   for (size_t i = 0; i < post_in_tensors.size(); i++) {
105     if (post_in_tensors[i] == op->outputs()[0]) {
106       post_in_tensors[i] = trans_op->inputs()[0];
107       break;
108     }
109   }
110   post_op->set_inputs(post_in_tensors);
111 }
112 
UpdateNC2NHTransNodePostOp(NPUOp * op,NPUOp * trans_op,NPUOp * post_op)113 void NPUPassUtils::UpdateNC2NHTransNodePostOp(NPUOp *op, NPUOp *trans_op, NPUOp *post_op) {
114   // The input tensor should be replaced with the output tensor of trans_op.
115   auto post_in_tensors = post_op->inputs();
116   mindspore::MSTensor old_in_tensor;
117   // find out which input tensor of post_op should be updated
118   for (size_t i = 0; i < post_in_tensors.size(); ++i) {
119     if (OpInputFromOp(post_op, post_in_tensors.at(i)) == op) {
120       old_in_tensor = post_in_tensors.at(i);
121       break;
122     }
123   }
124   if (old_in_tensor == nullptr) {
125     MS_LOG(WARNING) << "Could not find in tensor index";
126     return;
127   }
128   std::replace(post_in_tensors.begin(), post_in_tensors.end(), old_in_tensor, trans_op->outputs().at(0));
129   post_op->set_inputs(post_in_tensors);
130 
131   // For post_op after trans, op in in_ops should be replaced with trans_op.
132   auto post_in_ops = post_op->in_ops();
133   if (op == nullptr) {
134     post_in_ops.push_back(trans_op);
135   } else {
136     std::replace(post_in_ops.begin(), post_in_ops.end(), op, trans_op);
137   }
138   post_op->set_in_ops(post_in_ops);
139 }
140 
IsNhwc2Nchw(NPUOp * op)141 bool NPUPassUtils::IsNhwc2Nchw(NPUOp *op) {
142   if (op == nullptr) {
143     return false;
144   }
145   if (op->type() != schema::PrimitiveType_Transpose) {
146     return false;
147   }
148   auto transpose_op = static_cast<TransposeNPUOp *>(op);
149   std::vector<int> perm = transpose_op->GetPerm();
150   std::vector<int> nh2nc_perm = {0, 3, 1, 2};
151   if (perm != nh2nc_perm) {
152     return false;
153   }
154   return true;
155 }
156 
IsNchw2Nhwc(NPUOp * op)157 bool NPUPassUtils::IsNchw2Nhwc(NPUOp *op) {
158   if (op == nullptr) {
159     return false;
160   }
161   if (op->type() != schema::PrimitiveType_Transpose) {
162     return false;
163   }
164   auto transpose_op = static_cast<TransposeNPUOp *>(op);
165   std::vector<int> perm = transpose_op->GetPerm();
166   std::vector<int> nc2nh_perm = {0, 2, 3, 1};
167   if (perm != nc2nh_perm) {
168     return false;
169   }
170   return true;
171 }
172 
OpInputFromOp(NPUOp * op,mindspore::MSTensor in_tensor)173 NPUOp *NPUPassUtils::OpInputFromOp(NPUOp *op, mindspore::MSTensor in_tensor) {
174   // given op and input tensor index, get which op output this tensor.
175   // If input tensor is graph input, return nullptr.
176   if (op == nullptr) {
177     return nullptr;
178   }
179   auto in_ops = op->in_ops();
180   auto output_contain = [in_tensor](NPUOp *op) {
181     auto outputs = op->outputs();
182     return std::find(outputs.begin(), outputs.end(), in_tensor) != outputs.end();
183   };
184   auto it = std::find_if(in_ops.begin(), in_ops.end(), output_contain);
185   if (it == in_ops.end()) {
186     return nullptr;
187   }
188   return *it;
189 }
190 
GetNonConstInputs(NPUOp * op)191 std::vector<mindspore::MSTensor> NPUPassUtils::GetNonConstInputs(NPUOp *op) {
192   if (op == nullptr) {
193     return std::vector<mindspore::MSTensor>{};
194   }
195   auto type = op->type();
196   auto it = nodes2const_index.find(type);
197   if (it != nodes2const_index.end()) {
198     auto const_input_indices = it->second;
199     std::vector<mindspore::MSTensor> non_const_in_tensors;
200     auto in_tensors = op->inputs();
201     for (auto i = 0; i < in_tensors.size(); ++i) {
202       if (const_input_indices.find(i) == const_input_indices.end()) {
203         non_const_in_tensors.push_back(in_tensors[i]);
204       }
205     }
206     return non_const_in_tensors;
207   }
208   return op->inputs();
209 }
210 
Scale4dCase(NPUOp * op)211 bool NPUPassUtils::Scale4dCase(NPUOp *op) {
212   if (op == nullptr) {
213     return false;
214   }
215   if (op->type() != schema::PrimitiveType_ScaleFusion) {
216     return false;
217   }
218   auto scale_op = static_cast<ScaleNPUOp *>(op);
219   auto axis = scale_op->GetAxis();
220   auto in_tensor = op->inputs().at(0);
221   auto scale_tensor = op->inputs().at(1);
222   return in_tensor.Shape().size() == NPU_SHAPE_SIZE && scale_tensor.Shape().size() == 1 &&
223          (axis == NHWC_C || axis == -1);
224 }
225 
AssistDataNHWC2NCHW(int * data,size_t unit_size)226 void NPUPassUtils::AssistDataNHWC2NCHW(int *data, size_t unit_size) {
227   MS_ASSERT(data != nullptr);
228   for (size_t i = 0; i < unit_size; ++i) {
229     int c = data[3 * unit_size + i];
230     // n h w c
231     // n c h w
232     data[3 * unit_size + i] = data[2 * unit_size + i];
233     data[2 * unit_size + i] = data[unit_size + i];
234     data[unit_size + i] = c;
235   }
236 }
237 
MaskDataNHWC2NCHW(int mask)238 int NPUPassUtils::MaskDataNHWC2NCHW(int mask) {
239   int mask_vec[4];
240   for (int i = 0; i < 4; ++i) {
241     mask_vec[i] = (uint32_t)(mask) & (1 << i);
242   }
243   AssistDataNHWC2NCHW(mask_vec, 1);
244   int ret = 0;
245   for (int i = 0; i < 4; ++i) {
246     if (mask_vec[i]) {
247       ret += 1 << i;
248     }
249   }
250   return ret;
251 }
252 }  // namespace mindspore
253