[IE CLDNN] Fixed fmt traits map (#3088)
[platform/upstream/dldt.git] / inference-engine / thirdparty / clDNN / utils / codegen / generate_api_wrappers.py
1 #!/usr/bin/env python2
2
3 # INTEL CONFIDENTIAL
4 # Copyright 2016 Intel Corporation
5 #
6 # The source code contained or described herein and all documents related to the source code ("Material") are owned by
7 # Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its
8 # suppliers and licensors. The Material contains trade secrets and proprietary and confidential information of Intel
9 # or its suppliers and licensors. The Material is protected by worldwide copyright and trade secret laws and treaty
10 # provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted,
11 # transmitted, distributed, or disclosed in any way without Intel's prior express written permission.
12 #
13 # No license under any patent, copyright, trade secret or other intellectual property right is granted to
14 # or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement,
15 # estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel
16 # in writing.
17 #
18 #
19 # For details about script please contact following people:
20 #  * [Version: 1.0] Walkowiak, Marcin <marcin.walkowiak@intel.com>
21
22 import argparse
23 import os
24
25 import re
26
27
28 # Pattern that filters file names that are headers.
29 headerFileNamePattern = re.compile('^[a-zA-Z0-9_]+\\.[hH]$')
30 # Marker that marks API function/data member (at its beginning).
31 apiMemberMarker = 'CLDNN_API'
32 # Macros that should be expanded to API functions.
33 apiMacroMemberMatchers = [
34     (re.compile('^\\s*CLDNN_DECLARE_PRIMITIVE_TYPE_ID\\s*\\(\\s*([a-zA-Z0-9_]+)\\s*\\)\\s*;', re.MULTILINE),
35      'cldnn_primitive_type_id cldnn_\\1_type_id(cldnn_status* status)')
36 ]
37 # C language and project reserved keywords (that cannot be used as function/parameter name).
38 reservedKeywords = [
39     'auto', 'else', 'long', 'switch', 'break', 'enum', 'register', 'typedef', 'case', 'extern', 'return', 'union',
40     'char', 'float', 'short', 'unsigned', 'const', 'for', 'signed', 'void', 'continue', 'goto', 'sizeof', 'volatile',
41     'default', 'if', 'static', 'while', 'do', 'int', 'struct', '_Packed', 'double'
42 ]
43 # C language and project reserved keyword patterns (that cannot be used as function/parameter name).
44 reservedKeywordPatterns = [
45     re.compile('^__[a-z0-9_]+__$', re.IGNORECASE)
46 ]
47
48
49 apiMemberMatcher = re.compile('^\\s*' + re.escape(apiMemberMarker) + '\\s+([^;]+);', re.MULTILINE)
50 typeIdentifierSplitter = re.compile('^(.*?)([a-zA-Z_][a-zA-Z0-9_]*)$')
51
52
53 def stripCommentsAndPreprocessor(content):
54     """ Strips out comments and preprocessor constructs from text written in C language (or compatible).
55
56     :param content: Text with code written in C language (or compatible).
57     :type content: str
58     :return: Content of C language code with comments and preprocessor constructs stripped out.
59     :rtype: str
60     """
61     # FSA states:
62     # 0 - normal context, start state
63     # 1 - string context
64     # 2 - string context, after \ character (character escape)
65     # 3 - normal context, after / character (possible comment)
66     # 4 - multi-line comment context
67     # 5 - multi-line comment context, after * character (possible end of comment)
68     # 6 - single-line comment context
69     # 7 - single-line comment context, after \ character (escape)
70     # 8 - preprocessor definition/instruction context
71     # 9 - preprocessor definition/instruction context, after \ character (escape)
72
73     state = 0
74     strippedOutputArray = []
75     for c in content:
76         # normal context, start state
77         if state == 0:
78             if c == '"':
79                 state = 1   # string
80                 strippedOutputArray.append(c)
81             elif c == '/':
82                 state = 3   # possible comment (no out)
83             elif c == '#':
84                 state = 8   # preprocessor (no out)
85             else:
86                 strippedOutputArray.append(c)
87         # string context
88         elif state == 1:
89             if c == '\\':
90                 state = 2   # escape sequence
91                 strippedOutputArray.append(c)
92             elif c == '"':
93                 state = 0
94                 strippedOutputArray.append(c)
95             else:
96                 strippedOutputArray.append(c)
97         # string context, after \ character (character escape)
98         elif state == 2:
99             state = 1   # do not leave string context on any character
100             strippedOutputArray.append(c)
101         # normal context, after / character (possible comment)
102         elif state == 3:
103             if c == '*':
104                 state = 4   # multi-line comment (no out)
105             elif c == '/':
106                 state = 6   # single-line comment (no out)
107             else:
108                 state = 0   # not comment (flush previous token)
109                 strippedOutputArray.append('/')
110                 strippedOutputArray.append(c)
111         # multi-line comment context
112         elif state == 4:
113             if c == '*':
114                 state = 5   # possible end of comment (no out)
115         # multi-line comment context, after * character (possible end of comment)
116         elif state == 5:
117             if c == '/':
118                 state = 0   # end of comment (no out)
119             elif c == '*':
120                 pass   # not end of comment, but check next token for possible end of comment (no out)
121             else:
122                 state = 4   # not end of comment (no out)
123         # single-line comment context
124         elif state == 6:
125             if c == '\n':
126                 state = 0   # end of comment (append new line)
127                 strippedOutputArray.append('\n')
128             elif c == '\\':
129                 state = 7   # escape in comment (can escape new line character) (no out)
130         # single-line comment context, after \ character (escape)
131         elif state == 7:
132             state = 6   # do not leave comment on any character (no out)
133         # preprocessor definition/instruction context
134         elif state == 8:
135             if c == '\n':
136                 state = 0   # end of preprocessor construct (no out)
137             elif c == '\\':
138                 state = 9   # escape in preprocessor construct (no out)
139         # preprocessor definition/instruction context, after \ character (escape)
140         elif state == 9:
141             state = 8   # do not leave preprocessor construct on any character (no out)
142
143     return ''.join(strippedOutputArray)
144
145
146 def isReservedName(name):
147     """ Determines whether specified name is reserved in C language or project.
148
149     :param name: Name to check.
150     :type name: str
151     :return: True, if name is reserved; otherwise, False.
152     :rtype: bool
153     """
154     if name.strip() in reservedKeywords:
155         return True
156     for keywordPattern in reservedKeywordPatterns:
157         if keywordPattern.match(name.strip()):
158             return True
159     return False
160
161
162 automaticSplitVarIndex = 0
163
164
165 def splitTypeAndIdentifier(decl):
166     match = typeIdentifierSplitter.match(decl.strip())
167     if match and not isReservedName(match.group(2)):
168         return match.group(1).strip(), match.group(2).strip()
169     else:
170         global automaticSplitVarIndex
171         automaticSplitVarIndex += 1
172         return decl.strip(), 'arg{0:05d}'.format(automaticSplitVarIndex)
173
174
175 def parseApiMemberDeclarator(apiDecl):
176     parenLevel = 0
177     state = 0
178
179     name = ''
180     returnType = ''
181     isFunction = False
182     paramDecls = []   # Collection of extracted parameter declarations
183     attrs = ''
184
185     # Reversed array where tokens are collected:
186     nameRArray = []         # API member name
187     returnTypeRArray = []   # Return type declaration
188     paramRArray = []        # Parameter declarator
189
190     cLoc = len(apiDecl)
191     cAttributeSplitLoc = cLoc
192     while cLoc > 0:
193         cLoc -= 1
194         c = apiDecl[cLoc]
195
196         if parenLevel == 0:
197             # API member declarator context, start state
198             if state == 0:
199                 if c == ')':
200                     state = 1   # possible function declarator
201                     isFunction = True
202                     attrs = apiDecl[cLoc + 1:]
203             # function parameter declaration
204             elif state == 1:
205                 if c == ')':   # nesting of parentheses (stop normal parsing, only collect tokens)
206                     parenLevel += 1
207                     paramRArray.append(c)
208                 elif c == '(':
209                     state = 2   # end of parameters declaration (move to function name, store parameter if needed)
210                     if len(paramRArray) > 0:
211                         paramDecls.append(''.join(paramRArray[::-1]).strip())
212                         paramRArray = []
213                 elif c == ',':   # start of next parameter declaration
214                     paramDecls.append(''.join(paramRArray[::-1]).strip())
215                     paramRArray = []
216                 else:
217                     paramRArray.append(c)
218             # function name (optional whitespace)
219             elif state == 2:
220                 if not c.isspace():
221                     cLoc += 1
222                     state = 3   # ignore whitespace until non-whitespace character is encountered (re-parse token)
223             # function name
224             elif state == 3:
225                 if c.isalnum() or c == '_':
226                     nameRArray.append(c)
227                 else:
228                     name = ''.join(nameRArray[::-1]).strip()
229                     nameRArray = []
230
231                     cLoc += 1   # re-parse unmatched token
232                     if isReservedName(name):
233                         cAttributeSplitLoc = cLoc
234
235                         name = ''
236                         returnType = ''
237                         isFunction = False
238                         paramDecls = []
239                         attrs = apiDecl[cLoc:]
240
241                         state = 0   # if parsed function declaration has reserved name, it need to be treated as attribute
242                     else:
243                         state = 4   # name is not reserved - treat next tokens as return type
244             # return type declarator
245             elif state == 4:
246                 returnTypeRArray.append(c)
247         else:
248             # Nesting of parentheses - collect tokens only.
249             if c == ')':
250                 parenLevel += 1
251             elif c == '(':
252                 parenLevel -= 1
253             paramRArray.append(c)
254
255     if isFunction:
256         if len(nameRArray) > 0:
257             name = ''.join(nameRArray[::-1]).strip()
258         if len(returnTypeRArray) > 0:
259             returnType = ''.join(returnTypeRArray[::-1]).strip()
260         if len(paramRArray) > 0:
261             paramDecls.append(''.join(paramRArray[::-1]).strip())
262     else:
263         returnType, name = splitTypeAndIdentifier(apiDecl[:cAttributeSplitLoc])
264
265     paramDeclInfos = []
266     for decl in reversed(paramDecls):
267         paramType, paramName = splitTypeAndIdentifier(decl)
268         paramDeclInfos.append({'name': paramName, 'type': paramType})
269
270     return {
271         'name': name,
272         'isFunction': isFunction,
273         'returnType': returnType,
274         'params': paramDeclInfos,
275         'attrs': attrs
276     }
277
278
279 # Tests:
280 # print parseApiMemberDeclarator('int const   __attribute__((pure)) ')
281 # print parseApiMemberDeclarator('int foo1   __attribute__((pure)) ')
282 # print parseApiMemberDeclarator('int foo1')
283 # print parseApiMemberDeclarator('void a(int, const a*bb)')
284 # print parseApiMemberDeclarator('int foo __attribute__((static))')
285 # print parseApiMemberDeclarator('int foo()__attribute__((static))')
286 # print parseApiMemberDeclarator('int foo()__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
287 # print parseApiMemberDeclarator('int foo (int a, int b)__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
288 # print parseApiMemberDeclarator('DD(int,a)* foo(int a, const D(1,I())* b)__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
289
290
291 def parseHeaderFile(headerFilePath):
292     """ Opens, reads and parses header file and extracts information about API functions inside.
293
294     :param headerFilePath: Path to header file that will be parsed.
295     :return: List of API function declarations. Each declaration contains dictionary describing function name,
296              parameter types and return type.
297     :rtype: list
298     """
299     apiMembersInfo = []
300
301     headerFile = file(headerFilePath)
302     headerContent = headerFile.read()
303     strippedContent = stripCommentsAndPreprocessor(headerContent)
304     matchedFunctionDecls = apiMemberMatcher.findall(strippedContent)
305     for decl in matchedFunctionDecls:
306         apiMembersInfo.append(parseApiMemberDeclarator(decl))
307
308     for matcher, replace in apiMacroMemberMatchers:
309         matchedMacroDecls = matcher.finditer(strippedContent)
310         for decl in matchedMacroDecls:
311             apiMembersInfo.append(parseApiMemberDeclarator(decl.expand(replace)))
312
313     return apiMembersInfo
314
315
316 def main(parsedOptions):
317     """ Main script function.
318
319     The script generates header file with wrappers for all API functions from headers contained in specific directory.
320
321     :param parsedOptions: Arguments parsed by argparse.ArgumentParser class.
322     :return: Exit code for script.
323     :rtype: int
324     """
325     scanDirectory = parsedOptions.dir if parsedOptions.dir is not None and parsedOptions.dir != '' else os.curdir
326
327     apiMembersInfo = []
328
329     for scanDir, scanSubdirectories, scanFileNames in os.walk(scanDirectory):
330         for scanFileName in scanFileNames:
331             if headerFileNamePattern.match(scanFileName):
332                 apiMembersInfo.extend(parseHeaderFile(os.path.join(scanDir, scanFileName)))
333
334     print r'''/*******************************************************************************
335 * Copyright 2016 Intel Corporation
336 *
337 * Licensed under the Apache License, Version 2.0 (the "License");
338 * you may not use this file except in compliance with the License.
339 * You may obtain a copy of the License at
340 *
341 *     http://www.apache.org/licenses/LICENSE-2.0
342 *
343 * Unless required by applicable law or agreed to in writing, software
344 * distributed under the License is distributed on an "AS IS" BASIS,
345 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
346 * See the License for the specific language governing permissions and
347 * limitations under the License.
348 *******************************************************************************/
349
350 /*********************************************************
351  * AUTOGENERATED FILE; DO NOT EDIT
352  ********************************************************/
353
354 #ifdef _WIN32
355 #include <windows.h>
356 typedef HINSTANCE lib_handle_t;
357 #define NULL_LIB 0
358 #else
359 #define _GNU_SOURCE /* for dlvsym() */
360 #include <dlfcn.h>
361 typedef void * lib_handle_t;
362 #define NULL_LIB NULL
363 #endif
364
365 #include <assert.h>
366 #include <stdio.h>
367
368 #include "cldnn_prv.h"
369 #include "cldnn/cldnn.h"
370
371 static inline lib_handle_t load_library(const char *lib_name)
372 {
373 #ifdef _WIN32
374     return LoadLibraryEx(lib_name, NULL, 0);
375 #else
376     return dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL);
377 #endif
378 }
379
380 static inline void *load_symbol(const lib_handle_t lib,
381         const char *name)
382 {
383 #ifdef _WIN32
384     return GetProcAddress(lib, name);
385 #else
386     return dlsym(lib, name);
387 #endif
388 }
389
390 '''
391
392     for apiMemberInfo in apiMembersInfo:
393         if apiMemberInfo['isFunction']:
394             print '{0} (*{1}_fptr)({2}){5} = NULL;\n{0} {1}({4}){5} {{\n    assert({1}_fptr != NULL);\n    return {1}_fptr({3});\n}}\n'.format(
395                 apiMemberInfo['returnType'],
396                 apiMemberInfo['name'],
397                 ', '.join([x['type'] for x in apiMemberInfo['params']]),
398                 ', '.join([x['name'] for x in apiMemberInfo['params']]),
399                 ', '.join([x['type'] + ' ' + x['name'] for x in apiMemberInfo['params']]),
400                 (' ' + apiMemberInfo['attrs']) if len(apiMemberInfo['attrs']) > 0 else '')
401
402     print 'int cldnn_load_symbols(lib_handle_t handle) {'
403     for apiMemberInfo in apiMembersInfo:
404         if apiMemberInfo['isFunction']:
405             print '    {1}_fptr = ({0} (*)({2}){5}) load_symbol(handle, "{1}");\n    if ({1}_fptr == NULL) {{\n        return -1;\n    }}\n'.format(
406                 apiMemberInfo['returnType'],
407                 apiMemberInfo['name'],
408                 ', '.join([x['type'] for x in apiMemberInfo['params']]),
409                 ', '.join([x['name'] for x in apiMemberInfo['params']]),
410                 ', '.join([x['type'] + ' ' + x['name'] for x in apiMemberInfo['params']]),
411                 (' ' + apiMemberInfo['attrs']) if len(apiMemberInfo['attrs']) > 0 else '')
412     print '    return 0;\n}'
413
414     print r'''
415
416 enum _lib_status {
417     lib_unloaded = 1,
418     lib_loaded = 0,
419     lib_failed = -1
420 };
421
422 int cldnn_load_lib(const char *lib_name)
423 {
424     printf("begin cldnn_load_lib: %s\n", lib_name);
425     static int lib_status = lib_unloaded;
426     lib_handle_t lib_handle = NULL;
427
428     if (lib_status != lib_unloaded)
429         return lib_status;
430
431     lib_handle = load_library(lib_name);
432     if (lib_handle == NULL)
433     {
434         lib_status = lib_failed;
435         printf("Could not load library '%s'\n", lib_name);
436         return lib_status;
437     }
438
439     lib_status = cldnn_load_symbols(lib_handle) == 0 ? lib_loaded : lib_failed;
440     return lib_status;
441 }
442 '''
443
444
445 if __name__ == "__main__":
446     optParser = argparse.ArgumentParser(description = 'Generates wrappers for all API functions contained in headers' +
447                                                       'of specific directory.')
448
449     optParser.add_argument('dir', metavar = '<dir>', type = str, default = None,
450                            help = 'Directory to scan for header files. Default/None specified:' +
451                                   ' current working directory.')
452     optParser.add_argument('--version', action = 'version', version = '%(prog)s 1.0')
453
454     options = optParser.parse_args()
455
456     exitCode = main(options)
457     optParser.exit(exitCode)