From a12c4da93d1147384c08f8d4ffed16ddcb383e99 Mon Sep 17 00:00:00 2001 From: Siu Kwan Lam Date: Tue, 22 Jan 2013 10:09:14 -0600 Subject: [PATCH] Init commit for work on new binding This contains the foundation for the new binding as well as early work on Module and Type. --- newbinding/Makefile | 18 + .../binding/AssemblyAnnotationWriter.py | 6 + newbinding/binding/DerivedTypes.py | 13 + newbinding/binding/LLVMContext.py | 8 + newbinding/binding/Makefile | 0 newbinding/binding/Module.py | 25 + newbinding/binding/StringRef.py | 6 + newbinding/binding/Type.py | 96 ++++ newbinding/binding/_Debug.py | 3 + newbinding/binding/binding.py | 498 ++++++++++++++++++ newbinding/binding/gen.py | 206 ++++++++ newbinding/binding/namespace.py | 4 + newbinding/binding/raw_ostream.py | 17 + newbinding/binding/utils.py | 6 + newbinding/capsule.cpp | 143 +++++ newbinding/capsule.py | 74 +++ newbinding/include/llvm_binding/binding.h | 73 +++ .../include/llvm_binding/capsule_context.h | 48 ++ newbinding/include/llvm_binding/conversion.h | 93 ++++ newbinding/include/llvm_binding/extra.h | 34 ++ newbinding/setup.py | 54 ++ newbinding/test.py | 32 ++ newbinding/test2.py | 20 + 23 files changed, 1477 insertions(+) create mode 100644 newbinding/Makefile create mode 100644 newbinding/binding/AssemblyAnnotationWriter.py create mode 100644 newbinding/binding/DerivedTypes.py create mode 100644 newbinding/binding/LLVMContext.py create mode 100644 newbinding/binding/Makefile create mode 100644 newbinding/binding/Module.py create mode 100644 newbinding/binding/StringRef.py create mode 100644 newbinding/binding/Type.py create mode 100644 newbinding/binding/_Debug.py create mode 100644 newbinding/binding/binding.py create mode 100644 newbinding/binding/gen.py create mode 100644 newbinding/binding/namespace.py create mode 100644 newbinding/binding/raw_ostream.py create mode 100644 newbinding/binding/utils.py create mode 100644 newbinding/capsule.cpp create mode 100644 newbinding/capsule.py create mode 100644 newbinding/include/llvm_binding/binding.h create mode 100644 newbinding/include/llvm_binding/capsule_context.h create mode 100644 newbinding/include/llvm_binding/conversion.h create mode 100644 newbinding/include/llvm_binding/extra.h create mode 100644 newbinding/setup.py create mode 100644 newbinding/test.py create mode 100644 newbinding/test2.py diff --git a/newbinding/Makefile b/newbinding/Makefile new file mode 100644 index 0000000..ba902e3 --- /dev/null +++ b/newbinding/Makefile @@ -0,0 +1,18 @@ +PYMODS = _Debug raw_ostream Type DerivedTypes LLVMContext StringRef AssemblyAnnotationWriter Module + +all: _api.so _capsule.so + +_api.so _capsule.so: api.cpp capsule.cpp + python setup.py build_ext --inplace + + +api.cpp api.py: binding/*.py include/llvm_binding/*.h + cd binding; python gen.py api $(PYMODS) + mv binding/api.cpp api.cpp + mv binding/api.py api.py + +clean: + rm -f _api.so _capsule.so + rm -f api.cpp api.py + + diff --git a/newbinding/binding/AssemblyAnnotationWriter.py b/newbinding/binding/AssemblyAnnotationWriter.py new file mode 100644 index 0000000..5641232 --- /dev/null +++ b/newbinding/binding/AssemblyAnnotationWriter.py @@ -0,0 +1,6 @@ +from binding import * +from namespace import llvm + +AssemblyAnnotationWriter = llvm.Class() +AssemblyAnnotationWriter.include.add("llvm/Assembly/AssemblyAnnotationWriter.h") + diff --git a/newbinding/binding/DerivedTypes.py b/newbinding/binding/DerivedTypes.py new file mode 100644 index 0000000..10d5fbf --- /dev/null +++ b/newbinding/binding/DerivedTypes.py @@ -0,0 +1,13 @@ +from binding import * +from namespace import llvm +from LLVMContext import LLVMContext +from Type import Type + +FunctionType = Type.Subclass() +FunctionType.include.add('llvm/DerivedTypes.h') + +get = FunctionType.staticmethod(FunctionType.Pointer, Type.Pointer, Bool.From(bool)) +isVarArg = FunctionType.method(Bool.To(bool)) +getReturnType = FunctionType.method(Type.Pointer) +getParamType = FunctionType.method(Type.Pointer, Unsigned.From(int)) +getNumParams = FunctionType.method(Unsigned.To(int)) diff --git a/newbinding/binding/LLVMContext.py b/newbinding/binding/LLVMContext.py new file mode 100644 index 0000000..0965748 --- /dev/null +++ b/newbinding/binding/LLVMContext.py @@ -0,0 +1,8 @@ +from binding import * +from namespace import llvm + +LLVMContext = llvm.Class() +LLVMContext.include.add("llvm/LLVMContext.h") + +getGlobalContext = llvm.Function(LLVMContext.Ref) + diff --git a/newbinding/binding/Makefile b/newbinding/binding/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/newbinding/binding/Module.py b/newbinding/binding/Module.py new file mode 100644 index 0000000..a458401 --- /dev/null +++ b/newbinding/binding/Module.py @@ -0,0 +1,25 @@ +from binding import * +from namespace import llvm +from LLVMContext import LLVMContext +from StringRef import StringRef +from raw_ostream import raw_svector_ostream_helper +from AssemblyAnnotationWriter import AssemblyAnnotationWriter + +Module = llvm.Class() +Module.include.add("llvm/Module.h") + +new = Module.new(StringRef.From(str), LLVMContext.Ref) +delete = Module.delete() +getModuleIdentifier = Module.method(ConstStdString.To(str)) +setModuleIdentifier = Module.method(Void, StringRef.From(str)) +setDataLayout = Module.method(Void, StringRef.From(str)) +setTargetTriple = Module.method(Void, StringRef.From(str)) +setModuleInlineAsm = Module.method(Void, StringRef.From(str)) +appendModuleInlineAsm = Module.method(Void, StringRef.From(str)) +getContext = Module.method(LLVMContext.Ref) +dump = Module.method(Void) +print_ = Module.method(Void, raw_svector_ostream_helper.Ref, + AssemblyAnnotationWriter.Pointer) +print_.realname = 'print' +#getOrInsertFunction = Module.method(Constant, StringRef.From(str), FunctionType.Pointer) + diff --git a/newbinding/binding/StringRef.py b/newbinding/binding/StringRef.py new file mode 100644 index 0000000..554629b --- /dev/null +++ b/newbinding/binding/StringRef.py @@ -0,0 +1,6 @@ +from binding import * +from namespace import llvm + +StringRef = llvm.Class() +StringRef.include.add("llvm/ADT/StringRef.h") + diff --git a/newbinding/binding/Type.py b/newbinding/binding/Type.py new file mode 100644 index 0000000..19f6d61 --- /dev/null +++ b/newbinding/binding/Type.py @@ -0,0 +1,96 @@ +from binding import * +from namespace import llvm +from LLVMContext import LLVMContext +from raw_ostream import raw_svector_ostream_helper + +Type = llvm.Class() +Type.include.add('llvm/Type.h') + +IntegerType = Type.Subclass() +CompositeType = Type.Subclass() +SequentialType = CompositeType.Subclass() +PointerType = SequentialType.Subclass() + +getContext = Type.method(LLVMContext.Ref) +dump = Type.method(Void) +print_ = Type.method(Void, raw_svector_ostream_helper.Ref) +print_.realname = 'print' + +def type_checker(): + return Type.method(Bool.To(bool)) + +isVoidTy = type_checker() +isHalfTy = type_checker() +isFloatTy = type_checker() +isDoubleTy = type_checker() +isX86_FP80Ty = type_checker() +isFP128Ty = type_checker() +isPPC_FP128Ty = type_checker() +isFloatingPointTy = type_checker() +isX86_MMXTy = type_checker() +isFPOrFPVectorTy = type_checker() +isLabelTy = type_checker() +isMetadataTy = type_checker() +isIntOrIntVectorTy = type_checker() +isFunctionTy = type_checker() +isStructTy = type_checker() +isArrayTy = type_checker() +isPointerTy = type_checker() +isPtrOrPtrVectorTy = type_checker() +isVectorTy = type_checker() +isEmptyTy = type_checker() +isPrimitiveType = type_checker() +isDerivedType = type_checker() +isFirstClassType = type_checker() +isSingleValueType = type_checker() +isAggregateType = type_checker() +isSized = type_checker() + +isIntegerTy = Type.multimethod([Bool.To(bool)], + [Bool.To(bool), Unsigned.From(int)]) + + +def type_factory(): + return Type.staticmethod(Type.Pointer, LLVMContext.Ref) + +getVoidTy = type_factory() +getLabelTy = type_factory() +getHalfTy = type_factory() +getFloatTy = type_factory() +getDoubleTy = type_factory() +getMetadataTy = type_factory() +getX86_FP80Ty = type_factory() +getFP128Ty = type_factory() +getPPC_FP128Ty = type_factory() +getX86_MMXTy = type_factory() + +getIntNTy = Type.staticmethod(IntegerType.Pointer, LLVMContext.Ref, Unsigned.From(int)) + +def integer_factory(): + return Type.staticmethod(IntegerType.Pointer, LLVMContext.Ref) + +getInt1Ty = integer_factory() +getInt8Ty = integer_factory() +getInt16Ty = integer_factory() +getInt32Ty = integer_factory() +getInt64Ty = integer_factory() + +def pointer_factory(): + return Type.staticmethod(PointerType.Pointer, LLVMContext.Ref) + +getHalfPtrTy = pointer_factory() +getFloatPtrTy = pointer_factory() +getDoublePtrTy = pointer_factory() +getX86_FP80PtrTy = pointer_factory() +getFP128PtrTy = pointer_factory() +getPPC_FP128PtrTy = pointer_factory() +getX86_MMXPtrTy = pointer_factory() +getInt1PtrTy = pointer_factory() +getInt8PtrTy = pointer_factory() +getInt16PtrTy = pointer_factory() +getInt32PtrTy = pointer_factory() +getInt64PtrTy = pointer_factory() +getIntNPtrTy = Type.staticmethod(PointerType.Pointer, + LLVMContext.Ref, Unsigned.From(int)) + + diff --git a/newbinding/binding/_Debug.py b/newbinding/binding/_Debug.py new file mode 100644 index 0000000..be092f3 --- /dev/null +++ b/newbinding/binding/_Debug.py @@ -0,0 +1,3 @@ +from binding import * + +enable_capsule_dtor_debug = Function('', Void, Bool.From(bool)) diff --git a/newbinding/binding/binding.py b/newbinding/binding/binding.py new file mode 100644 index 0000000..1230813 --- /dev/null +++ b/newbinding/binding/binding.py @@ -0,0 +1,498 @@ +from utils import * + +_py2capi_fmtmap = { + str: 's#', +} + +NULL = 'NULL' + +_symbols = set() + +def new_symbol(name): + if name in _symbols: + ct = 1 + orig = name + while name in _symbols: + name = '%s%d' % (orig, ct) + ct += 1 + _symbols.add(name) + return name + +def parse_arguments(println, var, *args): + typecodes = [] + holders = [] + argvals = [] + for arg in args: + typecodes.append(arg.format) + val = declare(println, 'PyObject*') + argvals.append(val) + holders.append('&' + val) + + items = [var, '"%s"' % (''.join(typecodes))] + holders + println('if(!PyArg_ParseTuple(%s)) return NULL;' % ', '.join(items)) + + # unwrap + unwrapped = [] + for arg, val in zip(args, argvals): + unwrapped.append(arg.unwrap(println, val)) + + return unwrapped + +def mangle(name): + name = name.replace('_', '__').replace(' ', '_') + return name.replace('::', '_').rstrip('*&') + +def pycapsule_new(println, ptr, name, clsname, dtor='capsule_destructor'): + # build capsule + name_soften = mangle(name) + var = new_symbol('pycap_%s' % name_soften) + fmt = 'PyObject* %(var)s = PyCapsule_New(%(ptr)s, "%(name)s", %(dtor)s);' + println(fmt % locals()) + + # build context + fmt = 'new CapsuleContext("%(clsname)s")' + context = declare(println, 'CapsuleContext*', fmt % locals()) + + fmt = 'PyCapsule_SetContext(%(var)s, (void*)%(context)s)' + err = declare(println, 'int', fmt % locals()) + + println('if (%(err)s) return NULL;' % locals()) + return var + + +def declare(println, typ, init=None): + typ_soften = mangle(typ) + var = new_symbol('var_%s' % typ_soften) + if init is None: + println('%(typ)s %(var)s;' % locals()) + else: + println('%(typ)s %(var)s = %(init)s;' % locals()) + return var + + +def return_value(println, var): + println('return %(var)s;' % locals()) + + +def return_none(println): + println('Py_RETURN_NONE;') + + +def die_if_null(println, var): + println('if (!%(var)s) return NULL;' % locals()) + + +class Binding(object): + __rank_global = 0 + def __init__(self): + self.rank = Binding.__rank_global + Binding.__rank_global += 1 + self.include = set() + + def compile(self, name, println): + raise NotImplementedError(type(self)) + +class Ref(object): + def __init__(self, elem): + self.element = elem + + @property + def fullname(self): + return '%s&' % self.element.fullname + + @property + def capsule_name(self): + return self.element.capsule_name + + @property + def pointer(self): + return self.element.pointer + + def as_pointer(self, println, var): + init = '&%s' % (var) + casted = declare(println, self.pointer, init) + return casted + + @property + def format(self): + return 'O' + + def unwrap(self, println, var): + ptr = self.element.unwrap(println, var) + return declare(println, self.fullname, '*%s' % ptr) + + def wrap(self, println, var): + return self.element.wrap(println, self.as_pointer(println, var)) + + +class Pointer(object): + def __init__(self, elem): + self.element = elem + + @property + def fullname(self): + return '%s*' % self.element.fullname + + @property + def capsule_name(self): + return self.element.capsule_name + + @property + def pointer(self): + return self.element.pointer + + @property + def format(self): + return 'O' + + def unwrap(self, println, var): + ret = declare(println, self.fullname) + println2 = indent_println(println) + println('if (%(var)s == Py_None) {' % locals()) + println2('%(ret)s = NULL;' % locals()) + println('} else {') + ptr = self.element.unwrap(println2, var) + println2('%(ret)s = %(ptr)s;' % locals()) + println('}') + return ret + + def wrap(self, println, var): + return self.element.wrap(println, var) + + +class BuiltinType(object): + def __init__(self, name): + self.name = name + self.Ref = Ref(self) + self.Pointer = Pointer(self) + + @property + def fullname(self): + return self.name + + @property + def capsule_name(self): + return self.fullname + + def To(self, pytype): + return Wrapper(self, pytype) + + def From(self, pytype): + return Unwrapper(self, pytype) + +Void = BuiltinType('void') +Bool = BuiltinType('bool') +Unsigned = BuiltinType('unsigned') +ConstStdString = BuiltinType('const std::string') + +class Unwrapper(object): + def __init__(self, cls, pytype): + self.cls = cls + self.pytype = pytype + + @property + def fullname(self): + return str(self.pytype) + + @property + def format(self): + return 'O' + + def unwrap(self, println, var): + out = declare(println, self.cls.fullname) + conv = 'py_%s_to' % (self.pytype.__name__) + status = '%(conv)s(%(var)s, %(out)s)' % locals() + println('if (!%(status)s) return NULL;' % locals()) + return out + + +class Wrapper(object): + def __init__(self, cls, pytype): + self.cls = cls + self.pytype = pytype + + @property + def fullname(self): + return self.cls.fullname + + def wrap(self, println, var): + conv = 'py_%s_from' % (self.pytype.__name__) + func = '%(conv)s(%(var)s)' % locals() + out = declare(println, 'PyObject*', func) + println('if (!%(out)s) return NULL;' % locals()) + return out + +class Class(Binding): + def __init__(self, ns): + super(Class, self).__init__() + self.ctor = None + self.Ref = Ref(self) + self.Pointer = Pointer(self) + self.Subclass = lambda: Subclass(self) + self.ns = ns + self.methods = [] + self.name = None + + def To(self, pytype): + return Wrapper(self, pytype) + + def From(self, pytype): + return Unwrapper(self, pytype) + + def new(self, *args): + method = Constructor(self, self.Pointer, *args) + self.methods.append(method) + return method + + def delete(self): + method = Destructor(self, Void, self.Pointer) + self.methods.append(method) + return method + + def method(self, return_type, *args): + method = Method(self, return_type, self.Pointer, *args) + self.methods.append(method) + return method + + def staticmethod(self, return_type, *args): + sm = StaticMethod(self, return_type, *args) + self.methods.append(sm) + return sm + + def multimethod(self, *signatures): + mm = MultiMethod(self, signatures) + self.methods.append(mm) + return mm + + def compile(self, name, println): + # set name + self.name = self.name or name + + @property + def capsule_name(self): + return self.fullname + + @property + def pointer(self): + return '%s*' % self.fullname + + @property + def fullname(self): + return '::'.join([self.ns, self.name]) + + @property + def mangled_name(self): + return mangle(self.fullname) + + @property + def format(self): + return 'O' + + def unwrap(self, println, var): + typ = self.pointer + elty = self.fullname + cap = self.capsule_name + capptr = 'PyCapsule_GetPointer(%(var)s, "%(cap)s")' % locals() + ptr = declare(println, 'void*', capptr) + unwrapped = 'typecast<%(elty)s>::from(%(ptr)s)' % locals() + var = declare(println, typ, unwrapped) + println('if (!%(var)s) {' % locals()) + println2 = indent_println(println) + println2('PyErr_SetString(PyExc_TypeError, "Invalid cast");') + println2('return NULL;') + println('}') + die_if_null(println, var) + return var + + def wrap(self, println, var): + return pycapsule_new(println, var, self.capsule_name, self.fullname) + +class Subclass(Class): + def __init__(self, parent): + super(Subclass, self).__init__(parent.ns) + self.parent = parent + self.ns = self.parent.ns + + @property + def capsule_name(self): + return self.parent.capsule_name + +class Function(Binding): + def __init__(self, ns, return_type, *args): + super(Function, self).__init__() + self.return_type = return_type + self.args = args + self.ns = ns + self.name = None + + def compile(self, name, println): + # set name + self.name = self.name or name + # generate wrapper + println('static') + println('PyObject*') + println('%(name)s(PyObject* self, PyObject* args)' % locals()) + println('{') + self.compile_body(indent_println(println)) + println('}') + + def compile_body(self, println): + args = parse_arguments(println, 'args', *self.args) + call = '%s(%s)' % (self.fullname, ', '.join(args)) + if self.return_type is not Void: + callres = declare(println, self.return_type.fullname, call) + pycap = self.return_type.wrap(println, callres) + return_value(println, pycap) + else: + println('%s;' % call) + return_none(println) + + + @property + def fullname(self): + return '::'.join([self.ns, self.name]) + +class Method(Binding): + def __init__(self, cls, return_type, *args): + super(Method, self).__init__() + self.cls = cls + self.return_type = return_type + self.args = args + self.name = None + self._realname = None + + def compile(self, name, println): + # set name + self.name = self.name or name + # generate wrapper + println('static') + println('PyObject*') + mangled = self.mangled_name + println('%(mangled)s(PyObject* self, PyObject* args)' % locals()) + println('{') + self.compile_body(indent_println(println)) + println('}') + + def compile_body(self, println): + args = parse_arguments(println, 'args', *self.args) + this = args[0] + args = ', '.join(args[1:]) + name = self.realname + call = '%(this)s->%(name)s(%(args)s)' % locals() + if self.return_type is not Void: + obj = declare(println, self.return_type.fullname, call) + ret = self.return_type.wrap(println, obj) + return_value(println, ret) + else: + println('%s;' % call) + return_none(println) + + @property + def fullname(self): + return '::'.join([self.cls.fullname, self.name]) + + @property + def realname(self): + if not self._realname: + return self.name + else: + return self._realname + + @realname.setter + def realname(self, v): + self._realname = v + + + @property + def mangled_name(self): + return mangle(self.fullname) + +class MultiMethod(Binding): + '''Can only differs by the number of arguments. + ''' + def __init__(self, cls, signatures): + super(MultiMethod, self).__init__() + nargs = set() + for sig in signatures: + n = len(sig) + if n in nargs: + raise TypeError("MultiMethod only supports overloaded version" + "with different number of arguments") + nargs.add(n) + self.cls = cls + self.signatures = signatures + self.name = None + + def compile(self, name, println): + # set name + self.name = self.name or name + # generate wrapper + println('static') + println('PyObject*') + mangled = self.mangled_name + println('%(mangled)s(PyObject* self, PyObject* args)' % locals()) + println('{') + println2 = indent_println(println) + nargs = declare(println2, 'Py_ssize_t', 'PyTuple_Size(args)') + for sig in self.signatures: + expect = len(sig) + println2('if (%(nargs)s == %(expect)d) {' % locals()) + method = Method(self.cls, sig[0], self.cls.Pointer, *sig[1:]) + method.name = self.name + method.compile_body(indent_println(println2)) + println2('}') + println2('PyErr_SetString(PyExc_TypeError, "Wrong # of args");') + println2('return NULL;') + println('}') + + @property + def fullname(self): + return '::'.join([self.cls.fullname, self.name]) + + @property + def mangled_name(self): + return mangle(self.fullname) + +class StaticMethod(Method): + def compile_body(self, println): + args = parse_arguments(println, 'args', *self.args) + args = ', '.join(args) + fullname = self.fullname + call = '%(fullname)s(%(args)s)' % locals() + if self.return_type is not Void: + obj = declare(println, self.return_type.fullname, call) + ret = self.return_type.wrap(println, obj) + return_value(println, ret) + else: + println('%s;' % call) + return_none(println) + +class Constructor(StaticMethod): + def compile_body(self, println): + args = parse_arguments(println, 'args', *self.args) + args = ', '.join(args) + name = self.cls.fullname + ctor = 'new %(name)s(%(args)s)' % locals() + obj = declare(println, self.cls.pointer, ctor) + ret = self.return_type.wrap(println, obj) + return_value(println, ret) + +class Destructor(Method): + def compile_body(self, println): + args = parse_arguments(println, 'args', *self.args) + assert len(args) == 1 + dtor = 'delete %s;' % args[0] + println(dtor) + return_none(println) + + +class Namespace(object): + def __init__(self, name): + self.name = name + + def Class(self, *args, **kwargs): + return Class(self.name, *args, **kwargs) + + def Function(self, *args, **kwargs): + return Function(self.name, *args, **kwargs) + diff --git a/newbinding/binding/gen.py b/newbinding/binding/gen.py new file mode 100644 index 0000000..f662979 --- /dev/null +++ b/newbinding/binding/gen.py @@ -0,0 +1,206 @@ +import sys +from binding import * +from utils import * +from cStringIO import StringIO + +extension_entry = ''' + +extern "C" { + +#if (PY_MAJOR_VERSION >= 3) + +PyObject * +PyInit_%(module)s(void) +{ + PyObject *module = create_python_module("%(module)s", %(methtable)s); + if (module) { + if (populate_submodules(module, submodules)) + return module; + } + return NULL; +} + +#else + +PyMODINIT_FUNC +init%(module)s(void) +{ + PyObject *module = create_python_module("%(module)s", %(methtable)s); + if (module) { + populate_submodules(module, submodules); + } +} +#endif + +} // end extern C + +''' + +def build_methoddef(name, defns, println): + println('static PyMethodDef %s[] = {' % name) + for name, func in defns: + println('{ "%(name)s", (PyCFunction)%(func)s, METH_VARARGS, NULL },' % + locals()) + else: + println('{ NULL }') + println('};') + println('') + + +class Context(object): + def __init__(self): + self.includes = set() + self.functions = {} + self.classes = {} + self.definitions = [] + + def generate_cpp(self, println): + for i in self.includes: + println('#include "%s"' % i) + + println('\n'.join(self.definitions)) + + # global function + defns = [] + for name, func in self.functions.items(): + defns.append((name, func.name)) + build_methoddef('global_functions', defns, println) + + # classes + for name, cls in self.classes.items(): + defns = [] + for meth in cls.methods: + defns.append((meth.name, meth.mangled_name)) + println("// %s" % cls.fullname) + build_methoddef(cls.mangled_name, defns, println) + + println('static SubModuleEntry submodules[] = {') + for name, cls in self.classes.items(): + table = cls.mangled_name + println('{ "%(name)s", %(table)s },' % locals()) + println('{ NULL },') + println('};') + println('') + + # generate entry + println(extension_entry % {'module': '_api', + 'methtable': 'global_functions',}) + + def generate_py(self, println): + println('import _api, capsule') + println('') + # global function + for name in self.functions: + println('def %(name)s(*args):' % locals()) + println2 = indent_println(println) + println2('args = map(capsule.unwrap, args)') + println2('ptr = _api.%(name)s(*args)' % locals()) + println2('return capsule.wrap(ptr)') + println('') + # classes + classes = sorted(self.classes.items(), key=lambda x: x[1].rank) + + for name, cls in classes: + if isinstance(cls, Subclass): + parent = cls.parent.name + else: + parent = 'capsule.Wrapper' + println('@capsule.register_class') + println('class %(name)s(%(parent)s):' % locals()) + self.generate_py_class(indent_println(println), cls) + println('') + + def generate_py_class(self, println, cls): + if len(cls.methods) == 0: + println('pass') + else: + mod = cls.name + for method in cls.methods: + name = method.name + if isinstance(method, StaticMethod): + println('@staticmethod') + println('def %(name)s(*args):' % locals()) + println2 = indent_println(println) + println2('args = map(capsule.unwrap, args)') + println2('ret = _api.%(mod)s.%(name)s(*args)' % locals()) + println2('return capsule.wrap(ret)') + elif isinstance(method, Destructor): + println('_delete_ = _api.%(mod)s.%(name)s' % locals()) + else: + println('def %(name)s(self, *args):' % locals()) + println2 = indent_println(println) + println2('args = map(capsule.unwrap, args)') + println2('ret = _api.%(mod)s.%(name)s(self._ptr, *args)' % + locals()) + println2('return capsule.wrap(ret)') + println('') + + + def add_module(self, modname): + module = __import__(modname) + allsyms = [(k, v) for k, v in vars(module).items() + if isinstance(v, Binding)] + symtab = sorted(allsyms, key=lambda x: x[1].rank) + + # generate includes + for k, v in symtab: + self.includes |= v.include + + # compile everything + for k, v in symtab: + buf = StringIO() + def println_to_def(s): + buf.write(s) + buf.write('\n') + v.compile(k, println_to_def) + self.definitions.append(buf.getvalue()) + buf.close() + + # generate py defintion table for global functions + for k, v in symtab: + if isinstance(v, Function): + if v.name in self.functions: + raise NameError("Duplicated function name: %s" % v.name) + self.functions[v.name] = v + + # generate sub module tables for classes + submodules = [] + for k, v in symtab: + if isinstance(v, Class): + if v.name in self.classes: + if v is not self.classes[v.name]: + raise NameError("Duplicated class: %s" % v.name) + self.classes[v.name] = v + + +def populate_headers(println): + includes = [ + 'llvm_binding/conversion.h', + 'llvm_binding/binding.h', + 'llvm_binding/extra.h', + 'llvm_binding/capsule_context.h', + ] + for inc in includes: + println('#include "%s"' % inc) + +def wrap_println(f): + def println(s): + f.write(s) + f.write('\n') + return println + +if __name__ == '__main__': + + context = Context() + for mod in sys.argv[2:]: + context.add_module(mod) + + filename = sys.argv[1] + with open('%s.cpp' % filename, 'w') as outfile: + println = wrap_println(outfile) + populate_headers(println) + context.generate_cpp(println) + with open('%s.py' % filename, 'w') as outfile: + println = wrap_println(outfile) + context.generate_py(println) + diff --git a/newbinding/binding/namespace.py b/newbinding/binding/namespace.py new file mode 100644 index 0000000..02af8c9 --- /dev/null +++ b/newbinding/binding/namespace.py @@ -0,0 +1,4 @@ +from binding import * + +extra = Namespace('llvm_extra') +llvm = Namespace('llvm') diff --git a/newbinding/binding/raw_ostream.py b/newbinding/binding/raw_ostream.py new file mode 100644 index 0000000..0275db4 --- /dev/null +++ b/newbinding/binding/raw_ostream.py @@ -0,0 +1,17 @@ +from binding import * +from namespace import llvm +from LLVMContext import LLVMContext +from StringRef import StringRef + +raw_ostream = llvm.Class() +raw_ostream.include.add("llvm/Support/raw_ostream.h") + +raw_svector_ostream = raw_ostream.Subclass() +raw_svector_ostream.include.add("llvm/Support/raw_os_ostream.h") + +# extra class to help binding +raw_svector_ostream_helper = raw_svector_ostream.Subclass() +create = raw_svector_ostream_helper.staticmethod( + raw_svector_ostream_helper.Pointer) +delete = raw_svector_ostream_helper.delete() +str = raw_svector_ostream_helper.method(StringRef.To(str)) diff --git a/newbinding/binding/utils.py b/newbinding/binding/utils.py new file mode 100644 index 0000000..6c39990 --- /dev/null +++ b/newbinding/binding/utils.py @@ -0,0 +1,6 @@ + +def indent_println(println): + def _println(s): + println("%s%s" % (' '* 4, s)) + return _println + diff --git a/newbinding/capsule.cpp b/newbinding/capsule.cpp new file mode 100644 index 0000000..40d135b --- /dev/null +++ b/newbinding/capsule.cpp @@ -0,0 +1,143 @@ +#include +#include + +static +PyObject* getName(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + const char* name = PyCapsule_GetName(obj); + if (!name) return NULL; + + return PyString_FromString(name); +} + +static +PyObject* getPointer(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + void* pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj)); + if (!pointer) return NULL; + + return PyLong_FromVoidPtr(pointer); +} + +static +PyObject* check(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + if (PyCapsule_CheckExact(obj)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +// ------------------ +// PyCapsule Context +// ------------------ + +static +CapsuleContext* getContext(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + return NULL; + } + void* context = PyCapsule_GetContext(obj); + if (!context) { + PyErr_SetString(PyExc_TypeError, "PyCapsule has no context."); + return NULL; + } + return (CapsuleContext*)context; +} + +static +PyObject* getClassName(PyObject* self, PyObject* args) { + CapsuleContext* context = getContext(self, args); + if (!context) { + return NULL; + } else { + return PyString_FromString(context->className); + } +} + +static +PyObject* setDestructor(PyObject* self, PyObject* args) { + PyObject* cap; + PyObject* callable; + if (!PyArg_ParseTuple(args, "OO", &cap, &callable)) { + return NULL; + } + PyObject* arglist = Py_BuildValue("(O)", cap); + CapsuleContext* context = getContext(self, arglist); + Py_DECREF(arglist); + if (!context) { + return NULL; + } else { + void* ptr; + if (callable != Py_None) { + if (PyCallable_Check(callable)) { + ptr = callable; + } else { + PyErr_SetString(PyExc_TypeError, "Argument is not callable."); + return NULL; + } + } else { + ptr = NULL; + } + context->destructor = (Destructor_Fn)ptr; + } + Py_RETURN_NONE; +} + +static PyMethodDef core_methods[] = { +#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL } + declmethod(getName), + declmethod(getPointer), + declmethod(check), + declmethod(getClassName), + declmethod(setDestructor), + { NULL }, +#undef declmethod +}; + +// Module main function, hairy because of py3k port +extern "C" { + +#if (PY_MAJOR_VERSION >= 3) + struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_capsule", + NULL, + -1, + core_methods, + NULL, NULL, NULL, NULL + }; +#define INITERROR return NULL + PyObject * + PyInit__capsule(void) +#else +#define INITERROR return + PyMODINIT_FUNC + init_capsule(void) +#endif + { +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create( &module_def ); +#else + PyObject *module = Py_InitModule("_capsule", core_methods); +#endif + if (module == NULL) + INITERROR; +#if PY_MAJOR_VERSION >= 3 + + return module; +#endif + } + +} // end extern C diff --git a/newbinding/capsule.py b/newbinding/capsule.py new file mode 100644 index 0000000..b82154b --- /dev/null +++ b/newbinding/capsule.py @@ -0,0 +1,74 @@ +import _capsule +from weakref import WeakValueDictionary + +_pyclasses = {} +_addr2obj = WeakValueDictionary() + + +def _sentry(ptr): + assert _capsule.check(ptr) + + +def classof(cap): + cls = _capsule.getClassName(cap) + return _pyclasses[cls] + + +def wrap(cap): + '''Wrap a PyCapsule with the corresponding Wrapper class. + If `cap` is not a PyCapsule, returns `cap` + ''' + if not _capsule.check(cap): # bypass if cap is not a PyCapsule + return cap + addr = _capsule.getPointer(cap) + try: + # find cached object by pointer address + obj = _addr2obj[addr] + except KeyError: + # create new object and cache it + cls = classof(cap) + obj = cls(cap) + _addr2obj[addr] = obj # cache object by address + # set destructor if cls.delete is defined + if hasattr(cls, '_delete_'): + _capsule.setDestructor(cap, cls._delete_) + else: + assert classof(obj._ptr) is classof(cap) + # Unset destructor for capsules that are repeated + _capsule.setDestructor(cap, None) + return obj + + +def unwrap(obj): + '''Unwrap a Wrapper instance into the underlying PyCapsule. + If `obj` is not a Wrapper instance, returns `obj`. + ''' + if isinstance(obj, Wrapper): + return obj._ptr + else: + return obj + + +def register_class(cls): + clsname = cls.__name__ + _pyclasses['llvm::%s' % clsname] = cls + return cls + + +class Wrapper(object): + + __slots__ = '__ptr' + + def __init__(self, ptr): + _sentry(ptr) + self.__ptr = ptr + + @property + def _ptr(self): + return self.__ptr + + def _release_ownership(self): + _capsule.setDestructor(self._ptr, None) + + + diff --git a/newbinding/include/llvm_binding/binding.h b/newbinding/include/llvm_binding/binding.h new file mode 100644 index 0000000..f7bfde6 --- /dev/null +++ b/newbinding/include/llvm_binding/binding.h @@ -0,0 +1,73 @@ +#include +#include + +#if (PY_MAJOR_VERSION >= 3) + +static +PyObject* +create_python_module(const char *name, PyMethodDef* methtable){ + PyModuleDef module_def_tmp = { + PyModuleDef_HEAD_INIT, + name, + NULL, + -1, + methtable, + NULL, NULL, NULL, NULL + }; + + PyModuleDef* module_def = new PyModuleDef(module_def_tmp); // will leak?? + PyObject* module = PyModule_Create(module_def); + if (module == NULL){ + delete module_def; + return NULL; + } + return module; +} + +#else + +static +PyObject* +create_python_module(const char *name, PyMethodDef* methtable){ + PyObject* module = Py_InitModule(name, methtable); + if (module == NULL) return NULL; + return module; +} + +#endif + +static +PyObject* +create_python_submodule(PyObject* parent, const char* name, + PyMethodDef* methtable) +{ + const char* parentname = PyModule_GetName(parent); + const unsigned len_parent = strlen(parentname); + const unsigned len_sub = strlen(name); + const unsigned len = len_parent + 1 + len_sub; + char* fullname = new char[len + 1]; + strcpy(fullname, parentname); + fullname[len_parent] = '.'; + strcpy(fullname + len_parent + 1, name); + PyObject* submod = create_python_module(fullname, methtable); + delete [] fullname; + if (!submod) + return NULL; + if( -1 == PyModule_AddObject(parent, name, submod) ) + return NULL; + return submod; +} + +struct SubModuleEntry{ + const char* name; + PyMethodDef* methtable; +}; + +static +int populate_submodules(PyObject* parent, SubModuleEntry* entries){ + for(SubModuleEntry* iter = entries; iter->name; ++iter){ + if (!create_python_submodule(parent, iter->name, iter->methtable)) + return 0; + } + return 1; +} \ No newline at end of file diff --git a/newbinding/include/llvm_binding/capsule_context.h b/newbinding/include/llvm_binding/capsule_context.h new file mode 100644 index 0000000..bb45566 --- /dev/null +++ b/newbinding/include/llvm_binding/capsule_context.h @@ -0,0 +1,48 @@ +#ifndef LLVMPY_CAPSULE_CONTEXT_H_ +#define LLVMPY_CAPSULE_CONTEXT_H_ +#include +#include + +typedef PyObject* Destructor_Fn; + +static bool CapsuleContextDebug = false; + +struct CapsuleContext { + const char* className; + Destructor_Fn destructor; + + CapsuleContext(const char* cn, Destructor_Fn dtor=NULL) + : className(cn), destructor(dtor) { } +}; + +void capsule_destructor(PyObject* capsule){ + using std::cerr; + using std::endl; + CapsuleContext* context = (CapsuleContext*)PyCapsule_GetContext(capsule); + if (context->destructor) { + if (CapsuleContextDebug) { + cerr << clock() + << " == DEBUG ==" + << " destroy pointer: " + << context->className + << endl; + } + PyObject_CallMethodObjArgs(context->destructor, capsule, NULL); + } else { + if (CapsuleContextDebug) { + cerr << clock() + << " == DEBUG ==" + << " keep pointer alive: " + << context->className + << endl; + } + } + delete context; +} + +void enable_capsule_dtor_debug(bool enabled){ + CapsuleContextDebug = enabled; +} + +#endif //LLVMPY_CAPSULE_CONTEXT_H_ + diff --git a/newbinding/include/llvm_binding/conversion.h b/newbinding/include/llvm_binding/conversion.h new file mode 100644 index 0000000..c3d6e36 --- /dev/null +++ b/newbinding/include/llvm_binding/conversion.h @@ -0,0 +1,93 @@ +#include +#include +#include + +// python object unwrapper + +static +int py_str_to(PyObject *strobj, llvm::StringRef &strref){ + // type check + if (!PyString_Check(strobj)) { + // raises TypeError + PyErr_SetString(PyExc_TypeError, "Expecting a str"); + return 0; + } + // get len and buffer + const Py_ssize_t len = PyString_Size(strobj); + const char * buf = PyString_AsString(strobj); + if (!buf) { + // raises TypeError + return 0; + } + // set output + strref = llvm::StringRef(buf, len); + // success + return 1; +} + +static +int py_int_to(PyObject *intobj, unsigned & val){ + if (!PyInt_Check(intobj)) { + // raise TypeError + PyErr_SetString(PyExc_TypeError, "Expecting an int"); + return 0; + } + val = PyInt_AsUnsignedLongMask(intobj); + // success + return 1; +} + +static +int py_bool_to(PyObject *boolobj, bool & val){ + if (!PyBool_Check(boolobj)) { + // raise TypeError + PyErr_SetString(PyExc_TypeError, "Expecting a bool"); + return 0; + } + if (boolobj == Py_True) { + val = true; + } else if (boolobj == Py_False) { + val = false; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid boolean object"); + return 0; + } + // success + return 1; +} + +// python object wrapper + +static +PyObject* py_str_from(const std::string &str){ + return PyString_FromStringAndSize(str.c_str(), str.size()); +} + +static +PyObject* py_bool_from(bool val){ + if (val) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static +PyObject* py_int_from(int val){ + return PyInt_FromLong(val); +} + +// casting +template +struct typecast { + template static + Td* from(Ts* src) { + return llvm::dyn_cast(src); + } + + static + Td* from(void* src) { + return static_cast(src); + } +}; + diff --git a/newbinding/include/llvm_binding/extra.h b/newbinding/include/llvm_binding/extra.h new file mode 100644 index 0000000..a7388a2 --- /dev/null +++ b/newbinding/include/llvm_binding/extra.h @@ -0,0 +1,34 @@ +#include +#include +#include + +namespace llvm{ + +class raw_svector_ostream_helper: public raw_svector_ostream { + SmallVectorImpl *SV; +public: + static + raw_svector_ostream_helper* create(){ + SmallVectorImpl* sv = new SmallVector(); + return new raw_svector_ostream_helper(sv); + } + + ~raw_svector_ostream_helper(){ + delete SV; + } + +protected: + + explicit + raw_svector_ostream_helper(SmallVectorImpl* sv) + : raw_svector_ostream(*sv), SV(sv) {} + +private: + // no copy + raw_svector_ostream_helper(const raw_svector_ostream_helper&); + // no assign + void operator = (const raw_svector_ostream_helper&); +}; + +} + diff --git a/newbinding/setup.py b/newbinding/setup.py new file mode 100644 index 0000000..6e2ceb5 --- /dev/null +++ b/newbinding/setup.py @@ -0,0 +1,54 @@ +import sys, os +from distutils.core import setup, Extension + +llvm_config = os.environ.get('LLVM_CONFIG_PATH') + +def run_llvm_config(args): + cmd = llvm_config + ' ' + ' '.join(args) + return os.popen(cmd).read().rstrip() + +def get_libs_and_objs(components): + parts = run_llvm_config(['--libs'] + components).split() + libs = [] + objs = [] + for part in parts: + if part.startswith('-l'): + libs.append(part[2:]) + elif part.endswith('.o'): + objs.append(part) + return libs, objs + +incdir = run_llvm_config(['--includedir']) +libdir = run_llvm_config(['--libdir']) +ldflags = run_llvm_config(['--ldflags']) +macros = [('__STDC_CONSTANT_MACROS', None), + ('__STDC_LIMIT_MACROS', None)] + +extra_link_args = ldflags.split() + +libs_core, objs_core = get_libs_and_objs(['core', 'analysis', 'scalaropts', + 'executionengine', 'jit', 'native', + 'interpreter', 'bitreader', + 'bitwriter', 'instrumentation', 'ipa', + 'ipo', 'transformutils', 'asmparser', + 'linker', 'support', 'vectorize']) + +ext_modules = [Extension(name='_api', + sources=['api.cpp'], + include_dirs = ['include', incdir], + library_dirs = [libdir], + libraries = libs_core, + define_macros = macros, + extra_objects = objs_core, + extra_link_args = extra_link_args), + Extension(name='_capsule', + sources=['capsule.cpp'], + include_dirs = ['include'],), + ] + + +setup(name = 'llvmpy2', + description = 'Python bindings for LLVM', + author = 'Siu Kwan Lam', + ext_modules = ext_modules, + license = "BSD") diff --git a/newbinding/test.py b/newbinding/test.py new file mode 100644 index 0000000..bed9e1a --- /dev/null +++ b/newbinding/test.py @@ -0,0 +1,32 @@ +import _api + +context = _api.getGlobalContext() +modname = "modname" +module = _api.Module.new(modname, context) +assert modname == _api.Module.getModuleIdentifier(module) +modname2 = "newmodname" +_api.Module.setModuleIdentifier(module, modname2) +assert modname2 == _api.Module.getModuleIdentifier(module) + +_api.Module.dump(module) + +_api.Module.delete(module) + +voidty = _api.Type.getVoidTy(context) +assert _api.Type.isVoidTy(voidty) +assert not _api.Type.isLabelTy(voidty) + +int21ty = _api.Type.getIntNTy(context, 21) +assert _api.Type.isIntegerTy(int21ty) +assert _api.Type.isIntegerTy(int21ty, 21) +_api.Type.dump(int21ty) + +halfty = _api.Type.getHalfTy(context) +assert _api.Type.isHalfTy(halfty) +_api.Type.dump(halfty) + +fnty = _api.FunctionType.get(halfty, False) +_api.Type.dump(fnty) + + + diff --git a/newbinding/test2.py b/newbinding/test2.py new file mode 100644 index 0000000..25a59d7 --- /dev/null +++ b/newbinding/test2.py @@ -0,0 +1,20 @@ +import api +#api.enable_capsule_dtor_debug(True) +context = api.getGlobalContext() + +m = api.Module.new("modname", context) +print m.getModuleIdentifier() +m.setModuleIdentifier('modname2') +print m.getModuleIdentifier() +m.dump() +os = api.raw_svector_ostream_helper.create() +m.print_(os, None) +print os.str() + +int1ty = api.Type.getInt1Ty(context) +int1ty.dump() + +print int1ty.isIntegerTy(1) + + +