1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
5 #include "cldnn_custom_layer.h"
6 #include "xml_parse_utils.h"
7 #include <description_buffer.hpp>
17 #include "simple_math.h"
19 using namespace InferenceEngine;
20 using namespace XMLParseUtils;
22 #define CheckAndReturnError(cond, errorMsg) \
23 if (cond) { std::stringstream ss; ss << errorMsg; m_ErrorMessage = ss.str(); return; }
24 #define CheckNodeTypeAndReturnError(node, type) \
25 CheckAndReturnError((std::string(node.name()).compare(type)), "Wrong node! expected: " << #type << " found: " << node.name())
26 #define CheckStrAttrAndReturnError(node, attr, value) \
27 CheckAndReturnError(GetStrAttr(node, attr, "").compare(value), "Wrong attribute value! expected: " << value << " found: " << GetStrAttr(node, attr, ""))
28 #define CheckIntAttrAndReturnError(node, attr, value) \
29 CheckAndReturnError(GetIntAttr(node, attr, -1) != (value), "Wrong attribute value! expected: " << value << " found: " << GetIntAttr(node, attr, -1))
31 namespace CLDNNPlugin {
33 void CLDNNCustomLayer::LoadSingleLayer(const pugi::xml_node & node) {
35 CheckNodeTypeAndReturnError(node, "CustomLayer");
36 CheckStrAttrAndReturnError(node, "type", "SimpleGPU");
37 CheckIntAttrAndReturnError(node, "version", 1);
38 m_layerName = GetStrAttr(node, "name", "");
39 CheckAndReturnError(m_layerName.length() == 0, "Missing Layer name in CustomLayer");
41 // Process child nodes
42 ProcessKernelNode(node.child("Kernel"));
43 ProcessBuffersNode(node.child("Buffers"));
44 ProcessCompilerOptionsNode(node.child("CompilerOptions"));
45 ProcessWorkSizesNode(node.child("WorkSizes"));
48 void CLDNNCustomLayer::ProcessKernelNode(const pugi::xml_node & node) {
49 CheckNodeTypeAndReturnError(node, "Kernel");
50 CheckAndReturnError(m_kernelSource.length() > 0, "Multiple definition of Kernel");
51 m_kernelEntry = GetStrAttr(node, "entry", "");
52 CheckAndReturnError(m_kernelEntry.length() == 0, "No Kernel entry in layer: " << GetStrAttr(node.parent(), "name"));
54 // Handle Source nodes
55 for (auto sourceNode = node.child("Source"); !sourceNode.empty(); sourceNode = sourceNode.next_sibling("Source")) {
57 std::string filename = m_configDir + "/" + GetStrAttr(sourceNode, "filename", "");
58 std::ifstream inputFile(filename);
59 CheckAndReturnError(!inputFile.is_open(), "Couldn't open kernel file: " << filename);
62 std::string fileContent;
63 inputFile.seekg(0, std::ios::end);
64 fileContent.reserve(inputFile.tellg());
65 inputFile.seekg(0, std::ios::beg);
67 fileContent.assign((std::istreambuf_iterator<char>(inputFile)),
68 std::istreambuf_iterator<char>());
70 // append to source string
71 m_kernelSource.append("\n// Custom Layer Kernel " + filename + "\n\n");
72 m_kernelSource.append(fileContent);
75 // Handle Define nodes
76 for (auto defineNode = node.child("Define"); !defineNode.empty(); defineNode = defineNode.next_sibling("Define")) {
78 kd.name = GetStrAttr(defineNode, "name", "");
79 CheckAndReturnError((kd.name.length() == 0), "Missing name for define node");
80 kd.param = GetStrAttr(defineNode, "param", "");
81 kd.default_value = GetStrAttr(defineNode, "default", "");
82 std::string type = GetStrAttr(defineNode, "type", "");
83 if (type.compare("int[]") == 0 || type.compare("float[]") == 0) {
84 kd.prefix = "(" + type + ") {";
87 m_defines.push_back(kd);
91 void CLDNNCustomLayer::ProcessBuffersNode(const pugi::xml_node & node) {
92 CheckNodeTypeAndReturnError(node, "Buffers");
93 for (auto tensorNode = node.child("Tensor"); !tensorNode.empty(); tensorNode = tensorNode.next_sibling("Tensor")) {
95 kp.format = FormatFromString(GetStrAttr(tensorNode, "format", "BFYX"));
96 CheckAndReturnError(kp.format == cldnn::format::format_num, "Tensor node has an invalid format: " << GetStrAttr(tensorNode, "format"));
97 kp.paramIndex = GetIntAttr(tensorNode, "arg-index", -1);
98 CheckAndReturnError(kp.paramIndex == -1, "Tensor node has no arg-index");
99 kp.portIndex = GetIntAttr(tensorNode, "port-index", -1);
100 CheckAndReturnError(kp.portIndex == -1, "Tensor node has no port-index");
101 std::string typeStr = GetStrAttr(tensorNode, "type");
102 if (typeStr.compare("input") == 0) {
103 kp.type = ParamType::Input;
104 } else if (typeStr.compare("output") == 0) {
105 kp.type = ParamType::Output;
107 CheckAndReturnError(true, "Tensor node has an invalid type: " << typeStr);
109 m_kernelParams.push_back(kp);
111 for (auto dataNode = node.child("Data"); !dataNode.empty(); dataNode = dataNode.next_sibling("Data")) {
113 kp.type = ParamType::Data;
114 kp.paramIndex = GetIntAttr(dataNode, "arg-index", -1);
115 CheckAndReturnError(kp.paramIndex == -1, "Data node has no arg-index");
116 kp.blobName = GetStrAttr(dataNode, "name", "");
117 CheckAndReturnError(kp.blobName.empty(), "Data node has no name");
118 m_kernelParams.push_back(kp);
122 void CLDNNCustomLayer::ProcessCompilerOptionsNode(const pugi::xml_node & node) {
124 return; // Optional node doesn't exist
126 CheckNodeTypeAndReturnError(node, "CompilerOptions");
127 CheckAndReturnError(m_compilerOptions.length() > 0, "Multiple definition of CompilerOptions");
128 m_compilerOptions = GetStrAttr(node, "options", "");
131 void CLDNNCustomLayer::ProcessWorkSizesNode(const pugi::xml_node & node) {
133 return; // Optional node doesn't exist
135 CheckNodeTypeAndReturnError(node, "WorkSizes");
137 m_wgDimInputIdx = -1;
138 std::string dim_src_string = node.attribute("dim").as_string("");
139 if (!dim_src_string.empty() && "output" != dim_src_string) {
140 // try to locate index separator
141 auto pos = dim_src_string.find_first_of(',');
142 auto flag = dim_src_string.substr(0, pos);
143 CheckAndReturnError(("input" != flag), "Invalid WG dim source: " << flag);
146 if (pos != std::string::npos) {
147 // user explicitly set input index in config
148 auto input_idx_string = dim_src_string.substr(pos + 1, std::string::npos);
149 input_idx = std::stoi(input_idx_string);
151 CheckAndReturnError((input_idx < 0), "Invalid input tensor index: " << input_idx);
152 m_wgDimInputIdx = input_idx;
155 std::string gws = node.attribute("global").as_string("");
156 while (!gws.empty()) {
157 auto pos = gws.find_first_of(',');
158 auto rule = gws.substr(0, pos);
159 CheckAndReturnError(!IsLegalSizeRule(rule), "Invalid WorkSize: " << rule);
160 m_globalSizeRules.push_back(rule);
161 if (pos == std::string::npos) {
164 gws = gws.substr(pos + 1, std::string::npos);
168 std::string lws = node.attribute("local").as_string("");
169 while (!lws.empty()) {
170 auto pos = lws.find_first_of(',');
171 auto rule = lws.substr(0, pos);
172 CheckAndReturnError(!IsLegalSizeRule(rule), "Invalid WorkSize: " << rule);
173 m_localSizeRules.push_back(rule);
174 if (pos == std::string::npos) {
177 lws = lws.substr(pos + 1, std::string::npos);
182 bool CLDNNCustomLayer::IsLegalSizeRule(const std::string & rule) {
183 SimpleMathExpression expr;
185 { 'b', 1 }, { 'B', 1 },
186 { 'f', 1 }, { 'F', 1 },
187 { 'y', 1 }, { 'Y', 1 },
188 { 'x', 1 }, { 'X', 1 },
190 if (!expr.SetExpression(rule)) {
202 cldnn::format CLDNNCustomLayer::FormatFromString(const std::string & str) {
203 static const std::map<std::string, cldnn::format> FormatNameToType = {
204 { "BFYX" , cldnn::format::bfyx },
205 { "bfyx" , cldnn::format::bfyx },
207 { "BYXF" , cldnn::format::byxf },
208 { "byxf" , cldnn::format::byxf },
210 { "FYXB" , cldnn::format::fyxb },
211 { "fyxb" , cldnn::format::fyxb },
213 { "YXFB" , cldnn::format::yxfb },
214 { "yxfb" , cldnn::format::yxfb },
216 { "ANY" , cldnn::format::any },
217 { "any" , cldnn::format::any },
219 auto it = FormatNameToType.find(str);
220 if (it != FormatNameToType.end())
223 return cldnn::format::format_num;
226 void CLDNNCustomLayer::LoadFromFile(const std::string configFile, CLDNNCustomLayerMap& customLayers, bool can_be_missed) {
227 pugi::xml_document xmlDoc;
228 pugi::xml_parse_result res = xmlDoc.load_file(configFile.c_str());
229 if (res.status != pugi::status_ok) {
231 // config file might not exist - like global config, for example
234 THROW_IE_EXCEPTION << "Error loading custom layer configuration file: " << configFile << ", " << res.description()
235 << " at offset " << res.offset;
241 char* abs_path_ptr = _fullpath(path, configFile.c_str(), MAX_PATH);
244 char* abs_path_ptr = realpath(configFile.c_str(), path);
246 if (abs_path_ptr == nullptr) {
247 THROW_IE_EXCEPTION << "Error loading custom layer configuration file: " << configFile << ", "
248 << "Can't get canonicalized absolute pathname.";
251 std::string abs_file_name(path);
252 // try extracting directory from config path
253 std::string dir_path;
254 std::size_t dir_split_pos = abs_file_name.find_last_of("/\\");
255 std::size_t colon_pos = abs_file_name.find_first_of(":");
256 std::size_t first_slash_pos = abs_file_name.find_first_of("/");
258 if (dir_split_pos != std::string::npos &&
259 (colon_pos != std::string::npos || first_slash_pos == 0)) {
261 dir_path = abs_file_name.substr(0, dir_split_pos);
263 THROW_IE_EXCEPTION << "Error loading custom layer configuration file: " << configFile << ", "
264 << "Path is not valid";
267 for (auto r = xmlDoc.document_element(); r; r = r.next_sibling()) {
268 CLDNNCustomLayerPtr layer = std::make_shared<CLDNNCustomLayer>(CLDNNCustomLayer(dir_path));
269 layer->LoadSingleLayer(r);
270 if (layer->Error()) {
271 customLayers.clear();
272 THROW_IE_EXCEPTION << layer->m_ErrorMessage;
274 customLayers[layer->Name()] = layer;
279 }; // namespace CLDNNPlugin