1 /**
2 * Copyright 2020-2022 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 #include "backend/common/graph_kernel/graph_kernel_helper.h"
17
18 #include <algorithm>
19 #include <map>
20 #include <set>
21 #include <tuple>
22 #include <utility>
23
24 #include "backend/common/graph_kernel/adapter/fake_abstract_shape.h"
25 #include "backend/common/graph_kernel/core/graph_builder.h"
26 #include "backend/common/graph_kernel/graph_kernel_flags.h"
27 #include "base/base.h"
28 #include "include/backend/anf_runtime_algorithm.h"
29 #include "include/common/utils/utils.h"
30 #include "include/common/utils/anfalgo.h"
31 #include "include/common/utils/python_adapter.h"
32 #include "ir/tensor.h"
33 #include "ir/func_graph.h"
34 #include "ir/func_graph_cloner.h"
35 #include "kernel/framework_utils.h"
36 #include "kernel/graph_kernel/akg/akg_kernel_json_decoder.h"
37 #include "kernel/graph_kernel/graph_kernel_json_generator.h"
38 #include "kernel/kernel.h"
39 #include "mindspore/core/ops/sequence_ops.h"
40 #include "pipeline/jit/ps/action.h"
41 #include "utils/hash_set.h"
42 #include "utils/check_convert_utils.h"
43
44 namespace mindspore::graphkernel {
45 namespace {
46 constexpr auto kPatternOpaque = "Opaque";
GenJson(const AnfNodePtrList & op_nodes,const std::pair<AnfNodePtrList,AnfNodePtrList> & in_and_out,const DumpOption & dump_option,nlohmann::json * op_desc,std::map<std::string,AnfNodePtr> * address_node_map=nullptr)47 bool GenJson(const AnfNodePtrList &op_nodes, const std::pair<AnfNodePtrList, AnfNodePtrList> &in_and_out,
48 const DumpOption &dump_option, nlohmann::json *op_desc,
49 std::map<std::string, AnfNodePtr> *address_node_map = nullptr) {
50 GraphKernelJsonGenerator graph_kernel_json_generator(dump_option);
51 if (!graph_kernel_json_generator.CollectFusedJson(op_nodes, in_and_out.first, in_and_out.second)) {
52 MS_LOG(ERROR) << "Collect json desc failed.";
53 return false;
54 }
55
56 *op_desc = graph_kernel_json_generator.kernel_json();
57 if (address_node_map != nullptr) {
58 *address_node_map = graph_kernel_json_generator.address_node_map();
59 }
60 std::string fused_name;
61 (void)std::for_each(op_nodes.begin(), op_nodes.end(), [&fused_name](const AnfNodePtr &node) {
62 (void)fused_name.append(common::AnfAlgo::GetCNodeName(node)).append("_");
63 });
64 MS_LOG(DEBUG) << "Collect fusion json: " << fused_name;
65 return true;
66 }
67 } // namespace
68
GetOutputAbstract(const AnfNodePtr & node,size_t output_idx)69 AbstractBasePtr GetOutputAbstract(const AnfNodePtr &node, size_t output_idx) {
70 auto out_spec = node->abstract();
71 if (out_spec->isa<abstract::AbstractTuple>()) {
72 return out_spec->cast<abstract::AbstractTuplePtr>()->elements()[output_idx];
73 }
74 return out_spec;
75 }
76
77 // Build for new node, processor comes from context
BuildSelectKernelBuildInfo(const std::vector<std::string> & inputs_format,const std::vector<TypeId> & inputs_type,const std::vector<std::string> & output_formats,const std::vector<TypeId> & output_types)78 kernel::KernelBuildInfoPtr BuildSelectKernelBuildInfo(const std::vector<std::string> &inputs_format,
79 const std::vector<TypeId> &inputs_type,
80 const std::vector<std::string> &output_formats,
81 const std::vector<TypeId> &output_types) {
82 return BuildSelectKernelBuildInfo(inputs_format, inputs_type, output_formats, output_types,
83 kernel::GetProcessorFromContext());
84 }
85
86 // Build for new node with given processor
BuildSelectKernelBuildInfo(const std::vector<std::string> & inputs_format,const std::vector<TypeId> & inputs_type,const std::vector<std::string> & output_formats,const std::vector<TypeId> & output_types,const kernel::Processor & processor)87 kernel::KernelBuildInfoPtr BuildSelectKernelBuildInfo(const std::vector<std::string> &inputs_format,
88 const std::vector<TypeId> &inputs_type,
89 const std::vector<std::string> &output_formats,
90 const std::vector<TypeId> &output_types,
91 const kernel::Processor &processor) {
92 kernel::KernelBuildInfo::KernelBuildInfoBuilder graph_info_builder;
93 graph_info_builder.SetInputsFormat(inputs_format);
94 graph_info_builder.SetInputsDeviceType(inputs_type);
95 graph_info_builder.SetOutputsFormat(output_formats);
96 graph_info_builder.SetOutputsDeviceType(output_types);
97 graph_info_builder.SetProcessor(processor);
98 graph_info_builder.SetKernelType(KernelType::AKG_KERNEL);
99 graph_info_builder.SetFusionType(kPatternOpaque);
100 return graph_info_builder.Build();
101 }
102
AnfToJsonDesc(const AnfNodePtrList & nodes,const DumpOption & dump_option,nlohmann::json * op_desc,std::map<std::string,AnfNodePtr> * address_node_map)103 bool AnfToJsonDesc(const AnfNodePtrList &nodes, const DumpOption &dump_option, nlohmann::json *op_desc,
104 std::map<std::string, AnfNodePtr> *address_node_map) {
105 MS_EXCEPTION_IF_NULL(op_desc);
106 if (nodes.empty()) {
107 MS_LOG(ERROR) << "Input nodes is empty.";
108 return false;
109 }
110 bool has_graph_kernel = std::any_of(nodes.begin(), nodes.end(), common::AnfAlgo::IsGraphKernel);
111 bool is_single_graph_kernel = has_graph_kernel && nodes.size() == 1;
112
113 FuncGraphPtr fg;
114 AnfNodePtrList op_nodes;
115 AnfNodePtrList inputs;
116 AnfNodePtrList outputs;
117 if (is_single_graph_kernel) {
118 fg = common::AnfAlgo::GetCNodeFuncGraphPtr(nodes[0]);
119 kernel::GetValidKernelNodes(fg, &op_nodes, &inputs, &outputs);
120 } else if (!has_graph_kernel) {
121 std::tie(fg, inputs, outputs) = BuildGraphFromNodes(nodes);
122 op_nodes = nodes;
123 } else {
124 // When there are basic and composite ops, the composite ops should be inline to the basic ones' graph,
125 // so a new graph generation should be done (because they may in the main graph!).
126 // If address_node_map is wanted, we should map the new node in new graph to the old nodes. But... not support now.
127 MS_LOG(EXCEPTION) << "No support mixed with basic and composite ops now!";
128 }
129 std::pair<AnfNodePtrList, AnfNodePtrList> in_and_out = std::make_pair(inputs, outputs);
130 return GenJson(op_nodes, in_and_out, dump_option, op_desc, address_node_map);
131 }
132
AnfToJsonDesc(const AnfNodePtrList & nodes,const DumpOption & dump_option,nlohmann::json * op_desc)133 bool AnfToJsonDesc(const AnfNodePtrList &nodes, const DumpOption &dump_option, nlohmann::json *op_desc) {
134 MS_EXCEPTION_IF_NULL(op_desc);
135 if (nodes.empty()) {
136 MS_LOG(ERROR) << "Input nodes is empty.";
137 return false;
138 }
139
140 FuncGraphPtr fg;
141
142 if (nodes.size() == 1 && common::AnfAlgo::IsGraphKernel(nodes[0])) {
143 fg = common::AnfAlgo::GetCNodeFuncGraphPtr(nodes[0]);
144 } else {
145 std::tie(fg, std::ignore, std::ignore) = BuildSingleGraphFromNodes(nodes);
146 }
147
148 AnfNodePtrList op_nodes;
149 AnfNodePtrList inputs;
150 AnfNodePtrList outputs;
151 kernel::GetValidKernelNodes(fg, &op_nodes, &inputs, &outputs);
152
153 auto mng = fg->manager();
154 if (mng == nullptr) {
155 mng = Manage(fg, false);
156 fg->set_manager(mng);
157 }
158 std::pair<AnfNodePtrList, AnfNodePtrList> in_and_out = std::make_pair(inputs, outputs);
159 return GenJson(op_nodes, in_and_out, dump_option, op_desc);
160 }
161
AnfToJsonDesc(const std::vector<AnfNodePtrList> & graphs,const DumpOption & dump_option,nlohmann::json * op_desc)162 bool AnfToJsonDesc(const std::vector<AnfNodePtrList> &graphs, const DumpOption &dump_option, nlohmann::json *op_desc) {
163 MS_EXCEPTION_IF_NULL(op_desc);
164 std::vector<nlohmann::json> graphs_desc;
165 for (auto const &graph_nodes : graphs) {
166 nlohmann::json desc;
167 if (!AnfToJsonDesc(graph_nodes, dump_option, &desc)) {
168 MS_LOG(ERROR) << "Collect json desc failed.";
169 return false;
170 }
171 graphs_desc.push_back(desc);
172 }
173 if (graphs_desc.empty()) {
174 MS_LOG(ERROR) << "Collect zero json desc.";
175 return false;
176 }
177
178 if (graphs_desc.size() > 1) {
179 nlohmann::json op_json_desc;
180 op_json_desc[kJsonKeyMultiGraph] = true;
181 op_json_desc[kJsonKeyGraphDesc] = graphs_desc;
182 *op_desc = op_json_desc;
183 return true;
184 }
185
186 *op_desc = graphs_desc[0];
187 return true;
188 }
189
JsonDescToAnf(const std::string & json_desc)190 FuncGraphPtr JsonDescToAnf(const std::string &json_desc) {
191 kernel::AkgKernelJsonDecoder akg_kernel_json_decoder;
192 auto fg = akg_kernel_json_decoder.DecodeFusedNodes(json_desc);
193 if (fg == nullptr) {
194 MS_LOG(ERROR) << "Akg decode json to graph failed. json is: " << json_desc;
195 return nullptr;
196 }
197 return fg;
198 }
199
GetFormat(const AnfNodePtr & node)200 std::string GetFormat(const AnfNodePtr &node) { return AnfAlgo::GetOutputFormat(node, 0); }
201
GetType(const AnfNodePtr & node)202 TypePtr GetType(const AnfNodePtr &node) {
203 const auto &abstract = node->abstract();
204 auto type = abstract->BuildType();
205 MS_EXCEPTION_IF_NULL(type);
206 return type;
207 }
208
GetShape(const AnfNodePtr & node)209 ShapeVector GetShape(const AnfNodePtr &node) {
210 auto abstract = node->abstract();
211 MS_EXCEPTION_IF_NULL(abstract);
212 auto shape = abstract->GetShapeTrack();
213 if (shape == nullptr) {
214 MS_LOG(EXCEPTION) << "The shape of node " << node->fullname_with_scope() << " is nullptr";
215 } else if (!shape->isa<abstract::Shape>()) {
216 MS_LOG(EXCEPTION) << "The shape of node " << node->fullname_with_scope() << " should be of type Shape, but got "
217 << shape->ToString();
218 }
219 auto shape_vec = shape->cast<abstract::ShapePtr>()->shape();
220 if (shape_vec.empty()) {
221 shape_vec.push_back(1);
222 }
223 return shape_vec;
224 }
225
GetDeviceShape(const AnfNodePtr & node)226 ShapeVector GetDeviceShape(const AnfNodePtr &node) {
227 ShapeVector res_device_shape = AnfAlgo::GetOutputDeviceShape(node, 0);
228 return res_device_shape.empty() ? ShapeVector({1}) : res_device_shape;
229 }
230
GetReduceAxis(const AnfNodePtr & node)231 std::vector<int64_t> GetReduceAxis(const AnfNodePtr &node) {
232 auto cnode = node->cast<CNodePtr>();
233 MS_EXCEPTION_IF_NULL(cnode);
234 auto axis_node = cnode->input(kIndex2)->cast<ValueNodePtr>();
235 MS_EXCEPTION_IF_NULL(axis_node);
236
237 std::vector<int64_t> axis;
238 auto &v = axis_node->value();
239 if (v->isa<ValueList>() || v->isa<ValueTuple>()) {
240 auto vec = v->isa<ValueList>() ? v->cast<ValueListPtr>()->value() : v->cast<ValueTuplePtr>()->value();
241 for (auto value : vec) {
242 if (value->isa<Int64Imm>()) {
243 axis.push_back(GetValue<int64_t>(value));
244 } else {
245 MS_LOG(EXCEPTION) << "Element in attribute 'axis' should be of type int64 in node "
246 << node->fullname_with_scope();
247 }
248 }
249 } else if (v->isa<Int64Imm>()) {
250 axis.push_back(GetValue<int64_t>(v));
251 } else if (v->isa<tensor::Tensor>()) {
252 axis = CheckAndConvertUtils::CheckTensorIntValue("axis", v, "ReduceSum");
253 } else {
254 MS_LOG(EXCEPTION) << "Attribute 'axis' should be a list or tuple in node " << node->fullname_with_scope();
255 }
256
257 return axis;
258 }
259
260 // Deprecated. use GkUtils::NewRealCNode
CreateCNode(const std::vector<AnfNodePtr> & inputs,const FuncGraphPtr & func_graph,const DataInfo & out_info,bool use_fake_abstract)261 CNodePtr CreateCNode(const std::vector<AnfNodePtr> &inputs, const FuncGraphPtr &func_graph, const DataInfo &out_info,
262 bool use_fake_abstract) {
263 // Limitation: 1. Node's attributes should be set out of this function; 2. only one output.
264 MS_EXCEPTION_IF_NULL(out_info.type);
265 auto out_type = out_info.type;
266 if (auto otype = out_info.type->cast<TensorTypePtr>(); otype != nullptr) {
267 out_type = otype->element();
268 }
269
270 // Create CNode.
271 auto cnode = func_graph->NewCNode(inputs);
272 MS_EXCEPTION_IF_NULL(cnode);
273
274 // Setup abstract.
275 if (use_fake_abstract) {
276 auto abs_shape = GetFakeAbstractShape(out_info.shape, out_info.format);
277 auto abs_tensor = std::make_shared<abstract::AbstractTensor>(out_type, abs_shape);
278 cnode->set_abstract(abs_tensor);
279 } else {
280 auto abs_tensor = std::make_shared<abstract::AbstractTensor>(out_type, out_info.shape);
281 cnode->set_abstract(abs_tensor);
282 }
283
284 // Setup kernel info.
285 auto kernel_info = std::make_shared<device::KernelInfo>();
286 cnode->set_kernel_info(kernel_info);
287 std::vector<size_t> feature_map_input_indexs;
288 kernel_info->set_feature_map_flag(false);
289 for (size_t i = 1; i < inputs.size(); ++i) {
290 if (AnfAlgo::IsFeatureMapOutput(inputs[i])) {
291 kernel_info->set_feature_map_flag(true);
292 feature_map_input_indexs.push_back(i);
293 }
294 }
295 if (inputs.size() == 1) {
296 kernel_info->set_feature_map_flag(true);
297 }
298 if (AnfUtils::IsRealKernel(cnode)) {
299 // if the node only has the primitive(such as getNext) or the node's input has a feature map input
300 // then the node's output is a feature map output
301 SetNodeAttrSafely(kIsFeatureMapOutput, MakeValue(kernel_info->is_feature_map()), cnode);
302 SetNodeAttrSafely(kIsFeatureMapInputList, MakeValue(feature_map_input_indexs), cnode);
303 }
304
305 // Setup kernel build info.
306 std::vector<std::string> input_formats;
307 std::vector<TypeId> input_types;
308 for (size_t i = 1; i < inputs.size(); ++i) {
309 auto kernel_with_index = common::AnfAlgo::VisitKernel(inputs[i], 0);
310 auto input_format = AnfAlgo::GetOutputFormat(kernel_with_index.first, kernel_with_index.second);
311 input_formats.push_back(input_format);
312 auto input_type = AnfAlgo::GetOutputDeviceDataType(kernel_with_index.first, kernel_with_index.second);
313 input_types.push_back(input_type);
314 }
315
316 std::vector<std::string> output_formats = {out_info.format};
317 std::vector<TypeId> output_types = {out_type->type_id()};
318
319 kernel::KernelBuildInfo::KernelBuildInfoBuilder info_builder;
320 info_builder.SetInputsFormat(input_formats);
321 info_builder.SetInputsDeviceType(input_types);
322 info_builder.SetOutputsFormat(output_formats);
323 info_builder.SetOutputsDeviceType(output_types);
324 info_builder.SetProcessor(kernel::GetProcessorFromContext());
325 info_builder.SetKernelType(KernelType::AKG_KERNEL);
326 info_builder.SetFusionType(kPatternOpaque);
327 auto selected_info = info_builder.Build();
328 AnfAlgo::SetSelectKernelBuildInfo(selected_info, cnode.get());
329
330 func_graph->AddNode(cnode);
331 return cnode;
332 }
333
SetNodeAttrSafely(const std::string & key,const ValuePtr & value,const AnfNodePtr & node)334 void SetNodeAttrSafely(const std::string &key, const ValuePtr &value, const AnfNodePtr &node) {
335 common::AnfAlgo::SetNodeAttrSafely(key, value, node);
336 }
337
IsBufferStitchNode(const AnfNodePtr & node)338 bool IsBufferStitchNode(const AnfNodePtr &node) {
339 auto cnode = node->cast<CNodePtr>();
340 MS_EXCEPTION_IF_NULL(cnode);
341 auto input = cnode->input(kAnfPrimitiveIndex);
342 if (!IsValueNode<FuncGraph>(input)) {
343 return common::AnfAlgo::HasNodeAttr(kAttrStitch, cnode);
344 }
345
346 auto func_graph = GetValueNode<FuncGraphPtr>(input);
347 MS_EXCEPTION_IF_NULL(func_graph);
348 AnfNodePtrList sub_nodes;
349 kernel::GetValidKernelNodes(func_graph, &sub_nodes);
350 for (auto sub_node : sub_nodes) {
351 auto sub_cnode = sub_node->cast<CNodePtr>();
352 MS_EXCEPTION_IF_NULL(sub_cnode);
353 if (common::AnfAlgo::HasNodeAttr(kAttrStitch, sub_cnode)) {
354 return true;
355 }
356 }
357
358 return false;
359 }
360
CheckDefaultFormat(const AnfNodePtr & node)361 bool CheckDefaultFormat(const AnfNodePtr &node) {
362 MS_EXCEPTION_IF_NULL(node);
363 if (node->kernel_info() == nullptr) {
364 return true;
365 }
366 auto build_info = AnfAlgo::GetSelectKernelBuildInfo(node);
367 if (build_info == nullptr) {
368 return true;
369 }
370 auto inputs_format = build_info->GetAllInputFormats();
371 if (!std::all_of(inputs_format.begin(), inputs_format.end(),
372 [](const std::string &format) { return IsOneOfDefaultFormat(format); })) {
373 return false;
374 }
375 auto outputs_format = build_info->GetAllOutputFormats();
376 return std::all_of(outputs_format.begin(), outputs_format.end(),
377 [](const std::string &format) { return IsOneOfDefaultFormat(format); });
378 }
379
CreateTensorValueNode(const DataInfo & info,void * value_ptr,size_t data_length)380 ValueNodePtr CreateTensorValueNode(const DataInfo &info, void *value_ptr, size_t data_length) {
381 // Create tensor value.
382 if (info.type == nullptr) {
383 MS_LOG(EXCEPTION) << "Data type can not be nullptr when creating scalar tensor!";
384 }
385
386 tensor::TensorPtr tensor = std::make_shared<tensor::Tensor>(info.type->type_id(), info.shape);
387 MS_EXCEPTION_IF_NULL(tensor);
388 tensor::DeviceInfo device_info{info.format, info.type};
389 tensor->set_device_info(device_info);
390 auto data_ptr = tensor->data_c();
391 MS_EXCEPTION_IF_NULL(data_ptr);
392 auto ret_code = memcpy_s(data_ptr, static_cast<size_t>(tensor->data().nbytes()), value_ptr, data_length);
393 if (ret_code != EOK) {
394 MS_LOG(EXCEPTION) << "Failed to copy data into scalar tensor, memcpy_s errorno: " << ret_code;
395 }
396
397 // Create value node.
398 ValueNodePtr new_value_node = std::make_shared<ValueNode>(tensor);
399 new_value_node->set_abstract(tensor->ToAbstract());
400 auto kernel_info = std::make_shared<device::KernelInfo>();
401 new_value_node->set_kernel_info(kernel_info);
402 auto kernel_build_info_builder = std::make_shared<kernel::KernelBuildInfo::KernelBuildInfoBuilder>();
403 kernel_build_info_builder->SetOutputsFormat(std::vector<std::string>{info.format});
404 std::vector<TypeId> types = {info.type->type_id()};
405 kernel_build_info_builder->SetOutputsDeviceType(types);
406 AnfAlgo::SetSelectKernelBuildInfo(kernel_build_info_builder->Build(), new_value_node.get());
407
408 return new_value_node;
409 }
410 } // namespace mindspore::graphkernel
411