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