Init commit for work on new binding

This contains the foundation for the new binding as well as early work on Module and Type.
This commit is contained in:
Siu Kwan Lam 2013-01-22 10:09:14 -06:00
commit a12c4da93d
23 changed files with 1477 additions and 0 deletions

18
newbinding/Makefile Normal file
View file

@ -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

View file

@ -0,0 +1,6 @@
from binding import *
from namespace import llvm
AssemblyAnnotationWriter = llvm.Class()
AssemblyAnnotationWriter.include.add("llvm/Assembly/AssemblyAnnotationWriter.h")

View file

@ -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))

View file

@ -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)

View file

View file

@ -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)

View file

@ -0,0 +1,6 @@
from binding import *
from namespace import llvm
StringRef = llvm.Class()
StringRef.include.add("llvm/ADT/StringRef.h")

View file

@ -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))

View file

@ -0,0 +1,3 @@
from binding import *
enable_capsule_dtor_debug = Function('', Void, Bool.From(bool))

View file

@ -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)

206
newbinding/binding/gen.py Normal file
View file

@ -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)

View file

@ -0,0 +1,4 @@
from binding import *
extra = Namespace('llvm_extra')
llvm = Namespace('llvm')

View file

@ -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))

View file

@ -0,0 +1,6 @@
def indent_println(println):
def _println(s):
println("%s%s" % (' '* 4, s))
return _println

143
newbinding/capsule.cpp Normal file
View file

@ -0,0 +1,143 @@
#include <Python.h>
#include <llvm_binding/capsule_context.h>
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

74
newbinding/capsule.py Normal file
View file

@ -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)

View file

@ -0,0 +1,73 @@
#include <Python.h>
#include <cstring>
#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;
}

View file

@ -0,0 +1,48 @@
#ifndef LLVMPY_CAPSULE_CONTEXT_H_
#define LLVMPY_CAPSULE_CONTEXT_H_
#include <iostream>
#include <ctime>
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_

View file

@ -0,0 +1,93 @@
#include <Python.h>
#include <llvm/Support/Casting.h>
#include <llvm/ADT/StringRef.h>
// 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<class Td>
struct typecast {
template<class Ts> static
Td* from(Ts* src) {
return llvm::dyn_cast<Td>(src);
}
static
Td* from(void* src) {
return static_cast<Td*>(src);
}
};

View file

@ -0,0 +1,34 @@
#include <Python.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/Support/raw_ostream.h>
namespace llvm{
class raw_svector_ostream_helper: public raw_svector_ostream {
SmallVectorImpl<char> *SV;
public:
static
raw_svector_ostream_helper* create(){
SmallVectorImpl<char>* sv = new SmallVector<char, 16>();
return new raw_svector_ostream_helper(sv);
}
~raw_svector_ostream_helper(){
delete SV;
}
protected:
explicit
raw_svector_ostream_helper(SmallVectorImpl<char>* 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&);
};
}

54
newbinding/setup.py Normal file
View file

@ -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")

32
newbinding/test.py Normal file
View file

@ -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)

20
newbinding/test2.py Normal file
View file

@ -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)