forked from bartvdbraak/blender
592 lines
17 KiB
Python
592 lines
17 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2014 Blender Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License
|
|
|
|
# This script generates either header or implementation file from
|
|
# a CUDA header files.
|
|
#
|
|
# Usage: cuew hdr|impl [/path/to/cuda/includes]
|
|
# - hdr means header file will be generated and printed to stdout.
|
|
# - impl means implementation file will be generated and printed to stdout.
|
|
# - /path/to/cuda/includes is a path to a folder with cuda.h and cudaGL.h
|
|
# for which wrangler will be generated.
|
|
|
|
import os
|
|
import sys
|
|
from cuda_errors import CUDA_ERRORS
|
|
from pycparser import c_parser, c_ast, parse_file
|
|
from subprocess import Popen, PIPE
|
|
|
|
INCLUDE_DIR = "/usr/include"
|
|
LIB = "CUEW"
|
|
REAL_LIB = "CUDA"
|
|
VERSION_MAJOR = "1"
|
|
VERSION_MINOR = "2"
|
|
COPYRIGHT = """/*
|
|
* Copyright 2011-2014 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/"""
|
|
FILES = ["cuda.h", "cudaGL.h"]
|
|
|
|
TYPEDEFS = []
|
|
FUNC_TYPEDEFS = []
|
|
SYMBOLS = []
|
|
DEFINES = []
|
|
DEFINES_V2 = []
|
|
ERRORS = []
|
|
|
|
|
|
class FuncDefVisitor(c_ast.NodeVisitor):
|
|
indent = 0
|
|
prev_complex = False
|
|
dummy_typedefs = ['size_t', 'CUdeviceptr']
|
|
|
|
def _get_quals_string(self, node):
|
|
if node.quals:
|
|
return ' '.join(node.quals) + ' '
|
|
return ''
|
|
|
|
def _get_ident_type(self, node):
|
|
if isinstance(node, c_ast.PtrDecl):
|
|
return self._get_ident_type(node.type.type) + '*'
|
|
if isinstance(node, c_ast.ArrayDecl):
|
|
return self._get_ident_type(node.type)
|
|
elif isinstance(node, c_ast.Struct):
|
|
if node.name:
|
|
return 'struct ' + node.name
|
|
else:
|
|
self.indent += 1
|
|
struct = self._stringify_struct(node)
|
|
self.indent -= 1
|
|
return "struct {\n" + \
|
|
struct + (" " * self.indent) + "}"
|
|
elif isinstance(node, c_ast.Union):
|
|
self.indent += 1
|
|
union = self._stringify_struct(node)
|
|
self.indent -= 1
|
|
return "union {\n" + union + (" " * self.indent) + "}"
|
|
elif isinstance(node, c_ast.Enum):
|
|
return 'enum ' + node.name
|
|
elif isinstance(node, c_ast.TypeDecl):
|
|
return self._get_ident_type(node.type)
|
|
else:
|
|
return node.names[0]
|
|
|
|
def _stringify_param(self, param):
|
|
param_type = param.type
|
|
result = self._get_quals_string(param)
|
|
result += self._get_ident_type(param_type)
|
|
if param.name:
|
|
result += ' ' + param.name
|
|
if isinstance(param_type, c_ast.ArrayDecl):
|
|
# TODO(sergey): Workaround to deal with the
|
|
# preprocessed file where array size got
|
|
# substituded.
|
|
dim = param_type.dim.value
|
|
if param.name == "reserved" and dim == "64":
|
|
dim = "CU_IPC_HANDLE_SIZE"
|
|
result += '[' + dim + ']'
|
|
return result
|
|
|
|
def _stringify_params(self, params):
|
|
result = []
|
|
for param in params:
|
|
result.append(self._stringify_param(param))
|
|
return ', '.join(result)
|
|
|
|
def _stringify_struct(self, node):
|
|
result = ""
|
|
children = node.children()
|
|
for child in children:
|
|
member = self._stringify_param(child[1])
|
|
result += (" " * self.indent) + member + ";\n"
|
|
return result
|
|
|
|
def _stringify_enum(self, node):
|
|
result = ""
|
|
children = node.children()
|
|
for child in children:
|
|
if isinstance(child[1], c_ast.EnumeratorList):
|
|
enumerators = child[1].enumerators
|
|
for enumerator in enumerators:
|
|
result += (" " * self.indent) + enumerator.name
|
|
if enumerator.value:
|
|
result += " = " + enumerator.value.value
|
|
result += ",\n"
|
|
if enumerator.name.startswith("CUDA_ERROR_"):
|
|
ERRORS.append(enumerator.name)
|
|
return result
|
|
|
|
def visit_Decl(self, node):
|
|
if node.type.__class__.__name__ == 'FuncDecl':
|
|
if isinstance(node.type, c_ast.FuncDecl):
|
|
func_decl = node.type
|
|
func_decl_type = func_decl.type
|
|
|
|
typedef = 'typedef '
|
|
symbol_name = None
|
|
|
|
if isinstance(func_decl_type, c_ast.TypeDecl):
|
|
symbol_name = func_decl_type.declname
|
|
typedef += self._get_quals_string(func_decl_type)
|
|
typedef += self._get_ident_type(func_decl_type.type)
|
|
typedef += ' CUDAAPI'
|
|
typedef += ' t' + symbol_name
|
|
elif isinstance(func_decl_type, c_ast.PtrDecl):
|
|
ptr_type = func_decl_type.type
|
|
symbol_name = ptr_type.declname
|
|
typedef += self._get_quals_string(ptr_type)
|
|
typedef += self._get_ident_type(func_decl_type)
|
|
typedef += ' CUDAAPI'
|
|
typedef += ' t' + symbol_name
|
|
|
|
typedef += '(' + \
|
|
self._stringify_params(func_decl.args.params) + \
|
|
');'
|
|
|
|
SYMBOLS.append(symbol_name)
|
|
FUNC_TYPEDEFS.append(typedef)
|
|
|
|
def visit_Typedef(self, node):
|
|
if node.name in self.dummy_typedefs:
|
|
return
|
|
|
|
complex = False
|
|
type = self._get_ident_type(node.type)
|
|
quals = self._get_quals_string(node)
|
|
|
|
if isinstance(node.type.type, c_ast.Struct):
|
|
self.indent += 1
|
|
struct = self._stringify_struct(node.type.type)
|
|
self.indent -= 1
|
|
typedef = quals + type + " {\n" + struct + "} " + node.name
|
|
complex = True
|
|
elif isinstance(node.type.type, c_ast.Enum):
|
|
self.indent += 1
|
|
enum = self._stringify_enum(node.type.type)
|
|
self.indent -= 1
|
|
typedef = quals + type + " {\n" + enum + "} " + node.name
|
|
complex = True
|
|
else:
|
|
typedef = quals + type + " " + node.name
|
|
if complex or self.prev_complex:
|
|
typedef = "\ntypedef " + typedef + ";"
|
|
else:
|
|
typedef = "typedef " + typedef + ";"
|
|
|
|
TYPEDEFS.append(typedef)
|
|
|
|
self.prev_complex = complex
|
|
|
|
|
|
def get_latest_cpp():
|
|
path_prefix = "/usr/bin"
|
|
for cpp_version in ["9", "8", "7", "6", "5", "4"]:
|
|
test_cpp = os.path.join(path_prefix, "cpp-4." + cpp_version)
|
|
if os.path.exists(test_cpp):
|
|
return test_cpp
|
|
return None
|
|
|
|
|
|
def preprocess_file(filename, cpp_path):
|
|
args = [cpp_path, "-I./"]
|
|
if filename.endswith("GL.h"):
|
|
args.append("-DCUDAAPI= ")
|
|
args.append(filename)
|
|
|
|
try:
|
|
pipe = Popen(args,
|
|
stdout=PIPE,
|
|
universal_newlines=True)
|
|
text = pipe.communicate()[0]
|
|
except OSError as e:
|
|
raise RuntimeError("Unable to invoke 'cpp'. " +
|
|
'Make sure its path was passed correctly\n' +
|
|
('Original error: %s' % e))
|
|
|
|
return text
|
|
|
|
|
|
def parse_files():
|
|
parser = c_parser.CParser()
|
|
cpp_path = get_latest_cpp()
|
|
|
|
for filename in FILES:
|
|
filepath = os.path.join(INCLUDE_DIR, filename)
|
|
dummy_typedefs = {}
|
|
text = preprocess_file(filepath, cpp_path)
|
|
|
|
if filepath.endswith("GL.h"):
|
|
dummy_typedefs = {
|
|
"CUresult": "int",
|
|
"CUgraphicsResource": "void *",
|
|
"CUdevice": "void *",
|
|
"CUcontext": "void *",
|
|
"CUdeviceptr": "void *",
|
|
"CUstream": "void *"
|
|
}
|
|
|
|
text = "typedef int GLint;\n" + text
|
|
text = "typedef unsigned int GLuint;\n" + text
|
|
text = "typedef unsigned int GLenum;\n" + text
|
|
text = "typedef long size_t;\n" + text
|
|
|
|
for typedef in sorted(dummy_typedefs):
|
|
text = "typedef " + dummy_typedefs[typedef] + " " + \
|
|
typedef + ";\n" + text
|
|
|
|
ast = parser.parse(text, filepath)
|
|
|
|
with open(filepath) as f:
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
if line.startswith("#define"):
|
|
line = line[8:-1]
|
|
token = line.split()
|
|
if token[0] not in ("__cuda_cuda_h__",
|
|
"CUDA_CB",
|
|
"CUDAAPI"):
|
|
DEFINES.append(token)
|
|
|
|
for line in lines:
|
|
# TODO(sergey): Use better matching rule for _v2 symbols.
|
|
if line[0].isspace() and line.lstrip().startswith("#define"):
|
|
line = line[12:-1]
|
|
token = line.split()
|
|
if len(token) == 2 and token[1].endswith("_v2"):
|
|
DEFINES_V2.append(token)
|
|
|
|
v = FuncDefVisitor()
|
|
for typedef in dummy_typedefs:
|
|
v.dummy_typedefs.append(typedef)
|
|
v.visit(ast)
|
|
|
|
FUNC_TYPEDEFS.append('')
|
|
SYMBOLS.append('')
|
|
|
|
|
|
def print_copyright():
|
|
print(COPYRIGHT)
|
|
print("")
|
|
|
|
|
|
def open_header_guard():
|
|
print("#ifndef __%s_H__" % (LIB))
|
|
print("#define __%s_H__" % (LIB))
|
|
print("")
|
|
print("#ifdef __cplusplus")
|
|
print("extern \"C\" {")
|
|
print("#endif")
|
|
print("")
|
|
|
|
|
|
def close_header_guard():
|
|
print("")
|
|
print("#ifdef __cplusplus")
|
|
print("}")
|
|
print("#endif")
|
|
print("")
|
|
print("#endif /* __%s_H__ */" % (LIB))
|
|
|
|
|
|
def print_header():
|
|
print_copyright()
|
|
open_header_guard()
|
|
|
|
# Fot size_t.
|
|
print("#include <stdlib.h>")
|
|
print("")
|
|
|
|
print("/* Defines. */")
|
|
print("#define %s_VERSION_MAJOR %s" % (LIB, VERSION_MAJOR))
|
|
print("#define %s_VERSION_MINOR %s" % (LIB, VERSION_MINOR))
|
|
print("")
|
|
for define in DEFINES:
|
|
print('#define %s' % (' '.join(define)))
|
|
print("")
|
|
|
|
print("""/* Functions which changed 3.1 -> 3.2 for 64 bit stuff,
|
|
* the cuda library has both the old ones for compatibility and new
|
|
* ones with _v2 postfix,
|
|
*/""")
|
|
for define in DEFINES_V2:
|
|
print('#define %s' % (' '.join(define)))
|
|
print("")
|
|
|
|
print("/* Types. */")
|
|
|
|
# We handle this specially because of the file is
|
|
# getting preprocessed.
|
|
print("""#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64)
|
|
typedef unsigned long long CUdeviceptr;
|
|
#else
|
|
typedef unsigned int CUdeviceptr;
|
|
#endif
|
|
""")
|
|
|
|
for typedef in TYPEDEFS:
|
|
print('%s' % (typedef))
|
|
|
|
# TDO(sergey): This is only specific to CUDA wrapper.
|
|
print("""
|
|
#ifdef _WIN32
|
|
# define CUDAAPI __stdcall
|
|
# define CUDA_CB __stdcall
|
|
#else
|
|
# define CUDAAPI
|
|
# define CUDA_CB
|
|
#endif
|
|
""")
|
|
|
|
print("/* Function types. */")
|
|
for func_typedef in FUNC_TYPEDEFS:
|
|
print('%s' % (func_typedef))
|
|
print("")
|
|
|
|
print("/* Function declarations. */")
|
|
for symbol in SYMBOLS:
|
|
if symbol:
|
|
print('extern t%s *%s;' % (symbol, symbol))
|
|
else:
|
|
print("")
|
|
|
|
print("")
|
|
print("enum {")
|
|
print(" CUEW_SUCCESS = 0,")
|
|
print(" CUEW_ERROR_OPEN_FAILED = -1,")
|
|
print(" CUEW_ERROR_ATEXIT_FAILED = -2,")
|
|
print("};")
|
|
print("")
|
|
print("int %sInit(void);" % (LIB.lower()))
|
|
# TODO(sergey): Get rid of hardcoded CUresult.
|
|
print("const char *%sErrorString(CUresult result);" % (LIB.lower()))
|
|
print("const char *cuewCompilerPath(void);")
|
|
print("int cuewCompilerVersion(void);")
|
|
|
|
close_header_guard()
|
|
|
|
|
|
def print_dl_wrapper():
|
|
print("""#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# define VC_EXTRALEAN
|
|
# include <windows.h>
|
|
|
|
/* Utility macros. */
|
|
|
|
typedef HMODULE DynamicLibrary;
|
|
|
|
# define dynamic_library_open(path) LoadLibrary(path)
|
|
# define dynamic_library_close(lib) FreeLibrary(lib)
|
|
# define dynamic_library_find(lib, symbol) GetProcAddress(lib, symbol)
|
|
#else
|
|
# include <dlfcn.h>
|
|
|
|
typedef void* DynamicLibrary;
|
|
|
|
# define dynamic_library_open(path) dlopen(path, RTLD_NOW)
|
|
# define dynamic_library_close(lib) dlclose(lib)
|
|
# define dynamic_library_find(lib, symbol) dlsym(lib, symbol)
|
|
#endif
|
|
""")
|
|
|
|
|
|
def print_dl_helper_macro():
|
|
print("""#define %s_LIBRARY_FIND_CHECKED(name) \\
|
|
name = (t##name *)dynamic_library_find(lib, #name); \\
|
|
assert(name);
|
|
|
|
#define %s_LIBRARY_FIND(name) \\
|
|
name = (t##name *)dynamic_library_find(lib, #name);
|
|
|
|
static DynamicLibrary lib;""" % (REAL_LIB, REAL_LIB))
|
|
print("")
|
|
|
|
|
|
def print_dl_close():
|
|
print("""static void %sExit(void) {
|
|
if(lib != NULL) {
|
|
/* Ignore errors. */
|
|
dynamic_library_close(lib);
|
|
lib = NULL;
|
|
}
|
|
}""" % (LIB.lower()))
|
|
print("")
|
|
|
|
|
|
def print_lib_path():
|
|
# TODO(sergey): get rid of hardcoded libraries.
|
|
print("""#ifdef _WIN32
|
|
/* Expected in c:/windows/system or similar, no path needed. */
|
|
const char *path = "nvcuda.dll";
|
|
#elif defined(__APPLE__)
|
|
/* Default installation path. */
|
|
const char *path = "/usr/local/cuda/lib/libcuda.dylib";
|
|
#else
|
|
const char *path = "libcuda.so";
|
|
#endif""")
|
|
|
|
|
|
def print_init_guard():
|
|
print(""" static int initialized = 0;
|
|
static int result = 0;
|
|
int error, driver_version;
|
|
|
|
if (initialized) {
|
|
return result;
|
|
}
|
|
|
|
initialized = 1;
|
|
|
|
error = atexit(cuewExit);
|
|
if (error) {
|
|
result = CUEW_ERROR_ATEXIT_FAILED;
|
|
return result;
|
|
}
|
|
|
|
/* Load library. */
|
|
lib = dynamic_library_open(path);
|
|
|
|
if (lib == NULL) {
|
|
result = CUEW_ERROR_OPEN_FAILED;
|
|
return result;
|
|
}""")
|
|
print("")
|
|
|
|
|
|
def print_driver_version_guard():
|
|
# TODO(sergey): Currently it's hardcoded for CUDA only.
|
|
print(""" /* Detect driver version. */
|
|
driver_version = 1000;
|
|
|
|
%s_LIBRARY_FIND_CHECKED(cuDriverGetVersion);
|
|
if (cuDriverGetVersion) {
|
|
cuDriverGetVersion(&driver_version);
|
|
}
|
|
|
|
/* We require version 4.0. */
|
|
if (driver_version < 4000) {
|
|
result = CUEW_ERROR_OPEN_FAILED;
|
|
return result;
|
|
}""" % (REAL_LIB))
|
|
|
|
|
|
def print_dl_init():
|
|
print("int %sInit(void) {" % (LIB.lower()))
|
|
|
|
print(" /* Library paths. */")
|
|
print_lib_path()
|
|
print_init_guard()
|
|
print_driver_version_guard()
|
|
|
|
print(" /* Fetch all function pointers. */")
|
|
for symbol in SYMBOLS:
|
|
if symbol:
|
|
print(" %s_LIBRARY_FIND(%s);" % (REAL_LIB, symbol))
|
|
else:
|
|
print("")
|
|
|
|
print("")
|
|
print(" result = CUEW_SUCCESS;")
|
|
print(" return result;")
|
|
|
|
print("}")
|
|
|
|
|
|
def print_implementation():
|
|
print_copyright()
|
|
|
|
# TODO(sergey): Get rid of hardcoded header.
|
|
print("""#ifdef _MSC_VER
|
|
# define snprintf _snprintf
|
|
# define popen _popen
|
|
# define pclose _pclose
|
|
# define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
""")
|
|
print("#include <cuew.h>")
|
|
print("#include <assert.h>")
|
|
print("#include <stdio.h>")
|
|
print("#include <string.h>")
|
|
print("#include <sys/stat.h>")
|
|
print("")
|
|
|
|
print_dl_wrapper()
|
|
print_dl_helper_macro()
|
|
|
|
print("/* Function definitions. */")
|
|
for symbol in SYMBOLS:
|
|
if symbol:
|
|
print('t%s *%s;' % (symbol, symbol))
|
|
else:
|
|
print("")
|
|
print("")
|
|
|
|
print_dl_close()
|
|
|
|
print("/* Implementation function. */")
|
|
print_dl_init()
|
|
|
|
print("")
|
|
# TODO(sergey): Get rid of hardcoded CUresult.
|
|
print("const char *%sErrorString(CUresult result) {" % (LIB.lower()))
|
|
print(" switch(result) {")
|
|
print(" case CUDA_SUCCESS: return \"No errors\";")
|
|
|
|
for error in ERRORS:
|
|
if error in CUDA_ERRORS:
|
|
str = CUDA_ERRORS[error]
|
|
else:
|
|
str = error[11:]
|
|
print(" case %s: return \"%s\";" % (error, str))
|
|
|
|
print(" default: return \"Unknown CUDA error value\";")
|
|
print(" }")
|
|
print("}")
|
|
|
|
from cuda_extra import extra_code
|
|
print(extra_code)
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if len(sys.argv) != 2 and len(sys.argv) != 3:
|
|
print("Usage: %s hdr|impl [/path/to/cuda/toolkit/include]" %
|
|
(sys.argv[0]))
|
|
exit(1)
|
|
|
|
if len(sys.argv) == 3:
|
|
INCLUDE_DIR = sys.argv[2]
|
|
|
|
parse_files()
|
|
|
|
if sys.argv[1] == "hdr":
|
|
print_header()
|
|
elif sys.argv[1] == "impl":
|
|
print_implementation()
|
|
else:
|
|
print("Unknown command %s" % (sys.argv[1]))
|
|
exit(1)
|