同步操作将从 Gitee 极速下载/Halide 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#include <iostream>#include <string>#include "CodeGen_C.h"#include "Module.h"#include "PythonExtensionGen.h"#include "Util.h"namespace Halide {namespace Internal {using std::ostream;using std::ostringstream;using std::string;namespace {string sanitize_name(const string &name) {ostringstream oss;for (char c : name) {if (c == '.' || c == '_') {oss << "_";} else if (!isalnum(c)) {oss << "_" << (int)c;} else {oss << c;}}return oss.str();}string remove_namespaces(const string &name) {size_t i = name.find_last_of(':');if (i == string::npos) {return name;} else {return name.substr(i + 1);}}bool can_convert(const LoweredArgument *arg) {if (arg->type.is_handle()) {if (arg->name == "__user_context") {/* __user_context is a void* pointer to a user supplied memory region.* We allow the Python callee to pass PyObject* pointers to that. */return true;} else {return false;}}if (arg->type.is_vector()) {return false;}if (arg->type.is_float() && arg->type.bits() != 32 && arg->type.bits() != 64 && arg->type.bits() != 16) {return false;}if ((arg->type.is_int() || arg->type.is_uint()) &&arg->type.bits() != 1 &&arg->type.bits() != 8 && arg->type.bits() != 16 &&arg->type.bits() != 32 && arg->type.bits() != 64) {return false;}return true;}std::pair<string, string> print_type(const LoweredArgument *arg) {// Excluded by can_convert() above:internal_assert(!arg->type.is_vector());if (arg->type.is_handle()) {/* Handles can be any pointer. However, from Python, all you can pass to* a function is a PyObject*, so we can restrict to that. */return std::make_pair("O", "PyObject*");} else if (arg->is_buffer()) {return std::make_pair("O", "PyObject*");} else if (arg->type.is_float() && arg->type.bits() == 32) {return std::make_pair("f", "float");} else if (arg->type.is_float() && arg->type.bits() == 64) {return std::make_pair("d", "double");// } else if (arg->type.is_float() && arg->type.bits() == 16) {// TODO: can't pass scalar float16 type} else if (arg->type.bits() == 1) {// "b" expects an unsigned char, so we assume that bool == uint8.return std::make_pair("b", "bool");} else if (arg->type.is_int() && arg->type.bits() == 64) {return std::make_pair("L", "long long");} else if (arg->type.is_uint() && arg->type.bits() == 64) {return std::make_pair("K", "unsigned long long");} else if (arg->type.is_int()) {return std::make_pair("i", "int");} else if (arg->type.is_uint()) {return std::make_pair("I", "unsigned int");} else {return std::make_pair("E", "unknown type");}}const char kModuleRegistrationCode[] = R"INLINE_CODE(static_assert(PY_MAJOR_VERSION >= 3, "Python bindings for Halide require Python 3+");namespace Halide::PythonExtensions {#define X(name) extern PyObject *name(PyObject *module, PyObject *args, PyObject *kwargs);HALIDE_PYTHON_EXTENSION_FUNCTIONS#undef X} // namespace Halide::PythonExtensionsnamespace {#define _HALIDE_STRINGIFY(x) #x#define _HALIDE_EXPAND_AND_STRINGIFY(x) _HALIDE_STRINGIFY(x)#define _HALIDE_CONCAT(x, y) x##y#define _HALIDE_EXPAND_AND_CONCAT(x, y) _HALIDE_CONCAT(x, y)PyMethodDef _methods[] = {#define X(name) {#name, reinterpret_cast<PyCFunction>(Halide::PythonExtensions::name), METH_VARARGS | METH_KEYWORDS, nullptr},HALIDE_PYTHON_EXTENSION_FUNCTIONS#undef X{0, 0, 0, nullptr}, // sentinel};PyModuleDef _moduledef = {PyModuleDef_HEAD_INIT, // base_HALIDE_EXPAND_AND_STRINGIFY(HALIDE_PYTHON_EXTENSION_MODULE_NAME), // namenullptr, // doc-1, // size_methods, // methodsnullptr, // slotsnullptr, // traversenullptr, // clearnullptr, // free};#ifndef HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERSvoid _module_halide_error(void *user_context, const char *msg) {// Most Python code probably doesn't want to log the error text to stderr,// so we won't do that by default.#ifdef HALIDE_PYTHON_EXTENSION_LOG_ERRORS_TO_STDERRPyGILState_STATE s = PyGILState_Ensure();PySys_FormatStderr("%s\n", msg);PyGILState_Release(s);#endif}void _module_halide_print(void *user_context, const char *msg) {PyGILState_STATE s = PyGILState_Ensure();PySys_FormatStdout("%s", msg);PyGILState_Release(s);}#endif // HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERS} // namespacenamespace Halide::PythonRuntime {bool unpack_buffer(PyObject *py_obj,int py_getbuffer_flags,const char *name,int dimensions,Py_buffer &py_buf,halide_dimension_t *halide_dim,halide_buffer_t &halide_buf,bool &py_buf_valid) {py_buf_valid = false;memset(&py_buf, 0, sizeof(py_buf));if (PyObject_GetBuffer(py_obj, &py_buf, PyBUF_FORMAT | PyBUF_STRIDED_RO | PyBUF_ANY_CONTIGUOUS | py_getbuffer_flags) < 0) {PyErr_Format(PyExc_ValueError, "Invalid argument %s: Expected %d dimensions, got %d", name, dimensions, py_buf.ndim);return false;}py_buf_valid = true;if (dimensions && py_buf.ndim != dimensions) {PyErr_Format(PyExc_ValueError, "Invalid argument %s: Expected %d dimensions, got %d", name, dimensions, py_buf.ndim);return false;}/* We'll get a buffer that's either:* C_CONTIGUOUS (last dimension varies the fastest, i.e., has stride=1) or* F_CONTIGUOUS (first dimension varies the fastest, i.e., has stride=1).* The latter is preferred, since it's already in the format that Halide* needs. It can can be achieved in numpy by passing order='F' during array* creation. However, if we do get a C_CONTIGUOUS buffer, flip the dimensions* (transpose) so we can process it without having to reallocate.*/int i, j, j_step;if (PyBuffer_IsContiguous(&py_buf, 'F')) {j = 0;j_step = 1;} else if (PyBuffer_IsContiguous(&py_buf, 'C')) {j = py_buf.ndim - 1;j_step = -1;} else {/* Python checks all dimensions and strides, so this typically indicates* a bug in the array's buffer protocol. */PyErr_Format(PyExc_ValueError, "Invalid buffer: neither C nor Fortran contiguous");return false;}for (i = 0; i < py_buf.ndim; ++i, j += j_step) {halide_dim[i].min = 0;halide_dim[i].stride = (int)(py_buf.strides[j] / py_buf.itemsize); // strides is in byteshalide_dim[i].extent = (int)py_buf.shape[j];halide_dim[i].flags = 0;if (py_buf.suboffsets && py_buf.suboffsets[i] >= 0) {// Halide doesn't support arrays of pointers. But we should never see this// anyway, since we specified PyBUF_STRIDED.PyErr_Format(PyExc_ValueError, "Invalid buffer: suboffsets not supported");return false;}}if (halide_dim[py_buf.ndim - 1].extent * halide_dim[py_buf.ndim - 1].stride * py_buf.itemsize != py_buf.len) {PyErr_Format(PyExc_ValueError, "Invalid buffer: length %ld, but computed length %ld",py_buf.len, py_buf.shape[0] * py_buf.strides[0]);return false;}memset(&halide_buf, 0, sizeof(halide_buf));if (!py_buf.format) {halide_buf.type.code = halide_type_uint;halide_buf.type.bits = 8;} else {/* Convert struct type code. See* https://docs.python.org/2/library/struct.html#module-struct */char *p = py_buf.format;while (strchr("@<>!=", *p)) {p++; // ignore little/bit endian (and alignment)}if (*p == 'f' || *p == 'd' || *p == 'e') {// 'f', 'd', and 'e' are float, double, and half, respectively.halide_buf.type.code = halide_type_float;} else if (*p >= 'a' && *p <= 'z') {// lowercase is signed int.halide_buf.type.code = halide_type_int;} else {// uppercase is unsigned int.halide_buf.type.code = halide_type_uint;}const char *type_codes = "bBhHiIlLqQfde"; // integers and floatsif (*p == '?') {// Special-case bool, so that it is a distinct type vs uint8_t// (even though the memory layout is identical)halide_buf.type.bits = 1;} else if (strchr(type_codes, *p)) {halide_buf.type.bits = (uint8_t)py_buf.itemsize * 8;} else {// We don't handle 's' and 'p' (char[]) and 'P' (void*)PyErr_Format(PyExc_ValueError, "Invalid data type for %s: %s", name, py_buf.format);return false;}}halide_buf.type.lanes = 1;halide_buf.dimensions = py_buf.ndim;halide_buf.dim = halide_dim;halide_buf.host = (uint8_t *)py_buf.buf;return true;}} // namespace Halide::PythonRuntimeextern "C" {HALIDE_EXPORT_SYMBOL PyObject *_HALIDE_EXPAND_AND_CONCAT(PyInit_, HALIDE_PYTHON_EXTENSION_MODULE_NAME)() {PyObject *m = PyModule_Create(&_moduledef);#ifndef HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERShalide_set_error_handler(_module_halide_error);halide_set_custom_print(_module_halide_print);#endif // HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERSreturn m;}} // extern "C")INLINE_CODE";} // namespacePythonExtensionGen::PythonExtensionGen(std::ostream &dest): dest(dest) {}void PythonExtensionGen::compile(const Module &module) {dest << "#include <string>\n";dest << "#include <Python.h>\n";dest << "#include \"HalideRuntime.h\"\n\n";std::vector<std::string> fnames;// Emit extern decls of the Halide-generated functions we use directly// into this file, so that we don't have to #include the relevant .h// file directly; this simplifies certain compile/build setups (since// we don't have to build files in tandem and/or get include paths right),// and should be totally safe, since we are using the same codegen logic// that would be in the .h file anyway.if (!module.functions().empty()) {// The CodeGen_C dtor must run to finish codegen correctly,// so wrap this in braces{CodeGen_C extern_decl_gen(dest, module.target(), CodeGen_C::CPlusPlusExternDecl);extern_decl_gen.compile(module);}dest << R"INLINE_CODE(namespace Halide::PythonRuntime {extern bool unpack_buffer(PyObject *py_obj,int py_getbuffer_flags,const char *name,int dimensions,Py_buffer &py_buf,halide_dimension_t *halide_dim,halide_buffer_t &halide_buf,bool &py_buf_valid);} // namespace Halide::PythonRuntimenamespace {template<int dimensions>struct PyHalideBuffer {// Must allocate at least 1, even if d=0static constexpr int dims_to_allocate = (dimensions < 1) ? 1 : dimensions;Py_buffer py_buf;halide_dimension_t halide_dim[dims_to_allocate];halide_buffer_t halide_buf;bool py_buf_needs_release = false;bool unpack(PyObject *py_obj, int py_getbuffer_flags, const char *name) {return Halide::PythonRuntime::unpack_buffer(py_obj, py_getbuffer_flags, name, dimensions, py_buf, halide_dim, halide_buf, py_buf_needs_release);}~PyHalideBuffer() {if (py_buf_needs_release) {PyBuffer_Release(&py_buf);}}PyHalideBuffer() = default;PyHalideBuffer(const PyHalideBuffer &other) = delete;PyHalideBuffer &operator=(const PyHalideBuffer &other) = delete;PyHalideBuffer(PyHalideBuffer &&other) = delete;PyHalideBuffer &operator=(PyHalideBuffer &&other) = delete;};} // namespace)INLINE_CODE";for (const auto &f : module.functions()) {if (f.linkage == LinkageType::ExternalPlusMetadata) {compile(f);fnames.push_back(remove_namespaces(f.name));}}}dest << "\n";if (!fnames.empty()) {dest << "#ifndef HALIDE_PYTHON_EXTENSION_OMIT_MODULE_DEFINITION\n";dest << "\n";dest << "#ifndef HALIDE_PYTHON_EXTENSION_MODULE_NAME\n";dest << "#define HALIDE_PYTHON_EXTENSION_MODULE_NAME " << module.name() << "\n";dest << "#endif // HALIDE_PYTHON_EXTENSION_MODULE_NAME\n";dest << "\n";dest << "#ifndef HALIDE_PYTHON_EXTENSION_FUNCTIONS\n";dest << "#define HALIDE_PYTHON_EXTENSION_FUNCTIONS";for (const auto &fname : fnames) {dest << " X(" << fname << ")";}dest << "\n";dest << "#endif // HALIDE_PYTHON_EXTENSION_FUNCTIONS\n";dest << "\n";}dest << kModuleRegistrationCode;if (!fnames.empty()) {dest << "#endif // HALIDE_PYTHON_EXTENSION_OMIT_MODULE_DEFINITION\n";}}void PythonExtensionGen::compile(const LoweredFunc &f) {const std::vector<LoweredArgument> &args = f.args;const string basename = remove_namespaces(f.name);std::vector<string> arg_names(args.size());for (size_t i = 0; i < args.size(); i++) {arg_names[i] = sanitize_name(args[i].name);}Indentation indent;indent.indent = 0;dest << "namespace Halide::PythonExtensions {\n";dest << "\n";dest << "namespace {\n";dest << "\n";dest << indent << "const char* const " << basename << "_kwlist[] = {\n";indent.indent += 2;for (size_t i = 0; i < args.size(); i++) {dest << indent << "\"" << arg_names[i] << "\",\n";}dest << indent << "nullptr\n";indent.indent -= 2;dest << indent << "};\n";dest << "\n";dest << "} // namespace\n";dest << "\n";dest << "// " << f.name << "\n";dest << "PyObject *" << basename << "(PyObject *module, PyObject *args, PyObject *kwargs) {\n";indent.indent += 2;for (const auto &arg : args) {if (!can_convert(&arg)) {/* Some arguments can't be converted to Python yet. In those* cases, just add a dummy function that always throws an* Exception. */// TODO: Add support for handles and vectors.// TODO: might make more sense to simply fail at Halide compile time!dest << indent << "PyErr_Format(PyExc_NotImplementedError, "<< "\"Can't convert argument " << arg.name << " from Python\");\n";dest << indent << "return nullptr;\n";dest << "}\n";dest << "} // namespace Halide::PythonExtensions\n";return;}}for (size_t i = 0; i < args.size(); i++) {dest << indent << print_type(&args[i]).second << " py_" << arg_names[i] << ";\n";}dest << indent << "if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"";for (const auto &arg : args) {dest << print_type(&arg).first;}dest << "\", (char**)" << basename << "_kwlist\n";indent.indent += 2;for (size_t i = 0; i < args.size(); i++) {dest << indent << ", &py_" << arg_names[i] << "\n";}indent.indent -= 2;dest << indent << ")) {\n";indent.indent += 2;dest << indent << "PyErr_Format(PyExc_ValueError, \"Internal error\");\n";dest << indent << "return nullptr;\n";indent.indent -= 2;dest << indent << "}\n";for (size_t i = 0; i < args.size(); i++) {if (args[i].is_buffer()) {const auto &name = arg_names[i]; // must use sanitized names heredest << indent << "PyHalideBuffer<" << (int)args[i].dimensions << "> b_" << name << ";\n";}}for (size_t i = 0; i < args.size(); i++) {if (args[i].is_buffer()) {const auto &name = arg_names[i]; // must use sanitized names heredest << indent << "if (!b_" << name << ".unpack(py_" << name << ", "<< (args[i].is_output() ? "PyBUF_WRITABLE" : "0") << ", "<< basename << "_kwlist[" << i << "])) return nullptr;\n";}}dest << "\n";// Mark all input buffers as having a dirty host, so that the Halide call will// do a lazy-copy-to-GPU if needed.for (size_t i = 0; i < args.size(); i++) {if (args[i].is_buffer() && args[i].is_input()) {dest << indent << "b_" << arg_names[i] << ".halide_buf.set_host_dirty();\n";}}dest << indent << "int result;\n";dest << indent << "Py_BEGIN_ALLOW_THREADS\n";dest << indent << "result = " << f.name << "(\n";indent.indent += 2;for (size_t i = 0; i < args.size(); i++) {if (args[i].is_buffer()) {dest << indent << "&b_" << arg_names[i] << ".halide_buf";} else {dest << indent << "py_" << arg_names[i] << "";}if (i < args.size() - 1) {dest << ",";}dest << "\n";}indent.indent -= 2;dest << indent << ");\n";dest << indent << "Py_END_ALLOW_THREADS\n";// Since the Python Buffer protocol is host-memory-only, we *must*// flush results back to host, otherwise the output buffer will contain// random garbage. (We need a better solution for this, see https://github.com/halide/Halide/issues/6868)for (size_t i = 0; i < args.size(); i++) {if (args[i].is_buffer() && args[i].is_output()) {dest << indent << "if (result == 0) result = halide_copy_to_host(nullptr, &b_" << arg_names[i] << ".halide_buf);\n";}}dest << indent << "if (result != 0) {\n";indent.indent += 2;dest << indent << "#ifndef HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERS\n";dest << indent << "PyErr_Format(PyExc_RuntimeError, \"Halide Runtime Error: %d\", result);\n";dest << indent << "#else\n";dest << indent << "PyErr_Format(PyExc_ValueError, \"Halide error %d\", result);\n";dest << indent << "#endif // HALIDE_PYTHON_EXTENSION_OMIT_ERROR_AND_PRINT_HANDLERS\n";dest << indent << "return nullptr;\n";indent.indent -= 2;dest << indent << "}\n";dest << "\n";dest << indent << "Py_INCREF(Py_None);\n";dest << indent << "return Py_None;\n";indent.indent -= 2;dest << "}\n";dest << "\n";dest << "} // namespace Halide::PythonExtensions\n";}} // namespace Internal} // namespace Halide
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。