diff --git a/CHANGELOG b/CHANGELOG index 12bb294..45d6a5f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,35 @@ -2013-8-28 0.12.0: --------------------- +2014-04-28 0.12.5: +--------------------- + * Fixes memory leaks (#92) + * Fixes tarball (#99) + +2014-03-20 0.12.4: +--------------------- + * Add dylib_import_library and friends + * Fix BasicBlock downcast + * Module hashing + * Fix test script + +2014-02-18 0.12.3: +--------------------- + * Fix deprecation message for py2.6 + * Fix llvm_cbuilder for using deprecated_alloca + * Merged PR #88 by cantora + * Merged PR #94 by cgohlke + +2014-02-04 0.12.2: +--------------------- + * enhance wrapper efficiency by moving some capsule code into C++ + * fix unclosed file handler in avx_support + * multiple-dimension insert_value, extract_value + * various minor fixes + +2013-11-11 0.12.1: +--------------------- + * various bug fixes + +2013-08-28 0.12.0: +--------------------- * update to LLVM 3.3 and maintain compatibility with LLVM 3.2 * add LLRT for minimal support for 64-bit divmod on 32-bit platform * start to adopt MCJIT (not quite usable on win32) diff --git a/MANIFEST.in b/MANIFEST.in index 295794c..25a211d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ -include CHANGELOG LICENSE README setup.py MANIFEST.in +include CHANGELOG LICENSE README.rst setup.py MANIFEST.in versioneer.py recursive-include llvm * +recursive-include llvmpy * recursive-include www * recursive-include test * recursive-include tools * diff --git a/README.rst b/README.rst index 0fbcd24..a1abf84 100644 --- a/README.rst +++ b/README.rst @@ -30,10 +30,13 @@ Quickstart to separate your custom build from the default system package. Please replace ``LLVM_INSTALL_PATH`` with your own path. -3. Run ``REQUIRES_RTTI=1 make`` to build. +3. Run ``REQUIRES_RTTI=1 make install`` to build and install. **Note**: With LLVM 3.2, the default build configuration has C++ RTTI disabled. However, llvmpy requires RTTI. + + **Note**: Use ``make -j2 install`` to enable concurrent build. + Replace ``2`` with the actual number of processor you have. 4. Get llvm-py and install it:: diff --git a/buildscripts/condarecipe/run_test.py b/buildscripts/condarecipe/run_test.py index 281a599..682b8d4 100644 --- a/buildscripts/condarecipe/run_test.py +++ b/buildscripts/condarecipe/run_test.py @@ -4,6 +4,8 @@ import llvm from llvm.core import Module from llvm.ee import EngineBuilder +from llvm.utils import check_intrinsics + m = Module.new('fjoidajfa') eb = EngineBuilder.new(m) target = eb.select_target() @@ -13,7 +15,8 @@ if sys.platform == 'darwin': s = {'64bit': 'x86_64', '32bit': 'x86'}[platform.architecture()[0]] assert target.triple.startswith(s + '-apple-darwin') -assert llvm.test(verbosity=2) == 0 +assert llvm.test(verbosity=2, run_isolated=False) == 0 +#check_intrinsics.main() print('llvm.__version__: %s' % llvm.__version__) #assert llvm.__version__ == '0.12.0' diff --git a/docs/source/index.rst b/docs/source/index.rst index 7798b77..fbf5c8b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Documentation for llvmpy -======================== +llvmpy +====== Contents: @@ -22,7 +22,7 @@ Contents: Indices and tables -================== +------------------ * :ref:`genindex` * :ref:`modindex` diff --git a/example/vector_instr.py b/example/vector_instr.py new file mode 100644 index 0000000..e49d06a --- /dev/null +++ b/example/vector_instr.py @@ -0,0 +1,152 @@ +''' +This example shows: +1) how to use vector instructions +2) how to take advantage of LLVM loop vectorization to transform scalar + operations to vector operations +''' + +from __future__ import print_function +import llvm.core as lc +import llvm.ee as le +import llvm.passes as lp +from ctypes import CFUNCTYPE, POINTER, c_int, c_float + +def build_manual_vector(): + mod = lc.Module.new('manual.vector') + intty = lc.Type.int(32) + vecty = lc.Type.vector(lc.Type.float(), 4) + aryty = lc.Type.pointer(lc.Type.float()) + fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty]) + fn = mod.add_function(fnty, name='vector_add') + bbentry = fn.append_basic_block('entry') + bbloopcond = fn.append_basic_block('loop.cond') + bbloopbody = fn.append_basic_block('loop.body') + bbexit = fn.append_basic_block('exit') + builder = lc.Builder.new(bbentry) + + # populate function body + in1, in2, out, size = fn.args + ZERO = lc.Constant.null(intty) + loopi_ptr = builder.alloca(intty) + builder.store(ZERO, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbloopcond) + + loopi = builder.load(loopi_ptr) + loopcond = builder.icmp(lc.ICMP_ULT, loopi, size) + + builder.cbranch(loopcond, bbloopbody, bbexit) + builder.position_at_end(bbloopbody) + + vecaryty = lc.Type.pointer(vecty) + in1asvec = builder.bitcast(builder.gep(in1, [loopi]), vecaryty) + in2asvec = builder.bitcast(builder.gep(in2, [loopi]), vecaryty) + outasvec = builder.bitcast(builder.gep(out, [loopi]), vecaryty) + + vec1 = builder.load(in1asvec) + vec2 = builder.load(in2asvec) + + vecout = builder.fadd(vec1, vec2) + + builder.store(vecout, outasvec) + + next = builder.add(loopi, lc.Constant.int(intty, 4)) + builder.store(next, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbexit) + + builder.ret_void() + + return mod, fn + + +def build_auto_vector(): + mod = lc.Module.new('auto.vector') + # Loop vectorize is sensitive to the size of the index size(!?) + intty = lc.Type.int(tuple.__itemsize__ * 8) + aryty = lc.Type.pointer(lc.Type.float()) + fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty]) + fn = mod.add_function(fnty, name='vector_add') + bbentry = fn.append_basic_block('entry') + bbloopcond = fn.append_basic_block('loop.cond') + bbloopbody = fn.append_basic_block('loop.body') + bbexit = fn.append_basic_block('exit') + builder = lc.Builder.new(bbentry) + + # populate function body + in1, in2, out, size = fn.args + in1.add_attribute(lc.ATTR_NO_ALIAS) + in2.add_attribute(lc.ATTR_NO_ALIAS) + out.add_attribute(lc.ATTR_NO_ALIAS) + ZERO = lc.Constant.null(intty) + loopi_ptr = builder.alloca(intty) + builder.store(ZERO, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbloopcond) + + loopi = builder.load(loopi_ptr) + loopcond = builder.icmp(lc.ICMP_ULT, loopi, size) + + builder.cbranch(loopcond, bbloopbody, bbexit) + builder.position_at_end(bbloopbody) + + in1elem = builder.load(builder.gep(in1, [loopi])) + in2elem = builder.load(builder.gep(in2, [loopi])) + + outelem = builder.fadd(in1elem, in2elem) + + builder.store(outelem, builder.gep(out, [loopi])) + + next = builder.add(loopi, lc.Constant.int(intty, 1)) + builder.store(next, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbexit) + + builder.ret_void() + + return mod, fn + +def example(title, module_builder, opt): + print(title.center(80, '=')) + mod, fn = module_builder() + + eb = le.EngineBuilder.new(mod).opt(3) + if opt: + print('opt') + tm = eb.select_target() + pms = lp.build_pass_managers(mod=mod, tm=tm, opt=3, loop_vectorize=True, + fpm=False) + pms.pm.run(mod) + + print(mod) + print(mod.to_native_assembly()) + + engine = eb.create() + ptr = engine.get_pointer_to_function(fn) + + callable = CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), + POINTER(c_float), c_int)(ptr) + + N = 20 + in1 = (c_float * N)(*range(N)) + in2 = (c_float * N)(*range(N)) + out = (c_float * N)() + + print('in1: ', list(in1)) + print('in1: ', list(in2)) + + callable(in1, in2, out, N) + + print('out', list(out)) + + +def main(): + example('manual vector function', build_manual_vector, False) + example('auto vector function', build_auto_vector, True) + +if __name__ == '__main__': + main() diff --git a/llvm/__init__.py b/llvm/__init__.py index dc9852f..9b24112 100644 --- a/llvm/__init__.py +++ b/llvm/__init__.py @@ -9,6 +9,8 @@ version = extra.get_llvm_version() del extra class Wrapper(object): + __slots__ = '__ptr' + def __init__(self, ptr): assert ptr self.__ptr = ptr @@ -32,14 +34,14 @@ def _extract_ptrs(objs): class LLVMException(Exception): pass -def test(verbosity=1): +def test(verbosity=3, run_isolated=True): """test(verbosity=1) -> TextTestResult Run self-test, and return the number of failures + errors """ from llvm.tests import run - result = run(verbosity=verbosity) - - return len(result.failures) + len(result.errors) + result = run(verbosity=verbosity, run_isolated=run_isolated) + errct = len(result.failures) + len(result.errors) + return errct diff --git a/llvm/core.py b/llvm/core.py index 8a4ac2d..4db8135 100644 --- a/llvm/core.py +++ b/llvm/core.py @@ -41,7 +41,7 @@ import contextlib, weakref import llvm from llvm._intrinsic_ids import * - +from llvm.deprecated import deprecated from llvmpy import api #===----------------------------------------------------------------------=== @@ -379,6 +379,7 @@ class Module(llvm.Wrapper): module_obj = Module.new('my_module') """ + __slots__ = '__weakref__' __cache = weakref.WeakValueDictionary() def __new__(cls, ptr): @@ -451,12 +452,12 @@ class Module(llvm.Wrapper): """ return str(self._ptr) + def __hash__(self): + return id(self._ptr) + def __eq__(self, rhs): - assert isinstance(rhs, Module), type(rhs) if isinstance(rhs, Module): - return str(self) == str(rhs) - else: - return False + return self._ptr == rhs._ptr def __ne__(self, rhs): return not (self == rhs) @@ -688,6 +689,7 @@ class Type(llvm.Wrapper): Use one of the static methods to create an instance. Example: ty = Type.double() """ + __slots__ = '__name__' _type_ = api.llvm.Type def __init__(self, ptr): @@ -880,6 +882,7 @@ class Type(llvm.Wrapper): class IntegerType(Type): """Represents an integer type.""" + __slots__ = () _type_ = api.llvm.IntegerType @property @@ -889,6 +892,7 @@ class IntegerType(Type): class FunctionType(Type): """Represents a function type.""" + __slots__ = () _type_ = api.llvm.FunctionType @property @@ -918,6 +922,7 @@ class FunctionType(Type): class StructType(Type): """Represents a structure type.""" _type_ = api.llvm.StructType + __slots__ = () @property def element_count(self): @@ -976,6 +981,7 @@ class StructType(Type): class ArrayType(Type): """Represents an array type.""" _type_ = api.llvm.ArrayType + __slots__ = () @property def element(self): @@ -987,6 +993,7 @@ class ArrayType(Type): class PointerType(Type): _type_ = api.llvm.PointerType + __slots__ = () @property def pointee(self): @@ -998,6 +1005,7 @@ class PointerType(Type): class VectorType(Type): _type_ = api.llvm.VectorType + __slots__ = () @property def element(self): @@ -1009,6 +1017,7 @@ class VectorType(Type): class Value(llvm.Wrapper): _type_ = api.llvm.Value + __slots__ = '__weakref__' def __init__(self, builder, ptr): assert builder is _ValueFactory @@ -1077,6 +1086,7 @@ class Value(llvm.Wrapper): class User(Value): _type_ = api.llvm.User + __slots__ = () @property def operand_count(self): @@ -1091,6 +1101,7 @@ class User(Value): class Constant(User): _type_ = api.llvm.Constant + __slots__ = () @staticmethod def null(ty): @@ -1276,6 +1287,7 @@ class Constant(User): class ConstantExpr(Constant): _type_ = api.llvm.ConstantExpr + __slots__ = () @property def opcode(self): @@ -1286,19 +1298,20 @@ class ConstantExpr(Constant): return self._ptr.getOpcodeName() class ConstantAggregateZero(Constant): - pass + __slots__ = () class ConstantDataArray(Constant): - pass + __slots__ = () class ConstantDataVector(Constant): - pass + __slots__ = () class ConstantInt(Constant): _type_ = api.llvm.ConstantInt + __slots__ = () @property def z_ext_value(self): @@ -1314,30 +1327,32 @@ class ConstantInt(Constant): class ConstantFP(Constant): - pass + __slots__ = () class ConstantArray(Constant): - pass + __slots__ = () class ConstantStruct(Constant): - pass + __slots__ = () class ConstantVector(Constant): - pass + __slots__ = () class ConstantPointerNull(Constant): - pass + __slots__ = () class UndefValue(Constant): - pass + __slots__ = () + class GlobalValue(Constant): _type_ = api.llvm.GlobalValue + __slots__ = () def _get_linkage(self): return self._ptr.getLinkage() @@ -1383,6 +1398,7 @@ class GlobalValue(Constant): class GlobalVariable(GlobalValue): _type_ = api.llvm.GlobalVariable + __slots__ = () @staticmethod def new(module, ty, name, addrspace=0): @@ -1443,6 +1459,7 @@ class GlobalVariable(GlobalValue): thread_local = property(_get_thread_local, _set_thread_local) class Argument(Value): + __slots__ = () _type_ = api.llvm.Argument _valid_attrs = frozenset([ATTR_BY_VAL, ATTR_NEST, ATTR_NO_ALIAS, ATTR_NO_CAPTURE, ATTR_STRUCT_RET]) @@ -1543,6 +1560,7 @@ class Argument(Value): return self._ptr.hasStructRetAttr() class Function(GlobalValue): + __slots__ = () _type_ = api.llvm.Function @staticmethod @@ -1681,6 +1699,7 @@ class Function(GlobalValue): #===----------------------------------------------------------------------=== class InlineAsm(Value): + __slots__ = () _type_ = api.llvm.InlineAsm @staticmethod @@ -1695,6 +1714,7 @@ class InlineAsm(Value): #===----------------------------------------------------------------------=== class MetaData(Value): + __slots__ = () _type_ = api.llvm.MDNode @staticmethod @@ -1751,6 +1771,7 @@ class MetaDataString(Value): class NamedMetaData(llvm.Wrapper): + __slots__ = () @staticmethod def get_or_insert(mod, name): @@ -1780,6 +1801,7 @@ class NamedMetaData(llvm.Wrapper): #===----------------------------------------------------------------------=== class Instruction(User): + __slots__ = () _type_ = api.llvm.Instruction @property @@ -1861,6 +1883,7 @@ class Instruction(User): class CallOrInvokeInstruction(Instruction): + __slots__ = () _type_ = api.llvm.CallInst, api.llvm.InvokeInst def _get_cc(self): @@ -1916,6 +1939,7 @@ class CallOrInvokeInstruction(Instruction): class PHINode(Instruction): + __slots__ = () _type_ = api.llvm.PHINode @property @@ -1933,6 +1957,7 @@ class PHINode(Instruction): class SwitchInstruction(Instruction): + __slots__ = () _type_ = api.llvm.SwitchInst def add_case(self, const, bblk): @@ -1940,6 +1965,7 @@ class SwitchInstruction(Instruction): class CompareInstruction(Instruction): + __slots__ = () _type_ = api.llvm.CmpInst @property @@ -1951,11 +1977,40 @@ class CompareInstruction(Instruction): return FCMPEnum.get(n) +class AllocaInstruction(Instruction): + __slots__ = () + _type_ = api.llvm.AllocaInst + + @property + def alignment(self): + return self._ptr.getAlignment() + + @alignment.setter + def alignment(self, n): + self._ptr.setAlignment(n) + + @property + def array_size(self): + return self._ptr.getArraySize() + + @array_size.setter + def array_size(self, value): + return self._ptr.setArraySize(value._ptr)._ptr + + @property + def is_array(self): + return self._ptr.isArrayAllocation() + + @property + def is_static(self): + return self._ptr.isStaticAlloca() + #===----------------------------------------------------------------------=== # Basic block #===----------------------------------------------------------------------=== class BasicBlock(Value): + __slots__ = () _type_ = api.llvm.BasicBlock def insert_before(self, name): @@ -1982,6 +2037,7 @@ class BasicBlock(Value): class _ValueFactory(object): + __slots__ = () cache = weakref.WeakValueDictionary() # value ID -> class map @@ -2010,7 +2066,8 @@ class _ValueFactory(object): VALUE_INSTRUCTION + OPCODE_INVOKE : CallOrInvokeInstruction, VALUE_INSTRUCTION + OPCODE_SWITCH : SwitchInstruction, VALUE_INSTRUCTION + OPCODE_ICMP : CompareInstruction, - VALUE_INSTRUCTION + OPCODE_FCMP : CompareInstruction + VALUE_INSTRUCTION + OPCODE_FCMP : CompareInstruction, + VALUE_INSTRUCTION + OPCODE_ALLOCA : AllocaInstruction, } @classmethod @@ -2057,6 +2114,7 @@ _atomic_orderings = { } class Builder(llvm.Wrapper): + __slots__ = () @staticmethod def new(basic_block): @@ -2218,7 +2276,6 @@ class Builder(llvm.Wrapper): # memory def malloc(self, ty, name=""): - context = api.llvm.getGlobalContext() allocsz = api.llvm.ConstantExpr.getSizeOf(ty._ptr) ity = allocsz.getType() malloc = api.llvm.CallInst.CreateMalloc(self.basic_block._ptr, @@ -2232,7 +2289,6 @@ class Builder(llvm.Wrapper): return _make_value(inst) def malloc_array(self, ty, size, name=""): - context = api.llvm.getGlobalContext() allocsz = api.llvm.ConstantExpr.getSizeOf(ty._ptr) ity = allocsz.getType() malloc = api.llvm.CallInst.CreateMalloc(self.basic_block._ptr, @@ -2245,12 +2301,13 @@ class Builder(llvm.Wrapper): inst = self._ptr.Insert(malloc, name) return _make_value(inst) - def alloca(self, ty, name=""): - intty = Type.int() - return _make_value(self._ptr.CreateAlloca(ty._ptr, None, name)) + def alloca(self, ty, size=None, name=""): + sizeptr = size._ptr if size else None + return _make_value(self._ptr.CreateAlloca(ty._ptr, sizeptr, name)) + @deprecated def alloca_array(self, ty, size, name=""): - return _make_value(self._ptr.CreateAlloca(ty._ptr, size._ptr, name)) + return self.alloca(ty, size, name=name) def free(self, ptr): free = api.llvm.CallInst.CreateFree(ptr._ptr, self.basic_block._ptr) @@ -2337,15 +2394,20 @@ class Builder(llvm.Wrapper): # misc def extract_value(self, retval, idx, name=""): - return _make_value(self._ptr.CreateExtractValue(retval._ptr, [idx], name)) + if not isinstance(idx, (tuple, list)): + idx = [idx] + return _make_value(self._ptr.CreateExtractValue(retval._ptr, idx, + name)) # obsolete synonym for extract_value getresult = extract_value def insert_value(self, retval, rhs, idx, name=""): + if not isinstance(idx, (tuple, list)): + idx = [idx] return _make_value(self._ptr.CreateInsertValue(retval._ptr, rhs._ptr, - [idx], + idx, name)) def phi(self, ty, name=""): diff --git a/llvm/deprecated.py b/llvm/deprecated.py new file mode 100644 index 0000000..6db5fe7 --- /dev/null +++ b/llvm/deprecated.py @@ -0,0 +1,25 @@ +""" +Shameless borrowed from Smart_deprecation_warnings +https://wiki.python.org/moin/PythonDecoratorLibrary +""" + +import warnings +import functools + + +def deprecated(func): + """This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used.""" + + @functools.wraps(func) + def new_func(*args, **kwargs): + warnings.warn_explicit( + "Call to deprecated function %s." % (func.__name__,), + category=DeprecationWarning, + filename=func.func_code.co_filename, + lineno=func.func_code.co_firstlineno + 1 + ) + return func(*args, **kwargs) + + return new_func diff --git a/llvm/ee.py b/llvm/ee.py index c687eaa..f615b61 100644 --- a/llvm/ee.py +++ b/llvm/ee.py @@ -238,3 +238,27 @@ def dylib_add_symbol(name, ptr): def dylib_address_of_symbol(name): return api.llvm.sys.DynamicLibrary.SearchForAddressOfSymbol(name) + +def dylib_import_library(filename): + """Permanently import a dynamic library. + + Returns a DynamicLibrary object + + Raises RuntimeError + """ + return DynamicLibrary(filename) + + +class DynamicLibrary(object): + def __init__(self, filename): + """ + Raises RuntimeError + """ + self._ptr = api.llvm.sys.DynamicLibrary.getPermanentLibrary( + filename) + + def get_address_of_symbol(self, symbol): + """ + Get the address of `symbol` (str) as integer + """ + return self._ptr.getAddressOfSymbol(symbol) diff --git a/llvm/passes.py b/llvm/passes.py index 0a4c524..e5ed085 100644 --- a/llvm/passes.py +++ b/llvm/passes.py @@ -80,6 +80,15 @@ class PassManagerBuilder(llvm.Wrapper): self._ptr.BBVectorize = enable vectorize = bbvectorize + + @property + def slpvectorize(self): + return self._ptr.SLPVectorize + + @slpvectorize.setter + def slpvectorize(self, enable): + self._ptr.SLPVectorize = enable + else: @property def vectorize(self): @@ -311,13 +320,15 @@ class TargetTransformInfo(Pass): # Helpers #===----------------------------------------------------------------------=== -def build_pass_managers(tm, opt=2, loop_vectorize=False, vectorize=False, - inline_threshold=2000, pm=True, fpm=True, mod=None): +def build_pass_managers(tm, opt=2, size=0, loop_vectorize=False, + slp_vectorize=False, vectorize=False, + inline_threshold=None, pm=True, fpm=True, mod=None): ''' tm --- The TargetMachine for which the passes are optimizing for. The TargetMachine must stay alive until the pass managers are removed. opt --- [0-3] Optimization level. Default to 2. + size --- [0-2] Optimize for size. Default to 0. loop_vectorize --- [boolean] Whether to use loop-vectorizer. vectorize --- [boolean] Whether to use basic-block vectorizer. inline_threshold --- [int] Threshold for the inliner. @@ -326,6 +337,18 @@ def build_pass_managers(tm, opt=2, loop_vectorize=False, vectorize=False, fpm --- [boolean] Whether to build a function-level pass-manager. mod --- [Module] The module object for the FunctionPassManager. ''' + if inline_threshold is None: + if 0 < opt < 3: + inline_threshold = 225 + + if size == 1: + inline_threshold = 75 + elif size == 2: + inline_threshold = 25 + + if opt >= 3: + inline_threshold = 275 + if pm: pm = PassManager.new() if fpm: @@ -338,22 +361,27 @@ def build_pass_managers(tm, opt=2, loop_vectorize=False, vectorize=False, pmb.opt_level = opt pmb.vectorize = vectorize pmb.loop_vectorize = loop_vectorize + if llvm.version >= (3, 3): + pmb.slp_vectorize = slp_vectorize if inline_threshold: pmb.use_inliner_with_threshold(inline_threshold) if pm: pm.add(tm.target_data.clone()) pm.add(TargetLibraryInfo.new(tm.triple)) - if llvm.version == (3, 2): + if llvm.version <= (3, 2): pm.add(TargetTransformInfo.new(tm)) + else: + tm.add_analysis_passes(pm) pmb.populate(pm) if fpm: fpm.add(tm.target_data.clone()) fpm.add(TargetLibraryInfo.new(tm.triple)) - if llvm.version == (3, 2): + if llvm.version <= (3, 2): fpm.add(TargetTransformInfo.new(tm)) + else: + tm.add_analysis_passes(fpm) pmb.populate(fpm) - fpm.initialize() from collections import namedtuple return namedtuple('passmanagers', ['pm', 'fpm'])(pm=pm, fpm=fpm) diff --git a/llvm/target.py b/llvm/target.py index e70fd1b..f68b023 100644 --- a/llvm/target.py +++ b/llvm/target.py @@ -191,6 +191,10 @@ class TargetMachine(llvm.Wrapper): def target(self): return self._ptr.getTarget() + if llvm.version >= (3, 3): + def add_analysis_passes(self, pm): + self._ptr.addAnalysisPasses(pm._ptr) + if llvm.version >= (3, 4): @property def reg_info(self): diff --git a/llvm/tests/__init__.py b/llvm/tests/__init__.py index 483201b..0bc4223 100644 --- a/llvm/tests/__init__.py +++ b/llvm/tests/__init__.py @@ -12,7 +12,7 @@ tests = [] # stores unittest.TestCase objects isolated_tests = [] # stores modue name -def run(verbosity=1): +def run(verbosity=1, run_isolated=True): print('llvmpy is installed in: ' + os.path.dirname(__file__)) print('llvmpy version: ' + llvm.__version__) print(sys.version) @@ -37,23 +37,32 @@ def run(verbosity=1): if sys.version_info[:2] > (2, 6): kwargs['buffer'] = True runner = unittest.TextTestRunner(**kwargs) - testresult = runner.run(suite) - if testresult: + try: + from guppy import hpy + except ImportError: + testresult = runner.run(suite) + else: + hp = hpy() + hp.setref() + testresult = runner.run(suite) + print(hp.heap()) + + if testresult and run_isolated: # Run isolated tests print("run isolated tests".center(80, '-')) for test in isolated_tests: print(('testing %s' % test).center(80)) - cmd = [sys.executable, '-m', test] - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - for line in p.stdout: - print(line, end='') - p.wait() - if p.returncode: - raise Exception("%s returned: %d" % p.returncode) + cmd = [sys.executable, '-m', test] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + for line in p.stdout: + print(line.decode('utf8'), end='') + p.wait() + if p.returncode: + raise Exception("%s returned: %d" % (test, p.returncode)) return testresult diff --git a/llvm/tests/test_alloca.py b/llvm/tests/test_alloca.py new file mode 100644 index 0000000..60e3cf5 --- /dev/null +++ b/llvm/tests/test_alloca.py @@ -0,0 +1,24 @@ +import unittest +from llvm.core import Type, Module, Builder, Constant +from .support import TestCase, tests + +class TestAlloca(TestCase): + def test_alloca_alignment(self): + m = Module.new('') + f = m.add_function(Type.function(Type.void(), []), "foo") + b = Builder.new(f.append_basic_block('')) + inst = b.alloca(Type.int(32)) + inst.alignment = 4 + b.ret_void() + m.verify() + + self.assertTrue(inst.is_static) + self.assertFalse(inst.is_array) + self.assertEqual(inst.alignment, 4) + self.assertEqual(str(inst.array_size), 'i32 1') + +tests.append(TestAlloca) + +if __name__ == '__main__': + unittest.main() + diff --git a/llvm/tests/test_native.py b/llvm/tests/test_native.py index 63eab5a..04dde76 100644 --- a/llvm/tests/test_native.py +++ b/llvm/tests/test_native.py @@ -6,8 +6,9 @@ import subprocess import tempfile from distutils.spawn import find_executable from llvm.core import (Module, Type, Builder, Constant) -from .support import TestCase, IS_PY3K, tests +from .support import TestCase, IS_PY3K, tests, skip_if +@skip_if(sys.platform in ('win32', 'darwin')) class TestNative(TestCase): def setUp(self): @@ -60,10 +61,10 @@ class TestNative(TestCase): self._compile(src) def test_object(self): - # if sys.platform == 'darwin': - # # skip this test on MacOSX for now - # return - + ''' + Note: Older Darwin with GCC will report missing _main symbol when + compile the object file to an executable. + ''' m = self._make_module() output = m.to_native_object() @@ -73,8 +74,7 @@ class TestNative(TestCase): self._compile(src) -if sys.platform != 'win32': - tests.append(TestNative) +tests.append(TestNative) if __name__ == '__main__': unittest.main() diff --git a/llvm/tests/test_struct.py b/llvm/tests/test_struct.py index 3783a8c..8d61111 100644 --- a/llvm/tests/test_struct.py +++ b/llvm/tests/test_struct.py @@ -1,5 +1,5 @@ import unittest -from llvm.core import Type +from llvm.core import Type, Module, Builder, Constant from .support import TestCase, tests class TestStruct(TestCase): @@ -8,6 +8,19 @@ class TestStruct(TestCase): tb = Type.struct([Type.int(32), Type.float()]) self.assertTrue(ta.is_layout_identical(tb)) + def test_struct_extract_value_2d(self): + ta = Type.struct([Type.int(32), Type.float()]) + tb = Type.struct([ta, Type.float()]) + m = Module.new('') + f = m.add_function(Type.function(Type.void(), []), "foo") + b = Builder.new(f.append_basic_block('')) + v = Constant.undef(tb) + ins = b.insert_value(v, Constant.real(Type.float(), 1.234), [0, 1]) + ext = b.extract_value(ins, [0, 1]) + b.ret_void() + m.verify() + self.assertEqual(str(ext), 'float 0x3FF3BE76C0000000') + tests.append(TestStruct) if __name__ == '__main__': diff --git a/llvm/utils/__init__.py b/llvm/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llvm/utils/check_intrinsics.py b/llvm/utils/check_intrinsics.py new file mode 100644 index 0000000..702dae3 --- /dev/null +++ b/llvm/utils/check_intrinsics.py @@ -0,0 +1,181 @@ +from __future__ import print_function, absolute_import +import sys +from llvm.core import Type, Function, Builder, Module +import llvm.core as lc +import llvm.ee as le +import multiprocessing +from ctypes import * + +INTRINSICS = {} + +CTYPES_MAP = { + Type.int(): c_int32, + Type.int(64): c_int64, + Type.float(): c_float, + Type.double(): c_double, +} + + +def register(name, retty, *args): + def wrap(fn): + INTRINSICS[name] = (retty, args), fn + return fn + return wrap + + +def intr_impl(intrcode, *types): + def impl(module, builder, args): + intr = Function.intrinsic(module, intrcode, types) + r = builder.call(intr, args) + return r + return impl + + +register("llvm.powi.f64", Type.double(), Type.double(), Type.int())\ + (intr_impl(lc.INTR_POWI, Type.double())) + +register("llvm.powi.f32", Type.float(), Type.float(), Type.int())\ + (intr_impl(lc.INTR_POWI, Type.float())) + +register("llvm.pow.f64", Type.double(), Type.double(), Type.double())\ + (intr_impl(lc.INTR_POW, Type.double())) + +register("llvm.pow.f32", Type.float(), Type.float(), Type.float())\ + (intr_impl(lc.INTR_POW, Type.float())) + +register("llvm.sin.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_SIN, Type.double())) + +register("llvm.sin.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_SIN, Type.float())) + +register("llvm.cos.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_COS, Type.double())) + +register("llvm.cos.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_COS, Type.float())) + +register("llvm.log.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_LOG, Type.double())) + +register("llvm.log.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_LOG, Type.float())) + +register("llvm.log2.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_LOG2, Type.double())) + +register("llvm.log2.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_LOG2, Type.float())) + +register("llvm.log10.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_LOG10, Type.double())) + +register("llvm.log10.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_LOG10, Type.float())) + +register("llvm.sqrt.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_SQRT, Type.double())) + +register("llvm.sqrt.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_SQRT, Type.float())) + +register("llvm.exp.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_EXP, Type.double())) + +register("llvm.exp.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_EXP, Type.float())) + +register("llvm.exp2.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_EXP2, Type.double())) + +register("llvm.exp2.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_EXP2, Type.float())) + +register("llvm.fabs.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_FABS, Type.double())) + +register("llvm.fabs.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_FABS, Type.float())) + +register("llvm.floor.f64", Type.double(), Type.double())\ + (intr_impl(lc.INTR_FLOOR, Type.double())) + +register("llvm.floor.f32", Type.float(), Type.float())\ + (intr_impl(lc.INTR_FLOOR, Type.float())) + + +def build_test(name): + (retty, args), impl = INTRINSICS[name] + module = Module.new("test.%s" % name) + fn = module.add_function(Type.function(retty, args), name="test_%s" % name) + builder = Builder.new(fn.append_basic_block("")) + retval = impl(module, builder, fn.args) + builder.ret(retval) + fn.verify() + module.verify() + return module, fn + + +def run_test(name): + module, fn = build_test(name) + eb = le.EngineBuilder.new(module).mcjit(True) + engine = eb.create() + ptr = engine.get_pointer_to_function(fn) + + argtys = fn.type.pointee.args + retty = fn.type.pointee.return_type + cargtys = [CTYPES_MAP[a] for a in argtys] + cretty = CTYPES_MAP[retty] + cfunc = CFUNCTYPE(cretty, *cargtys)(ptr) + args = [1] * len(cargtys) + cfunc(*args) + + +def spawner(name): + print("Testing %s" % name) + proc = multiprocessing.Process(target=run_test, args=(name,)) + + print('-' * 80) + proc.start() + proc.join() + + if proc.exitcode != 0: + print("FAILED") + ok = False + else: + print("PASSED") + ok = True + print('=' * 80) + print() + + return ok + +USAGE = """ +Args: [name] + +name: intrinsic name to test + +If no name is given, test all intrinsics. + +""" + + +def main(argv=()): + if len(argv) == 1: + intrname = argv[1] + spawner(intrname) + elif not argv: + failed = [] + for name in sorted(INTRINSICS): + if not spawner(name): + failed.append(name) + + print("Summary:") + for name in failed: + print("%s failed" % name) + else: + print(USAGE) + + +if __name__ == '__main__': + main(argv=sys.argv[1:]) diff --git a/llvm/workaround/avx_support.py b/llvm/workaround/avx_support.py index 0b1c2da..4c65dc5 100644 --- a/llvm/workaround/avx_support.py +++ b/llvm/workaround/avx_support.py @@ -12,7 +12,11 @@ http://software.intel.com/sites/default/files/m/a/b/3/4/d/41604-319433-012a.pdf """ -import sys, os, subprocess +import sys +import os +import subprocess +import contextlib + def detect_avx_support(option='detect'): '''Detect AVX support''' @@ -36,13 +40,25 @@ def detect_unix_like(): except IOError: return False - for line in info: - if line.lstrip().startswith('flags'): - features = line.split() - if 'avx' in features and 'xsave' in features: - # enable AVX if flags contain AVX - return True - return False + with contextlib.closing(info): + for line in info: + if line.lstrip().startswith('flags'): + features = line.split() + if 'avx' in features and 'xsave' in features: + # enable AVX if flags contain AVX + return True + return False + + +@contextlib.contextmanager +def _close_popen(popen): + if sys.version_info[0] >= 3: + with popen: + yield + else: + yield + popen.stdout.close() + def detect_osx_like(): try: @@ -50,9 +66,11 @@ def detect_osx_like(): stdout=subprocess.PIPE) except OSError: return False - features = info.stdout.read().decode('UTF8') - features = features.split() - return 'AVX1.0' in features and 'OSXSAVE' in features and 'XSAVE' in features + + with _close_popen(info): + features = info.stdout.read().decode('UTF8') + features = features.split() + return 'AVX1.0' in features and 'OSXSAVE' in features and 'XSAVE' in features if __name__ == '__main__': diff --git a/llvm_cbuilder/builder.py b/llvm_cbuilder/builder.py index eaabd67..a2c0bda 100644 --- a/llvm_cbuilder/builder.py +++ b/llvm_cbuilder/builder.py @@ -349,7 +349,7 @@ class CBuilder(object): elif not isinstance(count, lc.Value): count = self.constant(types.int, count).value - ptr = self.builder.alloca_array(ty, count, name=name) + ptr = self.builder.alloca(ty, size=count, name=name) return CArray(self, ptr) def ret(self, val=None): diff --git a/llvmpy/capsule.cpp b/llvmpy/capsule.cpp index 561b28a..5e0279d 100644 --- a/llvmpy/capsule.cpp +++ b/llvmpy/capsule.cpp @@ -1,55 +1,95 @@ #include +#include #include #include #include +#include +#include +#include + +static PyObject* TheAPIModule = NULL; +static PyObject* TheCapsuleModule = NULL; +static PyObject* TheCapsuleClass = NULL; +static PyObject* TheWrapperClass = NULL; +static PyObject* TheCache = NULL; +static PyObject* TheAddrDtorDict = NULL; +static PyObject* TheClassesDict = NULL; +static PyObject* TheAddrRefCt = NULL; +static PyObject* ConstantOne = NULL; +static PyObject* TheDowncastModule = NULL; 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); +PyObject* GetAPIModule(){ + if (NULL == TheAPIModule) + TheAPIModule = PyImport_ImportModule("llvmpy._api"); + return TheAPIModule; } 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); +PyObject* GetCapsuleModule(){ + if (NULL == TheCapsuleModule) + TheCapsuleModule = PyImport_ImportModule("llvmpy.capsule"); + return TheCapsuleModule; } 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; - } +PyObject* GetCapsuleClass() { + if (NULL == TheCapsuleClass) + TheCapsuleClass = PyObject_GetAttrString(GetCapsuleModule(), + "Capsule"); + return TheCapsuleClass; } -// ------------------ -// PyCapsule Context -// ------------------ +static +PyObject* GetWrapperClass() { + if (NULL == TheWrapperClass) + TheWrapperClass = PyObject_GetAttrString(GetCapsuleModule(), + "Wrapper"); + return TheWrapperClass; +} static -CapsuleContext* getContext(PyObject* self, PyObject* args) { - PyObject* obj; - if (!PyArg_ParseTuple(args, "O", &obj)) { - return NULL; - } +PyObject* GetCache() { + if (NULL == TheCache) + TheCache = PyObject_GetAttrString(GetCapsuleModule(), "_cache"); + return TheCache; +} + +static +PyObject* GetAddrDtorDict() { + if (NULL == TheAddrDtorDict) + TheAddrDtorDict = PyObject_GetAttrString(GetCapsuleModule(), + "_addr2dtor"); + return TheAddrDtorDict; +} + +static +PyObject* GetClassesDict() { + if (NULL == TheClassesDict) + TheClassesDict = PyObject_GetAttrString(GetCapsuleModule(), + "_pyclasses"); + return TheClassesDict; +} + +static +PyObject* GetAddrRefCt() { + if (NULL == TheAddrRefCt) + TheAddrRefCt = PyObject_GetAttrString(GetCapsuleModule(), + "_addr2refct"); + return TheAddrRefCt; +} + +static +PyObject* GetDowncastModule() { + if (NULL == TheDowncastModule) + TheDowncastModule = PyObject_GetAttrString(GetAPIModule(), + "downcast"); + return TheDowncastModule; + +} + +static +CapsuleContext* GetContext(PyObject *obj) { void* context = PyCapsule_GetContext(obj); if (!context) { PyErr_SetString(PyExc_TypeError, "PyCapsule has no context."); @@ -59,9 +99,8 @@ CapsuleContext* getContext(PyObject* self, PyObject* args) { } static -PyObject* getClassName(PyObject* self, PyObject* args) { - CapsuleContext* context = getContext(self, args); - //Assert(context->_magic == 0xdead); +PyObject* GetClassName(PyObject* obj) { + CapsuleContext* context = GetContext(obj); if (!context) { return NULL; } else { @@ -69,6 +108,476 @@ PyObject* getClassName(PyObject* self, PyObject* args) { } } +static +PyObject* getClassName(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + return NULL; + } + return GetClassName(obj); +} + +static +PyObject* GetName(PyObject* obj) { + const char* name = PyCapsule_GetName(obj); + if (!name) return NULL; + return PyString_FromString(name); +} + +static +PyObject* getName(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + return GetName(obj); +} + +static +PyObject* GetPointer(PyObject* obj){ + void *pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj)); + if (!pointer) return NULL; + return PyLong_FromVoidPtr(pointer); +} + +static +PyObject* getPointer(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + return GetPointer(obj); +} + +static +bool Check(PyObject* obj){ + return PyCapsule_CheckExact(obj); +} + +static +PyObject* check(PyObject* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)){ + return NULL; + } + if (Check(obj)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + + +static PyObject* Unwrap(PyObject* obj) { + if (PyObject_IsInstance(obj, GetWrapperClass())) { + return PyObject_GetAttrString(obj, "_ptr"); + } else { + Py_INCREF(obj); + return obj; + } +} + +/* +Unwrap a Wrapper instance into the underlying PyCapsule. +If `obj` is not a Wrapper instance, returns `obj`. +*/ +static PyObject* unwrap(PyObject* self, PyObject* args) { + PyObject *obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + return NULL; + } + return Unwrap(obj); +} + +static bool HasOwnership(PyObject* obj) { + PyObject* addr = GetPointer(obj); + PyObject* name = GetName(obj); + auto_pyobject nameaddr = PyTuple_Pack(2, name, addr); + PyObject* dtor = PyDict_GetItem(GetAddrDtorDict(), *nameaddr); + if (!dtor || dtor == Py_None) { + return false; + } else { + return true; + } +} + +static PyObject* has_ownership(PyObject* self, PyObject* args) { + PyObject *obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + return NULL; + } + if (HasOwnership(obj)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static +void NormalizeString(std::ostream &os, const char* str) { + for(; *str; ++str){ + if (*str == ':') { + os << '_'; + if(*(str + 1) == ':') { ++str; } + } else { + os << *str; + } + } +} + +static +PyObject* WrapCore(PyObject *oldCap, bool owned) { + auto_pyobject cap = PyObject_CallFunctionObjArgs(GetCapsuleClass(), oldCap, + NULL); + auto_pyobject cls = PyObject_CallMethod(*cap, "get_class", ""); + auto_pyobject addr = GetPointer(oldCap); + auto_pyobject name = GetName(oldCap); + + // look up cached object + auto_pyobject cache_cls = PyObject_GetItem(GetCache(), *cls); + Assert(*cache_cls); + int addr_in_cache = PyMapping_HasKey(*cache_cls, *addr); + + PyObject* obj = NULL; + if (addr_in_cache) { + obj = PyObject_GetItem(*cache_cls, *addr); + } else { + if (!owned) { + auto_pyobject hasDtor = PyObject_CallMethod(*cls, "_has_dtor", ""); + if (PyObject_IsTrue(*hasDtor)) { + auto_pyobject key = PyTuple_Pack(2, *name, *addr); + auto_pyobject val = PyObject_GetAttrString(*cls, "_delete_"); + + int ok = PyDict_SetItem(GetAddrDtorDict(), *key, *val); + Assert(ok != -1); + } + } + obj = PyObject_CallMethod(*cap, "instantiate", ""); + int ok = PyObject_SetItem(*cache_cls, *addr, obj); + Assert(ok != -1); + } + + Assert(obj); + return obj; +} + +static +PyObject* Wrap(PyObject* cap, bool owned){ + if (!Check(cap)) { + if (PyList_Check(cap)) { + const int N = PyList_Size(cap); + PyObject* result = PyList_New(N); + + for (int i = 0; i < N; ++i){ + PyObject* item = PyList_GetItem(cap, i); + if (!item) + return NULL; + PyObject* out = Wrap(item, false); + if (!out) return NULL; + if (-1 == PyList_SetItem(result, i, out)) + return NULL; + } + + return result; + } else { + Py_INCREF(cap); + return cap; + } + } + + return WrapCore(cap, owned); +} + + +static +PyObject* wrap(PyObject* self, PyObject* args) { + PyObject* obj; + PyObject* owned = NULL; + if (!PyArg_ParseTuple(args, "O|O", &obj, &owned)) { + return NULL; + } + bool ownedFlag = false; + if (owned) { + ownedFlag = PyObject_IsTrue(owned); + } + return Wrap(obj, ownedFlag); +} + +static +PyObject* downcast(PyObject* self, PyObject* args) { + PyObject *obj, *cls; + if (!PyArg_ParseTuple(args, "OO", &obj, &cls)) { + return NULL; + } + + auto_pyobject objType = PyObject_Type(obj); + + if (*objType == cls) { + Py_INCREF(obj); + return obj; + } + + PyObject* apiModule = GetAPIModule(); + + auto_pyobject fromTy = PyObject_GetAttrString(obj, "_llvm_type_"); + auto_pyobject toTy = PyObject_GetAttrString(cls, "_llvm_type_"); + + std::ostringstream oss; + + auto_pyobject fromTyStr = PyObject_Str(*fromTy); + auto_pyobject toTyStr = PyObject_Str(*toTy); + + const char * fromCS = PyString_AsString(*fromTyStr); + const char * toCS = PyString_AsString(*toTyStr); + + oss << "downcast_"; + NormalizeString(oss, fromCS); + oss << "_to_"; + NormalizeString(oss, toCS); + std::string fname = oss.str(); + + auto_pyobject caster = PyObject_GetAttrString(GetDowncastModule(), + fname.c_str()); + + if (!caster) { + std::ostringstream oss; + oss << "Downcast from " << fromCS << " to " << toCS; + std::string errmsg = oss.str(); + PyErr_SetString(PyExc_TypeError, errmsg.c_str()); + return NULL; + } + + auto_pyobject oldObj = Unwrap(obj); + auto_pyobject newObj = PyObject_CallFunctionObjArgs(*caster, *oldObj, + NULL); + + bool used_to_own = HasOwnership(*oldObj); + + PyObject *result = Wrap(*newObj, !used_to_own); + + int status = PyObject_Not(result); + switch(status) { + case 0: + return result; + case 1: + default: + PyErr_SetString(PyExc_ValueError, "Downcast failed"); + Py_XDECREF(result); + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct CapsuleObject { + PyObject_HEAD; + PyObject *capsule; +}; + +static +void Capsule_dealloc(CapsuleObject* self) { + Py_XDECREF(self->capsule); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static +int Capsule_init(CapsuleObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *cap; + if (!PyArg_ParseTuple(args, "O", &cap)) { + return -1; + } + + if (!Check(cap)) { + PyErr_SetString(PyExc_TypeError, "Expected PyCapsule object"); + return -1; + } + + Py_INCREF(cap); + self->capsule = cap; + + PyObject* addr2refct = GetAddrRefCt(); + + auto_pyobject ptr = GetPointer(self->capsule); + auto_pyobject refct = PyObject_GetItem(addr2refct, *ptr); + auto_pyobject inc = PyNumber_InPlaceAdd(*refct, ConstantOne); + return PyObject_SetItem(addr2refct, *ptr, *inc); +} + +static +PyObject* Capsule_getclassname(CapsuleObject* self, void *closure) { + return GetClassName(self->capsule); +} + +static +PyObject* Capsule_getname(CapsuleObject* self, void *closure) { + return GetName(self->capsule); +} + +static +PyObject* Capsule_getpointer(CapsuleObject* self, void *closure) { + return GetPointer(self->capsule); +} + + +static +PyObject* Capsule_GetClass(CapsuleObject *self){ + PyObject *pycls = GetClassesDict(); + auto_pyobject key = GetClassName(self->capsule); + return PyDict_GetItem(pycls, *key); // borrowed reference +} + +static +PyObject* Capsule_get_class(CapsuleObject* self, PyObject* args) { + PyObject* out = Capsule_GetClass(self); + Py_XINCREF(out); + return out; +} + +static +PyObject* Capsule_instantiate(CapsuleObject* self, PyObject* args) { + return PyObject_CallFunctionObjArgs(Capsule_GetClass(self), self, NULL); +} + +/// Rotate Right +static unsigned long RotR(unsigned long hash, int offset){ + if (offset == 0) return hash; + + unsigned long out = hash << (sizeof(hash) * 8 - offset); + out |= hash >> offset; + return out; +} + + +/* +This is called everytime an object is returned from LLVM. +It derserves to be optimized to reduce unnecessary Python object allocation. +The following implements a simple hash function that uses XOR and +right-rotation. +*/ +static +long Capsule_hash(CapsuleObject *self) { + const char* name = PyCapsule_GetName(self->capsule); + void *pointer = PyCapsule_GetPointer(self->capsule, name); + + unsigned long hash = 0xabcd1234 ^ (unsigned long)pointer; + + // The first loop accounts for the different LLVM class name and the + // length of the name. + for(const char* p = name; *p != '\0'; ++p) { + hash ^= *p; + hash = RotR(hash, 11); + } + + // The second loop accounts for the pointer identity. + for(int i = 0; i capsule, + PyCapsule_GetName(a->capsule)); + void* pb = PyCapsule_GetPointer(b->capsule, + PyCapsule_GetName(b->capsule)); + return pa == pb; + } + return false; +} + +static +PyObject* Capsule_richcmp(PyObject *a, PyObject *b, int op) { + bool ret = Capsule_eq(a, b); + switch(op) { + case Py_EQ: break; + case Py_NE: ret = !ret; break; + default: + return Py_NotImplemented; + } + if (ret) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static PyMemberDef Capsule_members[] = { + {"capsule", T_OBJECT_EX, offsetof(CapsuleObject, capsule), READONLY, "capsule"}, + { NULL }, +}; + +static PyMethodDef Capsule_methods[] = { + { "get_class", (PyCFunction)Capsule_get_class, METH_NOARGS, + "Get Capsule class"}, + { "instantiate", (PyCFunction)Capsule_instantiate, METH_NOARGS, + "create a new instance"}, + { NULL }, +}; + +static PyGetSetDef Capsule_getseters[] = { + {"classname", (getter)Capsule_getclassname, NULL, "class name", NULL}, + {"name", (getter)Capsule_getname, NULL, "name", NULL}, + {"pointer", (getter)Capsule_getpointer, NULL, "pointer", NULL}, + { NULL }, +}; + +static PyTypeObject CapsuleType = { +#if (PY_MAJOR_VERSION < 3) + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ +#else + PyVarObject_HEAD_INIT(NULL, 0) +#endif + "_capsule.Capsule", /*tp_name*/ + sizeof(CapsuleObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Capsule_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)Capsule_hash, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Capsule object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)Capsule_richcmp, /*tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Capsule_methods, /* tp_methods */ + Capsule_members, /* tp_members */ + Capsule_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Capsule_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/////////////////////////////////////////////////////////////////////////////// + static PyMethodDef core_methods[] = { #define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL } @@ -76,6 +585,10 @@ static PyMethodDef core_methods[] = { declmethod(getPointer), declmethod(check), declmethod(getClassName), + declmethod(unwrap), + declmethod(wrap), + declmethod(has_ownership), + declmethod(downcast), { NULL }, #undef declmethod }; @@ -106,10 +619,21 @@ extern "C" { #else PyObject *module = Py_InitModule("_capsule", core_methods); #endif - if (module == NULL) + if (module == NULL){ INITERROR; + } + + if (module) { + CapsuleType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CapsuleType) < 0) { + INITERROR; + } + Py_INCREF(&CapsuleType); + PyModule_AddObject(module, "Capsule", (PyObject*)(&CapsuleType)); + + ConstantOne = PyInt_FromLong(1); + } #if PY_MAJOR_VERSION >= 3 - return module; #endif } diff --git a/llvmpy/capsule.py b/llvmpy/capsule.py index 9de0571..3fb9a39 100644 --- a/llvmpy/capsule.py +++ b/llvmpy/capsule.py @@ -1,19 +1,27 @@ -from weakref import WeakKeyDictionary, WeakValueDictionary, ref +from weakref import WeakValueDictionary from collections import defaultdict import logging +from llvmpy._capsule import (unwrap, has_ownership, downcast, wrap, + getClassName, getName, getPointer, Capsule) + + logger = logging.getLogger(__name__) NO_DEBUG = False + + def silent_logger(): ''' Silent logger for unless we have a error message. ''' + global NO_DEBUG logger.setLevel(logging.ERROR) NO_DEBUG = True # comment out the line below to re-enable logging at DEBUG level. silent_logger() + def set_debug(enabled): ''' Side-effect: configure logger with it is not configured. @@ -28,79 +36,63 @@ def set_debug(enabled): else: logger.setLevel(logging.WARNING) -def _capsule_weakref_dtor(item): - addr = item.pointer - name = item.name - _addr2refct[addr] -= 1 - refct = _addr2refct[addr] - assert refct >= 0, "RefCt drop below 0" - if refct == 0: - dtor = _addr2dtor.pop((name, addr), None) - if dtor is not None: - if not NO_DEBUG: - # Some globals in logger could be removed by python GC - # at interpreter teardown. - # That can cause exception raised and ignored message. - logger.debug('Destroy %s %s', name, hex(addr)) - dtor(item.capsule) -class Capsule(object): - "Wraps PyCapsule so that we can build weakref of it." +#class Capsule(object): +# "Wraps PyCapsule so that we can build weakref of it." +# from ._capsule import check, getClassName, getName, getPointer +# __slots__ = "capsule" +# +# def __init__(self, capsule): +# assert Capsule.valid(capsule) +# self.capsule = capsule +# +# #weak = WeakRef(self, _capsule_weakref_dtor) +# #weak.pointer = self.pointer +# #weak.capsule = capsule +# #weak.name = self.name +# #_capsule2weak[self] = weak +# _addr2refct[self.pointer] += 1 +# +# @property +# def classname(self): +# return self.getClassName(self.capsule) +# +# @property +# def name(self): +# return self.getName(self.capsule) +# +# @property +# def pointer(self): +# return self.getPointer(self.capsule) +# +# @staticmethod +# def valid(capsule): +# return Capsule.check(capsule) +# +# def get_class(self): +# return _pyclasses[self.classname] +# +# def instantiate(self): +# cls = self.get_class() +# return cls(self) +# +# def __eq__(self, other): +# if isinstance(other, Capsule) and self.pointer == other.pointer: +# assert self.name == other.name +# return True +# else: +# return False +# +# def __hash__(self): +# return hash((self.pointer, self.name)) +# +# def __ne__(self, other): +# return not (self == other) - from ._capsule import check, getClassName, getName, getPointer - def __init__(self, capsule): - assert Capsule.valid(capsule) - self.capsule = capsule - - weak = WeakRef(self, _capsule_weakref_dtor) - weak.pointer = self.pointer - weak.capsule = capsule - weak.name = self.name - _capsule2weak[self] = weak - _addr2refct[self.pointer] += 1 - - @property - def classname(self): - return self.getClassName(self.capsule) - - @property - def name(self): - return self.getName(self.capsule) - - @property - def pointer(self): - return self.getPointer(self.capsule) - - @staticmethod - def valid(capsule): - return Capsule.check(capsule) - - def get_class(self): - return _pyclasses[self.classname] - - def instantiate(self): - cls = self.get_class() - return cls(self) - - def __eq__(self, other): - if self.pointer == other.pointer: - assert self.name == other.name - return True - else: - return False - - def __hash__(self): - return hash((self.pointer, self.name)) - - def __ne__(self, other): - return not (self == other) - -class WeakRef(ref): - pass _addr2refct = defaultdict(lambda: 0) -_capsule2weak = WeakKeyDictionary() +#_capsule2weak = WeakKeyDictionary() _addr2dtor = {} _pyclasses = {} @@ -108,15 +100,16 @@ _pyclasses = {} # NOTE: The same 'addr' may appear in multiple class bins. _cache = defaultdict(WeakValueDictionary) + def release_ownership(old): logger.debug('Release %s', old) - addr = Capsule.getPointer(old) - name = Capsule.getName(old) + addr = getPointer(old) + name = getName(old) if _addr2dtor.get((name, addr)) is None: - clsname = Capsule.getClassName(old) + clsname = getClassName(old) if not _pyclasses[clsname]._has_dtor(): return - # Guard duplicated release + # Guard duplicated release raise Exception("Already released") _addr2dtor[(name, addr)] = None @@ -126,61 +119,86 @@ def obtain_ownership(cap): if cls._has_dtor(): addr = cap.pointer name = cap.name - assert _addr2dtor[addr] is None + assert _addr2dtor[(name, addr)] is None _addr2dtor[(name, addr)] = cls._delete_ -def has_ownership(cap): - addr = Capsule.getPointer(cap) - name = Capsule.getName(cap) - return _addr2dtor.get((name, addr)) is not None -def wrap(cap, owned=False): - '''Wrap a PyCapsule with the corresponding Wrapper class. - If `cap` is not a PyCapsule, returns `cap` - ''' - if not Capsule.valid(cap): - if isinstance(cap, list): - return list(map(wrap, cap)) - return cap # bypass if cap is not a PyCapsule and not a list +#def has_ownership(cap): +# addr = Capsule.getPointer(cap) +# name = Capsule.getName(cap) +# return _addr2dtor.get((name, addr)) is not None - cap = Capsule(cap) - cls = cap.get_class() - addr = cap.pointer - name = cap.name - # lookup cached object - if cls in _cache and addr in _cache[cls]: - obj = _cache[cls][addr] - else: - if not owned and cls._has_dtor(): - _addr2dtor[(name, addr)] = cls._delete_ - obj = cap.instantiate() - _cache[cls][addr] = obj # cache it - return obj -def unwrap(obj): +#def wrap(cap, owned=False): +# '''Wrap a PyCapsule with the corresponding Wrapper class. +# If `cap` is not a PyCapsule, returns `cap` +# ''' +# if not Capsule.valid(cap): +# if isinstance(cap, list): +# return list(map(wrap, cap)) +# return cap # bypass if cap is not a PyCapsule and not a list +# +# cap = Capsule(cap) +# cls = cap.get_class() +# addr = cap.pointer +# name = cap.name +# # lookup cached object +# if cls in _cache and addr in _cache[cls]: +# obj = _cache[cls][addr] +# else: +# if not owned and cls._has_dtor(): +# _addr2dtor[(name, addr)] = cls._delete_ +# obj = cap.instantiate() +# _cache[cls][addr] = obj # cache it +# 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 +# if isinstance(obj, Wrapper): +# return obj._ptr +# else: +# return obj def register_class(clsname): def _wrapped(cls): _pyclasses[clsname] = cls return cls + return _wrapped class Wrapper(object): - __slots__ = '__capsule' def __init__(self, capsule): self.__capsule = capsule + def __del__(self): + if _addr2refct is None: + # System is tearing down + # No need to free anything + return + + item = self.__capsule + addr = item.pointer + name = item.name + + _addr2refct[addr] -= 1 + refct = _addr2refct[addr] + assert refct >= 0, "RefCt drop below 0" + if refct == 0: + dtor = _addr2dtor.pop((name, addr), None) + if dtor is not None: + if not NO_DEBUG: + # Some globals in logger could be removed by python GC + # at interpreter teardown. + # That can cause exception raised and ignored message. + logger.debug('Destroy %s %s', name, hex(addr)) + dtor(item.capsule) + @property def _capsule(self): return self.__capsule @@ -193,10 +211,11 @@ class Wrapper(object): return hash(self._capsule) def __eq__(self, other): - return self._capsule == other._capsule + if isinstance(other, Wrapper): + return self._capsule == other._capsule def __ne__(self, other): - return not(self == other) + return not (self == other) def _downcast(self, newcls): return downcast(self, newcls) @@ -205,24 +224,25 @@ class Wrapper(object): def _has_dtor(cls): return hasattr(cls, '_delete_') -def downcast(obj, cls): - from . import _api - if type(obj) is cls: - return obj - fromty = obj._llvm_type_ - toty = cls._llvm_type_ - logger.debug("Downcast %s to %s" , fromty, toty) - fname = 'downcast_%s_to_%s' % (fromty, toty) - fname = fname.replace('::', '_') - if not hasattr(_api.downcast, fname): - fmt = "Downcast from %s to %s is not supported" - raise TypeError(fmt % (fromty, toty)) - caster = getattr(_api.downcast, fname) - old = unwrap(obj) - new = caster(old) - used_to_own = has_ownership(old) - res = wrap(new, owned=not used_to_own) - if not res: - raise ValueError("Downcast failed") - return res +#def downcast(obj, cls): +# from . import _api +# +# if type(obj) is cls: +# return obj +# fromty = obj._llvm_type_ +# toty = cls._llvm_type_ +# logger.debug("Downcast %s to %s", fromty, toty) +# fname = 'downcast_%s_to_%s' % (fromty, toty) +# fname = fname.replace('::', '_') +# if not hasattr(_api.downcast, fname): +# fmt = "Downcast from %s to %s is not supported" +# raise TypeError(fmt % (fromty, toty)) +# caster = getattr(_api.downcast, fname) +# old = unwrap(obj) +# new = caster(old) +# used_to_own = has_ownership(old) +# res = wrap(new, owned=not used_to_own) +# if not res: +# raise ValueError("Downcast failed") +# return res diff --git a/llvmpy/gen/binding.py b/llvmpy/gen/binding.py index 30e1614..5a6d8f4 100644 --- a/llvmpy/gen/binding.py +++ b/llvmpy/gen/binding.py @@ -1,5 +1,4 @@ import inspect, textwrap -import functools import codegen as cg import os @@ -8,10 +7,12 @@ namespaces = {} RESERVED = frozenset(['None']) + def makedir(directory): if not os.path.exists(directory): os.makedirs(directory) + class SubModule(object): def __init__(self): self.methods = [] @@ -293,6 +294,10 @@ class Class(SubModule, _Type): writer.println('@capsule.register_class("%s")' % self.fullname) with writer.block('class %(clsname)s(%(bases)s):' % locals()): writer.println('_llvm_type_ = "%s"' % self.fullname) + if self.bases: + writer.println('__slots__ = ()') + else: + writer.println('__slots__ = "__weakref__"') for enum in self.enums: enum.compile_py(writer) for meth in self.methods: @@ -399,6 +404,7 @@ class Enum(object): writer.println(fmt % locals()) writer.println() + class Method(object): _kind_ = 'meth' @@ -516,6 +522,7 @@ class Method(object): with writer.block('if len(%s) > %d:' % (unwrapped, i)): writer.release_ownership('%s[%d]' % (unwrapped, i)) + class CustomMethod(Method): def __init__(self, methodname, retty, *argtys): super(CustomMethod, self).__init__(retty, *argtys) @@ -594,6 +601,7 @@ class CustomFunction(Function): def fullname(self): return self.realname + class Destructor(Method): _kind_ = 'dtor' @@ -625,6 +633,7 @@ class Constructor(StaticMethod): ret = writer.declare(retty.fullname, stmt) return ret + class ref(_Type): def __init__(self, element): assert isinstance(element, Class), type(element) @@ -686,13 +695,16 @@ class ptr(_Type): return writer.pycapsule_new(val, self.element.capsule_name, self.element.fullname) + class ownedptr(ptr): pass + def const(ptr_or_ref): ptr_or_ref.const = True return ptr_or_ref + class cast(_Type): format = 'O' @@ -757,6 +769,7 @@ class CustomPythonMethod(object): for line in self.sourcelines: writer.println(line) + class CustomPythonStaticMethod(CustomPythonMethod): def compile_py(self, writer): writer.println('@staticmethod') @@ -845,6 +858,7 @@ class Attr(object): TARGETS_BUILT = os.environ.get('LLVM_TARGETS_BUILT', '').split() + def _parse_llvm_version(ver): import re m = re.compile(r'(\d+)\.(\d+)').match(ver) diff --git a/llvmpy/gen/gen.py b/llvmpy/gen/gen.py index cd63b68..bb408a1 100644 --- a/llvmpy/gen/gen.py +++ b/llvmpy/gen/gen.py @@ -9,7 +9,7 @@ extern "C" { #if (PY_MAJOR_VERSION >= 3) -PyObject * +PyMODINIT_FUNC PyInit_%(module)s(void) { PyObject *module = create_python_module("%(module)s", meth_%(ns)s); diff --git a/llvmpy/include/llvm_binding/extra.h b/llvmpy/include/llvm_binding/extra.h index f8f7766..bdd86d3 100644 --- a/llvmpy/include/llvm_binding/extra.h +++ b/llvmpy/include/llvm_binding/extra.h @@ -40,7 +40,7 @@ namespace extra{ using namespace llvm; - + class raw_svector_ostream_helper: public raw_svector_ostream { SmallVectorImpl *SV; public: @@ -85,7 +85,7 @@ PyObject* make_raw_ostream_for_printing(PyObject* self, PyObject* args) { using extra::raw_svector_ostream_helper; using llvm::raw_svector_ostream; - + if (!PyArg_ParseTuple(args, "")) { return NULL; } @@ -99,7 +99,7 @@ PyObject* make_small_vector_from_types(PyObject* self, PyObject* args) { using llvm::Type; typedef llvm::SmallVector SmallVector_Type; - + SmallVector_Type* SV = new SmallVector_Type; Py_ssize_t size = PyTuple_Size(args); for (Py_ssize_t i = 0; i < size; ++i) { @@ -186,8 +186,8 @@ PyObject* iterator_to_pylist_deref(iterator begin, iterator end, { PyObject* list = PyList_New(0); for(; begin != end; ++begin) { - PyObject* cap = pycapsule_new(&*begin, capsuleName, className); - PyList_Append(list, cap); + auto_pyobject cap = pycapsule_new(&*begin, capsuleName, className); + PyList_Append(list, *cap); } return list; } @@ -198,8 +198,8 @@ PyObject* iterator_to_pylist(iterator begin, iterator end, { PyObject* list = PyList_New(0); for(; begin != end; ++begin) { - PyObject* cap = pycapsule_new(*begin, capsuleName, className); - PyList_Append(list, cap); + auto_pyobject cap = pycapsule_new(*begin, capsuleName, className); + PyList_Append(list, *cap); } return list; } @@ -420,7 +420,7 @@ PyObject* ExecutionEngine_RunFunction(llvm::ExecutionEngine* EE, PyErr_SetString(PyExc_RuntimeError, "Failed to index into args?"); return NULL; } - + GenericValue* gv = static_cast( PyCapsule_GetPointer(obj, GVN)); @@ -575,11 +575,11 @@ PyObject* TargetMachine_addPassesToEmitFile( bool status = TM->addPassesToEmitFile(PM, fso, FTy, disableVerify); if (status) { StringRef sr = rso.str(); - PyObject* buf = PyString_FromStringAndSize(sr.data(), sr.size()); + auto_pyobject buf = PyString_FromStringAndSize(sr.data(), sr.size()); if (!buf) { return NULL; } - if (-1 == PyFile_WriteObject(buf, Out, Py_PRINT_RAW)){ + if (-1 == PyFile_WriteObject(*buf, Out, Py_PRINT_RAW)){ return NULL; } Py_RETURN_TRUE; @@ -626,7 +626,7 @@ PyObject* Linker_LinkInModule(llvm::Linker* Linker, if (! failed) { Py_RETURN_FALSE; } else { - + auto_pyobject buf = PyBytes_FromString(errmsg.c_str()); if (NULL == callwrite(ErrMsg, *buf)){ return NULL; @@ -861,6 +861,27 @@ PyObject* DynamicLibrary_LoadLibraryPermanently(const char * Filename, } } +static +PyObject* DynamicLibrary_getPermanentLibrary(const char * Filename, + PyObject* ErrMsg = 0) +{ + using namespace llvm::sys; + std::string errmsg; + DynamicLibrary dylib = DynamicLibrary::getPermanentLibrary(Filename, &errmsg); + if (!dylib.isValid()) { + if (ErrMsg) { + auto_pyobject buf = PyBytes_FromString(errmsg.c_str()); + if (!callwrite(ErrMsg, *buf)) + return NULL; + } + PyErr_SetString(PyExc_RuntimeError, errmsg.c_str()); + return NULL; + } + return pycapsule_new(new DynamicLibrary(dylib), + "llvm::sys::DynamicLibrary", + "llvm::sys::DynamicLibrary"); +} + class PassRegistryEnumerator : public llvm::PassRegistrationListener{ public: PyObject* List; @@ -870,7 +891,8 @@ public: inline virtual void passEnumerate(const llvm::PassInfo * pass_info){ PyObject* passArg = PyString_FromString(pass_info->getPassArgument()); PyObject* passName = PyString_FromString(pass_info->getPassName()); - PyList_Append(List, Py_BuildValue("(OO)", passArg, passName)); + auto_pyobject pair = Py_BuildValue("(OO)", passArg, passName); + PyList_Append(List, *pair); } }; @@ -988,7 +1010,7 @@ fail: } static -PyObject* MCDisassembler_getInstruction(llvm::MCDisassembler *disasm, +PyObject* MCDisassembler_getInstruction(llvm::MCDisassembler *disasm, llvm::MCInst &instr, const llvm::MemoryObject ®ion, uint64_t address @@ -998,7 +1020,7 @@ PyObject* MCDisassembler_getInstruction(llvm::MCDisassembler *disasm, llvm::MCDisassembler::DecodeStatus status; size = 0; - status = disasm->getInstruction(instr, size, region, address, + status = disasm->getInstruction(instr, size, region, address, llvm::nulls(), llvm::nulls()); return Py_BuildValue("(i,i)", int(status), size); } @@ -1046,5 +1068,5 @@ PyObject* llvm_sys_isBigEndianHost() else Py_RETURN_FALSE; } -#endif +#endif diff --git a/llvmpy/src/BasicBlock.py b/llvmpy/src/BasicBlock.py index 6efe704..3c82e17 100644 --- a/llvmpy/src/BasicBlock.py +++ b/llvmpy/src/BasicBlock.py @@ -1,12 +1,13 @@ from binding import * from .namespace import llvm -from .Value import Function, BasicBlock +from .Value import Function, BasicBlock, Value from .Instruction import Instruction, TerminatorInst from .LLVMContext import LLVMContext from .ADT.StringRef import StringRef @BasicBlock class BasicBlock: + _downcast_ = Value Create = StaticMethod(ptr(BasicBlock), ref(LLVMContext), cast(str, StringRef), ptr(Function), diff --git a/llvmpy/src/Instruction.py b/llvmpy/src/Instruction.py index 2771015..585b7ec 100644 --- a/llvmpy/src/Instruction.py +++ b/llvmpy/src/Instruction.py @@ -346,6 +346,9 @@ class AllocaInst: isStaticAlloca = Method(cast(Bool, bool)) getArraySize = Method(ptr(Value)) getAllocatedType = Method(ptr(Type)) + getAlignment = Method(cast(Unsigned, int)) + setAlignment = Method(Void, cast(int, Unsigned)) + getArraySize = Method(ptr(Value)) @CastInst diff --git a/llvmpy/src/Support/DynamicLibrary.py b/llvmpy/src/Support/DynamicLibrary.py index 7dce97a..e3b1d37 100644 --- a/llvmpy/src/Support/DynamicLibrary.py +++ b/llvmpy/src/Support/DynamicLibrary.py @@ -26,3 +26,10 @@ class DynamicLibrary: cast(str, StringRef), # symbolName cast(int, VoidPtr), # address ) + + getPermanentLibrary = CustomStaticMethod( + 'DynamicLibrary_getPermanentLibrary', + PyObjectPtr, + cast(str, ConstCharPtr), # filename + PyObjectPtr, # std::string * errmsg = 0 + ).require_only(1) diff --git a/llvmpy/src/Target/TargetMachine.py b/llvmpy/src/Target/TargetMachine.py index 492d603..1b52359 100644 --- a/llvmpy/src/Target/TargetMachine.py +++ b/llvmpy/src/Target/TargetMachine.py @@ -53,6 +53,9 @@ class TargetMachine: getVectorTargetTransformInfo = Method(const( ownedptr(VectorTargetTransformInfo))) + else: + addAnalysisPasses = Method(Void, ref(PassManagerBase)) + addPassesToEmitFile = Method(cast(bool, Bool), ref(PassManagerBase), ref(formatted_raw_ostream), diff --git a/setup.py b/setup.py index 15601ee..08d59b6 100644 --- a/setup.py +++ b/setup.py @@ -189,7 +189,8 @@ setup( 'llpython', 'llvm_array', 'llvmpy.api', 'llvmpy.api.llvm', - 'llvm.tests',], + 'llvm.tests', + 'llvm.utils',], package_data = {'llvm': ['llrt/*.ll']}, py_modules = ['llvmpy', 'llvmpy._capsule', diff --git a/test/inlineasm.py b/test/inlineasm.py index 64a4cbe..26ee4b5 100644 --- a/test/inlineasm.py +++ b/test/inlineasm.py @@ -3,7 +3,7 @@ # Import the llvm-py modules. from llvm import * from llvm.core import * -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase import logging import unittest diff --git a/test/loopvectorize.py b/test/loopvectorize.py index 2c12d5b..dc11cb3 100644 --- a/test/loopvectorize.py +++ b/test/loopvectorize.py @@ -2,7 +2,7 @@ from llvm.core import * from llvm.passes import * from llvm.ee import * import llvm -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase from os.path import dirname, join as join_path diff --git a/test/metadata.py b/test/metadata.py index 3228159..0062bcc 100644 --- a/test/metadata.py +++ b/test/metadata.py @@ -1,7 +1,7 @@ from __future__ import print_function import unittest -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase from llvm.core import * class TestMetaData(TestCase): diff --git a/test/tbaa.py b/test/tbaa.py index ad11081..4a24de8 100644 --- a/test/tbaa.py +++ b/test/tbaa.py @@ -1,6 +1,6 @@ from llvm.core import * from llvm.tbaa import * -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase import unittest class TestTBAABuilder(TestCase): diff --git a/test/test.py b/test/test.py index 3b46281..e23aca3 100644 --- a/test/test.py +++ b/test/test.py @@ -7,7 +7,7 @@ import unittest, sys, logging from llvm import * from llvm.core import * -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase class TestModule(TestCase): diff --git a/test/test_debuginfo.py b/test/test_debuginfo.py index b6a5d6f..d31563c 100644 --- a/test/test_debuginfo.py +++ b/test/test_debuginfo.py @@ -4,7 +4,7 @@ import unittest import llvm.ee from llvm.core import * from llvm import _dwarf, debuginfo -from llvm.test_llvmpy import TestCase +from llvm.tests.support import TestCase class TestDebugInfo(TestCase):