diff options
Diffstat (limited to 'polly/lib/External/isl/interface/cpp.cc')
| -rw-r--r-- | polly/lib/External/isl/interface/cpp.cc | 1457 |
1 files changed, 1457 insertions, 0 deletions
diff --git a/polly/lib/External/isl/interface/cpp.cc b/polly/lib/External/isl/interface/cpp.cc new file mode 100644 index 00000000000..955f94415d7 --- /dev/null +++ b/polly/lib/External/isl/interface/cpp.cc @@ -0,0 +1,1457 @@ +/* + * Copyright 2016, 2017 Tobias Grosser. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as + * representing official policies, either expressed or implied, of + * Tobias Grosser. + */ + +#include <cstdarg> +#include <cstdio> +#include <iostream> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +#include "cpp.h" +#include "isl_config.h" + +/* Print string formatted according to "fmt" to ostream "os". + * + * This osprintf method allows us to use printf style formatting constructs when + * writing to an ostream. + */ +static void osprintf(ostream &os, const char *format, ...) +{ + va_list arguments; + char *string_pointer; + size_t size; + + va_start(arguments, format); + size = vsnprintf(NULL, 0, format, arguments); + string_pointer = new char[size + 1]; + va_end(arguments); + va_start(arguments, format); + vsnprintf(string_pointer, size + 1, format, arguments); + va_end(arguments); + os << string_pointer; + delete[] string_pointer; +} + +/* Convert "l" to a string. + */ +static std::string to_string(long l) +{ + std::ostringstream strm; + strm << l; + return strm.str(); +} + +/* Generate a cpp interface based on the extracted types and functions. + * + * Print first a set of forward declarations for all isl wrapper + * classes, then the declarations of the classes, and at the end all + * implementations. + * + * If checked C++ bindings are being generated, + * then wrap them in a namespace to avoid conflicts + * with the default C++ bindings (with automatic checks using exceptions). + */ +void cpp_generator::generate() +{ + ostream &os = cout; + + osprintf(os, "\n"); + osprintf(os, "namespace isl {\n\n"); + if (checked) + osprintf(os, "namespace checked {\n\n"); + + print_forward_declarations(os); + osprintf(os, "\n"); + print_declarations(os); + osprintf(os, "\n"); + print_implementations(os); + + if (checked) + osprintf(os, "} // namespace checked\n"); + osprintf(os, "} // namespace isl\n"); +} + +/* Print forward declarations for all classes to "os". +*/ +void cpp_generator::print_forward_declarations(ostream &os) +{ + map<string, isl_class>::iterator ci; + + osprintf(os, "// forward declarations\n"); + + for (ci = classes.begin(); ci != classes.end(); ++ci) + print_class_forward_decl(os, ci->second); +} + +/* Print all declarations to "os". + */ +void cpp_generator::print_declarations(ostream &os) +{ + map<string, isl_class>::iterator ci; + bool first = true; + + for (ci = classes.begin(); ci != classes.end(); ++ci) { + if (first) + first = false; + else + osprintf(os, "\n"); + + print_class(os, ci->second); + } +} + +/* Print all implementations to "os". + */ +void cpp_generator::print_implementations(ostream &os) +{ + map<string, isl_class>::iterator ci; + bool first = true; + + for (ci = classes.begin(); ci != classes.end(); ++ci) { + if (first) + first = false; + else + osprintf(os, "\n"); + + print_class_impl(os, ci->second); + } +} + +/* Print declarations for class "clazz" to "os". + */ +void cpp_generator::print_class(ostream &os, const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "// declarations for isl::%s\n", cppname); + + print_class_factory_decl(os, clazz); + osprintf(os, "\n"); + osprintf(os, "class %s {\n", cppname); + print_class_factory_decl(os, clazz, " friend "); + osprintf(os, "\n"); + osprintf(os, " %s *ptr = nullptr;\n", name); + osprintf(os, "\n"); + print_private_constructors_decl(os, clazz); + osprintf(os, "\n"); + osprintf(os, "public:\n"); + print_public_constructors_decl(os, clazz); + print_constructors_decl(os, clazz); + print_copy_assignment_decl(os, clazz); + print_destructor_decl(os, clazz); + print_ptr_decl(os, clazz); + print_get_ctx_decl(os); + osprintf(os, "\n"); + print_methods_decl(os, clazz); + + osprintf(os, "};\n"); +} + +/* Print forward declaration of class "clazz" to "os". + */ +void cpp_generator::print_class_forward_decl(ostream &os, + const isl_class &clazz) +{ + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "class %s;\n", cppname); +} + +/* Print global factory functions to "os". + * + * Each class has two global factory functions: + * + * set manage(__isl_take isl_set *ptr); + * set manage_copy(__isl_keep isl_set *ptr); + * + * A user can construct isl C++ objects from a raw pointer and indicate whether + * they intend to take the ownership of the object or not through these global + * factory functions. This ensures isl object creation is very explicit and + * pointers are not converted by accident. Thanks to overloading, manage() and + * manage_copy() can be called on any isl raw pointer and the corresponding + * object is automatically created, without the user having to choose the right + * isl object type. + */ +void cpp_generator::print_class_factory_decl(ostream &os, + const isl_class &clazz, const std::string &prefix) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + os << prefix; + osprintf(os, "inline %s manage(__isl_take %s *ptr);\n", cppname, name); + os << prefix; + osprintf(os, "inline %s manage_copy(__isl_keep %s *ptr);\n", + cppname, name); +} + +/* Print declarations of private constructors for class "clazz" to "os". + * + * Each class has currently one private constructor: + * + * 1) Constructor from a plain isl_* C pointer + * + * Example: + * + * set(__isl_take isl_set *ptr); + * + * The raw pointer constructor is kept private. Object creation is only + * possible through manage() or manage_copy(). + */ +void cpp_generator::print_private_constructors_decl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, " inline explicit %s(__isl_take %s *ptr);\n", cppname, + name); +} + +/* Print declarations of public constructors for class "clazz" to "os". + * + * Each class currently has two public constructors: + * + * 1) A default constructor + * 2) A copy constructor + * + * Example: + * + * set(); + * set(const set &set); + */ +void cpp_generator::print_public_constructors_decl(ostream &os, + const isl_class &clazz) +{ + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + osprintf(os, " inline /* implicit */ %s();\n", cppname); + + osprintf(os, " inline /* implicit */ %s(const %s &obj);\n", + cppname, cppname); +} + +/* Print declarations for constructors for class "class" to "os". + * + * For each isl function that is marked as __isl_constructor, + * add a corresponding C++ constructor. + * + * Example: + * + * inline /\* implicit *\/ union_set(basic_set bset); + * inline /\* implicit *\/ union_set(set set); + * inline explicit val(ctx ctx, long i); + * inline explicit val(ctx ctx, const std::string &str); + */ +void cpp_generator::print_constructors_decl(ostream &os, + const isl_class &clazz) +{ + set<FunctionDecl *>::const_iterator in; + const set<FunctionDecl *> &constructors = clazz.constructors; + + for (in = constructors.begin(); in != constructors.end(); ++in) { + FunctionDecl *cons = *in; + string fullname = cons->getName(); + function_kind kind = function_kind_constructor; + + print_method_decl(os, clazz, fullname, cons, kind); + } +} + +/* Print declarations of copy assignment operator for class "clazz" + * to "os". + * + * Each class has one assignment operator. + * + * isl:set &set::operator=(set obj) + * + */ +void cpp_generator::print_copy_assignment_decl(ostream &os, + const isl_class &clazz) +{ + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, " inline %s &operator=(%s obj);\n", cppname, cppname); +} + +/* Print declaration of destructor for class "clazz" to "os". + */ +void cpp_generator::print_destructor_decl(ostream &os, const isl_class &clazz) +{ + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, " inline ~%s();\n", cppname); +} + +/* Print declaration of pointer functions for class "clazz" to "os". + * + * To obtain a raw pointer three functions are provided: + * + * 1) __isl_give isl_set *copy() + * + * Returns a pointer to a _copy_ of the internal object + * + * 2) __isl_keep isl_set *get() + * + * Returns a pointer to the internal object + * + * 3) __isl_give isl_set *release() + * + * Returns a pointer to the internal object and resets the + * internal pointer to nullptr. + * + * We also provide functionality to explicitly check if a pointer is + * currently managed by this object. + * + * 4) bool is_null() + * + * Check if the current object is a null pointer. + * + * The functions get() and release() model the value_ptr proposed in + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf. + * The copy() function is an extension to allow the user to explicitly + * copy the underlying object. + * + * Also generate a declaration to delete copy() for r-values, for + * r-values release() should be used to avoid unnecessary copies. + */ +void cpp_generator::print_ptr_decl(ostream &os, const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + + osprintf(os, " inline __isl_give %s *copy() const &;\n", name); + osprintf(os, " inline __isl_give %s *copy() && = delete;\n", name); + osprintf(os, " inline __isl_keep %s *get() const;\n", name); + osprintf(os, " inline __isl_give %s *release();\n", name); + osprintf(os, " inline bool is_null() const;\n"); +} + +/* Print the declaration of the get_ctx method. + */ +void cpp_generator::print_get_ctx_decl(ostream &os) +{ + osprintf(os, " inline ctx get_ctx() const;\n"); +} + +/* Print declarations for methods in class "clazz" to "os". + */ +void cpp_generator::print_methods_decl(ostream &os, const isl_class &clazz) +{ + map<string, set<FunctionDecl *> >::const_iterator it; + + for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it) + print_method_group_decl(os, clazz, it->first, it->second); +} + +/* Print declarations for methods "methods" of name "fullname" in class "clazz" + * to "os". + * + * "fullname" is the name of the generated C++ method. It commonly corresponds + * to the isl name, with the object type prefix dropped. + * In case of overloaded methods, the result type suffix has also been removed. + */ +void cpp_generator::print_method_group_decl(ostream &os, const isl_class &clazz, + const string &fullname, const set<FunctionDecl *> &methods) +{ + set<FunctionDecl *>::const_iterator it; + + for (it = methods.begin(); it != methods.end(); ++it) { + function_kind kind = get_method_kind(clazz, *it); + print_method_decl(os, clazz, fullname, *it, kind); + } +} + +/* Print declarations for "method" in class "clazz" to "os". + * + * "fullname" is the name of the generated C++ method. It commonly corresponds + * to the isl name, with the object type prefix dropped. + * In case of overloaded methods, the result type suffix has also been removed. + * + * "kind" specifies the kind of method that should be generated. + */ +void cpp_generator::print_method_decl(ostream &os, const isl_class &clazz, + const string &fullname, FunctionDecl *method, function_kind kind) +{ + print_method_header(os, clazz, method, fullname, true, kind); +} + +/* Print implementations for class "clazz" to "os". + */ +void cpp_generator::print_class_impl(ostream &os, const isl_class &clazz) +{ + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "// implementations for isl::%s\n", cppname); + + print_class_factory_impl(os, clazz); + osprintf(os, "\n"); + print_public_constructors_impl(os, clazz); + osprintf(os, "\n"); + print_private_constructors_impl(os, clazz); + osprintf(os, "\n"); + print_constructors_impl(os, clazz); + osprintf(os, "\n"); + print_copy_assignment_impl(os, clazz); + osprintf(os, "\n"); + print_destructor_impl(os, clazz); + osprintf(os, "\n"); + print_ptr_impl(os, clazz); + osprintf(os, "\n"); + print_get_ctx_impl(os, clazz); + osprintf(os, "\n"); + print_methods_impl(os, clazz); +} + +/* Print code for throwing an exception corresponding to the last error + * that occurred on "ctx". + * This assumes that a valid isl::ctx is available in the "ctx" variable, + * e.g., through a prior call to print_save_ctx. + */ +static void print_throw_last_error(ostream &os) +{ + osprintf(os, " exception::throw_last_error(ctx);\n"); +} + +/* Print code for throwing an exception on NULL input. + */ +static void print_throw_NULL_input(ostream &os) +{ + osprintf(os, " exception::throw_NULL_input(__FILE__, __LINE__);\n"); +} + +/* Print implementation of global factory functions to "os". + * + * Each class has two global factory functions: + * + * set manage(__isl_take isl_set *ptr); + * set manage_copy(__isl_keep isl_set *ptr); + * + * Unless checked C++ bindings are being generated, + * both functions require the argument to be non-NULL. + * An exception is thrown if anything went wrong during the copying + * in manage_copy. + * During the copying, isl is made not to print any error message + * because the error message is included in the exception. + */ +void cpp_generator::print_class_factory_impl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "%s manage(__isl_take %s *ptr) {\n", cppname, name); + if (!checked) { + osprintf(os, " if (!ptr)\n"); + print_throw_NULL_input(os); + } + osprintf(os, " return %s(ptr);\n", cppname); + osprintf(os, "}\n"); + + osprintf(os, "%s manage_copy(__isl_keep %s *ptr) {\n", cppname, + name); + if (!checked) { + osprintf(os, " if (!ptr)\n"); + print_throw_NULL_input(os); + osprintf(os, " auto ctx = %s_get_ctx(ptr);\n", name); + print_on_error_continue(os); + } + osprintf(os, " ptr = %s_copy(ptr);\n", name); + if (!checked) { + osprintf(os, " if (!ptr)\n"); + print_throw_last_error(os); + } + osprintf(os, " return %s(ptr);\n", cppname); + osprintf(os, "}\n"); +} + +/* Print implementations of private constructors for class "clazz" to "os". + */ +void cpp_generator::print_private_constructors_impl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "%s::%s(__isl_take %s *ptr)\n : ptr(ptr) {}\n", + cppname, cppname, name); +} + +/* Print implementations of public constructors for class "clazz" to "os". + * + * Throw an exception from the copy constructor if anything went wrong + * during the copying or if the input is NULL. + * During the copying, isl is made not to print any error message + * because the error message is included in the exception. + * No exceptions are thrown if checked C++ bindings + * are being generated, + */ +void cpp_generator::print_public_constructors_impl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "%s::%s()\n : ptr(nullptr) {}\n\n", cppname, cppname); + osprintf(os, "%s::%s(const %s &obj)\n : ptr(nullptr)\n", + cppname, cppname, cppname); + osprintf(os, "{\n"); + if (!checked) { + osprintf(os, " if (!obj.ptr)\n"); + print_throw_NULL_input(os); + osprintf(os, " auto ctx = %s_get_ctx(obj.ptr);\n", name); + print_on_error_continue(os); + } + osprintf(os, " ptr = obj.copy();\n"); + if (!checked) { + osprintf(os, " if (obj.ptr && !ptr)\n"); + print_throw_last_error(os); + } + osprintf(os, "}\n"); +} + +/* Print implementations of constructors for class "clazz" to "os". + */ +void cpp_generator::print_constructors_impl(ostream &os, + const isl_class &clazz) +{ + set<FunctionDecl *>::const_iterator in; + const set<FunctionDecl *> constructors = clazz.constructors; + + for (in = constructors.begin(); in != constructors.end(); ++in) { + FunctionDecl *cons = *in; + string fullname = cons->getName(); + function_kind kind = function_kind_constructor; + + print_method_impl(os, clazz, fullname, cons, kind); + } +} + +/* Print implementation of copy assignment operator for class "clazz" to "os". + */ +void cpp_generator::print_copy_assignment_impl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "%s &%s::operator=(%s obj) {\n", cppname, + cppname, cppname); + osprintf(os, " std::swap(this->ptr, obj.ptr);\n", name); + osprintf(os, " return *this;\n"); + osprintf(os, "}\n"); +} + +/* Print implementation of destructor for class "clazz" to "os". + */ +void cpp_generator::print_destructor_impl(ostream &os, + const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "%s::~%s() {\n", cppname, cppname); + osprintf(os, " if (ptr)\n"); + osprintf(os, " %s_free(ptr);\n", name); + osprintf(os, "}\n"); +} + +/* Print implementation of ptr() functions for class "clazz" to "os". + */ +void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "__isl_give %s *%s::copy() const & {\n", name, cppname); + osprintf(os, " return %s_copy(ptr);\n", name); + osprintf(os, "}\n\n"); + osprintf(os, "__isl_keep %s *%s::get() const {\n", name, cppname); + osprintf(os, " return ptr;\n"); + osprintf(os, "}\n\n"); + osprintf(os, "__isl_give %s *%s::release() {\n", name, cppname); + osprintf(os, " %s *tmp = ptr;\n", name); + osprintf(os, " ptr = nullptr;\n"); + osprintf(os, " return tmp;\n"); + osprintf(os, "}\n\n"); + osprintf(os, "bool %s::is_null() const {\n", cppname); + osprintf(os, " return ptr == nullptr;\n"); + osprintf(os, "}\n"); +} + +/* Print the implementation of the get_ctx method. + */ +void cpp_generator::print_get_ctx_impl(ostream &os, const isl_class &clazz) +{ + const char *name = clazz.name.c_str(); + std::string cppstring = type2cpp(clazz); + const char *cppname = cppstring.c_str(); + + osprintf(os, "ctx %s::get_ctx() const {\n", cppname); + osprintf(os, " return ctx(%s_get_ctx(ptr));\n", name); + osprintf(os, "}\n"); +} + +/* Print definitions for methods of class "clazz" to "os". + */ +void cpp_generator::print_methods_impl(ostream &os, const isl_class &clazz) +{ + map<string, set<FunctionDecl *> >::const_iterator it; + bool first = true; + + for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it) { + if (first) + first = false; + else + osprintf(os, "\n"); + print_method_group_impl(os, clazz, it->first, it->second); + } +} + +/* Print definitions for methods "methods" of name "fullname" in class "clazz" + * to "os". + * + * "fullname" is the name of the generated C++ method. It commonly corresponds + * to the isl name, with the object type prefix dropped. + * In case of overloaded methods, the result type suffix has also been removed. + * + * "kind" specifies the kind of method that should be generated. + */ +void cpp_generator::print_method_group_impl(ostream &os, const isl_class &clazz, + const string &fullname, const set<FunctionDecl *> &methods) +{ + set<FunctionDecl *>::const_iterator it; + bool first = true; + + for (it = methods.begin(); it != methods.end(); ++it) { + function_kind kind; + if (first) + first = false; + else + osprintf(os, "\n"); + kind = get_method_kind(clazz, *it); + print_method_impl(os, clazz, fullname, *it, kind); + } +} + +/* Print the use of "param" to "os". + * + * "load_from_this_ptr" specifies whether the parameter should be loaded from + * the this-ptr. In case a value is loaded from a this pointer, the original + * value must be preserved and must consequently be copied. Values that are + * loaded from parameters do not need to be preserved, as such values will + * already be copies of the actual parameters. It is consequently possible + * to directly take the pointer from these values, which saves + * an unnecessary copy. + * + * In case the parameter is a callback function, two parameters get printed, + * a wrapper for the callback function and a pointer to the actual + * callback function. The wrapper is expected to be available + * in a previously declared variable <name>_lambda, while + * the actual callback function is expected to be stored + * in a structure called <name>_data. + * The caller of this function must ensure that these variables exist. + */ +void cpp_generator::print_method_param_use(ostream &os, ParmVarDecl *param, + bool load_from_this_ptr) +{ + string name = param->getName().str(); + const char *name_str = name.c_str(); + QualType type = param->getOriginalType(); + + if (type->isIntegerType()) { + osprintf(os, "%s", name_str); + return; + } + + if (is_string(type)) { + osprintf(os, "%s.c_str()", name_str); + return; + } + + if (is_callback(type)) { + osprintf(os, "%s_lambda, ", name_str); + osprintf(os, "&%s_data", name_str); + return; + } + + if (!load_from_this_ptr && !is_callback(type)) + osprintf(os, "%s.", name_str); + + if (keeps(param)) { + osprintf(os, "get()"); + } else { + if (load_from_this_ptr) + osprintf(os, "copy()"); + else + osprintf(os, "release()"); + } +} + +/* Print code that checks that all isl object arguments to "method" are valid + * (not NULL) and throws an exception if they are not. + * "kind" specifies the kind of method that is being generated. + * + * If checked bindings are being generated, + * then no such check is performed. + */ +void cpp_generator::print_argument_validity_check(ostream &os, + FunctionDecl *method, function_kind kind) +{ + int n; + bool first = true; + + if (checked) + return; + + n = method->getNumParams(); + for (int i = 0; i < n; ++i) { + bool is_this; + ParmVarDecl *param = method->getParamDecl(i); + string name = param->getName().str(); + const char *name_str = name.c_str(); + QualType type = param->getOriginalType(); + + is_this = i == 0 && kind == function_kind_member_method; + if (!is_this && (is_isl_ctx(type) || !is_isl_type(type))) + continue; + + if (first) + osprintf(os, " if ("); + else + osprintf(os, " || "); + + if (is_this) + osprintf(os, "!ptr"); + else + osprintf(os, "%s.is_null()", name_str); + + first = false; + } + if (first) + return; + osprintf(os, ")\n"); + print_throw_NULL_input(os); +} + +/* Print code for saving a copy of the isl::ctx available at the start + * of the method "method" in a "ctx" variable, for use in exception handling. + * "kind" specifies what kind of method "method" is. + * + * If checked bindings are being generated, + * then the "ctx" variable is not needed. + * If "method" is a member function, then obtain the isl_ctx from + * the "this" object. + * If the first argument of the method is an isl::ctx, then use that one, + * assuming it is not already called "ctx". + * Otherwise, save a copy of the isl::ctx associated to the first argument + * of isl object type. + */ +void cpp_generator::print_save_ctx(ostream &os, FunctionDecl *method, + function_kind kind) +{ + int n; + ParmVarDecl *param = method->getParamDecl(0); + QualType type = param->getOriginalType(); + + if (checked) + return; + if (kind == function_kind_member_method) { + osprintf(os, " auto ctx = get_ctx();\n"); + return; + } + if (is_isl_ctx(type)) { + const char *name; + + name = param->getName().str().c_str(); + if (strcmp(name, "ctx") != 0) + osprintf(os, " auto ctx = %s;\n", name); + return; + } + n = method->getNumParams(); + for (int i = 0; i < n; ++i) { + ParmVarDecl *param = method->getParamDecl(i); + QualType type = param->getOriginalType(); + + if (!is_isl_type(type)) + continue; + osprintf(os, " auto ctx = %s.get_ctx();\n", + param->getName().str().c_str()); + return; + } +} + +/* Print code to make isl not print an error message when an error occurs + * within the current scope (if exceptions are available), + * since the error message will be included in the exception. + * If exceptions are not available, then exception::on_error + * is set to ISL_ON_ERROR_ABORT and isl is therefore made to abort instead. + * + * If checked bindings are being generated, + * then leave it to the user to decide what isl should do on error. + * Otherwise, assume that a valid isl::ctx is available in the "ctx" variable, + * e.g., through a prior call to print_save_ctx. + */ +void cpp_generator::print_on_error_continue(ostream &os) +{ + if (checked) + return; + osprintf(os, " options_scoped_set_on_error saved_on_error(ctx, " + "exception::on_error);\n"); +} + +/* Print code that checks whether the execution of the core of "method" + * was successful. + * + * If checked bindings are being generated, + * then no checks are performed. + * + * Otherwise, first check if any of the callbacks failed with + * an exception. If so, the "eptr" in the corresponding data structure + * contains the exception that was caught and that needs to be rethrown. + * Then check if the function call failed in any other way and throw + * the appropriate exception. + * In particular, if the return type is isl_stat or isl_bool, + * then a negative value indicates a failure. If the return type + * is an isl type, then a NULL value indicates a failure. + * Assume print_save_ctx has made sure that a valid isl::ctx + * is available in the "ctx" variable. + */ +void cpp_generator::print_exceptional_execution_check(ostream &os, + FunctionDecl *method) +{ + int n; + bool check_null, check_neg; + QualType return_type = method->getReturnType(); + + if (checked) + return; + + n = method->getNumParams(); + for (int i = 0; i < n; ++i) { + ParmVarDecl *param = method->getParamDecl(i); + const char *name; + + if (!is_callback(param->getOriginalType())) + continue; + name = param->getName().str().c_str(); + osprintf(os, " if (%s_data.eptr)\n", name); + osprintf(os, " std::rethrow_exception(%s_data.eptr);\n", + name); + } + + check_neg = is_isl_stat(return_type) || is_isl_bool(return_type); + check_null = is_isl_type(return_type); + if (!check_null && !check_neg) + return; + + if (check_neg) + osprintf(os, " if (res < 0)\n"); + else + osprintf(os, " if (!res)\n"); + print_throw_last_error(os); +} + +/* Print definition for "method" in class "clazz" to "os". + * + * "fullname" is the name of the generated C++ method. It commonly corresponds + * to the isl name, with the object type prefix dropped. + * In case of overloaded methods, the result type suffix has also been removed. + * + * "kind" specifies the kind of method that should be generated. + * + * This method distinguishes three kinds of methods: member methods, static + * methods, and constructors. + * + * Member methods call "method" by passing to the underlying isl function the + * isl object belonging to "this" as first argument and the remaining arguments + * as subsequent arguments. The result of the isl function is returned as a new + * object if the underlying isl function returns an isl_* ptr, as a bool + * if the isl function returns an isl_bool, as void if the isl functions + * returns an isl_stat, + * as std::string if the isl function returns 'const char *', and as + * unmodified return value otherwise. + * If checked C++ bindings are being generated, + * then an isl_bool return type is transformed into a boolean and + * an isl_stat into a stat since no exceptions can be generated + * on negative results from the isl function. + * + * Static methods call "method" by passing all arguments to the underlying isl + * function, as no this-pointer is available. The result is a newly managed + * isl C++ object. + * + * Constructors create a new object from a given set of input parameters. They + * do not return a value, but instead update the pointer stored inside the + * newly created object. + * + * If the method has a callback argument, we reduce the number of parameters + * that are exposed by one to hide the user pointer from the interface. On + * the C++ side no user pointer is needed, as arguments can be forwarded + * as part of the std::function argument which specifies the callback function. + * + * Unless checked C++ bindings are being generated, + * the inputs of the method are first checked for being valid isl objects and + * a copy of the associated isl::ctx is saved (if needed). + * If any failure occurs, either during the check for the inputs or + * during the isl function call, an exception is thrown. + * During the function call, isl is made not to print any error message + * because the error message is included in the exception. + */ +void cpp_generator::print_method_impl(ostream &os, const isl_class &clazz, + const string &fullname, FunctionDecl *method, function_kind kind) +{ + string methodname = method->getName(); + int num_params = method->getNumParams(); + QualType return_type = method->getReturnType(); + string rettype_str = type2cpp(return_type); + bool has_callback = false; + + print_method_header(os, clazz, method, fullname, false, kind); + osprintf(os, "{\n"); + print_argument_validity_check(os, method, kind); + print_save_ctx(os, method, kind); + print_on_error_continue(os); + + for (int i = 0; i < num_params; ++i) { + ParmVarDecl *param = method->getParamDecl(i); + if (is_callback(param->getType())) { + has_callback = true; + num_params -= 1; + print_callback_local(os, param); + } + } + + osprintf(os, " auto res = %s(", methodname.c_str()); + + for (int i = 0; i < num_params; ++i) { + ParmVarDecl *param = method->getParamDecl(i); + bool load_from_this_ptr = false; + + if (i == 0 && kind == function_kind_member_method) + load_from_this_ptr = true; + + print_method_param_use(os, param, load_from_this_ptr); + + if (i != num_params - 1) + osprintf(os, ", "); + } + osprintf(os, ");\n"); + + print_exceptional_execution_check(os, method); + if (kind == function_kind_constructor) { + osprintf(os, " ptr = res;\n"); + } else if (is_isl_type(return_type) || + (checked && + (is_isl_bool(return_type) || is_isl_stat(return_type)))) { + osprintf(os, " return manage(res);\n"); + } else if (has_callback) { + osprintf(os, " return %s(res);\n", rettype_str.c_str()); + } else if (is_string(return_type)) { + osprintf(os, " std::string tmp(res);\n"); + if (gives(method)) + osprintf(os, " free(res);\n"); + osprintf(os, " return tmp;\n"); + } else { + osprintf(os, " return res;\n"); + } + + osprintf(os, "}\n"); +} + +/* Print the header for "method" in class "clazz" to "os". + * + * Print the header of a declaration if "is_declaration" is set, otherwise print + * the header of a method definition. + * + * "fullname" is the name of the generated C++ method. It commonly corresponds + * to the isl name, with the object type prefix dropped. + * In case of overloaded methods, the result type suffix has also been removed. + * + * "kind" specifies the kind of method that should be generated. + * + * This function prints headers for member methods, static methods, and + * constructors, either for their declaration or definition. + * + * Member functions are declared as "const", as they do not change the current + * object, but instead create a new object. They always retrieve the first + * parameter of the original isl function from the this-pointer of the object, + * such that only starting at the second parameter the parameters of the + * original function become part of the method's interface. + * + * A function + * + * __isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1, + * __isl_take isl_set *s2); + * + * is translated into: + * + * inline set intersect(set set2) const; + * + * For static functions and constructors all parameters of the original isl + * function are exposed. + * + * Parameters that are defined as __isl_keep or are of type string, are passed + * as const reference, which allows the compiler to optimize the parameter + * transfer. + * + * Constructors are marked as explicit using the C++ keyword 'explicit' or as + * implicit using a comment in place of the explicit keyword. By annotating + * implicit constructors with a comment, users of the interface are made + * aware of the potential danger that implicit construction is possible + * for these constructors, whereas without a comment not every user would + * know that implicit construction is allowed in absence of an explicit keyword. + */ +void cpp_generator::print_method_header(ostream &os, const isl_class &clazz, + FunctionDecl *method, const string &fullname, bool is_declaration, + function_kind kind) +{ + string cname = fullname.substr(clazz.name.length() + 1); + string rettype_str = type2cpp(method->getReturnType()); + string classname = type2cpp(clazz); + int num_params = method->getNumParams(); + int first_param = 0; + + cname = rename_method(cname); + if (kind == function_kind_member_method) + first_param = 1; + + if (is_declaration) { + osprintf(os, " "); + + if (kind == function_kind_static_method) + osprintf(os, "static "); + + osprintf(os, "inline "); + + if (kind == function_kind_constructor) { + if (is_implicit_conversion(clazz, method)) + osprintf(os, "/* implicit */ "); + else + osprintf(os, "explicit "); + } + } + + if (kind != function_kind_constructor) + osprintf(os, "%s ", rettype_str.c_str()); + + if (!is_declaration) + osprintf(os, "%s::", classname.c_str()); + + if (kind != function_kind_constructor) + osprintf(os, "%s", cname.c_str()); + else + osprintf(os, "%s", classname.c_str()); + + osprintf(os, "("); + + for (int i = first_param; i < num_params; ++i) { + ParmVarDecl *param = method->getParamDecl(i); + QualType type = param->getOriginalType(); + string cpptype = type2cpp(type); + + if (is_callback(type)) + num_params--; + + if (keeps(param) || is_string(type) || is_callback(type)) + osprintf(os, "const %s &%s", cpptype.c_str(), + param->getName().str().c_str()); + else + osprintf(os, "%s %s", cpptype.c_str(), + param->getName().str().c_str()); + + if (i != num_params - 1) + osprintf(os, ", "); + } + + osprintf(os, ")"); + + if (kind == function_kind_member_method) + osprintf(os, " const"); + + if (is_declaration) + osprintf(os, ";"); + osprintf(os, "\n"); +} + +/* Generate the list of argument types for a callback function of + * type "type". If "cpp" is set, then generate the C++ type list, otherwise + * the C type list. + * + * For a callback of type + * + * isl_stat (*)(__isl_take isl_map *map, void *user) + * + * the following C++ argument list is generated: + * + * map + */ +string cpp_generator::generate_callback_args(QualType type, bool cpp) +{ + std::string type_str; + const FunctionProtoType *callback; + int num_params; + + callback = type->getPointeeType()->getAs<FunctionProtoType>(); + num_params = callback->getNumArgs(); + if (cpp) + num_params--; + + for (long i = 0; i < num_params; i++) { + QualType type = callback->getArgType(i); + + if (cpp) + type_str += type2cpp(type); + else + type_str += type.getAsString(); + + if (!cpp) + type_str += "arg_" + ::to_string(i); + + if (i != num_params - 1) + type_str += ", "; + } + + return type_str; +} + +/* Generate the full cpp type of a callback function of type "type". + * + * For a callback of type + * + * isl_stat (*)(__isl_take isl_map *map, void *user) + * + * the following type is generated: + * + * std::function<stat(map)> + */ +string cpp_generator::generate_callback_type(QualType type) +{ + std::string type_str; + const FunctionProtoType *callback = type->getPointeeType()->getAs<FunctionProtoType>(); + QualType return_type = callback->getReturnType(); + string rettype_str = type2cpp(return_type); + + type_str = "std::function<"; + type_str += rettype_str; + type_str += "("; + type_str += generate_callback_args(type, true); + type_str += ")>"; + + return type_str; +} + +/* Print the call to the C++ callback function "call", wrapped + * for use inside the lambda function that is used as the C callback function, + * in the case where checked C++ bindings are being generated. + * + * In particular, print + * + * stat ret = @call@; + * return ret.release(); + */ +void cpp_generator::print_wrapped_call_checked(ostream &os, + const string &call) +{ + osprintf(os, " stat ret = %s;\n", call.c_str()); + osprintf(os, " return ret.release();\n"); +} + +/* Print the call to the C++ callback function "call", wrapped + * for use inside the lambda function that is used as the C callback function. + * + * In particular, print + * + * ISL_CPP_TRY { + * @call@; + * return isl_stat_ok; + * } ISL_CPP_CATCH_ALL { + * data->eptr = std::current_exception(); + * return isl_stat_error; + * } + * + * where ISL_CPP_TRY is defined to "try" and ISL_CPP_CATCH_ALL to "catch (...)" + * (if exceptions are available). + * + * If checked C++ bindings are being generated, then + * the call is wrapped differently. + */ +void cpp_generator::print_wrapped_call(ostream &os, const string &call) +{ + if (checked) + return print_wrapped_call_checked(os, call); + + osprintf(os, " ISL_CPP_TRY {\n"); + osprintf(os, " %s;\n", call.c_str()); + osprintf(os, " return isl_stat_ok;\n"); + osprintf(os, " } ISL_CPP_CATCH_ALL {\n" + " data->eptr = std::current_exception();\n"); + osprintf(os, " return isl_stat_error;\n"); + osprintf(os, " }\n"); +} + +/* Print the local variables that are needed for a callback argument, + * in particular, print a lambda function that wraps the callback and + * a pointer to the actual C++ callback function. + * + * For a callback of the form + * + * isl_stat (*fn)(__isl_take isl_map *map, void *user) + * + * the following lambda function is generated: + * + * auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat { + * auto *data = static_cast<struct fn_data *>(arg_1); + * try { + * stat ret = (*data->func)(manage(arg_0)); + * return isl_stat_ok; + * } catch (...) { + * data->eptr = std::current_exception(); + * return isl_stat_error; + * } + * }; + * + * The pointer to the std::function C++ callback function is stored in + * a fn_data data structure for passing to the C callback function, + * along with an std::exception_ptr that is used to store any + * exceptions thrown in the C++ callback. + * + * struct fn_data { + * const std::function<stat(map)> *func; + * std::exception_ptr eptr; + * } fn_data = { &fn }; + * + * This std::function object represents the actual user + * callback function together with the locally captured state at the caller. + * + * The lambda function is expected to be used as a C callback function + * where the lambda itself is provided as the function pointer and + * where the user void pointer is a pointer to fn_data. + * The std::function object is extracted from the pointer to fn_data + * inside the lambda function. + * + * The std::exception_ptr object is not added to fn_data + * if checked C++ bindings are being generated. + * The body of the generated lambda function then is as follows: + * + * stat ret = (*data->func)(manage(arg_0)); + * return isl_stat(ret); + */ +void cpp_generator::print_callback_local(ostream &os, ParmVarDecl *param) +{ + string pname; + QualType ptype; + string call, c_args, cpp_args, rettype, last_idx; + const FunctionProtoType *callback; + int num_params; + + pname = param->getName().str(); + ptype = param->getType(); + + c_args = generate_callback_args(ptype, false); + cpp_args = generate_callback_type(ptype); + + callback = ptype->getPointeeType()->getAs<FunctionProtoType>(); + rettype = callback->getReturnType().getAsString(); + num_params = callback->getNumArgs(); + + last_idx = ::to_string(num_params - 1); + + call = "(*data->func)("; + for (long i = 0; i < num_params - 1; i++) { + call += "manage(arg_" + ::to_string(i) + ")"; + if (i != num_params - 2) + call += ", "; + } + call += ")"; + + osprintf(os, " struct %s_data {\n", pname.c_str()); + osprintf(os, " const %s *func;\n", cpp_args.c_str()); + if (!checked) + osprintf(os, " std::exception_ptr eptr;\n"); + osprintf(os, " } %s_data = { &%s };\n", pname.c_str(), pname.c_str()); + osprintf(os, " auto %s_lambda = [](%s) -> %s {\n", + pname.c_str(), c_args.c_str(), rettype.c_str()); + osprintf(os, + " auto *data = static_cast<struct %s_data *>(arg_%s);\n", + pname.c_str(), last_idx.c_str()); + print_wrapped_call(os, call); + osprintf(os, " };\n"); +} + +/* An array listing functions that must be renamed and the function name they + * should be renamed to. We currently rename functions in case their name would + * match a reserved C++ keyword, which is not allowed in C++. + */ +static const char *rename_map[][2] = { + { "union", "unite" }, +}; + +/* Rename method "name" in case the method name in the C++ bindings should not + * match the name in the C bindings. We do this for example to avoid + * C++ keywords. + */ +std::string cpp_generator::rename_method(std::string name) +{ + for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++) + if (name.compare(rename_map[i][0]) == 0) + return rename_map[i][1]; + + return name; +} + +/* Translate isl class "clazz" to its corresponding C++ type. + */ +string cpp_generator::type2cpp(const isl_class &clazz) +{ + return type2cpp(clazz.name); +} + +/* Translate type string "type_str" to its C++ name counterpart. +*/ +string cpp_generator::type2cpp(string type_str) +{ + return type_str.substr(4); +} + +/* Translate QualType "type" to its C++ name counterpart. + * + * An isl_bool return type is translated into "bool", + * while an isl_stat is translated into "void". + * The exceptional cases are handled through exceptions. + * If checked C++ bindings are being generated, then + * C++ counterparts of isl_bool and isl_stat need to be used instead. + */ +string cpp_generator::type2cpp(QualType type) +{ + if (is_isl_type(type)) + return type2cpp(type->getPointeeType().getAsString()); + + if (is_isl_bool(type)) + return checked ? "boolean" : "bool"; + + if (is_isl_stat(type)) + return checked ? "stat" : "void"; + + if (type->isIntegerType()) + return type.getAsString(); + + if (is_string(type)) + return "std::string"; + + if (is_callback(type)) + return generate_callback_type(type); + + die("Cannot convert type to C++ type"); +} + +/* Check if "subclass_type" is a subclass of "class_type". + */ +bool cpp_generator::is_subclass(QualType subclass_type, + const isl_class &class_type) +{ + std::string type_str = subclass_type->getPointeeType().getAsString(); + std::vector<std::string> superclasses; + std::vector<const isl_class *> parents; + std::vector<std::string>::iterator ci; + + superclasses = generator::find_superclasses(classes[type_str].type); + + for (ci = superclasses.begin(); ci < superclasses.end(); ci++) + parents.push_back(&classes[*ci]); + + while (!parents.empty()) { + const isl_class *candidate = parents.back(); + + parents.pop_back(); + + if (&class_type == candidate) + return true; + + superclasses = generator::find_superclasses(candidate->type); + + for (ci = superclasses.begin(); ci < superclasses.end(); ci++) + parents.push_back(&classes[*ci]); + } + + return false; +} + +/* Check if "cons" is an implicit conversion constructor of class "clazz". + * + * An implicit conversion constructor is generated in case "cons" has a single + * parameter, where the parameter type is a subclass of the class that is + * currently being generated. + */ +bool cpp_generator::is_implicit_conversion(const isl_class &clazz, + FunctionDecl *cons) +{ + ParmVarDecl *param = cons->getParamDecl(0); + QualType type = param->getOriginalType(); + + int num_params = cons->getNumParams(); + if (num_params != 1) + return false; + + if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz)) + return true; + + return false; +} + +/* Get kind of "method" in "clazz". + * + * Given the declaration of a static or member method, returns its kind. + */ +cpp_generator::function_kind cpp_generator::get_method_kind( + const isl_class &clazz, FunctionDecl *method) +{ + if (is_static(clazz, method)) + return function_kind_static_method; + else + return function_kind_member_method; +} |

