Compare commits
38 commits
master
...
llpython-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aacfce25bf | ||
|
|
ca17b03180 | ||
|
|
4f815224f9 | ||
|
|
f332fbf9de | ||
|
|
1adf3d871a | ||
|
|
9fe9e8eb39 | ||
|
|
e4f4d56fe6 | ||
|
|
a6cf8c4f71 | ||
|
|
d8297cf50b | ||
|
|
a8747d2125 | ||
|
|
fcf5420803 | ||
|
|
ecf88f93e2 | ||
|
|
fd41a93554 | ||
|
|
5f81cab357 | ||
|
|
68030d51d1 | ||
|
|
6c7d493441 | ||
|
|
0b24adb80a | ||
|
|
ee557c55cc | ||
|
|
722453502c | ||
|
|
c55198095f | ||
|
|
bba305774a | ||
|
|
4837bf194e | ||
|
|
102d6eac66 | ||
|
|
31119a48d4 | ||
|
|
b5a15a5019 | ||
|
|
c890cbdd92 | ||
|
|
420d0ed88d | ||
|
|
643f0706e9 | ||
|
|
abbf972768 | ||
|
|
329b10dbf0 | ||
|
|
8b075317cd | ||
|
|
c06f811473 | ||
|
|
c5d95e01e1 | ||
|
|
8ae1b70206 | ||
|
|
d3b252531d | ||
|
|
6f532f164a | ||
|
|
5ef00671e7 | ||
|
|
8787af154e |
107 changed files with 4614 additions and 5916 deletions
34
CHANGELOG
34
CHANGELOG
|
|
@ -1,35 +1,5 @@
|
|||
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:
|
||||
---------------------
|
||||
2013-8-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)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
include CHANGELOG LICENSE README.rst setup.py MANIFEST.in versioneer.py
|
||||
include CHANGELOG LICENSE README setup.py MANIFEST.in
|
||||
recursive-include llvm *
|
||||
recursive-include llvmpy *
|
||||
recursive-include www *
|
||||
recursive-include test *
|
||||
recursive-include tools *
|
||||
|
|
|
|||
|
|
@ -30,14 +30,11 @@ 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 install`` to build and install.
|
||||
3. Run ``REQUIRES_RTTI=1 make`` to build.
|
||||
|
||||
**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::
|
||||
|
||||
$ git clone git@github.com:llvmpy/llvmpy.git
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ 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()
|
||||
|
|
@ -15,8 +13,7 @@ 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, run_isolated=False) == 0
|
||||
#check_intrinsics.main()
|
||||
assert llvm.test(verbosity=2) == 0
|
||||
|
||||
print('llvm.__version__: %s' % llvm.__version__)
|
||||
#assert llvm.__version__ == '0.12.0'
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
llvmpy
|
||||
======
|
||||
Documentation for llvmpy
|
||||
========================
|
||||
|
||||
Contents:
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Contents:
|
|||
|
||||
|
||||
Indices and tables
|
||||
------------------
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
|
|
|
|||
|
|
@ -1,152 +0,0 @@
|
|||
'''
|
||||
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()
|
||||
88
llpython/addr_flow.py
Normal file
88
llpython/addr_flow.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
# Module imports
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .byte_flow import Instr, BytecodeFlowBuilder, demo_flow_builder
|
||||
from .opcode_util import build_basic_blocks, itercodeobjs
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definition(s)
|
||||
|
||||
class AddressFlowBuilder(BytecodeFlowBuilder):
|
||||
'''
|
||||
Builds on top of the BytecodeFlowBuilder with two important differences:
|
||||
|
||||
* Child nodes are represented by bytecode indices.
|
||||
|
||||
* All operations (other than purely stack manipulation operations)
|
||||
are retained in the block list (as opposed to being nested).
|
||||
|
||||
The resulting data structure describes a directed acyclic graph
|
||||
(DAG) in a similar fashion to BytecodeFlowBuilder:
|
||||
|
||||
* `flow_dag` ``:=`` ``{`` `blocks` ``*`` ``}``
|
||||
* `blocks` ``:=`` `block_index` ``:`` ``[`` `bytecode_tuple` ``*`` ``]``
|
||||
* `bytecode_tuple` ``:=`` ``(`` `opcode_index` ``,`` `opcode` ``,``
|
||||
`opname` ``,`` `arg` ``,`` ``[`` `opcode_index` ``*`` ``]`` ``)``
|
||||
'''
|
||||
def _visit_op(self, i, op, arg, opname, pops, pushes, appends):
|
||||
assert pops is not None, ('%s not well defined in opcode_util.'
|
||||
'OPCODE_MAP' % opname)
|
||||
if pops:
|
||||
if pops < 0:
|
||||
pops = arg - pops - 1
|
||||
assert pops <= len(self.stack), ("Stack underflow at instruction "
|
||||
"%d (%s)!" % (i, opname))
|
||||
stk_args = [stk_arg[0] for stk_arg in self.stack[-pops:]]
|
||||
del self.stack[-pops:]
|
||||
else:
|
||||
stk_args = []
|
||||
ret_val = Instr(i, op, opname, arg, stk_args)
|
||||
if pushes:
|
||||
self.stack.append(ret_val)
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
def op_IMPORT_FROM (self, i, op, arg):
|
||||
# References top of stack without popping, so we can't use the
|
||||
# generic machinery.
|
||||
opname = self.opmap[op][0]
|
||||
ret_val = Instr(i, op, opname, arg, [self.stack[-1][0]])
|
||||
self.stack.append(ret_val)
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
def op_JUMP_IF_FALSE (self, i, op, arg):
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [self.stack[-1][0]])
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
op_JUMP_IF_TRUE = op_JUMP_IF_FALSE
|
||||
|
||||
def op_LIST_APPEND (self, i, op, arg):
|
||||
'''This method is used for both LIST_APPEND, and SET_ADD
|
||||
opcodes.'''
|
||||
elem = self.stack.pop()
|
||||
container = self.stack[-arg]
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [container[0], elem[0]])
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
op_SET_ADD = op_LIST_APPEND
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
||||
def main(*args):
|
||||
return demo_flow_builder(AddressFlowBuilder, *args)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
main(*sys.argv[1:])
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of addr_flow.py
|
||||
437
llpython/af_to_api.py
Normal file
437
llpython/af_to_api.py
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# ______________________________________________________________________
|
||||
# Module imports
|
||||
|
||||
from __future__ import print_function, division, absolute_import
|
||||
|
||||
import inspect
|
||||
import opcode
|
||||
|
||||
import llvm.core as lc
|
||||
|
||||
from . import byte_control
|
||||
from . import addr_flow
|
||||
from . import opcode_util
|
||||
from .bytetype import lvoid, li1, lc_int, lc_long, l_pyobj_p
|
||||
from .bytecode_visitor import GenericFlowVisitor
|
||||
from .nobitey import get_string_constant
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definitions
|
||||
|
||||
class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
|
||||
'''
|
||||
Code generator for translating from a Python code object and its
|
||||
address flow (output by addr_flow.AddressFlowBuilder) into an LLVM
|
||||
function. The resulting LLVM function calls into a user-provided
|
||||
(or undefined) API function for each interpreter byte code.
|
||||
|
||||
Target API function names are based on a programmable name
|
||||
mangling scheme:
|
||||
|
||||
<PREFIX>OPCODE_NAME<POSTFIX>
|
||||
|
||||
The <PREFIX> and <POSTFIX> strings are determined by the prefix()
|
||||
and postfix() methods. These methods may either be overloaded, or
|
||||
specialized at construction time using _prefix and _postfix
|
||||
arguments. The OPCODE_NAME is the opcode name as determined by
|
||||
the map in opcode.opname.
|
||||
'''
|
||||
def __init__(self, _prefix=None, _postfix=None, **kwds):
|
||||
if _prefix is not None:
|
||||
if inspect.isfunction(_prefix):
|
||||
__prefix = _prefix
|
||||
else:
|
||||
def __prefix(opname, *opargs):
|
||||
return _prefix
|
||||
self.prefix = __prefix
|
||||
if _postfix is not None:
|
||||
if inspect.isfunction(_postfix):
|
||||
__postfix = _postfix
|
||||
else:
|
||||
def __postfix(opname, *opargs):
|
||||
return _postfix
|
||||
self.postfix = __postfix
|
||||
|
||||
def prefix(self, opname, *opargs):
|
||||
return ''
|
||||
|
||||
def postfix(self, opname, *opargs):
|
||||
return ''
|
||||
|
||||
def get_op_function(self, opname, *opargs, **kwds):
|
||||
print(opname, opargs, kwds)
|
||||
target_fn_ty = lc.Type.function(kwds.get('return_type', self.obj_type),
|
||||
[arg.type for arg in opargs])
|
||||
target_fn_name = ''.join((self.prefix(opname, *opargs), opname,
|
||||
self.postfix(opname, *opargs)))
|
||||
return self.llvm_module.get_or_insert_function(target_fn_ty,
|
||||
target_fn_name)
|
||||
|
||||
def call_op_function(self, index, opname, *opargs, **kwds):
|
||||
target_fn = self.get_op_function(opname, *opargs, **kwds)
|
||||
if target_fn.type.pointee.return_type != lvoid:
|
||||
name = kwds.get('name', 'op_%d' % index)
|
||||
result = self.builder.call(target_fn, opargs, name)
|
||||
else:
|
||||
result = self.builder.call(target_fn, opargs)
|
||||
return result
|
||||
|
||||
def translate_cfg(self, code_obj, cfg, llvm_module=None,
|
||||
monotype=l_pyobj_p, **kwds):
|
||||
'''
|
||||
Generate LLVM code for the given code object and it's control
|
||||
flow graph.
|
||||
|
||||
If no LLVM module is given as an argument, translate_cfg()
|
||||
creates a new module.
|
||||
|
||||
Returns the resulting LLVM module.
|
||||
'''
|
||||
assert inspect.iscode(code_obj)
|
||||
self.obj_type = monotype
|
||||
self.null = lc.Constant.null(self.obj_type)
|
||||
self.code_obj = code_obj
|
||||
self.cfg = cfg
|
||||
self.target_function_name = kwds.get(
|
||||
'target_function_name', 'co_%s_%x' % (code_obj.co_name,
|
||||
id(code_obj)))
|
||||
if llvm_module is None:
|
||||
llvm_module = lc.Module.new('lmod_' + self.target_function_name)
|
||||
self.llvm_module = llvm_module
|
||||
self.visit(cfg.blocks)
|
||||
del self.llvm_module
|
||||
del self.cfg
|
||||
del self.code_obj
|
||||
return llvm_module
|
||||
|
||||
def enter_flow_object(self, flow):
|
||||
'''
|
||||
Set up any state for dealing a new dictionary of basic blocks.
|
||||
'''
|
||||
super(AddressFlowToLLVMPyAPICalls, self).enter_flow_object(flow)
|
||||
self.nargs = opcode_util.get_nargs(self.code_obj)
|
||||
lltype = lc.Type.function(
|
||||
self.obj_type, tuple(self.obj_type for _ in range(self.nargs)))
|
||||
self.llvm_function = self.llvm_module.add_function(
|
||||
lltype, self.target_function_name)
|
||||
self.llvm_blocks = {}
|
||||
for block in self.block_list:
|
||||
if 0 in self.cfg.blocks_reaching[block]:
|
||||
bb = self.llvm_function.append_basic_block(
|
||||
'BLOCK_%d' % (block,))
|
||||
self.llvm_blocks[block] = bb
|
||||
self.symtab = {}
|
||||
self.values = {}
|
||||
|
||||
def exit_flow_object(self, flow):
|
||||
'''
|
||||
Clean up any state created while visiting the given dictionary
|
||||
of basic blocks.
|
||||
'''
|
||||
super(AddressFlowToLLVMPyAPICalls, self).exit_flow_object(flow)
|
||||
del self.symtab
|
||||
del self.llvm_blocks
|
||||
del self.llvm_function
|
||||
|
||||
def generate_co_init(self):
|
||||
'''
|
||||
Initialize the code object's local variables on the stack.
|
||||
'''
|
||||
for name in self.code_obj.co_varnames:
|
||||
ptr = self.builder.alloca(self.obj_type, name + '_p')
|
||||
self.symtab[name] = ptr
|
||||
self.builder.store(self.null, ptr)
|
||||
for arg_index, arg in zip(range(self.nargs), self.llvm_function.args):
|
||||
if arg_index == 0:
|
||||
arg.name = '_globals_%x' % id(self.code_obj)
|
||||
self.globals = arg
|
||||
else:
|
||||
local_index = arg_index - 1
|
||||
name = self.code_obj.co_varnames[local_index]
|
||||
arg.name = name
|
||||
self.call_op_function(
|
||||
None, 'STORE_FAST', arg, self.symtab[name],
|
||||
return_type=lvoid)
|
||||
|
||||
def generate_co_deinit(self, index):
|
||||
for value in self.symtab.values():
|
||||
self.call_op_function(index, 'DELETE_FAST',
|
||||
lc.Constant.int(li1, 0), value,
|
||||
return_type=lvoid)
|
||||
|
||||
def enter_block(self, block):
|
||||
'''
|
||||
Set up state for generating code in a new basic block. If
|
||||
this is the first basic block, initialize the local variables
|
||||
using generate_co_init().
|
||||
'''
|
||||
ret_val = False
|
||||
if block in self.llvm_blocks:
|
||||
self.llvm_block = self.llvm_blocks[block]
|
||||
self.builder = lc.Builder.new(self.llvm_block)
|
||||
if block == 0:
|
||||
self.generate_co_init()
|
||||
ret_val = True
|
||||
return ret_val
|
||||
|
||||
def exit_block(self, block):
|
||||
'''
|
||||
Tear down any state created for code generation in the current
|
||||
basic block. If the basic block isn't already terminated by a
|
||||
control flow statement, assume it branches to the next basic
|
||||
block.
|
||||
'''
|
||||
# XXX Isn't this really a bug in GenericFlowVisitor.visit()?
|
||||
if block in self.llvm_blocks:
|
||||
bb_instrs = self.llvm_block.instructions
|
||||
if ((len(bb_instrs) == 0) or
|
||||
(not bb_instrs[-1].is_terminator)):
|
||||
out_blocks = list(self.cfg.blocks_out[block])
|
||||
assert len(out_blocks) == 1, [str(i) for i in bb_instrs]
|
||||
self.builder.branch(self.llvm_blocks[out_blocks[0]])
|
||||
del self.builder
|
||||
del self.llvm_block
|
||||
|
||||
def _op(self, i, op, arg, *args, **kwds):
|
||||
args = [self.values[stkarg] for stkarg in args]
|
||||
if arg is not None:
|
||||
args.insert(0, lc.Constant.int(lc_int, arg))
|
||||
# XXX Modify the visitor! Should be passing Instr named
|
||||
# tuples here, and not looking up the operation name.
|
||||
result = self.call_op_function(i, self.opnames[op], *args, **kwds)
|
||||
self.values[i] = result
|
||||
return [result]
|
||||
|
||||
def _not_implemented(self, i, op, arg, *args, **kwds):
|
||||
raise NotImplementedError(self.opnames[op])
|
||||
|
||||
op_BINARY_ADD = _op
|
||||
op_BINARY_AND = _op
|
||||
op_BINARY_DIVIDE = _op
|
||||
op_BINARY_FLOOR_DIVIDE = _op
|
||||
op_BINARY_LSHIFT = _op
|
||||
op_BINARY_MODULO = _op
|
||||
op_BINARY_MULTIPLY = _op
|
||||
op_BINARY_OR = _op
|
||||
op_BINARY_POWER = _op
|
||||
op_BINARY_RSHIFT = _op
|
||||
op_BINARY_SUBSCR = _op
|
||||
op_BINARY_SUBTRACT = _op
|
||||
op_BINARY_TRUE_DIVIDE = _op
|
||||
op_BINARY_XOR = _op
|
||||
op_BREAK_LOOP = _op
|
||||
op_BUILD_CLASS = _op
|
||||
op_BUILD_LIST = _op
|
||||
op_BUILD_MAP = _op
|
||||
op_BUILD_SET = _op
|
||||
op_BUILD_SLICE = _op
|
||||
op_BUILD_TUPLE = _op
|
||||
op_CALL_FUNCTION = _op
|
||||
op_CALL_FUNCTION_KW = _op
|
||||
op_CALL_FUNCTION_VAR = _op
|
||||
op_CALL_FUNCTION_VAR_KW = _op
|
||||
op_COMPARE_OP = _op
|
||||
op_CONTINUE_LOOP = _op
|
||||
op_DELETE_ATTR = _op
|
||||
op_DELETE_DEREF = _op
|
||||
|
||||
def op_DELETE_FAST(self, i, op, arg, *args, **kwds):
|
||||
varname = self.code_obj.co_varnames[arg]
|
||||
result = self.call_op_function(i, 'DELETE_FAST',
|
||||
lc.Constant.int(li1, 1),
|
||||
self.symtab[varname], return_type=lvoid)
|
||||
return [result]
|
||||
|
||||
def op_DELETE_GLOBAL(self, i, op, arg, *args, **kwds):
|
||||
varname = get_string_constant(self.llvm_module,
|
||||
self.code_obj.co_names[arg])
|
||||
result = self.call_op_function(i, 'DELETE_GLOBAL', self.globals,
|
||||
varname, return_type=lvoid)
|
||||
self.values[i] = result
|
||||
return [result]
|
||||
|
||||
op_DELETE_NAME = _op
|
||||
op_DELETE_SLICE = _op
|
||||
op_DELETE_SUBSCR = _op
|
||||
op_DUP_TOP = _not_implemented
|
||||
op_DUP_TOPX = _not_implemented
|
||||
op_DUP_TOP_TWO = _not_implemented
|
||||
op_END_FINALLY = _op
|
||||
op_EXEC_STMT = _op
|
||||
op_EXTENDED_ARG = _op
|
||||
op_FOR_ITER = _op
|
||||
op_GET_ITER = _op
|
||||
op_IMPORT_FROM = _op
|
||||
op_IMPORT_NAME = _op
|
||||
op_IMPORT_STAR = _op
|
||||
op_INPLACE_ADD = _op
|
||||
op_INPLACE_AND = _op
|
||||
op_INPLACE_DIVIDE = _op
|
||||
op_INPLACE_FLOOR_DIVIDE = _op
|
||||
op_INPLACE_LSHIFT = _op
|
||||
op_INPLACE_MODULO = _op
|
||||
op_INPLACE_MULTIPLY = _op
|
||||
op_INPLACE_OR = _op
|
||||
op_INPLACE_POWER = _op
|
||||
op_INPLACE_RSHIFT = _op
|
||||
op_INPLACE_SUBTRACT = _op
|
||||
op_INPLACE_TRUE_DIVIDE = _op
|
||||
op_INPLACE_XOR = _op
|
||||
|
||||
def op_JUMP_ABSOLUTE(self, i, op, arg, *args, **kwds):
|
||||
return [self.builder.branch(self.llvm_blocks[arg])]
|
||||
|
||||
def op_JUMP_FORWARD(self, i, op, arg, *args, **kwds):
|
||||
return [self.builder.branch(self.llvm_blocks[i + arg + 3])]
|
||||
|
||||
op_JUMP_IF_FALSE = _not_implemented
|
||||
op_JUMP_IF_FALSE_OR_POP = _not_implemented
|
||||
op_JUMP_IF_TRUE = _not_implemented
|
||||
op_JUMP_IF_TRUE_OR_POP = _not_implemented
|
||||
op_LIST_APPEND = _op
|
||||
op_LOAD_ATTR = _op
|
||||
op_LOAD_BUILD_CLASS = _op
|
||||
op_LOAD_CLOSURE = _op
|
||||
|
||||
def op_LOAD_CONST(self, i, op, arg, *args, **kwds):
|
||||
py_val = self.code_obj.co_consts[arg]
|
||||
if isinstance(py_val, int):
|
||||
# XXX Add bounds check on integer values; use big int
|
||||
# (from string?) constructor if necessary.
|
||||
result = self.call_op_function(i, 'LOAD_CONST_INT',
|
||||
lc.Constant.int(lc_long, py_val))
|
||||
elif isinstance(py_val, float):
|
||||
result = self.call_op_function(i, 'LOAD_CONST_FLOAT',
|
||||
lc.Constant.double(py_val))
|
||||
elif py_val is None:
|
||||
result = self.call_op_function(i, 'LOAD_CONST_NONE')
|
||||
else:
|
||||
raise NotImplementedError('Constant conversion for %r' % (py_val,))
|
||||
self.values[i] = result
|
||||
return [result]
|
||||
|
||||
op_LOAD_DEREF = _op
|
||||
|
||||
def op_LOAD_FAST(self, i, op, arg, *args, **kwds):
|
||||
varname = self.code_obj.co_varnames[arg]
|
||||
args = self.symtab[varname],
|
||||
result = self.call_op_function(i, 'LOAD_FAST', *args)
|
||||
self.values[i] = result
|
||||
return [result]
|
||||
|
||||
def op_LOAD_GLOBAL(self, i, op, arg, *args, **kwds):
|
||||
varname = get_string_constant(self.llvm_module,
|
||||
self.code_obj.co_names[arg])
|
||||
result = self.call_op_function(i, 'LOAD_GLOBAL', self.globals, varname)
|
||||
self.values[i] = result
|
||||
return [result]
|
||||
|
||||
op_LOAD_LOCALS = _op
|
||||
op_LOAD_NAME = _op
|
||||
op_MAKE_CLOSURE = _op
|
||||
op_MAKE_FUNCTION = _op
|
||||
op_MAP_ADD = _op
|
||||
op_NOP = _op
|
||||
op_POP_BLOCK = _op
|
||||
op_POP_EXCEPT = _op
|
||||
|
||||
def _op_cbranch(self, i, op, arg, *args, **kwds):
|
||||
if op in opcode.hasjabs:
|
||||
branch_taken = self.llvm_blocks[arg]
|
||||
else:
|
||||
branch_taken = self.llvm_blocks[i + arg + 3]
|
||||
branch_not_taken = self.llvm_blocks[i + 3]
|
||||
_kwds = kwds.copy()
|
||||
_kwds.update(return_type=li1)
|
||||
test = self._op(i, op, None, *args, **_kwds)[0]
|
||||
return [test, self.builder.cbranch(test, branch_taken,
|
||||
branch_not_taken)]
|
||||
|
||||
op_POP_JUMP_IF_FALSE = _op_cbranch
|
||||
op_POP_JUMP_IF_TRUE = _op_cbranch
|
||||
|
||||
def op_POP_TOP(self, i, op, arg, *args):
|
||||
return [self.call_op_function(i, 'POP_TOP', self.values[args[0]],
|
||||
return_type=lvoid)]
|
||||
|
||||
op_PRINT_EXPR = _op
|
||||
op_PRINT_ITEM = _op
|
||||
op_PRINT_ITEM_TO = _op
|
||||
op_PRINT_NEWLINE = _op
|
||||
op_PRINT_NEWLINE_TO = _op
|
||||
op_RAISE_VARARGS = _op
|
||||
|
||||
def op_RETURN_VALUE(self, i, op, arg, *args):
|
||||
self.generate_co_deinit(i)
|
||||
return [self.builder.ret(self.values[args[0]])]
|
||||
|
||||
op_ROT_FOUR = _op
|
||||
op_ROT_THREE = _op
|
||||
op_ROT_TWO = _op
|
||||
op_SETUP_EXCEPT = _op
|
||||
op_SETUP_FINALLY = _op
|
||||
op_SETUP_LOOP = _op_cbranch
|
||||
op_SETUP_WITH = _op
|
||||
op_SET_ADD = _op
|
||||
op_SLICE = _op
|
||||
op_STOP_CODE = _op
|
||||
op_STORE_ATTR = _op
|
||||
op_STORE_DEREF = _op
|
||||
|
||||
def op_STORE_FAST(self, i, op, arg, *args):
|
||||
src_index, = args
|
||||
src = self.values[src_index]
|
||||
varname = self.code_obj.co_varnames[arg]
|
||||
dest = self.symtab[varname]
|
||||
result = self.call_op_function(i, 'STORE_FAST', src, dest,
|
||||
return_type=lvoid)
|
||||
return [result]
|
||||
|
||||
def op_STORE_GLOBAL(self, i, op, arg, *args):
|
||||
varname = get_string_constant(self.llvm_module,
|
||||
self.code_obj.co_names[arg])
|
||||
result = self.call_op_function(
|
||||
i, 'STORE_GLOBAL', self.values[args[0]], self.globals, varname,
|
||||
return_type=lvoid)
|
||||
return [result]
|
||||
|
||||
op_STORE_LOCALS = _not_implemented
|
||||
op_STORE_MAP = _op
|
||||
op_STORE_NAME = _op
|
||||
op_STORE_SLICE = _op
|
||||
op_STORE_SUBSCR = _op
|
||||
op_UNARY_CONVERT = _op
|
||||
op_UNARY_INVERT = _op
|
||||
op_UNARY_NEGATIVE = _op
|
||||
op_UNARY_NOT = _op
|
||||
op_UNARY_POSITIVE = _op
|
||||
op_UNPACK_EX = _op
|
||||
op_UNPACK_SEQUENCE = _op
|
||||
op_WITH_CLEANUP = _op
|
||||
op_YIELD_VALUE = _op
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Function definition(s)
|
||||
|
||||
def demo_translator(*args, **kwds):
|
||||
def _visit(obj):
|
||||
if inspect.isfunction(obj):
|
||||
obj = opcode_util.get_code_object(obj)
|
||||
print('\n; %s\n; %r' % ('_' * 70, obj))
|
||||
cfg = byte_control.ControlFlowBuilder.build_cfg_from_co(obj)
|
||||
cfg.blocks = addr_flow.AddressFlowBuilder().visit_cfg(cfg)
|
||||
print(AddressFlowToLLVMPyAPICalls(**kwds).translate_cfg(obj, cfg,
|
||||
**kwds))
|
||||
return opcode_util.visit_code_args(_visit, *args)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
demo_translator(*sys.argv[1:], _prefix = '_')
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of af_to_api.py
|
||||
|
|
@ -1,13 +1,45 @@
|
|||
# ______________________________________________________________________
|
||||
from __future__ import absolute_import
|
||||
import opcode
|
||||
from . import opcode_util
|
||||
import pprint
|
||||
# Module imports
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import opcode
|
||||
import pprint
|
||||
import inspect
|
||||
|
||||
from . import opcode_util
|
||||
from .bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin
|
||||
from .control_flow import ControlFlowGraph
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Module data
|
||||
|
||||
# The following opcodes branch based on the control (a.k.a. frame)
|
||||
# stack:
|
||||
RETURN_VALUE, CONTINUE_LOOP, BREAK_LOOP, END_FINALLY, RAISE_VARARGS = (
|
||||
opcode.opmap[opname] for opname in (
|
||||
'RETURN_VALUE', 'CONTINUE_LOOP', 'BREAK_LOOP', 'END_FINALLY',
|
||||
'RAISE_VARARGS'))
|
||||
|
||||
# The following opcodes push a new frame on the control stack:
|
||||
SETUP_EXCEPT, SETUP_FINALLY, SETUP_LOOP, SETUP_WITH = (
|
||||
opcode.opmap.get(opname, None) for opname in (
|
||||
'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_LOOP', 'SETUP_WITH'))
|
||||
|
||||
WHY_NOT = 1
|
||||
WHY_EXCEPTION = WHY_NOT << 1
|
||||
WHY_RERAISE = WHY_EXCEPTION << 1 # We don't worry about this code
|
||||
# during CFA, since its primary use
|
||||
# is to log traceback information;
|
||||
# WHY_RERAISE's bytecode control flow
|
||||
# is the same as WHY_EXCEPTION.
|
||||
WHY_RETURN = WHY_RERAISE << 1
|
||||
WHY_BREAK = WHY_RETURN << 1
|
||||
WHY_CONTINUE = WHY_BREAK << 1
|
||||
WHY_YIELD = WHY_CONTINUE << 1
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definition(s)
|
||||
|
||||
class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
||||
'''Visitor responsible for traversing a bytecode basic block map and
|
||||
|
|
@ -17,10 +49,10 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
|||
which is used by later transformers for dataflow analysis.
|
||||
'''
|
||||
def visit (self, flow, nargs = 0, *args, **kws):
|
||||
'''Given a bytecode flow, and an optional number of arguments,
|
||||
return a :py:class:`llpython.control_flow.ControlFlowGraph`
|
||||
instance describing the full control flow of the bytecode
|
||||
flow.'''
|
||||
'''Given a map of bytecode basic blocks, and an optional
|
||||
number of arguments, return a
|
||||
:py:class:`llpython.control_flow.ControlFlowGraph` instance
|
||||
describing the full control flow of the bytecode flow.'''
|
||||
self.nargs = nargs
|
||||
ret_val = super(ControlFlowBuilder, self).visit(flow, *args, **kws)
|
||||
del self.nargs
|
||||
|
|
@ -32,17 +64,26 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
|||
self.block_list = list(blocks.keys())
|
||||
self.block_list.sort()
|
||||
self.cfg = ControlFlowGraph()
|
||||
self.loop_stack = []
|
||||
self.control_stack = []
|
||||
self.continue_targets = {} # Map from SETUP_LOOP addresses to
|
||||
# start of loop addresses, based on
|
||||
# observed CONTINUE_LOOP opcodes.
|
||||
self.break_targets = set() # Set of SETUP_LOOP address that
|
||||
# have at least one observed
|
||||
# BREAK_LOOP opcode corresponding
|
||||
# to them.
|
||||
for block in self.block_list:
|
||||
self.cfg.add_block(block, blocks[block])
|
||||
|
||||
def exit_blocks (self, blocks):
|
||||
super(ControlFlowBuilder, self).exit_blocks(blocks)
|
||||
assert self.blocks == blocks
|
||||
self.cfg.unlink_unreachables()
|
||||
self.cfg.compute_dataflow()
|
||||
self.cfg.update_for_ssa()
|
||||
ret_val = self.cfg
|
||||
del self.loop_stack
|
||||
del self.continue_targets
|
||||
del self.control_stack
|
||||
del self.cfg
|
||||
del self.block_list
|
||||
del self.blocks
|
||||
|
|
@ -59,23 +100,91 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
|||
def _get_next_block (self, block):
|
||||
return self.block_list[self.block_list.index(block) + 1]
|
||||
|
||||
def _generate_handler_edge (self, block, i, op, arg, why):
|
||||
"""Given a reason (corresponding to the why code in
|
||||
Python/ceval.c), interrupt control flow, possibly using the
|
||||
control flow (a.k.a. frame) stack to calculate the next
|
||||
target.
|
||||
|
||||
Returns True if an edge was added to the CFG, False otherwise.
|
||||
Based on the opcode the return result may mean different
|
||||
things (for example: if why == WHY_RETURN, then a False return
|
||||
result means the function returned, and no edge was
|
||||
generated)."""
|
||||
ret_val = False
|
||||
if len(self.control_stack) > 0:
|
||||
handlers = set((SETUP_FINALLY, SETUP_WITH))
|
||||
if why == WHY_EXCEPTION:
|
||||
handlers.add(SETUP_EXCEPT)
|
||||
reversed_stack = reversed(self.control_stack)
|
||||
target = None
|
||||
for handler_i, handler_op, handler_arg in reversed_stack:
|
||||
if handler_op in handlers:
|
||||
target = handler_i + handler_arg + 3
|
||||
elif handler_op == SETUP_LOOP:
|
||||
if why == WHY_CONTINUE:
|
||||
# Only generate a WHY_CONTINUE edge if a continue
|
||||
# statement has been observed for this loop.
|
||||
if handler_i not in self.continue_targets:
|
||||
break
|
||||
elif op == CONTINUE_LOOP:
|
||||
target = arg
|
||||
assert target == self.continue_targets[handler_i]
|
||||
else:
|
||||
target = self.continue_targets[handler_i]
|
||||
elif why == WHY_BREAK:
|
||||
# Only generate a WHY_BREAK edge if a break
|
||||
# statement has been observed for this loop.
|
||||
if handler_i not in self.break_targets:
|
||||
break
|
||||
else:
|
||||
target = handler_i + handler_arg + 3
|
||||
if target is not None:
|
||||
self.cfg.add_edge(block, target)
|
||||
ret_val = True
|
||||
break
|
||||
return ret_val
|
||||
|
||||
def exit_block (self, block):
|
||||
assert block == self.block
|
||||
del self.block
|
||||
i, op, arg = self.blocks[block][-1]
|
||||
opname = opcode.opname[op]
|
||||
if op in opcode.hasjabs:
|
||||
goto_next = False
|
||||
if op == RETURN_VALUE:
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_RETURN)
|
||||
elif op == CONTINUE_LOOP:
|
||||
branched = self._generate_handler_edge(block, i, op, arg,
|
||||
WHY_CONTINUE)
|
||||
assert branched, ("Attempted to continue outside of loop %r" %
|
||||
(self.blocks[block][-1],))
|
||||
elif op == BREAK_LOOP:
|
||||
branched = self._generate_handler_edge(block, i, op, arg,
|
||||
WHY_BREAK)
|
||||
assert branched, ("Attempted to break outside of loop %r" %
|
||||
(self.blocks[block][-1],))
|
||||
elif op == RAISE_VARARGS:
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_EXCEPTION)
|
||||
elif op == END_FINALLY:
|
||||
# The following does a lot of redundant traversal of the
|
||||
# simulated frame stack, but it works, and keeps a lot of
|
||||
# special case logic out of _generate_handler_edge().
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_EXCEPTION)
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_RETURN)
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_BREAK)
|
||||
self._generate_handler_edge(block, i, op, arg, WHY_CONTINUE)
|
||||
goto_next = True # why == WHY_NOT
|
||||
elif op in opcode.hasjabs:
|
||||
self.cfg.add_edge(block, arg)
|
||||
elif op in opcode.hasjrel:
|
||||
self.cfg.add_edge(block, i + arg + 3)
|
||||
elif opname == 'BREAK_LOOP':
|
||||
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||
self.cfg.add_edge(block, loop_i + loop_arg + 3)
|
||||
elif opname != 'RETURN_VALUE':
|
||||
self.cfg.add_edge(block, self._get_next_block(block))
|
||||
if op in opcode_util.hascbranch:
|
||||
else:
|
||||
goto_next = True
|
||||
if op in opcode_util.hascbranch or goto_next:
|
||||
self.cfg.add_edge(block, self._get_next_block(block))
|
||||
|
||||
# ____________________________________________________________
|
||||
# LOAD/STORE_FAST
|
||||
|
||||
def op_LOAD_FAST (self, i, op, arg, *args, **kws):
|
||||
self.cfg.blocks_reads[self.block].add(arg)
|
||||
return super(ControlFlowBuilder, self).op_LOAD_FAST(i, op, arg, *args,
|
||||
|
|
@ -86,34 +195,97 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
|||
return super(ControlFlowBuilder, self).op_STORE_FAST(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
# ____________________________________________________________
|
||||
# *_LOOP: Special loop control flow.
|
||||
|
||||
def _get_current_loop (self):
|
||||
for handler in reversed(self.control_stack):
|
||||
if handler[1] == SETUP_LOOP:
|
||||
return handler
|
||||
return None, None, None
|
||||
|
||||
def op_BREAK_LOOP (self, i, op, arg, *args, **kws):
|
||||
handler_i, _, _ = self._get_current_loop()
|
||||
assert handler_i is not None
|
||||
self.break_targets.add(handler_i)
|
||||
|
||||
def op_CONTINUE_LOOP (self, i, op, arg, *args, **kws):
|
||||
"""
|
||||
CONTINUE_LOOP has to be handled differently than BREAK_LOOP,
|
||||
since its argument specifies where the start of the loop is
|
||||
(in the case of for-loops, FOR_ITER defines the true start of
|
||||
the loop, instead of SETUP_LOOP.)
|
||||
"""
|
||||
handler_i, _, _ = self._get_current_loop()
|
||||
assert handler_i is not None
|
||||
if handler_i in self.continue_targets:
|
||||
assert arg == self.continue_targets[handler_i]
|
||||
else:
|
||||
self.continue_targets[handler_i] = arg
|
||||
|
||||
# ____________________________________________________________
|
||||
# POP_BLOCK
|
||||
|
||||
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||
self.control_stack.pop()
|
||||
return super(ControlFlowBuilder, self).op_POP_BLOCK(i, op, arg, *args,
|
||||
**kws)
|
||||
# ____________________________________________________________
|
||||
# SETUP_*
|
||||
|
||||
def op_SETUP_EXCEPT (self, i, op, arg, *args, **kws):
|
||||
self.control_stack.append((i, op, arg))
|
||||
return super(ControlFlowBuilder, self).op_SETUP_EXCEPT(i, op, arg,
|
||||
*args, **kws)
|
||||
|
||||
def op_SETUP_FINALLY (self, i, op, arg, *args, **kws):
|
||||
self.control_stack.append((i, op, arg))
|
||||
return super(ControlFlowBuilder, self).op_SETUP_FINALLY(i, op, arg,
|
||||
*args, **kws)
|
||||
|
||||
def op_SETUP_LOOP (self, i, op, arg, *args, **kws):
|
||||
self.loop_stack.append((i, op, arg))
|
||||
self.control_stack.append((i, op, arg))
|
||||
return super(ControlFlowBuilder, self).op_SETUP_LOOP(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||
self.loop_stack.pop()
|
||||
return super(ControlFlowBuilder, self).op_POP_BLOCK(i, op, arg, *args,
|
||||
def op_SETUP_WITH (self, i, op, arg, *args, **kws):
|
||||
self.control_stack.append((i, op, arg))
|
||||
return super(ControlFlowBuilder, self).op_SETUP_WITH(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
# ____________________________________________________________
|
||||
# Class convenience methods
|
||||
|
||||
@classmethod
|
||||
def build_cfg_from_co(cls, co_obj):
|
||||
return cls().visit(opcode_util.build_basic_blocks(co_obj),
|
||||
co_obj.co_argcount)
|
||||
|
||||
@classmethod
|
||||
def build_cfg(cls, func):
|
||||
co_obj = opcode_util.get_code_object(func)
|
||||
return cls.build_cfg_from_co(co_obj)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def build_cfg (func):
|
||||
'''Given a Python function, create a bytecode flow, visit the flow
|
||||
object, and return a control flow graph.'''
|
||||
co_obj = opcode_util.get_code_object(func)
|
||||
return ControlFlowBuilder().visit(opcode_util.build_basic_blocks(co_obj),
|
||||
co_obj.co_argcount)
|
||||
return ControlFlowBuilder.build_cfg(func)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
||||
def main (*args, **kws):
|
||||
from tests import llfuncs
|
||||
if not args:
|
||||
args = ('doslice',)
|
||||
for arg in args:
|
||||
build_cfg(getattr(llfuncs, arg)).pprint()
|
||||
def _visit(obj):
|
||||
print("_" * 70)
|
||||
print(obj)
|
||||
if inspect.isfunction(obj):
|
||||
cfg = build_cfg(obj)
|
||||
else:
|
||||
cfg = ControlFlowBuilder.build_cfg_from_co(obj)
|
||||
cfg.pprint()
|
||||
return opcode_util.visit_code_args(_visit, *args, **kws)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
# ______________________________________________________________________
|
||||
|
||||
from __future__ import absolute_import
|
||||
import dis
|
||||
import opcode
|
||||
import pprint
|
||||
import inspect
|
||||
from collections import namedtuple
|
||||
|
||||
from .bytecode_visitor import BasicBlockVisitor
|
||||
from . import opcode_util
|
||||
from . import byte_control
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definition(s)
|
||||
|
||||
Instr = namedtuple('Instr', ('address', 'opcode', 'opname', 'oparg',
|
||||
'stackargs'))
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
|
|
@ -41,7 +52,7 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
del self.stack[-pops:]
|
||||
else:
|
||||
stk_args = []
|
||||
ret_val = (i, op, opname, arg, stk_args)
|
||||
ret_val = Instr(i, op, opname, arg, stk_args)
|
||||
if pushes:
|
||||
self.stack.append(ret_val)
|
||||
if appends:
|
||||
|
|
@ -63,13 +74,13 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
labels.sort()
|
||||
self.blocks = dict((index, [])
|
||||
for index in labels)
|
||||
self.loop_stack = []
|
||||
self.control_stack = []
|
||||
self.stacks = {}
|
||||
|
||||
def exit_blocks (self, blocks):
|
||||
ret_val = self.blocks
|
||||
del self.stacks
|
||||
del self.loop_stack
|
||||
del self.control_stack
|
||||
del self.blocks
|
||||
return ret_val
|
||||
|
||||
|
|
@ -113,11 +124,19 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
op_BINARY_XOR = _op
|
||||
|
||||
def op_BREAK_LOOP (self, i, op, arg):
|
||||
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||
if self.opnames[op] == 'BREAK_LOOP':
|
||||
# Break target was already computed in control flow analysis;
|
||||
# reuse that, replacing the opcode argument.
|
||||
blocks_out = tuple(self.cfg.blocks_out[self.block_no])
|
||||
assert len(blocks_out) == 1
|
||||
assert arg is None
|
||||
return self._op(i, op, loop_i + loop_arg + 3)
|
||||
arg = blocks_out[0]
|
||||
# else: Continue target is already in the argument. Note that
|
||||
# the argument might not be the same as CFG destination block,
|
||||
# since we might have a finally block to visit first.
|
||||
return self._op(i, op, arg)
|
||||
|
||||
#op_BUILD_CLASS = _op
|
||||
op_BUILD_CLASS = _op
|
||||
op_BUILD_LIST = _op
|
||||
op_BUILD_MAP = _op
|
||||
op_BUILD_SLICE = _op
|
||||
|
|
@ -127,7 +146,7 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
op_CALL_FUNCTION_VAR = _op
|
||||
op_CALL_FUNCTION_VAR_KW = _op
|
||||
op_COMPARE_OP = _op
|
||||
#op_CONTINUE_LOOP = _op
|
||||
op_CONTINUE_LOOP = op_BREAK_LOOP
|
||||
op_DELETE_ATTR = _op
|
||||
op_DELETE_FAST = _op
|
||||
op_DELETE_GLOBAL = _op
|
||||
|
|
@ -141,12 +160,29 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
def op_DUP_TOPX (self, i, op, arg):
|
||||
self.stack += self.stack[-arg:]
|
||||
|
||||
#op_END_FINALLY = _op
|
||||
#op_DUP_TOP_TWO = _not_implemented
|
||||
|
||||
# See the note regarding END_FINALLY in the definition of
|
||||
# opcope_util.OPCODE_MAP.
|
||||
op_END_FINALLY = _op
|
||||
|
||||
op_EXEC_STMT = _op
|
||||
#op_EXTENDED_ARG = _op
|
||||
|
||||
def op_EXTENDED_ARG (self, i, op, arg):
|
||||
raise ValueError("Unexpected EXTENDED_ARG opcode at index %d (should "
|
||||
"be removed by itercode)." % i)
|
||||
|
||||
op_FOR_ITER = _op
|
||||
op_GET_ITER = _op
|
||||
op_IMPORT_FROM = _op
|
||||
|
||||
def op_IMPORT_FROM (self, i, op, arg):
|
||||
# References top of stack without popping, so we can't use the
|
||||
# generic machinery.
|
||||
opname = self.opmap[op][0]
|
||||
ret_val = Instr(i, op, opname, arg, [self.stack[-1]])
|
||||
self.stack.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
op_IMPORT_NAME = _op
|
||||
op_IMPORT_STAR = _op
|
||||
op_INPLACE_ADD = _op
|
||||
|
|
@ -166,15 +202,26 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
op_JUMP_FORWARD = _op
|
||||
|
||||
def op_JUMP_IF_FALSE (self, i, op, arg):
|
||||
opname, _, _, _ = self.opmap[op]
|
||||
ret_val = (i, op, opname, arg, [self.stack[-1]])
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [self.stack[-1]])
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
#op_JUMP_IF_FALSE_OR_POP = _not_implemented
|
||||
op_JUMP_IF_TRUE = op_JUMP_IF_FALSE
|
||||
op_LIST_APPEND = _op
|
||||
#op_JUMP_IF_TRUE_OR_POP = op_JUMP_IF_FALSE_OR_POP
|
||||
|
||||
def op_LIST_APPEND (self, i, op, arg):
|
||||
'''This method is used for both LIST_APPEND, and SET_ADD
|
||||
opcodes.'''
|
||||
elem = self.stack.pop()
|
||||
container = self.stack[-arg]
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [container, elem])
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
op_LOAD_ATTR = _op
|
||||
op_LOAD_CLOSURE = _op
|
||||
#op_LOAD_BUILD_CLASS = _not_implemented
|
||||
#op_LOAD_CLOSURE = _not_implemented
|
||||
op_LOAD_CONST = _op
|
||||
op_LOAD_DEREF = _op
|
||||
op_LOAD_FAST = _op
|
||||
|
|
@ -186,8 +233,9 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
op_NOP = _op
|
||||
|
||||
def op_POP_BLOCK (self, i, op, arg):
|
||||
self.loop_stack.pop()
|
||||
return self._op(i, op, arg)
|
||||
_, _, _, target_stack_size = self.control_stack.pop()
|
||||
pops = len(self.stack) - target_stack_size
|
||||
return self._visit_op(i, op, arg, self.opnames[op], pops, 0, 1)
|
||||
|
||||
op_POP_JUMP_IF_FALSE = _op
|
||||
op_POP_JUMP_IF_TRUE = _op
|
||||
|
|
@ -210,19 +258,38 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
def op_ROT_TWO (self, i, op, arg):
|
||||
self.stack[-2:] = (self.stack[-1], self.stack[-2])
|
||||
|
||||
#op_SETUP_EXCEPT = _op
|
||||
#op_SETUP_FINALLY = _op
|
||||
def _op_SETUP (self, i, op, arg):
|
||||
self.control_stack.append((i, op, arg, len(self.stack)))
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [])
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
def op_SETUP_LOOP (self, i, op, arg):
|
||||
self.loop_stack.append((i, op, arg))
|
||||
self.block.append((i, op, self.opnames[op], arg, []))
|
||||
op_SETUP_EXCEPT = _op_SETUP
|
||||
op_SETUP_FINALLY = _op_SETUP
|
||||
op_SETUP_LOOP = _op_SETUP
|
||||
|
||||
def op_SETUP_WITH (self, i, op, arg):
|
||||
assert arg is not None
|
||||
# Care has to be taken here. SETUP_WITH pushes two things on
|
||||
# the value stack (the exit ), and once on the handler frame.
|
||||
ctx = self.stack.pop()
|
||||
# We signal that the value is an exit handler by setting arg to None
|
||||
exit_handler = Instr(i, op, self.opnames[op], None, [ctx])
|
||||
self.stack.append(exit_handler)
|
||||
ret_val = Instr(i, op, self.opnames[op], arg, [ctx])
|
||||
self.control_stack.append((i, op, arg, len(self.stack)))
|
||||
self.stack.append(ret_val)
|
||||
self.block.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
op_SET_ADD = op_LIST_APPEND
|
||||
op_SLICE = _op
|
||||
#op_STOP_CODE = _op
|
||||
#op_STOP_CODE = _not_implemented
|
||||
op_STORE_ATTR = _op
|
||||
op_STORE_DEREF = _op
|
||||
op_STORE_FAST = _op
|
||||
op_STORE_GLOBAL = _op
|
||||
#op_STORE_LOCALS = _not_implemented
|
||||
op_STORE_MAP = _op
|
||||
op_STORE_NAME = _op
|
||||
op_STORE_SLICE = _op
|
||||
|
|
@ -232,29 +299,68 @@ class BytecodeFlowBuilder (BasicBlockVisitor):
|
|||
op_UNARY_NEGATIVE = _op
|
||||
op_UNARY_NOT = _op
|
||||
op_UNARY_POSITIVE = _op
|
||||
op_UNPACK_SEQUENCE = _op
|
||||
#op_WITH_CLEANUP = _op
|
||||
#op_UNPACK_EX = _not_implemented
|
||||
|
||||
def op_UNPACK_SEQUENCE (self, i, op, arg):
|
||||
seq = self.stack.pop()
|
||||
opname = self.opnames[op]
|
||||
while arg > 0:
|
||||
arg -= 1
|
||||
ret_val = Instr(i, op, opname, arg, [seq])
|
||||
self.stack.append(ret_val)
|
||||
return ret_val
|
||||
|
||||
#op_WITH_CLEANUP = _not_implemented
|
||||
op_YIELD_VALUE = _op
|
||||
|
||||
@classmethod
|
||||
def build_flow(cls, func):
|
||||
'''Given a Python function, return a flow representation of that
|
||||
function.'''
|
||||
cfg = byte_control.build_cfg(func)
|
||||
return cls().visit_cfg(cfg)
|
||||
|
||||
@classmethod
|
||||
def build_flow_from_co(cls, code_obj):
|
||||
'''Given a Python code object, return a flow representation of
|
||||
that code object.'''
|
||||
cfg = byte_control.ControlFlowBuilder.build_cfg_from_co(code_obj)
|
||||
return cls().visit_cfg(cfg)
|
||||
|
||||
@classmethod
|
||||
def build_flows_from_co(cls, root_code_obj):
|
||||
'''Given a Python code object, return a map from that code
|
||||
object and any nested code objects to flow representations of
|
||||
those code objects.'''
|
||||
return dict((co, cls.build_flow_from_co(co))
|
||||
for co in opcode_util.itercodeobjs(root_code_obj))
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Function definition(s)
|
||||
|
||||
def build_flow(func):
|
||||
'''Kept for backwards compatibility in downstream modules. Use
|
||||
BytecodeFlowBuilder.build_flow() instead.'''
|
||||
return BytecodeFlowBuilder.build_flow(func)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def build_flow (func):
|
||||
'''Given a Python function, return a bytecode flow tree for that
|
||||
function.'''
|
||||
import byte_control
|
||||
cfg = byte_control.build_cfg(func)
|
||||
return BytecodeFlowBuilder().visit_cfg(cfg)
|
||||
def demo_flow_builder(builder_cls, *args):
|
||||
def _visit(obj):
|
||||
print("_" * 70)
|
||||
print(obj)
|
||||
if inspect.isfunction(obj):
|
||||
flow = builder_cls.build_flow(obj)
|
||||
else:
|
||||
flow = builder_cls.build_flow_from_co(obj)
|
||||
pprint.pprint(flow)
|
||||
return opcode_util.visit_code_args(_visit, *args)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
||||
def main (*args):
|
||||
import pprint
|
||||
from tests import llfuncs
|
||||
if not args:
|
||||
args = ('doslice',)
|
||||
for arg in args:
|
||||
pprint.pprint(build_flow(getattr(llfuncs, arg)))
|
||||
def main(*args):
|
||||
return demo_flow_builder(BytecodeFlowBuilder, *args)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# ______________________________________________________________________
|
||||
'''Defines a bytecode based LLVM translator for llpython code.
|
||||
'''
|
||||
|
|
@ -8,6 +9,7 @@ import opcode
|
|||
import types
|
||||
import logging
|
||||
|
||||
from llvm import LLVMException
|
||||
import llvm.core as lc
|
||||
|
||||
from . import opcode_util
|
||||
|
|
@ -38,6 +40,13 @@ _compare_mapping_sint = {'>':lc.ICMP_SGT,
|
|||
'<=':lc.ICMP_SLE,
|
||||
'!=':lc.ICMP_NE}
|
||||
|
||||
_compare_mapping_ptr = {'>':lc.ICMP_UGT,
|
||||
'<':lc.ICMP_ULT,
|
||||
'==':lc.ICMP_EQ,
|
||||
'>=':lc.ICMP_UGE,
|
||||
'<=':lc.ICMP_ULE,
|
||||
'!=':lc.ICMP_NE}
|
||||
|
||||
# XXX Stolen from numba.llvm_types:
|
||||
|
||||
class LLVMCaster (object):
|
||||
|
|
@ -118,6 +127,37 @@ class LLVMCaster (object):
|
|||
raise NotImplementedError(lkind1, lkind2)
|
||||
return ret_val
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def _convert_const(py_val):
|
||||
'''Convert a constant Python value into a comparable LLVM
|
||||
constant. Preserves Python values and data structures such as
|
||||
lists, tuples, and None.
|
||||
'''
|
||||
if isinstance(py_val, list):
|
||||
ret_val = [_convert_const(child) for child in py_val]
|
||||
elif isinstance(py_val, tuple):
|
||||
ret_val = tuple(_convert_const(child) for child in py_val)
|
||||
elif isinstance(py_val, int):
|
||||
ret_val = lc.Constant.int(bytetype.lc_int, py_val)
|
||||
elif isinstance(py_val, float):
|
||||
ret_val = lc.Constant.double(py_val)
|
||||
elif py_val == None:
|
||||
ret_val = py_val
|
||||
else:
|
||||
raise NotImplementedError('Constant conversion for %r' % (py_val,))
|
||||
return ret_val
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def get_or_insert_global_variable(llvm_module, variable_ty, variable_name):
|
||||
try:
|
||||
ret_val = llvm_module.get_global_variable_named(variable_name)
|
||||
# XXX Check LLVM value is of correct type?!
|
||||
except LLVMException:
|
||||
ret_val = llvm_module.add_global_variable(variable_ty, variable_name)
|
||||
return ret_val
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definitions
|
||||
|
||||
|
|
@ -196,6 +236,10 @@ class LLVMTranslator (BytecodeFlowVisitor):
|
|||
if self.llvm_function is None:
|
||||
self.llvm_function = self.llvm_module.add_function(
|
||||
self.llvm_type, self.target_function_name)
|
||||
if self.llvm_function.args and not self.llvm_function.args[0].name:
|
||||
for index in range(len(self.llvm_function.args)):
|
||||
argname = self.code_obj.co_varnames[index]
|
||||
self.llvm_function.args[index].name = argname
|
||||
self.llvm_blocks = {}
|
||||
self.llvm_definitions = {}
|
||||
self.pending_phis = {}
|
||||
|
|
@ -371,11 +415,12 @@ class LLVMTranslator (BytecodeFlowVisitor):
|
|||
raise NotImplementedError("LLVMTranslator.op_BUILD_SLICE")
|
||||
|
||||
def op_BUILD_TUPLE (self, i, op, arg, *args, **kws):
|
||||
return args
|
||||
return [args]
|
||||
|
||||
def op_CALL_FUNCTION (self, i, op, arg, *args, **kws):
|
||||
fn = args[0]
|
||||
args = args[1:]
|
||||
argcount = len(args)
|
||||
fn_name = getattr(fn, '__name__', None)
|
||||
if isinstance(fn, (types.FunctionType, types.MethodType)):
|
||||
ret_val = [fn(self.builder, *args)]
|
||||
|
|
@ -415,6 +460,9 @@ class LLVMTranslator (BytecodeFlowVisitor):
|
|||
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||
ret_val = [self.builder.fcmp(_compare_mapping_float[cmp_kind],
|
||||
arg1, arg2)]
|
||||
elif isinstance(arg1.type, lc.PointerType):
|
||||
ret_val = [self.builder.icmp(_compare_mapping_ptr[cmp_kind],
|
||||
arg1, arg2)]
|
||||
else:
|
||||
raise NotImplementedError('Comparison of type %r' % (arg1.type,))
|
||||
return ret_val
|
||||
|
|
@ -474,17 +522,7 @@ class LLVMTranslator (BytecodeFlowVisitor):
|
|||
raise NotImplementedError("LLVMTranslator.op_LOAD_ATTR")
|
||||
|
||||
def op_LOAD_CONST (self, i, op, arg, *args, **kws):
|
||||
py_val = self.code_obj.co_consts[arg]
|
||||
if isinstance(py_val, int):
|
||||
ret_val = [lc.Constant.int(bytetype.lc_int, py_val)]
|
||||
elif isinstance(py_val, float):
|
||||
ret_val = [lc.Constant.double(py_val)]
|
||||
elif py_val == None:
|
||||
ret_val = [None]
|
||||
else:
|
||||
raise NotImplementedError('Constant converstion for %r' %
|
||||
(py_val,))
|
||||
return ret_val
|
||||
return [_convert_const(self.code_obj.co_consts[arg])]
|
||||
|
||||
def op_LOAD_DEREF (self, i, op, arg, *args, **kws):
|
||||
name = self.code_obj.co_freevars[arg]
|
||||
|
|
@ -540,10 +578,23 @@ class LLVMTranslator (BytecodeFlowVisitor):
|
|||
return [self.builder.store(store_val, dest_addr)]
|
||||
|
||||
def op_UNARY_CONVERT (self, i, op, arg, *args, **kws):
|
||||
raise NotImplementedError("LLVMTranslator.op_UNARY_CONVERT")
|
||||
var_ty = args[0]
|
||||
if isinstance(var_ty, lc.Type):
|
||||
var_name = var_ty.__name__
|
||||
ret_val = [get_or_insert_global_variable(self.llvm_module, var_ty,
|
||||
var_name)]
|
||||
else:
|
||||
raise NotImplementedError("LLVMTranslator.op_UNARY_CONVERT: %r" %
|
||||
(var_ty,))
|
||||
return ret_val
|
||||
|
||||
def op_UNARY_INVERT (self, i, op, arg, *args, **kws):
|
||||
raise NotImplementedError("LLVMTranslator.op_UNARY_INVERT")
|
||||
arg1, = args
|
||||
if isinstance(arg1.type, lc.IntegerType):
|
||||
ret_val = [self.builder.xor(arg1, lc.Constant.int(arg1.type, -1))]
|
||||
else:
|
||||
raise NotImplementedError('Invert for type %r' % (arg1.type,))
|
||||
return ret_val
|
||||
|
||||
def op_UNARY_NEGATIVE (self, i, op, arg, *args, **kws):
|
||||
raise NotImplementedError("LLVMTranslator.op_UNARY_NEGATIVE")
|
||||
|
|
@ -590,7 +641,7 @@ def llpython_into (llvm_function, **kws):
|
|||
# Main (self-test) routine
|
||||
|
||||
def main (*args):
|
||||
from tests import llfuncs, llfunctys
|
||||
from .tests import llfuncs, llfunctys
|
||||
if not args:
|
||||
args = ('doslice',)
|
||||
elif 'all' in args:
|
||||
|
|
|
|||
|
|
@ -170,8 +170,8 @@ class BasicBlockVisitor (BytecodeVisitor):
|
|||
block_indices.sort()
|
||||
for block_index in block_indices:
|
||||
self.enter_block(block_index)
|
||||
for i, op, arg in blocks[block_index]:
|
||||
self.visit_op(i, op, arg)
|
||||
for op_tuple in blocks[block_index]:
|
||||
self.visit_op(*op_tuple)
|
||||
self.exit_block(block_index)
|
||||
return self.exit_blocks(blocks)
|
||||
|
||||
|
|
@ -189,7 +189,7 @@ class BasicBlockVisitor (BytecodeVisitor):
|
|||
|
||||
# ______________________________________________________________________
|
||||
|
||||
class BytecodeFlowVisitor (BytecodeVisitor):
|
||||
class GenericFlowVisitor (BytecodeVisitor):
|
||||
def visit (self, flow):
|
||||
self.block_list = list(flow.keys())
|
||||
self.block_list.sort()
|
||||
|
|
@ -208,15 +208,6 @@ class BytecodeFlowVisitor (BytecodeVisitor):
|
|||
del self.block_list
|
||||
return self.exit_flow_object(flow)
|
||||
|
||||
def visit_op (self, i, op, arg, *args, **kws):
|
||||
new_args = []
|
||||
for child_i, child_op, _, child_arg, child_args in args:
|
||||
new_args.extend(self.visit_op(child_i, child_op, child_arg,
|
||||
*child_args))
|
||||
ret_val = super(BytecodeFlowVisitor, self).visit_op(i, op, arg,
|
||||
*new_args)
|
||||
return ret_val
|
||||
|
||||
def enter_flow_object (self, flow):
|
||||
self.new_flow = {}
|
||||
|
||||
|
|
@ -233,6 +224,18 @@ class BytecodeFlowVisitor (BytecodeVisitor):
|
|||
|
||||
# ______________________________________________________________________
|
||||
|
||||
class BytecodeFlowVisitor (GenericFlowVisitor):
|
||||
def visit_op (self, i, op, arg, *args, **kws):
|
||||
new_args = []
|
||||
for child_i, child_op, _, child_arg, child_args in args:
|
||||
new_args.extend(self.visit_op(child_i, child_op, child_arg,
|
||||
*child_args))
|
||||
ret_val = super(BytecodeFlowVisitor, self).visit_op(i, op, arg,
|
||||
*new_args)
|
||||
return ret_val
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
class BenignBytecodeVisitorMixin (object):
|
||||
def _do_nothing (self, i, op, arg, *args, **kws):
|
||||
return [(i, op, self.opnames[op], arg, args)]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# ______________________________________________________________________
|
||||
|
||||
import ctypes
|
||||
|
|
@ -22,9 +23,10 @@ li8_ptr = lc.Type.pointer(li8)
|
|||
lc_int = lc.Type.int(ctypes.sizeof(ctypes.c_int) * 8)
|
||||
lc_long = lc.Type.int(ctypes.sizeof(ctypes.c_long) * 8)
|
||||
|
||||
l_pyobject_head = [lc_size_t, lc.Type.pointer(li32)]
|
||||
l_pyobject_head = [lc_size_t, li8_ptr]
|
||||
l_pyobject_head_struct = lc.Type.struct(l_pyobject_head)
|
||||
l_pyobj_p = l_pyobject_head_struct_p = lc.Type.pointer(l_pyobject_head_struct)
|
||||
l_pyobj_pp = lc.Type.pointer(l_pyobj_p)
|
||||
l_pyfunc = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
|
||||
strlen = lc.Type.function(lc_size_t, (li8_ptr,))
|
||||
|
|
@ -33,10 +35,38 @@ strndup = lc.Type.function(li8_ptr, (li8_ptr, lc_size_t))
|
|||
malloc = lc.Type.function(li8_ptr, (lc_size_t,))
|
||||
free = lc.Type.function(lvoid, (li8_ptr,))
|
||||
|
||||
Py_BuildValue = lc.Type.function(l_pyobj_p, [li8_ptr], True)
|
||||
PyArg_ParseTuple = lc.Type.function(lc_int, [l_pyobj_p, li8_ptr], True)
|
||||
PyBool_FromLong = lc.Type.function(l_pyobj_p, [lc_long])
|
||||
PyErr_GivenExceptionMatches = lc.Type.function(lc_int, (l_pyobj_p, l_pyobj_p))
|
||||
PyEval_SaveThread = lc.Type.function(li8_ptr, [])
|
||||
PyEval_RestoreThread = lc.Type.function(lc.Type.void(), [li8_ptr])
|
||||
PyEval_RestoreThread = lc.Type.function(lvoid, [li8_ptr])
|
||||
PyInt_AsLong = lc.Type.function(lc_long, [l_pyobj_p])
|
||||
PyInt_FromLong = lc.Type.function(l_pyobj_p, [lc_long])
|
||||
PyNumber_Add = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_Divide = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_Multiply = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_Remainder = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_Subtract = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_TrueDivide = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceAdd = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceDivide = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceMultiply = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceRemainder = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceSubtract = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
PyNumber_InPlaceTrueDivide = lc.Type.function(l_pyobj_p, (l_pyobj_p,
|
||||
l_pyobj_p))
|
||||
PyObject_IsTrue = lc.Type.function(lc_int, [l_pyobj_p])
|
||||
PyObject_RichCompare = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p,
|
||||
lc_int))
|
||||
PySequence_Contains = lc.Type.function(lc_int, (l_pyobj_p, l_pyobj_p))
|
||||
PyString_Check = lc.Type.function(lc_int, [l_pyobj_p])
|
||||
PyString_CheckExact = lc.Type.function(lc_int, [l_pyobj_p])
|
||||
PyString_Format = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||
Py_BuildValue = lc.Type.function(l_pyobj_p, [li8_ptr], True)
|
||||
Py_DecRef = lc.Type.function(lvoid, [l_pyobj_p])
|
||||
Py_IncRef = lc.Type.function(lvoid, [l_pyobj_p])
|
||||
|
||||
PyInt_Type = li8
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of bytetype.py
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ class ControlFlowGraph (object):
|
|||
|
||||
def unlink_unreachables (self):
|
||||
changed = True
|
||||
next_blocks = self.blocks.keys()
|
||||
next_blocks = set(self.blocks.keys())
|
||||
next_blocks.remove(0)
|
||||
while changed:
|
||||
changed = False
|
||||
blocks = next_blocks
|
||||
next_blocks = blocks[:]
|
||||
next_blocks = blocks.copy()
|
||||
for block in blocks:
|
||||
if len(self.blocks_in[block]) == 0:
|
||||
blocks_out = self.blocks_out[block]
|
||||
|
|
@ -100,15 +100,25 @@ class ControlFlowGraph (object):
|
|||
if hasattr(self, 'reaching_definitions'):
|
||||
del self.reaching_definitions
|
||||
|
||||
def idom (self, block):
|
||||
'''Compute the immediate dominator (idom) of the given block
|
||||
key. Returns None if the block has no in edges.
|
||||
def get_a_dom (self, block):
|
||||
'''Find an immediate predecessor of the given block such that
|
||||
the predecessor is either the only entry point, or the
|
||||
precessor is not in its own dominance set (a non-loop
|
||||
predecessor). Returns None if the given block has no
|
||||
predecessor.
|
||||
|
||||
Note that in the case where there are multiple immediate
|
||||
dominators (a join after a non-loop branch), this returns one
|
||||
of the predecessors, but is not guaranteed to reliably select
|
||||
one over the others (depends on the ordering of the set type
|
||||
iterator).'''
|
||||
Note that in the case where there are multiple dominators (a
|
||||
join after a non-loop branch), this returns one of the
|
||||
predecessors, but is not guaranteed to reliably select one
|
||||
over the others (depends on the ordering of the set type
|
||||
iterator).
|
||||
|
||||
Note: Previously, this method's documentation erroneously
|
||||
identified the return value as being the immediate dominator
|
||||
of the input block. Instead, it attempts to find a "nearby"
|
||||
dominator. Normally, the immediate dominator of a join is the
|
||||
least upperbound of the closed immediate dominance
|
||||
relationship over its two entrants.'''
|
||||
preds = self.blocks_in[block]
|
||||
npreds = len(preds)
|
||||
if npreds == 0:
|
||||
|
|
@ -142,7 +152,7 @@ class ControlFlowGraph (object):
|
|||
ret_val = {}
|
||||
for pred in preds:
|
||||
ret_val[pred] = self.block_writes_to_writer_map(pred)
|
||||
crnt = self.idom(pred)
|
||||
crnt = self.get_a_dom(pred)
|
||||
while crnt != None:
|
||||
crnt_writer_map = self.block_writes_to_writer_map(crnt)
|
||||
# This order of update favors the first definitions
|
||||
|
|
@ -150,7 +160,7 @@ class ControlFlowGraph (object):
|
|||
# visits blocks in reverse execution order.
|
||||
crnt_writer_map.update(ret_val[pred])
|
||||
ret_val[pred] = crnt_writer_map
|
||||
crnt = self.idom(crnt)
|
||||
crnt = self.get_a_dom(crnt)
|
||||
if not has_memoized:
|
||||
self.reaching_definitions = {}
|
||||
self.reaching_definitions[block] = ret_val
|
||||
|
|
|
|||
|
|
@ -1,147 +1,173 @@
|
|||
# ______________________________________________________________________
|
||||
|
||||
from collections import namedtuple
|
||||
import dis
|
||||
import opcode
|
||||
import inspect
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Module data
|
||||
|
||||
hasjump = opcode.hasjrel + opcode.hasjabs
|
||||
hascbranch = [op for op in hasjump
|
||||
if 'IF' in opcode.opname[op]
|
||||
or opcode.opname[op] in ('FOR_ITER', 'SETUP_LOOP')]
|
||||
# Note that opcode.hasjrel and opcode.hasjabs applies only to opcodes
|
||||
# that calculate a jump point based on the argument. This ignores
|
||||
# jumps that use the frame stack to calculate their targets, and
|
||||
# exceptions.
|
||||
|
||||
NON_ARG_JUMP_NAMES = ('BREAK_LOOP', 'RETURN_VALUE', 'END_FINALLY',
|
||||
'RAISE_VARARGS')
|
||||
NON_ARG_JUMPS = [opcode.opmap[opname]
|
||||
for opname in NON_ARG_JUMP_NAMES
|
||||
if opname in opcode.opmap]
|
||||
HAS_CBRANCH_NAMES = 'FOR_ITER',
|
||||
hasjump = opcode.hasjrel + opcode.hasjabs + NON_ARG_JUMPS
|
||||
hascbranch = [op for op, opname in ((op, opcode.opname[op])
|
||||
for op in hasjump)
|
||||
if 'IF' in opname
|
||||
or 'SETUP' in opname
|
||||
or opname in HAS_CBRANCH_NAMES]
|
||||
|
||||
OpcodeData = namedtuple('OpcodeData', ('pops', 'pushes', 'is_stmt'))
|
||||
NO_OPCODE_DATA = OpcodeData(None, None, None)
|
||||
|
||||
# Since the actual opcode value may change, manage opcode abstraction
|
||||
# data by opcode name.
|
||||
|
||||
OPCODE_MAP = {
|
||||
'BINARY_ADD': (2, 1, None),
|
||||
'BINARY_AND': (2, 1, None),
|
||||
'BINARY_DIVIDE': (2, 1, None),
|
||||
'BINARY_FLOOR_DIVIDE': (2, 1, None),
|
||||
'BINARY_LSHIFT': (2, 1, None),
|
||||
'BINARY_MODULO': (2, 1, None),
|
||||
'BINARY_MULTIPLY': (2, 1, None),
|
||||
'BINARY_OR': (2, 1, None),
|
||||
'BINARY_POWER': (2, 1, None),
|
||||
'BINARY_RSHIFT': (2, 1, None),
|
||||
'BINARY_SUBSCR': (2, 1, None),
|
||||
'BINARY_SUBTRACT': (2, 1, None),
|
||||
'BINARY_TRUE_DIVIDE': (2, 1, None),
|
||||
'BINARY_XOR': (2, 1, None),
|
||||
'BREAK_LOOP': (0, None, 1),
|
||||
'BUILD_CLASS': (None, None, None),
|
||||
'BUILD_LIST': (-1, 1, None),
|
||||
'BUILD_MAP': (None, None, None),
|
||||
'BUILD_SET': (None, None, None),
|
||||
'BUILD_SLICE': (None, None, None),
|
||||
'BUILD_TUPLE': (-1, 1, None),
|
||||
'CALL_FUNCTION': (-2, 1, None),
|
||||
'CALL_FUNCTION_KW': (-3, 1, None),
|
||||
'CALL_FUNCTION_VAR': (-3, 1, None),
|
||||
'CALL_FUNCTION_VAR_KW': (-4, 1, None),
|
||||
'COMPARE_OP': (2, 1, None),
|
||||
'CONTINUE_LOOP': (None, None, None),
|
||||
'DELETE_ATTR': (1, None, 1),
|
||||
'DELETE_DEREF': (None, None, None),
|
||||
'DELETE_FAST': (0, None, 1),
|
||||
'DELETE_GLOBAL': (0, None, 1),
|
||||
'DELETE_NAME': (0, None, 1),
|
||||
'DELETE_SLICE+0': (1, None, 1),
|
||||
'DELETE_SLICE+1': (2, None, 1),
|
||||
'DELETE_SLICE+2': (2, None, 1),
|
||||
'DELETE_SLICE+3': (3, None, 1),
|
||||
'DELETE_SUBSCR': (2, None, 1),
|
||||
'DUP_TOP': (None, None, None),
|
||||
'DUP_TOPX': (None, None, None),
|
||||
'DUP_TOP_TWO': (None, None, None),
|
||||
'END_FINALLY': (None, None, None),
|
||||
'EXEC_STMT': (None, None, None),
|
||||
'EXTENDED_ARG': (None, None, None),
|
||||
'FOR_ITER': (1, 1, 1),
|
||||
'GET_ITER': (1, 1, None),
|
||||
'IMPORT_FROM': (None, None, None),
|
||||
'IMPORT_NAME': (None, None, None),
|
||||
'IMPORT_STAR': (1, None, 1),
|
||||
'INPLACE_ADD': (2, 1, None),
|
||||
'INPLACE_AND': (2, 1, None),
|
||||
'INPLACE_DIVIDE': (2, 1, None),
|
||||
'INPLACE_FLOOR_DIVIDE': (2, 1, None),
|
||||
'INPLACE_LSHIFT': (2, 1, None),
|
||||
'INPLACE_MODULO': (2, 1, None),
|
||||
'INPLACE_MULTIPLY': (2, 1, None),
|
||||
'INPLACE_OR': (2, 1, None),
|
||||
'INPLACE_POWER': (2, 1, None),
|
||||
'INPLACE_RSHIFT': (2, 1, None),
|
||||
'INPLACE_SUBTRACT': (2, 1, None),
|
||||
'INPLACE_TRUE_DIVIDE': (2, 1, None),
|
||||
'INPLACE_XOR': (2, 1, None),
|
||||
'JUMP_ABSOLUTE': (0, None, 1),
|
||||
'JUMP_FORWARD': (0, None, 1),
|
||||
'JUMP_IF_FALSE': (1, 1, 1),
|
||||
'JUMP_IF_FALSE_OR_POP': (None, None, None),
|
||||
'JUMP_IF_TRUE': (1, 1, 1),
|
||||
'JUMP_IF_TRUE_OR_POP': (None, None, None),
|
||||
'LIST_APPEND': (2, 0, 1),
|
||||
'LOAD_ATTR': (1, 1, None),
|
||||
'LOAD_BUILD_CLASS': (None, None, None),
|
||||
'LOAD_CLOSURE': (None, None, None),
|
||||
'LOAD_CONST': (0, 1, None),
|
||||
'LOAD_DEREF': (0, 1, None),
|
||||
'LOAD_FAST': (0, 1, None),
|
||||
'LOAD_GLOBAL': (0, 1, None),
|
||||
'LOAD_LOCALS': (None, None, None),
|
||||
'LOAD_NAME': (0, 1, None),
|
||||
'MAKE_CLOSURE': (None, None, None),
|
||||
'MAKE_FUNCTION': (-2, 1, None),
|
||||
'MAP_ADD': (None, None, None),
|
||||
'NOP': (0, None, None),
|
||||
'POP_BLOCK': (0, None, 1),
|
||||
'POP_EXCEPT': (None, None, None),
|
||||
'POP_JUMP_IF_FALSE': (1, None, 1),
|
||||
'POP_JUMP_IF_TRUE': (1, None, 1),
|
||||
'POP_TOP': (1, None, 1),
|
||||
'PRINT_EXPR': (1, None, 1),
|
||||
'PRINT_ITEM': (1, None, 1),
|
||||
'PRINT_ITEM_TO': (2, None, 1),
|
||||
'PRINT_NEWLINE': (0, None, 1),
|
||||
'PRINT_NEWLINE_TO': (1, None, 1),
|
||||
'RAISE_VARARGS': (None, None, None),
|
||||
'RETURN_VALUE': (1, None, 1),
|
||||
'ROT_FOUR': (None, None, None),
|
||||
'ROT_THREE': (None, None, None),
|
||||
'ROT_TWO': (None, None, None),
|
||||
'SETUP_EXCEPT': (None, None, None),
|
||||
'SETUP_FINALLY': (None, None, None),
|
||||
'SETUP_LOOP': (None, None, None),
|
||||
'SETUP_WITH': (None, None, None),
|
||||
'SET_ADD': (None, None, None),
|
||||
'SLICE+0': (1, 1, None),
|
||||
'SLICE+1': (2, 1, None),
|
||||
'SLICE+2': (2, 1, None),
|
||||
'SLICE+3': (3, 1, None),
|
||||
'STOP_CODE': (None, None, None),
|
||||
'STORE_ATTR': (2, None, 1),
|
||||
'STORE_DEREF': (1, 0, 1),
|
||||
'STORE_FAST': (1, None, 1),
|
||||
'STORE_GLOBAL': (1, None, 1),
|
||||
'STORE_LOCALS': (None, None, None),
|
||||
'STORE_MAP': (1, None, 1),
|
||||
'STORE_NAME': (1, None, 1),
|
||||
'STORE_SLICE+0': (1, None, 1),
|
||||
'STORE_SLICE+1': (2, None, 1),
|
||||
'STORE_SLICE+2': (2, None, 1),
|
||||
'STORE_SLICE+3': (3, None, 1),
|
||||
'STORE_SUBSCR': (3, None, 1),
|
||||
'UNARY_CONVERT': (1, 1, None),
|
||||
'UNARY_INVERT': (1, 1, None),
|
||||
'UNARY_NEGATIVE': (1, 1, None),
|
||||
'UNARY_NOT': (1, 1, None),
|
||||
'UNARY_POSITIVE': (1, 1, None),
|
||||
'UNPACK_EX': (None, None, None),
|
||||
'UNPACK_SEQUENCE': (None, None, None),
|
||||
'WITH_CLEANUP': (None, None, None),
|
||||
'YIELD_VALUE': (1, None, 1),
|
||||
'BINARY_ADD': OpcodeData(2, 1, None),
|
||||
'BINARY_AND': OpcodeData(2, 1, None),
|
||||
'BINARY_DIVIDE': OpcodeData(2, 1, None),
|
||||
'BINARY_FLOOR_DIVIDE': OpcodeData(2, 1, None),
|
||||
'BINARY_LSHIFT': OpcodeData(2, 1, None),
|
||||
'BINARY_MODULO': OpcodeData(2, 1, None),
|
||||
'BINARY_MULTIPLY': OpcodeData(2, 1, None),
|
||||
'BINARY_OR': OpcodeData(2, 1, None),
|
||||
'BINARY_POWER': OpcodeData(2, 1, None),
|
||||
'BINARY_RSHIFT': OpcodeData(2, 1, None),
|
||||
'BINARY_SUBSCR': OpcodeData(2, 1, None),
|
||||
'BINARY_SUBTRACT': OpcodeData(2, 1, None),
|
||||
'BINARY_TRUE_DIVIDE': OpcodeData(2, 1, None),
|
||||
'BINARY_XOR': OpcodeData(2, 1, None),
|
||||
'BREAK_LOOP': OpcodeData(0, None, 1),
|
||||
'BUILD_CLASS': OpcodeData(3, 1, None),
|
||||
'BUILD_LIST': OpcodeData(-1, 1, None),
|
||||
'BUILD_MAP': OpcodeData(-1, 1, None),
|
||||
'BUILD_SET': OpcodeData(-1, 1, None),
|
||||
'BUILD_SLICE': OpcodeData(-1, 1, None), # oparg should only be 2 or 3
|
||||
'BUILD_TUPLE': OpcodeData(-1, 1, None),
|
||||
'CALL_FUNCTION': OpcodeData(-2, 1, None),
|
||||
'CALL_FUNCTION_KW': OpcodeData(-3, 1, None),
|
||||
'CALL_FUNCTION_VAR': OpcodeData(-3, 1, None),
|
||||
'CALL_FUNCTION_VAR_KW': OpcodeData(-4, 1, None),
|
||||
'COMPARE_OP': OpcodeData(2, 1, None),
|
||||
'CONTINUE_LOOP': OpcodeData(0, None, 1),
|
||||
'DELETE_ATTR': OpcodeData(1, None, 1),
|
||||
'DELETE_DEREF': NO_OPCODE_DATA,
|
||||
'DELETE_FAST': OpcodeData(0, None, 1),
|
||||
'DELETE_GLOBAL': OpcodeData(0, None, 1),
|
||||
'DELETE_NAME': OpcodeData(0, None, 1),
|
||||
'DELETE_SLICE+0': OpcodeData(1, None, 1),
|
||||
'DELETE_SLICE+1': OpcodeData(2, None, 1),
|
||||
'DELETE_SLICE+2': OpcodeData(2, None, 1),
|
||||
'DELETE_SLICE+3': OpcodeData(3, None, 1),
|
||||
'DELETE_SUBSCR': OpcodeData(2, None, 1),
|
||||
'DUP_TOP': NO_OPCODE_DATA,
|
||||
'DUP_TOPX': NO_OPCODE_DATA,
|
||||
'DUP_TOP_TWO': NO_OPCODE_DATA,
|
||||
|
||||
# The data for END_FINALLY is a total fabrication; END_FINALLY may
|
||||
# pop 1 or 3 values off the value stack, based on the type of the
|
||||
# top of the value stack. If, however, a value stack simulator
|
||||
# ignores the part of the CPython evaluator loop that pushes the
|
||||
# why code on the value stack for WHY_RETURN and WHY_CONTINUE (as
|
||||
# this table does), this should work out fine.
|
||||
'END_FINALLY': OpcodeData(0, 0, 1),
|
||||
|
||||
'EXEC_STMT': OpcodeData(3, 0, 1),
|
||||
'EXTENDED_ARG': NO_OPCODE_DATA,
|
||||
'FOR_ITER': OpcodeData(1, 1, 1),
|
||||
'GET_ITER': OpcodeData(1, 1, None),
|
||||
'IMPORT_FROM': NO_OPCODE_DATA,
|
||||
'IMPORT_NAME': OpcodeData(2, 1, None),
|
||||
'IMPORT_STAR': OpcodeData(1, None, 1),
|
||||
'INPLACE_ADD': OpcodeData(2, 1, None),
|
||||
'INPLACE_AND': OpcodeData(2, 1, None),
|
||||
'INPLACE_DIVIDE': OpcodeData(2, 1, None),
|
||||
'INPLACE_FLOOR_DIVIDE': OpcodeData(2, 1, None),
|
||||
'INPLACE_LSHIFT': OpcodeData(2, 1, None),
|
||||
'INPLACE_MODULO': OpcodeData(2, 1, None),
|
||||
'INPLACE_MULTIPLY': OpcodeData(2, 1, None),
|
||||
'INPLACE_OR': OpcodeData(2, 1, None),
|
||||
'INPLACE_POWER': OpcodeData(2, 1, None),
|
||||
'INPLACE_RSHIFT': OpcodeData(2, 1, None),
|
||||
'INPLACE_SUBTRACT': OpcodeData(2, 1, None),
|
||||
'INPLACE_TRUE_DIVIDE': OpcodeData(2, 1, None),
|
||||
'INPLACE_XOR': OpcodeData(2, 1, None),
|
||||
'JUMP_ABSOLUTE': OpcodeData(0, None, 1),
|
||||
'JUMP_FORWARD': OpcodeData(0, None, 1),
|
||||
'JUMP_IF_FALSE': OpcodeData(1, 1, 1),
|
||||
'JUMP_IF_FALSE_OR_POP': NO_OPCODE_DATA,
|
||||
'JUMP_IF_TRUE': OpcodeData(1, 1, 1),
|
||||
'JUMP_IF_TRUE_OR_POP': NO_OPCODE_DATA,
|
||||
'LIST_APPEND': NO_OPCODE_DATA,
|
||||
'LOAD_ATTR': OpcodeData(1, 1, None),
|
||||
'LOAD_BUILD_CLASS': NO_OPCODE_DATA,
|
||||
'LOAD_CLOSURE': NO_OPCODE_DATA,
|
||||
'LOAD_CONST': OpcodeData(0, 1, None),
|
||||
'LOAD_DEREF': OpcodeData(0, 1, None),
|
||||
'LOAD_FAST': OpcodeData(0, 1, None),
|
||||
'LOAD_GLOBAL': OpcodeData(0, 1, None),
|
||||
'LOAD_LOCALS': OpcodeData(0, 1, None),
|
||||
'LOAD_NAME': OpcodeData(0, 1, None),
|
||||
'MAKE_CLOSURE': NO_OPCODE_DATA,
|
||||
'MAKE_FUNCTION': OpcodeData(-2, 1, None),
|
||||
'MAP_ADD': NO_OPCODE_DATA,
|
||||
'NOP': OpcodeData(0, None, None),
|
||||
'POP_BLOCK': OpcodeData(0, None, 1),
|
||||
'POP_EXCEPT': NO_OPCODE_DATA,
|
||||
'POP_JUMP_IF_FALSE': OpcodeData(1, None, 1),
|
||||
'POP_JUMP_IF_TRUE': OpcodeData(1, None, 1),
|
||||
'POP_TOP': OpcodeData(1, None, 1),
|
||||
'PRINT_EXPR': OpcodeData(1, None, 1),
|
||||
'PRINT_ITEM': OpcodeData(1, None, 1),
|
||||
'PRINT_ITEM_TO': OpcodeData(2, None, 1),
|
||||
'PRINT_NEWLINE': OpcodeData(0, None, 1),
|
||||
'PRINT_NEWLINE_TO': OpcodeData(1, None, 1),
|
||||
'RAISE_VARARGS': OpcodeData(-1, None, 1),
|
||||
'RETURN_VALUE': OpcodeData(1, None, 1),
|
||||
'ROT_FOUR': NO_OPCODE_DATA,
|
||||
'ROT_THREE': NO_OPCODE_DATA,
|
||||
'ROT_TWO': NO_OPCODE_DATA,
|
||||
'SETUP_EXCEPT': NO_OPCODE_DATA,
|
||||
'SETUP_FINALLY': NO_OPCODE_DATA,
|
||||
'SETUP_LOOP': NO_OPCODE_DATA,
|
||||
'SETUP_WITH': NO_OPCODE_DATA,
|
||||
'SET_ADD': NO_OPCODE_DATA,
|
||||
'SLICE+0': OpcodeData(1, 1, None),
|
||||
'SLICE+1': OpcodeData(2, 1, None),
|
||||
'SLICE+2': OpcodeData(2, 1, None),
|
||||
'SLICE+3': OpcodeData(3, 1, None),
|
||||
'STOP_CODE': NO_OPCODE_DATA,
|
||||
'STORE_ATTR': OpcodeData(2, None, 1),
|
||||
'STORE_DEREF': OpcodeData(1, 0, 1),
|
||||
'STORE_FAST': OpcodeData(1, None, 1),
|
||||
'STORE_GLOBAL': OpcodeData(1, None, 1),
|
||||
'STORE_LOCALS': NO_OPCODE_DATA,
|
||||
'STORE_MAP': OpcodeData(1, None, 1),
|
||||
'STORE_NAME': OpcodeData(1, None, 1),
|
||||
'STORE_SLICE+0': OpcodeData(1, None, 1),
|
||||
'STORE_SLICE+1': OpcodeData(2, None, 1),
|
||||
'STORE_SLICE+2': OpcodeData(2, None, 1),
|
||||
'STORE_SLICE+3': OpcodeData(3, None, 1),
|
||||
'STORE_SUBSCR': OpcodeData(3, None, 1),
|
||||
'UNARY_CONVERT': OpcodeData(1, 1, None),
|
||||
'UNARY_INVERT': OpcodeData(1, 1, None),
|
||||
'UNARY_NEGATIVE': OpcodeData(1, 1, None),
|
||||
'UNARY_NOT': OpcodeData(1, 1, None),
|
||||
'UNARY_POSITIVE': OpcodeData(1, 1, None),
|
||||
'UNPACK_EX': NO_OPCODE_DATA,
|
||||
'UNPACK_SEQUENCE': NO_OPCODE_DATA,
|
||||
'WITH_CLEANUP': NO_OPCODE_DATA,
|
||||
'YIELD_VALUE': OpcodeData(1, None, 1),
|
||||
}
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
|
@ -167,6 +193,7 @@ def itercode(code, start = 0):
|
|||
i = i + 2
|
||||
if op == opcode.EXTENDED_ARG:
|
||||
extended_arg = oparg * 65536
|
||||
continue
|
||||
|
||||
delta = yield num, op, oparg
|
||||
if delta is not None:
|
||||
|
|
@ -176,6 +203,50 @@ def itercode(code, start = 0):
|
|||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def itercodeobjs(codeobj):
|
||||
"Iterator that traverses code objects via the co_consts member."
|
||||
yield codeobj
|
||||
for const in codeobj.co_consts:
|
||||
if inspect.iscode(const):
|
||||
for childobj in itercodeobjs(const):
|
||||
yield childobj
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def visit_code_args(visitor, *args, **kws):
|
||||
"""Utility function for testing or demonstrating various code
|
||||
analysis passes in llpython.
|
||||
|
||||
Takes a visitor function and a sequence of command line arguments.
|
||||
The visitor function should be able to handle either function
|
||||
objects or code objects."""
|
||||
def _visit_code_objs(root_obj):
|
||||
for code_obj in itercodeobjs(root_obj):
|
||||
visitor(code_obj)
|
||||
try:
|
||||
from .tests import llfuncs
|
||||
except ImportError:
|
||||
llfuncs = object()
|
||||
if not args:
|
||||
if 'default_args' in kws:
|
||||
args = kws['default_args']
|
||||
else:
|
||||
args = ('pymod',)
|
||||
for arg in args:
|
||||
if inspect.iscode(arg):
|
||||
_visit_code_objs(arg)
|
||||
elif inspect.isfunction(arg):
|
||||
_visit_code_objs(get_code_object(arg))
|
||||
elif arg.endswith('.py'):
|
||||
with open(arg) as in_file:
|
||||
in_source = in_file.read()
|
||||
in_codeobj = compile(in_source, arg, 'exec')
|
||||
_visit_code_objs(in_codeobj)
|
||||
else:
|
||||
visitor(getattr(llfuncs, arg))
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def extendlabels(code, labels = None):
|
||||
"""Extend the set of jump target labels to account for the
|
||||
passthrough targets of conditional branches.
|
||||
|
|
@ -194,14 +265,7 @@ def extendlabels(code, labels = None):
|
|||
i += 1
|
||||
if op >= dis.HAVE_ARGUMENT:
|
||||
i += 2
|
||||
label = -1
|
||||
if op in hasjump:
|
||||
label = i
|
||||
if label >= 0:
|
||||
if label not in labels:
|
||||
labels.append(label)
|
||||
elif op == opcode.opmap['BREAK_LOOP']:
|
||||
if i not in labels:
|
||||
if op in hasjump and i < n and i not in labels:
|
||||
labels.append(i)
|
||||
return labels
|
||||
|
||||
|
|
@ -221,5 +285,12 @@ def build_basic_blocks (co_obj):
|
|||
labels + [len(co_code)]))
|
||||
return blocks
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def get_nargs(co_obj):
|
||||
flags = co_obj.co_flags
|
||||
return (1 + co_obj.co_argcount + (1 if flags & 4 else 0) +
|
||||
(1 if flags & 8 else 0))
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of opcode_util.py
|
||||
|
|
|
|||
31
llpython/tests/test_addr_flow.py
Normal file
31
llpython/tests/test_addr_flow.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
from llpython import addr_flow
|
||||
|
||||
from . import test_byte_flow
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class (test case) definition(s)
|
||||
|
||||
class TestAddressFlowBuilder(unittest.TestCase, test_byte_flow.FlowTestMixin):
|
||||
BUILDER_CLS = addr_flow.AddressFlowBuilder
|
||||
|
||||
def fail_unless_valid_instruction(self, instr):
|
||||
super(TestAddressFlowBuilder, self).fail_unless_valid_instruction(
|
||||
instr)
|
||||
for arg_addr in instr[-1]:
|
||||
self.fail_unless_valid_address(arg_addr)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (unit test) routine
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of test_addr_flow.py
|
||||
16
llpython/tests/test_all.py
Normal file
16
llpython/tests/test_all.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
|
||||
import unittest
|
||||
|
||||
from .test_byte_control import TestByteControl
|
||||
from .test_byte_flow import TestBytecodeFlowBuilder
|
||||
from .test_addr_flow import TestAddressFlowBuilder
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of llpython/tests/test_all.py
|
||||
279
llpython/tests/test_byte_control.py
Normal file
279
llpython/tests/test_byte_control.py
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from llpython import byte_control
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Global data
|
||||
|
||||
# Technically we could also compute if we need special logic for the
|
||||
# old bytecode compiler by scanning for JUMP_IF_TRUE and JUMP_IF_FALSE
|
||||
# opcodes. These opcodes require additional POP_TOP's be inserted.
|
||||
OLD_BYTECODE_COMPILER = sys.version_info < (2, 7)
|
||||
|
||||
got_done = 0
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Utility function definitions
|
||||
|
||||
def do_something():
|
||||
global got_done
|
||||
got_done += 1
|
||||
print("Something good got done.")
|
||||
return got_done
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def do_something_else():
|
||||
raise Exception("Something bad got done, and I don't like it")
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Test function definitions
|
||||
|
||||
def try_finally_0(m, n): # why == WHY_RETURN
|
||||
try:
|
||||
return n - m
|
||||
finally:
|
||||
do_something()
|
||||
return do_something_else()
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def try_finally_1(m, n): # why == WHY_BREAK
|
||||
i = -1
|
||||
for i in range(m, n):
|
||||
try:
|
||||
if i == 101:
|
||||
break
|
||||
finally:
|
||||
do_something()
|
||||
return i
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def try_finally_2(m, n): # why == WHY_CONTINUE
|
||||
i = m
|
||||
while i < n:
|
||||
try:
|
||||
if i == 101:
|
||||
i += 200
|
||||
continue
|
||||
finally:
|
||||
do_something()
|
||||
i += 1
|
||||
return i
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def try_finally_3(m, n): # why == WHY_EXCEPTION (or WHY_RETURN)
|
||||
d = {}
|
||||
try:
|
||||
return d[n] - d[m]
|
||||
finally:
|
||||
do_something()
|
||||
return do_something_else()
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def try_finally_4(m, n): # why == WHY_NOT
|
||||
try:
|
||||
rv = n - m
|
||||
finally:
|
||||
do_something()
|
||||
return rv
|
||||
|
||||
# ____________________________________________________________
|
||||
|
||||
def try_finally_5(m, n):
|
||||
for i in range(m, n):
|
||||
try:
|
||||
if i == 99:
|
||||
break
|
||||
elif i == 121:
|
||||
continue
|
||||
elif i == 86:
|
||||
return
|
||||
elif i < -102:
|
||||
raise ValueError(i)
|
||||
else:
|
||||
try:
|
||||
if i < 0:
|
||||
raise ValueError(i)
|
||||
finally:
|
||||
do_something()
|
||||
finally:
|
||||
do_something()
|
||||
return do_something()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class (test case) definition(s)
|
||||
|
||||
class TestByteControl(unittest.TestCase):
|
||||
def fail_unless_cfg_match(self, test_cfg, block_count, edges):
|
||||
assert len(test_cfg.blocks) == block_count
|
||||
block_keys = list(test_cfg.blocks.keys())
|
||||
block_keys.sort()
|
||||
expected_blocks_in = dict((block_key, set())
|
||||
for block_key in block_keys)
|
||||
expected_blocks_out = dict((block_key, set())
|
||||
for block_key in block_keys)
|
||||
for from_block_ofs, to_block_ofs in edges:
|
||||
from_block = block_keys[from_block_ofs]
|
||||
to_block = block_keys[to_block_ofs]
|
||||
expected_blocks_in[to_block].add(from_block)
|
||||
expected_blocks_out[from_block].add(to_block)
|
||||
for block_key in block_keys:
|
||||
expected_in = expected_blocks_in[block_key]
|
||||
test_in = test_cfg.blocks_in[block_key]
|
||||
self.assertEqual(
|
||||
expected_in, test_in, '%r != %r for blocks_in[%d]' % (
|
||||
test_in, expected_in, block_key))
|
||||
expected_out = expected_blocks_out[block_key]
|
||||
test_out = test_cfg.blocks_out[block_key]
|
||||
self.assertEqual(
|
||||
expected_out, test_out, '%r != %r for set blocks_out[%d]' % (
|
||||
test_out, expected_out, block_key))
|
||||
|
||||
def test_raise(self):
|
||||
cfg = byte_control.build_cfg(do_something_else)
|
||||
self.fail_unless_cfg_match(cfg, 2, ())
|
||||
|
||||
def test_try_finally_0(self):
|
||||
"""
|
||||
Expected CFG (Python 2.7+):
|
||||
digraph CFG_try_finally_0 {
|
||||
BLOCK_0 -> BLOCK_3; // 0 -> 1
|
||||
BLOCK_0 -> BLOCK_15; // 0 -> 3
|
||||
BLOCK_3 -> BLOCK_15; // 1 -> 3
|
||||
// DEAD: BLOCK_11 -> BLOCK_15; // 2 -> 3
|
||||
BLOCK_15 -> BLOCK_23; // 3 -> 4, why == WHY_NOT
|
||||
BLOCK_23; // 4
|
||||
}
|
||||
(Possibly terminal blocks: 15, 23.)
|
||||
"""
|
||||
cfg = byte_control.build_cfg(try_finally_0)
|
||||
self.fail_unless_cfg_match(cfg, 5, ((0, 1), (0, 3), (1, 3),
|
||||
(3, 4)))
|
||||
|
||||
def test_try_finally_1(self):
|
||||
"""
|
||||
Expected CFG (Python 2.7+):
|
||||
digraph CFG_try_finally_1 {
|
||||
BLOCK_0 -> BLOCK_9; // 0 -> 1
|
||||
BLOCK_0 -> BLOCK_63; // 0 -> 11
|
||||
BLOCK_9 -> BLOCK_22; // 1 -> 2
|
||||
BLOCK_22 -> BLOCK_25; // 2 -> 3
|
||||
BLOCK_22 -> BLOCK_62; // 2 -> 10
|
||||
BLOCK_25 -> BLOCK_31; // 3 -> 4
|
||||
BLOCK_25 -> BLOCK_51; // 3 -> 8
|
||||
BLOCK_31 -> BLOCK_43; // 4 -> 5
|
||||
BLOCK_31 -> BLOCK_47; // 4 -> 7
|
||||
BLOCK_43 -> BLOCK_51; // 5 -> 8
|
||||
// DEAD: BLOCK_44 -> BLOCK_47; // 6 -> 7
|
||||
BLOCK_47 -> BLOCK_51; // 7 -> 8
|
||||
BLOCK_51 -> BLOCK_59; // 8 -> 9, why == WHY_NOT
|
||||
BLOCK_51 -> BLOCK_63; // 8 -> 11, why == WHY_BREAK, WHY_RETURN, ...
|
||||
BLOCK_59 -> BLOCK_22; // 9 -> 2
|
||||
BLOCK_62 -> BLOCK_63; // 10 -> 11
|
||||
BLOCK_63; // 11
|
||||
}
|
||||
(Possibly terminal blocks: 51, 63.)
|
||||
"""
|
||||
cfg = byte_control.build_cfg(try_finally_1)
|
||||
if not OLD_BYTECODE_COMPILER:
|
||||
self.fail_unless_cfg_match(
|
||||
cfg, 12, ((0, 1), (0, 11), (1, 2), (2, 3), (2, 10), (3, 4),
|
||||
(3, 8), (4, 5), (4, 7), (5, 8), (7, 8),
|
||||
(8, 9), (8, 11), (9, 2), (10, 11)))
|
||||
else:
|
||||
self.fail_unless_cfg_match(
|
||||
cfg, 13, ((0, 1), (0, 12), (1, 2), (2, 3), (2, 11), (3, 4),
|
||||
(3, 9), (4, 5), (4, 7), (5, 9), (7, 8),
|
||||
(8, 9), (9, 10), (9, 12), (10, 2), (11, 12)))
|
||||
|
||||
def test_try_finally_2(self):
|
||||
"""
|
||||
Expected CFG (Python 2.7+):
|
||||
digraph CFG_try_finally_2 {
|
||||
BLOCK_0 -> BLOCK_9; // 0 -> 1
|
||||
BLOCK_0 -> BLOCK_78; // 0 -> 10
|
||||
BLOCK_9 -> BLOCK_21; // 1 -> 2
|
||||
BLOCK_9 -> BLOCK_77; // 1 -> 9
|
||||
BLOCK_21 -> BLOCK_24; // 2 -> 3
|
||||
BLOCK_21 -> BLOCK_56; // 2 -> 7
|
||||
BLOCK_24 -> BLOCK_36; // 3 -> 4
|
||||
BLOCK_24 -> BLOCK_52; // 3 -> 6
|
||||
BLOCK_36 -> BLOCK_56; // 4 -> 7
|
||||
// DEAD: BLOCK_49 -> BLOCK_52; // 5 -> 6
|
||||
BLOCK_52 -> BLOCK_56; // 6 -> 7
|
||||
BLOCK_56 -> BLOCK_9; // 7 -> 1, why == WHY_CONTINUE
|
||||
BLOCK_56 -> BLOCK_64; // 7 -> 8, why == WHY_NOT
|
||||
BLOCK_64 -> BLOCK_9; // 8 -> 1
|
||||
BLOCK_77 -> BLOCK_78; // 9 -> 10
|
||||
BLOCK_78; // 10
|
||||
}
|
||||
(Possibly terminal blocks: 56, 78.)
|
||||
"""
|
||||
cfg = byte_control.build_cfg(try_finally_2)
|
||||
if not OLD_BYTECODE_COMPILER:
|
||||
self.fail_unless_cfg_match(
|
||||
cfg, 11, ((0, 1), (0, 10), (1, 2), (1, 9), (2, 3), (2, 7),
|
||||
(3, 4), (3, 6), (4, 7), (6, 7), (7, 1),
|
||||
(7, 8), (8, 1), (9, 10)))
|
||||
|
||||
def test_try_finally_3(self):
|
||||
"""
|
||||
Expected (Python 2.7+):
|
||||
digraph CFG_foo3 {
|
||||
BLOCK_0 -> BLOCK_9; // 0 -> 1
|
||||
BLOCK_0 -> BLOCK_29; // 0 -> 3
|
||||
BLOCK_9 -> BLOCK_29; // 1 -> 3
|
||||
// DEAD: BLOCK_25 -> BLOCK_29; // 2 -> 3
|
||||
BLOCK_29 -> BLOCK_37; // 3 -> 4, why == WHY_NOT
|
||||
BLOCK_37; // 4
|
||||
}
|
||||
(Possibly terminal blocks: 29, 37.)
|
||||
"""
|
||||
cfg = byte_control.build_cfg(try_finally_3)
|
||||
self.fail_unless_cfg_match(cfg, 5, ((0, 1), (0, 3), (1, 3),
|
||||
(3, 4)))
|
||||
|
||||
def test_try_finally_4(self):
|
||||
"""
|
||||
Expected:
|
||||
digraph CFG_foo4 {
|
||||
BLOCK_0 -> BLOCK_3; // 0 -> 1
|
||||
BLOCK_0 -> BLOCK_17; // 0 -> 2
|
||||
BLOCK_3 -> BLOCK_17; // 1 -> 2
|
||||
BLOCK_17 -> BLOCK_25; // 2 -> 3, why == WHY_NOT
|
||||
BLOCK_25; // 3
|
||||
}
|
||||
(Possibly terminal blocks: 17, 25.)
|
||||
"""
|
||||
cfg = byte_control.build_cfg(try_finally_4)
|
||||
self.fail_unless_cfg_match(cfg, 4, ((0, 1), (0, 2), (1, 2), (2, 3)))
|
||||
|
||||
def test_try_finally_5(self):
|
||||
cfg = byte_control.build_cfg(try_finally_5)
|
||||
if not OLD_BYTECODE_COMPILER:
|
||||
self.fail_unless_cfg_match(
|
||||
cfg, 26, ((0, 1), (0, 25), (1, 2), (2, 24), (2, 3), (3, 4),
|
||||
(3, 22), (4, 5), (4, 7), (5, 22), (7, 8),
|
||||
(7, 10), (8, 22), (10, 11), (10, 12),
|
||||
(11, 22), (12, 13), (12, 15), (13, 22),
|
||||
(15, 16), (15, 20), (16, 17), (16, 19), (17, 20),
|
||||
(19, 20), (20, 21), (20, 22), (21, 22),
|
||||
(22, 25), (22, 2), (22, 23), (23, 2), (24, 25)))
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of test_byte_control.py
|
||||
101
llpython/tests/test_byte_flow.py
Normal file
101
llpython/tests/test_byte_flow.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
from llpython import byte_flow
|
||||
from llpython import opcode_util
|
||||
|
||||
from . import test_byte_control as tbc
|
||||
from . import llfuncs
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class (test case) definition(s)
|
||||
|
||||
class FlowTestMixin(object):
|
||||
|
||||
def fail_unless_valid_address(self, address):
|
||||
self.failUnless(address >= 0)
|
||||
self.failUnless(address < self.max_addr)
|
||||
self.failUnless(address in self.valid_addrs)
|
||||
|
||||
def fail_unless_valid_instruction(self, instr):
|
||||
address = instr[0]
|
||||
self.visited.add(address)
|
||||
self.fail_unless_valid_address(instr[0])
|
||||
|
||||
def fail_unless_valid_flow(self, flow, func):
|
||||
self.failUnless(len(flow) > 0)
|
||||
func_code = opcode_util.get_code_object(func).co_code
|
||||
self.valid_addrs = set(addr for addr, _, _ in
|
||||
opcode_util.itercode(func_code))
|
||||
self.visited = set()
|
||||
self.max_addr = len(func_code)
|
||||
for block_index, block_instrs in flow.items():
|
||||
self.failUnless(block_index < self.max_addr)
|
||||
for instr in block_instrs:
|
||||
self.fail_unless_valid_instruction(instr)
|
||||
del self.max_addr
|
||||
# Make sure that all instructions identified by itercode were
|
||||
# checked at least once; they should be represented in the
|
||||
# resulting flow, even if their basic block is unreachable.
|
||||
self.failUnless(self.valid_addrs == self.visited,
|
||||
'Failed to visit following addresses: %r' %
|
||||
(self.valid_addrs - self.visited))
|
||||
del self.visited
|
||||
del self.valid_addrs
|
||||
return flow
|
||||
|
||||
def build_and_test_flow(self, func):
|
||||
return self.fail_unless_valid_flow(
|
||||
self.BUILDER_CLS.build_flow(func), func)
|
||||
|
||||
def test_doslice(self):
|
||||
self.build_and_test_flow(llfuncs.doslice)
|
||||
|
||||
def test_ipow(self):
|
||||
self.build_and_test_flow(llfuncs.ipow)
|
||||
|
||||
def test_pymod(self):
|
||||
self.build_and_test_flow(llfuncs.pymod)
|
||||
|
||||
def test_try_finally_0(self):
|
||||
self.build_and_test_flow(tbc.try_finally_0)
|
||||
|
||||
def test_try_finally_1(self):
|
||||
self.build_and_test_flow(tbc.try_finally_1)
|
||||
|
||||
def test_try_finally_2(self):
|
||||
self.build_and_test_flow(tbc.try_finally_2)
|
||||
|
||||
def test_try_finally_3(self):
|
||||
self.build_and_test_flow(tbc.try_finally_3)
|
||||
|
||||
def test_try_finally_4(self):
|
||||
self.build_and_test_flow(tbc.try_finally_4)
|
||||
|
||||
def test_try_finally_5(self):
|
||||
self.build_and_test_flow(tbc.try_finally_5)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
class TestBytecodeFlowBuilder(unittest.TestCase, FlowTestMixin):
|
||||
|
||||
BUILDER_CLS = byte_flow.BytecodeFlowBuilder
|
||||
|
||||
def fail_unless_valid_instruction(self, instr):
|
||||
super(TestBytecodeFlowBuilder, self).fail_unless_valid_instruction(
|
||||
instr)
|
||||
for child_instr in instr[-1]:
|
||||
self.fail_unless_valid_instruction(child_instr)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (unit test) routine
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of test_byte_flow.py
|
||||
284
llpython/type_flow.py
Normal file
284
llpython/type_flow.py
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
#! /usr/bin/env python
|
||||
# ______________________________________________________________________
|
||||
# Module imports
|
||||
|
||||
from .bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin
|
||||
|
||||
DEBUG_SIMPLIFY = False
|
||||
|
||||
if DEBUG_SIMPLIFY:
|
||||
from pprint import pprint as pp
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Class definition(s)
|
||||
|
||||
class TypeFlowBuilder(BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
||||
def __init__(self, co_obj, *args, **kws):
|
||||
super(TypeFlowBuilder, self).__init__(*args, **kws)
|
||||
self.co_obj = co_obj
|
||||
self.locals = {}
|
||||
self.globals = {}
|
||||
self.refs = {}
|
||||
self.type_flow = {}
|
||||
self.requirements = {}
|
||||
|
||||
def get_type_eqns(self):
|
||||
return self.type_flow, self.requirements, self.locals, self.globals
|
||||
|
||||
def simplify(self):
|
||||
"""
|
||||
This method isn't working as intended. It should simplify
|
||||
strongly connected components s.t. instead of outputing
|
||||
several types like the following:
|
||||
|
||||
{0: set(['in0']),
|
||||
3: set(['in1']),
|
||||
...
|
||||
10: set([0, 3, 34, 37, 62, 65, 'in0', 'in1']),
|
||||
...
|
||||
34: set([0, 3, 34, 37, 62, 65, 'in0', 'in1']),
|
||||
37: set(['in1']),
|
||||
...
|
||||
62: set([0, 3, 34, 37, 62, 65, 'in0', 'in1']),
|
||||
65: set(['in1']),
|
||||
...
|
||||
75: set([0, 3, 34, 37, 62, 65, 'in0', 'in1']),
|
||||
...}
|
||||
|
||||
It outputs the following:
|
||||
|
||||
{0: set(['in0']),
|
||||
3: set(['in1']),
|
||||
...
|
||||
10: set(['in0', 'in1']),
|
||||
...
|
||||
34: set(['in0', 'in1']),
|
||||
37: set(['in1']),
|
||||
...
|
||||
62: set(['in0', 'in1']),
|
||||
65: set(['in1']),
|
||||
...
|
||||
75: set(['in0', 'in1']),
|
||||
...}
|
||||
"""
|
||||
if not DEBUG_SIMPLIFY:
|
||||
raise NotImplementedError("See docstring.")
|
||||
type_flow = self.type_flow
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
next_flow = type_flow.copy()
|
||||
for index, types in type_flow.items():
|
||||
if isinstance(types, set):
|
||||
next_types = set.union(
|
||||
*(type_flow.get(child_index,
|
||||
set([child_index]))
|
||||
for child_index in types))
|
||||
else:
|
||||
next_types = set([types])
|
||||
if next_types != types:
|
||||
next_flow[index] = next_types
|
||||
changed = True
|
||||
pp(next_flow)
|
||||
print()
|
||||
type_flow = next_flow
|
||||
return type_flow
|
||||
|
||||
def _op(self, i, op, opname, arg, args, *extras, **kws):
|
||||
self.type_flow[i] = set(args)
|
||||
|
||||
op_BINARY_ADD = _op
|
||||
op_BINARY_AND = _op
|
||||
op_BINARY_DIVIDE = _op
|
||||
op_BINARY_FLOOR_DIVIDE = _op
|
||||
op_BINARY_LSHIFT = _op
|
||||
op_BINARY_MODULO = _op
|
||||
op_BINARY_MULTIPLY = _op
|
||||
op_BINARY_OR = _op
|
||||
op_BINARY_POWER = _op
|
||||
op_BINARY_RSHIFT = _op
|
||||
op_BINARY_SUBSCR = _op
|
||||
op_BINARY_SUBTRACT = _op
|
||||
op_BINARY_TRUE_DIVIDE = _op
|
||||
op_BINARY_XOR = _op
|
||||
|
||||
#op_BUILD_CLASS = _do_nothing
|
||||
#op_BUILD_LIST = _do_nothing
|
||||
#op_BUILD_MAP = _do_nothing
|
||||
#op_BUILD_SET = _do_nothing
|
||||
#op_BUILD_SLICE = _do_nothing
|
||||
#op_BUILD_TUPLE = _do_nothing
|
||||
|
||||
#op_CALL_FUNCTION = _do_nothing
|
||||
#op_CALL_FUNCTION_KW = _do_nothing
|
||||
#op_CALL_FUNCTION_VAR = _do_nothing
|
||||
#op_CALL_FUNCTION_VAR_KW = _do_nothing
|
||||
|
||||
def op_COMPARE_OP(self, i, op, opname, arg, args, *extras, **kws):
|
||||
self.requirements[i] = set(args)
|
||||
self.type_flow[i] = bool
|
||||
|
||||
#op_CONTINUE_LOOP = _do_nothing
|
||||
#op_DELETE_ATTR = _do_nothing
|
||||
#op_DELETE_DEREF = _do_nothing
|
||||
#op_DELETE_FAST = _do_nothing
|
||||
#op_DELETE_GLOBAL = _do_nothing
|
||||
#op_DELETE_NAME = _do_nothing
|
||||
#op_DELETE_SLICE = _do_nothing
|
||||
#op_DELETE_SUBSCR = _do_nothing
|
||||
#op_END_FINALLY = _do_nothing
|
||||
#op_EXEC_STMT = _do_nothing
|
||||
#op_EXTENDED_ARG = _do_nothing
|
||||
#op_FOR_ITER = _do_nothing
|
||||
#op_GET_ITER = _do_nothing
|
||||
#op_IMPORT_FROM = _do_nothing
|
||||
#op_IMPORT_NAME = _do_nothing
|
||||
#op_IMPORT_STAR = _do_nothing
|
||||
|
||||
op_INPLACE_ADD = _op
|
||||
op_INPLACE_AND = _op
|
||||
op_INPLACE_DIVIDE = _op
|
||||
op_INPLACE_FLOOR_DIVIDE = _op
|
||||
op_INPLACE_LSHIFT = _op
|
||||
op_INPLACE_MODULO = _op
|
||||
op_INPLACE_MULTIPLY = _op
|
||||
op_INPLACE_OR = _op
|
||||
op_INPLACE_POWER = _op
|
||||
op_INPLACE_RSHIFT = _op
|
||||
op_INPLACE_SUBTRACT = _op
|
||||
op_INPLACE_TRUE_DIVIDE = _op
|
||||
op_INPLACE_XOR = _op
|
||||
|
||||
#op_JUMP_ABSOLUTE = _do_nothing
|
||||
#op_JUMP_FORWARD = _do_nothing
|
||||
#op_JUMP_IF_FALSE = _do_nothing
|
||||
#op_JUMP_IF_FALSE_OR_POP = _do_nothing
|
||||
#op_JUMP_IF_TRUE = _do_nothing
|
||||
#op_JUMP_IF_TRUE_OR_POP = _do_nothing
|
||||
#op_LIST_APPEND = _do_nothing
|
||||
#op_LOAD_ATTR = _do_nothing
|
||||
#op_LOAD_BUILD_CLASS = _do_nothing
|
||||
#op_LOAD_CLOSURE = _do_nothing
|
||||
|
||||
def op_LOAD_CONST(self, i, op, opname, arg, args, *extras, **kws):
|
||||
self.type_flow[i] = type(self.co_obj.co_consts[arg])
|
||||
|
||||
def op_LOAD_DEREF(self, i, op, opname, arg, args, *extras, **kws):
|
||||
if arg not in self.refs:
|
||||
result = set(('inref%d' % arg,))
|
||||
self.refs[arg] = result
|
||||
else:
|
||||
result = self.refs[arg]
|
||||
self.type_flow[i] = result
|
||||
|
||||
def op_LOAD_FAST(self, i, op, opname, arg, args, *extras, **kws):
|
||||
if arg not in self.locals:
|
||||
if arg < self.co_obj.co_argcount:
|
||||
result = set(('in%d' % arg,))
|
||||
else:
|
||||
result = set()
|
||||
self.locals[arg] = result
|
||||
else:
|
||||
result = self.locals[arg]
|
||||
self.type_flow[i] = result
|
||||
|
||||
def op_LOAD_GLOBAL(self, i, op, opname, arg, args, *extras, **kws):
|
||||
if arg not in self.globals:
|
||||
result = set(('in%d' % arg,))
|
||||
self.globals[arg] = result
|
||||
else:
|
||||
result = self.globals[arg]
|
||||
self.type_flow[i] = result
|
||||
|
||||
#op_LOAD_LOCALS = _do_nothing
|
||||
#op_LOAD_NAME = _do_nothing
|
||||
#op_MAKE_CLOSURE = _do_nothing
|
||||
#op_MAKE_FUNCTION = _do_nothing
|
||||
#op_MAP_ADD = _do_nothing
|
||||
#op_NOP = _do_nothing
|
||||
#op_POP_BLOCK = _do_nothing
|
||||
#op_POP_EXCEPT = _do_nothing
|
||||
#op_POP_JUMP_IF_FALSE = _do_nothing
|
||||
#op_POP_JUMP_IF_TRUE = _do_nothing
|
||||
#op_POP_TOP = _do_nothing
|
||||
#op_PRINT_EXPR = _do_nothing
|
||||
#op_PRINT_ITEM = _do_nothing
|
||||
#op_PRINT_ITEM_TO = _do_nothing
|
||||
#op_PRINT_NEWLINE = _do_nothing
|
||||
#op_PRINT_NEWLINE_TO = _do_nothing
|
||||
#op_RAISE_VARARGS = _do_nothing
|
||||
|
||||
op_RETURN_VALUE = _op
|
||||
|
||||
#op_SETUP_EXCEPT = _do_nothing
|
||||
#op_SETUP_FINALLY = _do_nothing
|
||||
#op_SETUP_LOOP = _do_nothing
|
||||
#op_SETUP_WITH = _do_nothing
|
||||
#op_SET_ADD = _do_nothing
|
||||
|
||||
#op_SLICE = _do_nothing
|
||||
|
||||
#op_STOP_CODE = _do_nothing
|
||||
#op_STORE_ATTR = _do_nothing
|
||||
#op_STORE_DEREF = _do_nothing
|
||||
|
||||
def op_STORE_FAST(self, i, op, opname, arg, args, *extras, **kws):
|
||||
if arg not in self.locals:
|
||||
if arg < self.co_obj.co_argcount:
|
||||
result = set(('in%d' % arg,))
|
||||
else:
|
||||
result = set()
|
||||
self.locals[arg] = result
|
||||
else:
|
||||
result = self.locals[arg]
|
||||
assert len(args) == 1
|
||||
result.add(args[0])
|
||||
|
||||
#op_STORE_GLOBAL = _do_nothing
|
||||
#op_STORE_LOCALS = _do_nothing
|
||||
#op_STORE_MAP = _do_nothing
|
||||
#op_STORE_NAME = _do_nothing
|
||||
#op_STORE_SLICE = _do_nothing
|
||||
#op_STORE_SUBSCR = _do_nothing
|
||||
|
||||
op_UNARY_CONVERT = _op
|
||||
op_UNARY_INVERT = _op
|
||||
op_UNARY_NEGATIVE = _op
|
||||
op_UNARY_NOT = _op
|
||||
op_UNARY_POSITIVE = _op
|
||||
|
||||
#op_UNPACK_EX = _do_nothing
|
||||
#op_UNPACK_SEQUENCE = _do_nothing
|
||||
#op_WITH_CLEANUP = _do_nothing
|
||||
#op_YIELD_VALUE = _do_nothing
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Function definition(s)
|
||||
|
||||
def build_type_flow(func):
|
||||
from .opcode_util import get_code_object
|
||||
from .addr_flow import AddressFlowBuilder
|
||||
blocks = AddressFlowBuilder.build_flow(func)
|
||||
ty_builder = TypeFlowBuilder(get_code_object(func))
|
||||
ty_builder.visit(blocks)
|
||||
return ty_builder.get_type_eqns()
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
||||
def main(*args):
|
||||
import pprint
|
||||
from .tests import llfuncs
|
||||
if not args:
|
||||
args = ('pymod',)
|
||||
for arg in args:
|
||||
pprint.pprint(build_type_flow(getattr(llfuncs, arg)))
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
main(*sys.argv[1:])
|
||||
|
||||
# ______________________________________________________________________
|
||||
# End of type_flow.py
|
||||
|
|
@ -2,7 +2,6 @@ import re
|
|||
import sys
|
||||
from distutils.spawn import find_executable
|
||||
from os.path import abspath, dirname, isfile, join
|
||||
from os import listdir
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
|
||||
|
|
@ -40,17 +39,57 @@ def libs_options():
|
|||
# NOTE: instead of actually looking at the components requested,
|
||||
# we just print out a bunch of libs
|
||||
for lib in """
|
||||
LLVMAnalysis
|
||||
LLVMAsmParser
|
||||
LLVMAsmPrinter
|
||||
LLVMBitReader
|
||||
LLVMBitWriter
|
||||
LLVMCodeGen
|
||||
LLVMCore
|
||||
LLVMExecutionEngine
|
||||
LLVMInstCombine
|
||||
LLVMInstrumentation
|
||||
LLVMInterpreter
|
||||
LLVMipa
|
||||
LLVMipo
|
||||
LLVMJIT
|
||||
LLVMMCJIT
|
||||
LLVMLinker
|
||||
LLVMMC
|
||||
LLVMMCParser
|
||||
LLVMObject
|
||||
LLVMRuntimeDyld
|
||||
LLVMScalarOpts
|
||||
LLVMSelectionDAG
|
||||
LLVMSupport
|
||||
LLVMTarget
|
||||
LLVMTransformUtils
|
||||
LLVMVectorize
|
||||
LLVMX86AsmParser
|
||||
LLVMX86AsmPrinter
|
||||
LLVMX86CodeGen
|
||||
LLVMX86Desc
|
||||
LLVMX86Disassembler
|
||||
LLVMX86Info
|
||||
LLVMX86Utils
|
||||
LLVMDebugInfo
|
||||
Advapi32
|
||||
Shell32
|
||||
""".split():
|
||||
print('-l%s' % lib)
|
||||
|
||||
bpath = join(find_llvm_prefix(), 'lib')
|
||||
for filename in listdir(bpath):
|
||||
filepath = join(bpath, filename)
|
||||
if isfile(filepath) and filename.endswith('.lib') and filename.startswith('LLVM'):
|
||||
name = filename.split('.', 1)[0]
|
||||
print('-l%s' % name)
|
||||
if isfile(join(find_llvm_prefix(), 'lib', 'LLVMPTXCodeGen.lib')):
|
||||
print('-lLLVMPTXAsmPrinter')
|
||||
print('-lLLVMPTXCodeGen')
|
||||
print('-lLLVMPTXDesc')
|
||||
print('-lLLVMPTXInfo')
|
||||
|
||||
elif isfile(join(find_llvm_prefix(), 'lib', 'LLVMNVPTXCodeGen.lib')):
|
||||
print('-lLLVMNVPTXAsmPrinter')
|
||||
print('-lLLVMNVPTXCodeGen')
|
||||
print('-lLLVMNVPTXDesc')
|
||||
print('-lLLVMNVPTXInfo')
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@ del get_versions
|
|||
|
||||
|
||||
from llvmpy import extra
|
||||
|
||||
version = extra.get_llvm_version()
|
||||
del extra
|
||||
|
||||
class Wrapper(object):
|
||||
__slots__ = '__ptr'
|
||||
|
||||
def __init__(self, ptr):
|
||||
assert ptr
|
||||
self.__ptr = ptr
|
||||
|
|
@ -34,14 +31,14 @@ def _extract_ptrs(objs):
|
|||
class LLVMException(Exception):
|
||||
pass
|
||||
|
||||
def test(verbosity=3, run_isolated=True):
|
||||
def test(verbosity=1):
|
||||
"""test(verbosity=1) -> TextTestResult
|
||||
|
||||
Run self-test, and return the number of failures + errors
|
||||
"""
|
||||
from llvm.tests import run
|
||||
from llvm.test_llvmpy import run
|
||||
|
||||
result = run(verbosity=verbosity, run_isolated=run_isolated)
|
||||
errct = len(result.failures) + len(result.errors)
|
||||
result = run(verbosity=verbosity)
|
||||
|
||||
return len(result.failures) + len(result.errors)
|
||||
|
||||
return errct
|
||||
|
|
|
|||
125
llvm/core.py
125
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
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
|
|
@ -211,8 +211,6 @@ class CCEnum(Enum):
|
|||
CC_X86_THISCALL = ID.X86_ThisCall
|
||||
CC_PTX_KERNEL = ID.PTX_Kernel
|
||||
CC_PTX_DEVICE = ID.PTX_Device
|
||||
|
||||
if llvm.version <= (3, 3):
|
||||
CC_MBLAZE_INTR = ID.MBLAZE_INTR
|
||||
CC_MBLAZE_SVOL = ID.MBLAZE_SVOL
|
||||
|
||||
|
|
@ -379,7 +377,6 @@ class Module(llvm.Wrapper):
|
|||
|
||||
module_obj = Module.new('my_module')
|
||||
"""
|
||||
__slots__ = '__weakref__'
|
||||
__cache = weakref.WeakValueDictionary()
|
||||
|
||||
def __new__(cls, ptr):
|
||||
|
|
@ -452,12 +449,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 self._ptr == rhs._ptr
|
||||
return str(self) == str(rhs)
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, rhs):
|
||||
return not (self == rhs)
|
||||
|
|
@ -689,7 +686,6 @@ 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):
|
||||
|
|
@ -882,7 +878,6 @@ class Type(llvm.Wrapper):
|
|||
|
||||
class IntegerType(Type):
|
||||
"""Represents an integer type."""
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.IntegerType
|
||||
|
||||
@property
|
||||
|
|
@ -892,7 +887,6 @@ class IntegerType(Type):
|
|||
|
||||
class FunctionType(Type):
|
||||
"""Represents a function type."""
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.FunctionType
|
||||
|
||||
@property
|
||||
|
|
@ -922,7 +916,6 @@ class FunctionType(Type):
|
|||
class StructType(Type):
|
||||
"""Represents a structure type."""
|
||||
_type_ = api.llvm.StructType
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def element_count(self):
|
||||
|
|
@ -981,7 +974,6 @@ class StructType(Type):
|
|||
class ArrayType(Type):
|
||||
"""Represents an array type."""
|
||||
_type_ = api.llvm.ArrayType
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def element(self):
|
||||
|
|
@ -993,7 +985,6 @@ class ArrayType(Type):
|
|||
|
||||
class PointerType(Type):
|
||||
_type_ = api.llvm.PointerType
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def pointee(self):
|
||||
|
|
@ -1005,7 +996,6 @@ class PointerType(Type):
|
|||
|
||||
class VectorType(Type):
|
||||
_type_ = api.llvm.VectorType
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def element(self):
|
||||
|
|
@ -1017,7 +1007,6 @@ class VectorType(Type):
|
|||
|
||||
class Value(llvm.Wrapper):
|
||||
_type_ = api.llvm.Value
|
||||
__slots__ = '__weakref__'
|
||||
|
||||
def __init__(self, builder, ptr):
|
||||
assert builder is _ValueFactory
|
||||
|
|
@ -1086,7 +1075,6 @@ class Value(llvm.Wrapper):
|
|||
|
||||
class User(Value):
|
||||
_type_ = api.llvm.User
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def operand_count(self):
|
||||
|
|
@ -1101,7 +1089,6 @@ class User(Value):
|
|||
|
||||
class Constant(User):
|
||||
_type_ = api.llvm.Constant
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def null(ty):
|
||||
|
|
@ -1287,7 +1274,6 @@ class Constant(User):
|
|||
|
||||
class ConstantExpr(Constant):
|
||||
_type_ = api.llvm.ConstantExpr
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def opcode(self):
|
||||
|
|
@ -1298,20 +1284,19 @@ class ConstantExpr(Constant):
|
|||
return self._ptr.getOpcodeName()
|
||||
|
||||
class ConstantAggregateZero(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantDataArray(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantDataVector(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantInt(Constant):
|
||||
_type_ = api.llvm.ConstantInt
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def z_ext_value(self):
|
||||
|
|
@ -1327,32 +1312,30 @@ class ConstantInt(Constant):
|
|||
|
||||
|
||||
class ConstantFP(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantArray(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantStruct(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantVector(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class ConstantPointerNull(Constant):
|
||||
__slots__ = ()
|
||||
pass
|
||||
|
||||
|
||||
class UndefValue(Constant):
|
||||
__slots__ = ()
|
||||
|
||||
pass
|
||||
|
||||
class GlobalValue(Constant):
|
||||
_type_ = api.llvm.GlobalValue
|
||||
__slots__ = ()
|
||||
|
||||
def _get_linkage(self):
|
||||
return self._ptr.getLinkage()
|
||||
|
|
@ -1398,7 +1381,6 @@ class GlobalValue(Constant):
|
|||
|
||||
class GlobalVariable(GlobalValue):
|
||||
_type_ = api.llvm.GlobalVariable
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def new(module, ty, name, addrspace=0):
|
||||
|
|
@ -1459,7 +1441,6 @@ 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])
|
||||
|
|
@ -1560,7 +1541,6 @@ class Argument(Value):
|
|||
return self._ptr.hasStructRetAttr()
|
||||
|
||||
class Function(GlobalValue):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.Function
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1670,9 +1650,6 @@ class Function(GlobalValue):
|
|||
context = api.llvm.getGlobalContext()
|
||||
attrbldr = api.llvm.AttrBuilder.new()
|
||||
attrbldr.addAttribute(attr)
|
||||
if llvm.version >= (3, 3):
|
||||
attrs = api.llvm.Attribute.get(context, attrbldr)
|
||||
else:
|
||||
attrs = api.llvm.Attributes.get(context, attrbldr)
|
||||
self._ptr.removeFnAttr(attrs)
|
||||
|
||||
|
|
@ -1699,7 +1676,6 @@ class Function(GlobalValue):
|
|||
#===----------------------------------------------------------------------===
|
||||
|
||||
class InlineAsm(Value):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.InlineAsm
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1714,7 +1690,6 @@ class InlineAsm(Value):
|
|||
#===----------------------------------------------------------------------===
|
||||
|
||||
class MetaData(Value):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.MDNode
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1771,7 +1746,6 @@ class MetaDataString(Value):
|
|||
|
||||
|
||||
class NamedMetaData(llvm.Wrapper):
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def get_or_insert(mod, name):
|
||||
|
|
@ -1801,7 +1775,6 @@ class NamedMetaData(llvm.Wrapper):
|
|||
#===----------------------------------------------------------------------===
|
||||
|
||||
class Instruction(User):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.Instruction
|
||||
|
||||
@property
|
||||
|
|
@ -1883,7 +1856,6 @@ class Instruction(User):
|
|||
|
||||
|
||||
class CallOrInvokeInstruction(Instruction):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.CallInst, api.llvm.InvokeInst
|
||||
|
||||
def _get_cc(self):
|
||||
|
|
@ -1898,33 +1870,21 @@ class CallOrInvokeInstruction(Instruction):
|
|||
context = api.llvm.getGlobalContext()
|
||||
attrbldr = api.llvm.AttrBuilder.new()
|
||||
attrbldr.addAttribute(attr)
|
||||
if llvm.version >= (3, 3):
|
||||
attrs = api.llvm.Attribute.get(context, attrbldr)
|
||||
else:
|
||||
attrs = api.llvm.Attributes.get(context, attrbldr)
|
||||
|
||||
self._ptr.addAttribute(idx, attrs)
|
||||
|
||||
def remove_parameter_attribute(self, idx, attr):
|
||||
context = api.llvm.getGlobalContext()
|
||||
attrbldr = api.llvm.AttrBuilder.new()
|
||||
attrbldr.addAttribute(attr)
|
||||
if llvm.version >= (3, 3):
|
||||
attrs = api.llvm.Attribute.get(context, attrbldr)
|
||||
else:
|
||||
attrs = api.llvm.Attributes.get(context, attrbldr)
|
||||
|
||||
self._ptr.removeAttribute(idx, attrs)
|
||||
|
||||
def set_parameter_alignment(self, idx, align):
|
||||
context = api.llvm.getGlobalContext()
|
||||
attrbldr = api.llvm.AttrBuilder.new()
|
||||
attrbldr.addAlignmentAttr(align)
|
||||
if llvm.version >= (3, 3):
|
||||
attrs = api.llvm.Attribute.get(context, attrbldr)
|
||||
else:
|
||||
attrs = api.llvm.Attributes.get(context, attrbldr)
|
||||
|
||||
self._ptr.addAttribute(idx, attrs)
|
||||
|
||||
def _get_called_function(self):
|
||||
|
|
@ -1939,7 +1899,6 @@ class CallOrInvokeInstruction(Instruction):
|
|||
|
||||
|
||||
class PHINode(Instruction):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.PHINode
|
||||
|
||||
@property
|
||||
|
|
@ -1957,7 +1916,6 @@ class PHINode(Instruction):
|
|||
|
||||
|
||||
class SwitchInstruction(Instruction):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.SwitchInst
|
||||
|
||||
def add_case(self, const, bblk):
|
||||
|
|
@ -1965,7 +1923,6 @@ class SwitchInstruction(Instruction):
|
|||
|
||||
|
||||
class CompareInstruction(Instruction):
|
||||
__slots__ = ()
|
||||
_type_ = api.llvm.CmpInst
|
||||
|
||||
@property
|
||||
|
|
@ -1977,40 +1934,11 @@ 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):
|
||||
|
|
@ -2037,7 +1965,6 @@ class BasicBlock(Value):
|
|||
|
||||
|
||||
class _ValueFactory(object):
|
||||
__slots__ = ()
|
||||
cache = weakref.WeakValueDictionary()
|
||||
|
||||
# value ID -> class map
|
||||
|
|
@ -2066,8 +1993,7 @@ 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_ALLOCA : AllocaInstruction,
|
||||
VALUE_INSTRUCTION + OPCODE_FCMP : CompareInstruction
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
@ -2114,7 +2040,6 @@ _atomic_orderings = {
|
|||
}
|
||||
|
||||
class Builder(llvm.Wrapper):
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def new(basic_block):
|
||||
|
|
@ -2276,6 +2201,7 @@ 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,
|
||||
|
|
@ -2289,6 +2215,7 @@ 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,
|
||||
|
|
@ -2301,13 +2228,12 @@ class Builder(llvm.Wrapper):
|
|||
inst = self._ptr.Insert(malloc, name)
|
||||
return _make_value(inst)
|
||||
|
||||
def alloca(self, ty, size=None, name=""):
|
||||
sizeptr = size._ptr if size else None
|
||||
return _make_value(self._ptr.CreateAlloca(ty._ptr, sizeptr, name))
|
||||
def alloca(self, ty, name=""):
|
||||
intty = Type.int()
|
||||
return _make_value(self._ptr.CreateAlloca(ty._ptr, None, name))
|
||||
|
||||
@deprecated
|
||||
def alloca_array(self, ty, size, name=""):
|
||||
return self.alloca(ty, size, name=name)
|
||||
return _make_value(self._ptr.CreateAlloca(ty._ptr, size._ptr, name))
|
||||
|
||||
def free(self, ptr):
|
||||
free = api.llvm.CallInst.CreateFree(ptr._ptr, self.basic_block._ptr)
|
||||
|
|
@ -2394,20 +2320,15 @@ class Builder(llvm.Wrapper):
|
|||
# misc
|
||||
|
||||
def extract_value(self, retval, idx, name=""):
|
||||
if not isinstance(idx, (tuple, list)):
|
||||
idx = [idx]
|
||||
return _make_value(self._ptr.CreateExtractValue(retval._ptr, idx,
|
||||
name))
|
||||
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=""):
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
"""
|
||||
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
|
||||
195
llvm/ee.py
195
llvm/ee.py
|
|
@ -31,22 +31,33 @@
|
|||
"Execution Engine and related classes."
|
||||
|
||||
import sys
|
||||
from io import BytesIO
|
||||
import contextlib
|
||||
|
||||
import llvm
|
||||
from llvm import core
|
||||
from llvm.passes import TargetData, TargetTransformInfo
|
||||
from llvmpy import api, extra
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
# import items which were moved to target module
|
||||
# Enumerations
|
||||
#===----------------------------------------------------------------------===
|
||||
from llvm.target import (initialize_all, initialize_target,
|
||||
print_registered_targets, get_host_cpu_name, get_default_triple,
|
||||
TargetMachine,
|
||||
BO_BIG_ENDIAN, BO_LITTLE_ENDIAN,
|
||||
CM_DEFAULT, CM_JITDEFAULT, CM_SMALL, CM_KERNEL, CM_MEDIUM, CM_LARGE,
|
||||
RELOC_DEFAULT, RELOC_STATIC, RELOC_PIC, RELOC_DYNAMIC_NO_PIC)
|
||||
|
||||
BO_BIG_ENDIAN = 0
|
||||
BO_LITTLE_ENDIAN = 1
|
||||
|
||||
# CodeModel
|
||||
CM_DEFAULT = api.llvm.CodeModel.Model.Default
|
||||
CM_JITDEFAULT = api.llvm.CodeModel.Model.JITDefault
|
||||
CM_SMALL = api.llvm.CodeModel.Model.Small
|
||||
CM_KERNEL = api.llvm.CodeModel.Model.Kernel
|
||||
CM_MEDIUM = api.llvm.CodeModel.Model.Medium
|
||||
CM_LARGE = api.llvm.CodeModel.Model.Large
|
||||
|
||||
# Reloc
|
||||
RELOC_DEFAULT = api.llvm.Reloc.Model.Default
|
||||
RELOC_STATIC = api.llvm.Reloc.Model.Static
|
||||
RELOC_PIC = api.llvm.Reloc.Model.PIC_
|
||||
RELOC_DYNAMIC_NO_PIC = api.llvm.Reloc.Model.DynamicNoPIC
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
# Generic value
|
||||
|
|
@ -228,6 +239,150 @@ class ExecutionEngine(llvm.Wrapper):
|
|||
ptr = self._ptr.getDataLayout()
|
||||
return TargetData(ptr)
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
# Target machine
|
||||
#===----------------------------------------------------------------------===
|
||||
|
||||
def initialize_target(target, noraise=False):
|
||||
"""Initialize target by name.
|
||||
It is safe to initialize the same target multiple times.
|
||||
"""
|
||||
prefix = 'LLVMInitialize'
|
||||
postfixes = ['Target', 'TargetInfo', 'TargetMC', 'AsmPrinter', 'AsmParser']
|
||||
try:
|
||||
for postfix in postfixes:
|
||||
getattr(api, '%s%s%s' % (prefix, target, postfix))()
|
||||
except AttributeError:
|
||||
if noraise:
|
||||
return False
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def print_registered_targets():
|
||||
'''
|
||||
Note: print directly to stdout
|
||||
'''
|
||||
api.llvm.TargetRegistry.printRegisteredTargetsForVersion()
|
||||
|
||||
def get_host_cpu_name():
|
||||
'''return the string name of the host CPU
|
||||
'''
|
||||
return api.llvm.sys.getHostCPUName()
|
||||
|
||||
def get_default_triple():
|
||||
'''return the target triple of the host in str-rep
|
||||
'''
|
||||
return api.llvm.sys.getDefaultTargetTriple()
|
||||
|
||||
|
||||
class TargetMachine(llvm.Wrapper):
|
||||
|
||||
@staticmethod
|
||||
def new(triple='', cpu='', features='', opt=2, cm=CM_DEFAULT,
|
||||
reloc=RELOC_DEFAULT):
|
||||
if not triple:
|
||||
triple = get_default_triple()
|
||||
if not cpu:
|
||||
cpu = get_host_cpu_name()
|
||||
with contextlib.closing(BytesIO()) as error:
|
||||
target = api.llvm.TargetRegistry.lookupTarget(triple, error)
|
||||
if not target:
|
||||
raise llvm.LLVMException(error.getvalue())
|
||||
if not target.hasTargetMachine():
|
||||
raise llvm.LLVMException(target, "No target machine.")
|
||||
target_options = api.llvm.TargetOptions.new()
|
||||
tm = target.createTargetMachine(triple, cpu, features,
|
||||
target_options,
|
||||
reloc, cm, opt)
|
||||
if not tm:
|
||||
raise llvm.LLVMException("Cannot create target machine")
|
||||
return TargetMachine(tm)
|
||||
|
||||
@staticmethod
|
||||
def lookup(arch, cpu='', features='', opt=2, cm=CM_DEFAULT,
|
||||
reloc=RELOC_DEFAULT):
|
||||
'''create a targetmachine given an architecture name
|
||||
|
||||
For a list of architectures,
|
||||
use: `llc -help`
|
||||
|
||||
For a list of available CPUs,
|
||||
use: `llvm-as < /dev/null | llc -march=xyz -mcpu=help`
|
||||
|
||||
For a list of available attributes (features),
|
||||
use: `llvm-as < /dev/null | llc -march=xyz -mattr=help`
|
||||
'''
|
||||
triple = api.llvm.Triple.new()
|
||||
with contextlib.closing(BytesIO()) as error:
|
||||
target = api.llvm.TargetRegistry.lookupTarget(arch, triple, error)
|
||||
if not target:
|
||||
raise llvm.LLVMException(error.getvalue())
|
||||
if not target.hasTargetMachine():
|
||||
raise llvm.LLVMException(target, "No target machine.")
|
||||
target_options = api.llvm.TargetOptions.new()
|
||||
tm = target.createTargetMachine(str(triple), cpu, features,
|
||||
target_options,
|
||||
reloc, cm, opt)
|
||||
if not tm:
|
||||
raise llvm.LLVMException("Cannot create target machine")
|
||||
return TargetMachine(tm)
|
||||
|
||||
def _emit_file(self, module, cgft):
|
||||
pm = api.llvm.PassManager.new()
|
||||
os = extra.make_raw_ostream_for_printing()
|
||||
pm.add(api.llvm.DataLayout.new(str(self.target_data)))
|
||||
failed = self._ptr.addPassesToEmitFile(pm, os, cgft)
|
||||
pm.run(module)
|
||||
|
||||
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
if cgft == CGFT.CGFT_ObjectFile:
|
||||
return os.bytes()
|
||||
else:
|
||||
return os.str()
|
||||
|
||||
def emit_assembly(self, module):
|
||||
'''returns byte string of the module as assembly code of the target machine
|
||||
'''
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
return self._emit_file(module._ptr, CGFT.CGFT_AssemblyFile)
|
||||
|
||||
def emit_object(self, module):
|
||||
'''returns byte string of the module as native code of the target machine
|
||||
'''
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
return self._emit_file(module._ptr, CGFT.CGFT_ObjectFile)
|
||||
|
||||
@property
|
||||
def target_data(self):
|
||||
'''get target data of this machine
|
||||
'''
|
||||
return TargetData(self._ptr.getDataLayout())
|
||||
|
||||
@property
|
||||
def target_name(self):
|
||||
return self._ptr.getTarget().getName()
|
||||
|
||||
@property
|
||||
def target_short_description(self):
|
||||
return self._ptr.getTarget().getShortDescription()
|
||||
|
||||
@property
|
||||
def triple(self):
|
||||
return self._ptr.getTargetTriple()
|
||||
|
||||
@property
|
||||
def cpu(self):
|
||||
return self._ptr.getTargetCPU()
|
||||
|
||||
@property
|
||||
def feature_string(self):
|
||||
return self._ptr.getTargetFeatureString()
|
||||
|
||||
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
# Dynamic Library
|
||||
|
|
@ -238,27 +393,3 @@ 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)
|
||||
|
|
|
|||
|
|
@ -1,242 +0,0 @@
|
|||
import sys
|
||||
|
||||
import llvm
|
||||
if llvm.version < (3, 4):
|
||||
raise Exception("mc is not supported for llvm version less than 3.4")
|
||||
|
||||
from io import BytesIO
|
||||
import contextlib
|
||||
|
||||
from llvmpy import api, extra
|
||||
from llvmpy.api.llvm import MCDisassembler
|
||||
|
||||
class Operand(object):
|
||||
|
||||
def __init__(self, mcoperand, target_machine):
|
||||
'''
|
||||
@mcoperand: an MCOperand object
|
||||
@target_machine: an llvm.target.TargetMachine object
|
||||
'''
|
||||
|
||||
self.op = mcoperand
|
||||
if not self.op:
|
||||
raise llvm.LLVMException("null MCOperand argument")
|
||||
|
||||
self.tm = target_machine
|
||||
|
||||
def __str__(self):
|
||||
s = "invalid"
|
||||
if self.is_reg():
|
||||
s = "reg(%s)" % (self.reg_name())
|
||||
elif self.is_imm():
|
||||
s = "imm(0x%02x)" % (self.op.getImm())
|
||||
elif self.is_fp_imm():
|
||||
s = "imm(%r)" % (self.op.getFPImm())
|
||||
elif self.is_expr():
|
||||
s = "expr(%r)" % (self.op.getExpr().getKind())
|
||||
elif self.is_inst():
|
||||
s = repr(Instr(self.op.getInst()))
|
||||
|
||||
return s
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def reg_name(self):
|
||||
if self.is_reg():
|
||||
s = self.tm.reg_info.getName(self.op.getReg())
|
||||
if s.strip() == "":
|
||||
return "?"
|
||||
else:
|
||||
return s
|
||||
else:
|
||||
return ""
|
||||
|
||||
def is_reg(self):
|
||||
return self.op.isReg()
|
||||
|
||||
def is_imm(self):
|
||||
return self.op.isImm()
|
||||
|
||||
def is_fp_imm(self):
|
||||
return self.op.isFPImm()
|
||||
|
||||
def is_expr(self):
|
||||
return self.op.isExpr()
|
||||
|
||||
def is_inst(self):
|
||||
return self.op.isInst()
|
||||
|
||||
def get_imm(self):
|
||||
if self.is_imm():
|
||||
return self.op.getImm()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_fp_imm(self):
|
||||
if self.is_fp_imm():
|
||||
return self.op.getFPImm()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_inst(self):
|
||||
if self.is_inst():
|
||||
return Instr(self.op.getInst())
|
||||
else:
|
||||
return None
|
||||
|
||||
class Instr(object):
|
||||
|
||||
def __init__(self, mcinst, target_machine):
|
||||
'''
|
||||
@mcinst: an MCInst object
|
||||
@target_machine: an llvm.target.TargetMachine object
|
||||
'''
|
||||
|
||||
self.mcinst = mcinst
|
||||
if not self.mcinst:
|
||||
raise llvm.LLVMException("null MCInst argument")
|
||||
|
||||
self.tm = target_machine
|
||||
|
||||
def __str__(self):
|
||||
os = extra.make_raw_ostream_for_printing()
|
||||
self.tm.inst_printer.printInst(self.mcinst, os, "")
|
||||
return str(os.str())
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __len__(self):
|
||||
''' the number of operands '''
|
||||
return int(self.mcinst.size())
|
||||
|
||||
def operands(self):
|
||||
amt = self.mcinst.getNumOperands()
|
||||
if amt < 1:
|
||||
return []
|
||||
|
||||
l = []
|
||||
for i in range(0, amt):
|
||||
l.append(Operand(self.mcinst.getOperand(i), self.tm))
|
||||
|
||||
return l
|
||||
|
||||
@property
|
||||
def instr_desc(self):
|
||||
return self.tm.instr_info.get(self.opcode)
|
||||
|
||||
@property
|
||||
def flags(self):
|
||||
return self.instr_desc.getFlags()
|
||||
|
||||
@property
|
||||
def ts_flags(self):
|
||||
return self.instr_desc.TSFlags
|
||||
|
||||
@property
|
||||
def opcode(self):
|
||||
return self.mcinst.getOpcode()
|
||||
|
||||
def is_branch(self):
|
||||
return self.instr_desc.isBranch()
|
||||
|
||||
def is_cond_branch(self):
|
||||
return self.instr_desc.isConditionalBranch()
|
||||
|
||||
def is_uncond_branch(self):
|
||||
return self.instr_desc.isUnconditionalBranch()
|
||||
|
||||
def is_indirect_branch(self):
|
||||
return self.instr_desc.isIndirectBranch()
|
||||
|
||||
def is_call(self):
|
||||
return self.instr_desc.isCall()
|
||||
|
||||
def is_return(self):
|
||||
return self.instr_desc.isReturn()
|
||||
|
||||
def is_terminator(self):
|
||||
return self.instr_desc.isTerminator()
|
||||
|
||||
def is_barrier(self):
|
||||
return self.instr_desc.isBarrier()
|
||||
|
||||
class BadInstr(Instr):
|
||||
pass
|
||||
|
||||
class Disassembler(object):
|
||||
|
||||
def __init__(self, target_machine):
|
||||
self.tm = target_machine
|
||||
|
||||
@property
|
||||
def mdasm(self):
|
||||
return self.tm.disassembler
|
||||
|
||||
@property
|
||||
def mai(self):
|
||||
return self.tm.asm_info
|
||||
|
||||
def instr(self, mcinst):
|
||||
return Instr(mcinst, self.tm)
|
||||
|
||||
def bad_instr(self, mcinst):
|
||||
return BadInstr(mcinst, self.tm)
|
||||
|
||||
def decode(self, bs, base_addr, align=None):
|
||||
'''
|
||||
decodes the bytes in @bs into instructions and yields
|
||||
each instruction as it is decoded. @base_addr is the base address
|
||||
where the instruction bytes are from (not an offset into
|
||||
@bs). yields instructions in the form of (addr, data, inst) where
|
||||
addr is an integer, data is a tuple of integers and inst is an instance of
|
||||
llvm.mc.Instr. @align specifies the byte alignment of instructions and
|
||||
is only used if an un-decodable instruction is encountered, in which
|
||||
case the disassembler will skip the following bytes until the next
|
||||
aligned address. if @align is unspecified, the default alignment
|
||||
for the architecture will be used, however this may not be ideal
|
||||
for disassembly. for example, the default alignment for ARM is 1, but you
|
||||
probably want it to be 4 for the purposes of disassembling ARM
|
||||
instructions.
|
||||
'''
|
||||
|
||||
if isinstance(bs, str) and sys.version_info.major >= 3:
|
||||
bs = bytes(map(lambda c: ord(c), bs))
|
||||
elif not isinstance(bs, bytes):
|
||||
raise TypeError("expected bs to be either 'str' or 'bytes' but got %s" % type(bs))
|
||||
|
||||
code = api.llvm.StringRefMemoryObject.new(bs, base_addr)
|
||||
idx = 0
|
||||
if not isinstance(align, int) or align < 1:
|
||||
align = self.mai.getMinInstAlignment()
|
||||
|
||||
while(idx < code.getExtent()):
|
||||
inst = api.llvm.MCInst.new()
|
||||
addr = code.getBase() + idx
|
||||
status, size = self.mdasm.getInstruction(inst, code, addr)
|
||||
|
||||
if size < 1:
|
||||
size = (align - (idx % align))
|
||||
|
||||
amt_left = code.getExtent() - idx
|
||||
if amt_left >= size:
|
||||
data = code.readBytes(addr, size)
|
||||
elif amt_left < 1:
|
||||
break
|
||||
else:
|
||||
data = code.readBytes(addr, amt_left)
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
data = tuple(map(lambda b: ord(b), data))
|
||||
else:
|
||||
data = tuple(data)
|
||||
|
||||
if status == MCDisassembler.DecodeStatus.Fail:
|
||||
yield (addr, data, None)
|
||||
elif status == MCDisassembler.DecodeStatus.SoftFail:
|
||||
yield (addr, data, self.bad_instr(inst))
|
||||
else:
|
||||
yield (addr, data, self.instr(inst))
|
||||
|
||||
idx += size
|
||||
|
|
@ -80,15 +80,6 @@ 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):
|
||||
|
|
@ -129,7 +120,6 @@ class PassManagerBuilder(llvm.Wrapper):
|
|||
def disable_unroll_loops(self, disable):
|
||||
self._ptr.DisableUnrollLoops = disable
|
||||
|
||||
if llvm.version <= (3, 3):
|
||||
@property
|
||||
def disable_simplify_lib_calls(self):
|
||||
return self._ptr.DisableSimplifyLibCalls
|
||||
|
|
@ -320,15 +310,13 @@ class TargetTransformInfo(Pass):
|
|||
# Helpers
|
||||
#===----------------------------------------------------------------------===
|
||||
|
||||
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):
|
||||
def build_pass_managers(tm, opt=2, loop_vectorize=False, vectorize=False,
|
||||
inline_threshold=2000, 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.
|
||||
|
|
@ -337,18 +325,6 @@ def build_pass_managers(tm, opt=2, size=0, loop_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:
|
||||
|
|
@ -361,27 +337,22 @@ def build_pass_managers(tm, opt=2, size=0, loop_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)
|
||||
|
|
|
|||
266
llvm/target.py
266
llvm/target.py
|
|
@ -1,266 +0,0 @@
|
|||
import llvm
|
||||
from llvmpy import api, extra
|
||||
from io import BytesIO
|
||||
import contextlib
|
||||
from llvm.passes import TargetData
|
||||
|
||||
#===----------------------------------------------------------------------===
|
||||
# Enumerations
|
||||
#===----------------------------------------------------------------------===
|
||||
|
||||
BO_BIG_ENDIAN = 0
|
||||
BO_LITTLE_ENDIAN = 1
|
||||
|
||||
# CodeModel
|
||||
CM_DEFAULT = api.llvm.CodeModel.Model.Default
|
||||
CM_JITDEFAULT = api.llvm.CodeModel.Model.JITDefault
|
||||
CM_SMALL = api.llvm.CodeModel.Model.Small
|
||||
CM_KERNEL = api.llvm.CodeModel.Model.Kernel
|
||||
CM_MEDIUM = api.llvm.CodeModel.Model.Medium
|
||||
CM_LARGE = api.llvm.CodeModel.Model.Large
|
||||
|
||||
# Reloc
|
||||
RELOC_DEFAULT = api.llvm.Reloc.Model.Default
|
||||
RELOC_STATIC = api.llvm.Reloc.Model.Static
|
||||
RELOC_PIC = api.llvm.Reloc.Model.PIC_
|
||||
RELOC_DYNAMIC_NO_PIC = api.llvm.Reloc.Model.DynamicNoPIC
|
||||
|
||||
def initialize_all():
|
||||
api.llvm.InitializeAllTargets()
|
||||
api.llvm.InitializeAllTargetInfos()
|
||||
api.llvm.InitializeAllTargetMCs()
|
||||
api.llvm.InitializeAllAsmPrinters()
|
||||
api.llvm.InitializeAllDisassemblers()
|
||||
api.llvm.InitializeAllAsmParsers()
|
||||
|
||||
def initialize_target(target, noraise=False):
|
||||
"""Initialize target by name.
|
||||
It is safe to initialize the same target multiple times.
|
||||
"""
|
||||
prefix = 'LLVMInitialize'
|
||||
postfixes = ['Target', 'TargetInfo', 'TargetMC', 'AsmPrinter', 'AsmParser']
|
||||
try:
|
||||
for postfix in postfixes:
|
||||
getattr(api, '%s%s%s' % (prefix, target, postfix))()
|
||||
except AttributeError:
|
||||
if noraise:
|
||||
return False
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def print_registered_targets():
|
||||
'''
|
||||
Note: print directly to stdout
|
||||
'''
|
||||
api.llvm.TargetRegistry.printRegisteredTargetsForVersion()
|
||||
|
||||
def get_host_cpu_name():
|
||||
'''return the string name of the host CPU
|
||||
'''
|
||||
return api.llvm.sys.getHostCPUName()
|
||||
|
||||
def get_default_triple():
|
||||
'''return the target triple of the host in str-rep
|
||||
'''
|
||||
return api.llvm.sys.getDefaultTargetTriple()
|
||||
|
||||
class TargetMachine(llvm.Wrapper):
|
||||
|
||||
@staticmethod
|
||||
def new(triple='', cpu='', features='', opt=2, cm=CM_DEFAULT,
|
||||
reloc=RELOC_DEFAULT):
|
||||
if not triple:
|
||||
triple = get_default_triple()
|
||||
if not cpu:
|
||||
cpu = get_host_cpu_name()
|
||||
with contextlib.closing(BytesIO()) as error:
|
||||
target = api.llvm.TargetRegistry.lookupTarget(triple, error)
|
||||
if not target:
|
||||
raise llvm.LLVMException(error.getvalue())
|
||||
if not target.hasTargetMachine():
|
||||
raise llvm.LLVMException(target, "No target machine.")
|
||||
target_options = api.llvm.TargetOptions.new()
|
||||
tm = target.createTargetMachine(triple, cpu, features,
|
||||
target_options,
|
||||
reloc, cm, opt)
|
||||
if not tm:
|
||||
raise llvm.LLVMException("Cannot create target machine")
|
||||
return TargetMachine(tm)
|
||||
|
||||
@staticmethod
|
||||
def lookup(arch, cpu='', features='', opt=2, cm=CM_DEFAULT,
|
||||
reloc=RELOC_DEFAULT):
|
||||
'''create a targetmachine given an architecture name
|
||||
|
||||
For a list of architectures,
|
||||
use: `llc -help`
|
||||
|
||||
For a list of available CPUs,
|
||||
use: `llvm-as < /dev/null | llc -march=xyz -mcpu=help`
|
||||
|
||||
For a list of available attributes (features),
|
||||
use: `llvm-as < /dev/null | llc -march=xyz -mattr=help`
|
||||
'''
|
||||
triple = api.llvm.Triple.new()
|
||||
with contextlib.closing(BytesIO()) as error:
|
||||
target = api.llvm.TargetRegistry.lookupTarget(arch, triple, error)
|
||||
if not target:
|
||||
raise llvm.LLVMException(error.getvalue())
|
||||
if not target.hasTargetMachine():
|
||||
raise llvm.LLVMException(target, "No target machine.")
|
||||
target_options = api.llvm.TargetOptions.new()
|
||||
tm = target.createTargetMachine(str(triple), cpu, features,
|
||||
target_options,
|
||||
reloc, cm, opt)
|
||||
if not tm:
|
||||
raise llvm.LLVMException("Cannot create target machine")
|
||||
return TargetMachine(tm)
|
||||
|
||||
@staticmethod
|
||||
def x86():
|
||||
return TargetMachine.lookup('x86')
|
||||
|
||||
@staticmethod
|
||||
def x86_64():
|
||||
return TargetMachine.lookup('x86-64')
|
||||
|
||||
@staticmethod
|
||||
def arm():
|
||||
return TargetMachine.lookup('arm')
|
||||
|
||||
@staticmethod
|
||||
def thumb():
|
||||
return TargetMachine.lookup('thumb')
|
||||
|
||||
def _emit_file(self, module, cgft):
|
||||
pm = api.llvm.PassManager.new()
|
||||
os = extra.make_raw_ostream_for_printing()
|
||||
pm.add(api.llvm.DataLayout.new(str(self.target_data)))
|
||||
failed = self._ptr.addPassesToEmitFile(pm, os, cgft)
|
||||
pm.run(module)
|
||||
|
||||
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
if cgft == CGFT.CGFT_ObjectFile:
|
||||
return os.bytes()
|
||||
else:
|
||||
return os.str()
|
||||
|
||||
def emit_assembly(self, module):
|
||||
'''returns byte string of the module as assembly code of the target machine
|
||||
'''
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
return self._emit_file(module._ptr, CGFT.CGFT_AssemblyFile)
|
||||
|
||||
def emit_object(self, module):
|
||||
'''returns byte string of the module as native code of the target machine
|
||||
'''
|
||||
CGFT = api.llvm.TargetMachine.CodeGenFileType
|
||||
return self._emit_file(module._ptr, CGFT.CGFT_ObjectFile)
|
||||
|
||||
@property
|
||||
def target_data(self):
|
||||
'''get target data of this machine
|
||||
'''
|
||||
return TargetData(self._ptr.getDataLayout())
|
||||
|
||||
@property
|
||||
def target_name(self):
|
||||
return self._ptr.getTarget().getName()
|
||||
|
||||
@property
|
||||
def target_short_description(self):
|
||||
return self._ptr.getTarget().getShortDescription()
|
||||
|
||||
@property
|
||||
def triple(self):
|
||||
return self._ptr.getTargetTriple()
|
||||
|
||||
@property
|
||||
def cpu(self):
|
||||
return self._ptr.getTargetCPU()
|
||||
|
||||
@property
|
||||
def feature_string(self):
|
||||
return self._ptr.getTargetFeatureString()
|
||||
|
||||
@property
|
||||
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):
|
||||
mri = self._ptr.getRegisterInfo()
|
||||
if not mri:
|
||||
raise llvm.LLVMException("no reg info for this machine")
|
||||
|
||||
return mri
|
||||
|
||||
@property
|
||||
def subtarget_info(self):
|
||||
sti = self._ptr.getSubtargetImpl()
|
||||
if not sti:
|
||||
raise llvm.LLVMException("no subtarget info for this machine")
|
||||
|
||||
return sti
|
||||
|
||||
@property
|
||||
def asm_info(self):
|
||||
ai = self._ptr.getMCAsmInfo()
|
||||
if not ai:
|
||||
raise llvm.LLVMException("no asm info for this machine")
|
||||
|
||||
return ai
|
||||
|
||||
@property
|
||||
def instr_info(self):
|
||||
ii = self._ptr.getInstrInfo()
|
||||
if not ii:
|
||||
raise llvm.LLVMException("no instr info for this machine")
|
||||
|
||||
return ii
|
||||
|
||||
@property
|
||||
def instr_analysis(self):
|
||||
if not getattr(self, '_mia', False):
|
||||
self._mia = self.target.createMCInstrAnalysis(self.instr_info)
|
||||
if not self._mia:
|
||||
raise llvm.LLVMException("no instr analysis for this machine")
|
||||
|
||||
return self._mia
|
||||
|
||||
@property
|
||||
def disassembler(self):
|
||||
if not getattr(self, '_dasm', False):
|
||||
self._dasm = self.target.createMCDisassembler(self.subtarget_info)
|
||||
if not self._dasm:
|
||||
raise llvm.LLVMException("no disassembler for this machine")
|
||||
|
||||
return self._dasm
|
||||
|
||||
@property
|
||||
def inst_printer(self):
|
||||
if not getattr(self, '_mip', False):
|
||||
self._mip = self.target.createMCInstPrinter(
|
||||
self.asm_info.getAssemblerDialect(),
|
||||
self.asm_info,
|
||||
self.instr_info,
|
||||
self.reg_info,
|
||||
self.subtarget_info
|
||||
)
|
||||
if not self._mip:
|
||||
raise llvm.LLVMException("no instr printer for this machine")
|
||||
|
||||
return self._mip
|
||||
|
||||
def is_little_endian(self):
|
||||
return self.asm_info.isLittleEndian()
|
||||
|
||||
1603
llvm/test_llvmpy.py
Normal file
1603
llvm/test_llvmpy.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,72 +0,0 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
import subprocess
|
||||
import llvm
|
||||
|
||||
tests = [] # stores unittest.TestCase objects
|
||||
|
||||
# Isolated tests
|
||||
# Tests that affect process-wide settings
|
||||
isolated_tests = [] # stores modue name
|
||||
|
||||
|
||||
def run(verbosity=1, run_isolated=True):
|
||||
print('llvmpy is installed in: ' + os.path.dirname(__file__))
|
||||
print('llvmpy version: ' + llvm.__version__)
|
||||
print(sys.version)
|
||||
|
||||
files = filter(lambda s: s.startswith('test_') and s.endswith('.py'),
|
||||
os.listdir(os.path.dirname(__file__)))
|
||||
|
||||
for f in files:
|
||||
fname = f.split('.', 1)[0]
|
||||
__import__('.'.join([__name__, fname]))
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
for cls in tests:
|
||||
if cls:
|
||||
suite.addTest(unittest.makeSuite(cls))
|
||||
|
||||
# The default stream fails in IPython qtconsole on Windows,
|
||||
# so just using sys.stdout
|
||||
|
||||
kwargs = dict(verbosity=verbosity, stream=sys.stdout)
|
||||
|
||||
if sys.version_info[:2] > (2, 6):
|
||||
kwargs['buffer'] = True
|
||||
runner = unittest.TextTestRunner(**kwargs)
|
||||
|
||||
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.decode('utf8'), end='')
|
||||
p.wait()
|
||||
if p.returncode:
|
||||
raise Exception("%s returned: %d" % (test, p.returncode))
|
||||
|
||||
return testresult
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
from __future__ import print_function, division
|
||||
import sys
|
||||
import platform
|
||||
import unittest
|
||||
import contextlib
|
||||
import types
|
||||
from llvm.tests import tests, isolated_tests # re-expose symbol
|
||||
|
||||
IS_PY3K = sys.version_info[0] >= 3
|
||||
BITS = tuple.__itemsize__ * 8
|
||||
OS = sys.platform
|
||||
MACHINE = platform.machine()
|
||||
INTEL_CPUS = 'i386', 'x86_64'
|
||||
|
||||
if sys.version_info[:2] <= (2, 6):
|
||||
# create custom TestCase
|
||||
class _TestCase(unittest.TestCase):
|
||||
def assertIn(self, item, container):
|
||||
self.assertTrue(item in container)
|
||||
|
||||
def assertNotIn(self, item, container):
|
||||
self.assertFalse(item in container)
|
||||
|
||||
def assertLess(self, a, b):
|
||||
self.assertTrue(a < b)
|
||||
|
||||
def assertIs(self, a, b):
|
||||
self.assertTrue(a is b)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def assertRaises(self, exc):
|
||||
try:
|
||||
yield
|
||||
except exc:
|
||||
pass
|
||||
else:
|
||||
raise self.failureException("Did not raise %s" % exc)
|
||||
|
||||
else:
|
||||
_TestCase = unittest.TestCase
|
||||
|
||||
class TestCase(_TestCase):
|
||||
def assertClose(self, got, expect):
|
||||
rel = abs(got - expect) / expect
|
||||
self.assertTrue(rel < 1e-6, 'relative error = %f' % rel)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Tests decorators
|
||||
|
||||
def _skipped(name, msg):
|
||||
def _test(self):
|
||||
if hasattr(unittest, 'SkipTest'):
|
||||
raise unittest.SkipTest(msg)
|
||||
else:
|
||||
print('skipped %s' % name, msg)
|
||||
return _test
|
||||
|
||||
def skip_if(cond, msg=''):
|
||||
def skipper(test):
|
||||
if not isinstance(test, types.FunctionType):
|
||||
repl = None
|
||||
else:
|
||||
repl = _skipped(test, msg)
|
||||
return repl if cond else test
|
||||
return skipper
|
||||
|
||||
skip_if_not_64bits = skip_if(BITS != 64, msg='skipped not 64-bit')
|
||||
|
||||
skip_if_not_32bits = skip_if(BITS != 32, msg='skipped not 32-bits')
|
||||
|
||||
skip_if_win32 = skip_if(OS.startswith('win32'), msg='skipped win32')
|
||||
|
||||
skip_if_not_win32 = skip_if(not OS.startswith('win32'),
|
||||
msg='skipped not win32')
|
||||
skip_if_not_intel_cpu = skip_if(MACHINE not in INTEL_CPUS,
|
||||
msg='skipped not Intel CPU')
|
||||
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
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()
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type
|
||||
import llvm.core as lc
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestArgAttr(TestCase):
|
||||
def test_arg_attr(self):
|
||||
m = Module.new('oifjda')
|
||||
vptr = Type.pointer(Type.float())
|
||||
fnty = Type.function(Type.void(), [vptr] * 5)
|
||||
func = m.add_function(fnty, 'foo')
|
||||
attrs = [lc.ATTR_STRUCT_RET, lc.ATTR_BY_VAL, lc.ATTR_NEST,
|
||||
lc.ATTR_NO_ALIAS, lc.ATTR_NO_CAPTURE]
|
||||
for i, attr in enumerate(attrs):
|
||||
arg = func.args[i]
|
||||
self.assertEqual(i, arg.arg_no)
|
||||
arg.add_attribute(attr)
|
||||
self.assertTrue(attr in func.args[i])
|
||||
|
||||
tests.append(TestArgAttr)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import unittest
|
||||
import llvm
|
||||
from llvm.core import (Module, Type, Builder)
|
||||
from llvm.ee import EngineBuilder
|
||||
from .support import TestCase, tests, skip_if, skip_if_not_64bits
|
||||
|
||||
@skip_if(llvm.version < (3, 3))
|
||||
class TestArith(TestCase):
|
||||
'''
|
||||
Test basic arithmetic support with LLVM MCJIT
|
||||
'''
|
||||
def func_template(self, ty, op):
|
||||
m = Module.new('dofjaa')
|
||||
fnty = Type.function(ty, [ty, ty])
|
||||
fn = m.add_function(fnty, 'foo')
|
||||
bldr = Builder.new(fn.append_basic_block(''))
|
||||
bldr.ret(getattr(bldr, op)(*fn.args))
|
||||
|
||||
engine = EngineBuilder.new(m).mcjit(True).create()
|
||||
ptr = engine.get_pointer_to_function(fn)
|
||||
|
||||
from ctypes import c_uint32, c_uint64, c_float, c_double, CFUNCTYPE
|
||||
|
||||
maptypes = {
|
||||
Type.int(32): c_uint32,
|
||||
Type.int(64): c_uint64,
|
||||
Type.float(): c_float,
|
||||
Type.double(): c_double,
|
||||
}
|
||||
cty = maptypes[ty]
|
||||
prototype = CFUNCTYPE(*[cty] * 3)
|
||||
callee = prototype(ptr)
|
||||
callee(12, 23)
|
||||
|
||||
def template(self, iop, fop):
|
||||
inttys = [Type.int(32), Type.int(64)]
|
||||
flttys = [Type.float(), Type.double()]
|
||||
|
||||
if iop:
|
||||
for ty in inttys:
|
||||
self.func_template(ty, iop)
|
||||
if fop:
|
||||
for ty in flttys:
|
||||
self.func_template(ty, fop)
|
||||
|
||||
def test_add(self):
|
||||
self.template('add', 'fadd')
|
||||
|
||||
def test_sub(self):
|
||||
self.template('sub', 'fsub')
|
||||
|
||||
def test_mul(self):
|
||||
self.template('mul', 'fmul')
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_div(self):
|
||||
'''
|
||||
known failure due to unresolved external symbol __udivdi3
|
||||
'''
|
||||
self.template('udiv', None) # 'fdiv')
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_rem(self):
|
||||
'''
|
||||
known failure due to unresolved external symbol __umoddi3
|
||||
'''
|
||||
self.template('urem', None) # 'frem')
|
||||
|
||||
tests.append(TestArith)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import os
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from llvm.core import Module, Type
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestAsm(TestCase):
|
||||
def setUp(self):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
|
||||
def test_asm(self):
|
||||
# create a module
|
||||
m = Module.new('module1')
|
||||
m.add_global_variable(Type.int(), 'i')
|
||||
|
||||
# write it's assembly representation to a file
|
||||
asm = str(m)
|
||||
|
||||
testasm_ll = os.path.join(self.tmpdir, 'testasm.ll')
|
||||
with open(testasm_ll, "w") as fout:
|
||||
fout.write(asm)
|
||||
|
||||
# read it back into a module
|
||||
with open(testasm_ll) as fin:
|
||||
m2 = Module.from_assembly(fin)
|
||||
# The default `m.id` is '<string>'.
|
||||
m2.id = m.id # Copy the name from `m`
|
||||
|
||||
self.assertEqual(str(m2).strip(), asm.strip())
|
||||
|
||||
def test_bitcode(self):
|
||||
# create a module
|
||||
m = Module.new('module1')
|
||||
m.add_global_variable(Type.int(), 'i')
|
||||
|
||||
# write it's assembly representation to a file
|
||||
asm = str(m)
|
||||
|
||||
testasm_bc = os.path.join(self.tmpdir, 'testasm.bc')
|
||||
with open(testasm_bc, "wb") as fout:
|
||||
m.to_bitcode(fout)
|
||||
|
||||
# read it back into a module
|
||||
with open(testasm_bc, "rb") as fin:
|
||||
m2 = Module.from_bitcode(fin)
|
||||
# The default `m.id` is '<string>'.
|
||||
m2.id = m.id # Copy the name from `m`
|
||||
|
||||
self.assertEqual(str(m2).strip(), asm.strip())
|
||||
|
||||
tests.append(TestAsm)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder, Constant)
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestAtomic(TestCase):
|
||||
orderings = ['unordered', 'monotonic', 'acquire',
|
||||
'release', 'acq_rel', 'seq_cst']
|
||||
|
||||
atomic_op = ['xchg', 'add', 'sub', 'and', 'nand', 'or', 'xor',
|
||||
'max', 'min', 'umax', 'umin']
|
||||
|
||||
def test_atomic_cmpxchg(self):
|
||||
mod = Module.new('mod')
|
||||
functype = Type.function(Type.void(), [])
|
||||
func = mod.add_function(functype, name='foo')
|
||||
bb = func.append_basic_block('entry')
|
||||
bldr = Builder.new(bb)
|
||||
ptr = bldr.alloca(Type.int())
|
||||
|
||||
old = bldr.load(ptr)
|
||||
new = Constant.int(Type.int(), 1234)
|
||||
|
||||
for ordering in self.orderings:
|
||||
inst = bldr.atomic_cmpxchg(ptr, old, new, ordering)
|
||||
self.assertEqual(ordering, str(inst).strip().split(' ')[-1])
|
||||
|
||||
inst = bldr.atomic_cmpxchg(ptr, old, new, ordering, crossthread=False)
|
||||
self.assertEqual('singlethread', str(inst).strip().split(' ')[-2])
|
||||
|
||||
def test_atomic_rmw(self):
|
||||
mod = Module.new('mod')
|
||||
functype = Type.function(Type.void(), [])
|
||||
func = mod.add_function(functype, name='foo')
|
||||
bb = func.append_basic_block('entry')
|
||||
bldr = Builder.new(bb)
|
||||
ptr = bldr.alloca(Type.int())
|
||||
|
||||
val = Constant.int(Type.int(), 1234)
|
||||
|
||||
for ordering in self.orderings:
|
||||
inst = bldr.atomic_rmw('xchg', ptr, val, ordering)
|
||||
self.assertEqual(ordering, str(inst).split(' ')[-1])
|
||||
|
||||
for op in self.atomic_op:
|
||||
inst = bldr.atomic_rmw(op, ptr, val, ordering)
|
||||
self.assertEqual(op, str(inst).strip().split(' ')[3])
|
||||
|
||||
inst = bldr.atomic_rmw('xchg', ptr, val, ordering, crossthread=False)
|
||||
self.assertEqual('singlethread', str(inst).strip().split(' ')[-2])
|
||||
|
||||
for op in self.atomic_op:
|
||||
atomic_op = getattr(bldr, 'atomic_%s' % op)
|
||||
inst = atomic_op(ptr, val, ordering)
|
||||
self.assertEqual(op, str(inst).strip().split(' ')[3])
|
||||
|
||||
def test_atomic_ldst(self):
|
||||
mod = Module.new('mod')
|
||||
functype = Type.function(Type.void(), [])
|
||||
func = mod.add_function(functype, name='foo')
|
||||
bb = func.append_basic_block('entry')
|
||||
bldr = Builder.new(bb)
|
||||
ptr = bldr.alloca(Type.int())
|
||||
|
||||
for ordering in self.orderings:
|
||||
loaded = bldr.atomic_load(ptr, ordering)
|
||||
self.assert_('load atomic' in str(loaded))
|
||||
self.assertEqual(ordering,
|
||||
str(loaded).strip().split(' ')[-3].rstrip(','))
|
||||
self.assert_('align 1' in str(loaded))
|
||||
|
||||
stored = bldr.atomic_store(loaded, ptr, ordering)
|
||||
self.assert_('store atomic' in str(stored))
|
||||
self.assertEqual(ordering,
|
||||
str(stored).strip().split(' ')[-3].rstrip(','))
|
||||
self.assert_('align 1' in str(stored))
|
||||
|
||||
fenced = bldr.fence(ordering)
|
||||
self.assertEqual(['fence', ordering],
|
||||
str(fenced).strip().split(' '))
|
||||
|
||||
tests.append(TestAtomic)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import unittest
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
from llvm.core import Module
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestAttr(TestCase):
|
||||
def make_module(self):
|
||||
test_module = """
|
||||
define void @sum(i32*, i32*) {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
"""
|
||||
buf = StringIO(test_module)
|
||||
return Module.from_assembly(buf)
|
||||
|
||||
def test_align(self):
|
||||
m = self.make_module()
|
||||
f = m.get_function_named('sum')
|
||||
f.args[0].alignment = 16
|
||||
self.assert_("align 16" in str(f))
|
||||
self.assertEqual(f.args[0].alignment, 16)
|
||||
|
||||
tests.append(TestAttr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder, Constant)
|
||||
import llvm.core as lc
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestCmp(TestCase):
|
||||
def test_arg_attr(self):
|
||||
m = Module.new('oifjda')
|
||||
fnty = Type.function(Type.void(), [Type.int()])
|
||||
func = m.add_function(fnty, 'foo')
|
||||
bb = func.append_basic_block('')
|
||||
bldr = Builder.new(bb)
|
||||
|
||||
cmpinst = bldr.icmp(lc.ICMP_ULE, func.args[0],
|
||||
Constant.int(Type.int(), 123))
|
||||
self.assertTrue(repr(cmpinst.predicate).startswith('ICMP_ULE'))
|
||||
self.assertEqual(cmpinst.predicate, lc.ICMP_ULE)
|
||||
bldr.ret_void()
|
||||
|
||||
func.verify()
|
||||
|
||||
tests.append(TestCmp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder, Constant)
|
||||
import llvm.core as lc
|
||||
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestConstExpr(TestCase):
|
||||
|
||||
def test_constexpr_opcode(self):
|
||||
mod = Module.new('test_constexpr_opcode')
|
||||
func = mod.add_function(Type.function(Type.void(), []), name="foo")
|
||||
builder = Builder.new(func.append_basic_block('entry'))
|
||||
a = builder.inttoptr(Constant.int(Type.int(), 123),
|
||||
Type.pointer(Type.int()))
|
||||
self.assertTrue(isinstance(a, lc.ConstantExpr))
|
||||
self.assertEqual(a.opcode, lc.OPCODE_INTTOPTR)
|
||||
self.assertEqual(a.opcode_name, "inttoptr")
|
||||
|
||||
tests.append(TestConstExpr)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
import unittest
|
||||
import math
|
||||
from llvm.core import (Module, Type, Function, Builder,
|
||||
Constant)
|
||||
from llvm.ee import EngineBuilder
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
from llvm.workaround.avx_support import detect_avx_support
|
||||
from .support import TestCase, tests, skip_if_not_intel_cpu, skip_if
|
||||
|
||||
@skip_if_not_intel_cpu
|
||||
class TestCPUSupport(TestCase):
|
||||
|
||||
def _build_test_module(self):
|
||||
mod = Module.new('test')
|
||||
|
||||
float = Type.double()
|
||||
mysinty = Type.function( float, [float] )
|
||||
mysin = mod.add_function(mysinty, "mysin")
|
||||
block = mysin.append_basic_block("entry")
|
||||
b = Builder.new(block)
|
||||
|
||||
sqrt = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
||||
pow = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
||||
cos = Function.intrinsic(mod, lc.INTR_COS, [float])
|
||||
|
||||
mysin.args[0].name = "x"
|
||||
x = mysin.args[0]
|
||||
one = Constant.real(float, "1")
|
||||
cosx = b.call(cos, [x], "cosx")
|
||||
cos2 = b.call(pow, [cosx, Constant.int(Type.int(), 2)], "cos2")
|
||||
onemc2 = b.fsub(one, cos2, "onemc2") # Should use fsub
|
||||
sin = b.call(sqrt, [onemc2], "sin")
|
||||
b.ret(sin)
|
||||
return mod, mysin
|
||||
|
||||
def _template(self, mattrs):
|
||||
mod, func = self._build_test_module()
|
||||
ee = self._build_engine(mod, mattrs=mattrs)
|
||||
arg = le.GenericValue.real(Type.double(), 1.234)
|
||||
retval = ee.run_function(func, [arg])
|
||||
|
||||
golden = math.sin(1.234)
|
||||
answer = retval.as_real(Type.double())
|
||||
self.assertTrue(abs(answer-golden)/golden < 1e-5)
|
||||
|
||||
|
||||
def _build_engine(self, mod, mattrs):
|
||||
if mattrs:
|
||||
return EngineBuilder.new(mod).mattrs(mattrs).create()
|
||||
else:
|
||||
return EngineBuilder.new(mod).create()
|
||||
|
||||
def test_cpu_support2(self):
|
||||
features = 'sse3', 'sse41', 'sse42', 'avx'
|
||||
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
||||
print('disable mattrs', mattrs)
|
||||
self._template(mattrs)
|
||||
|
||||
def test_cpu_support3(self):
|
||||
features = 'sse41', 'sse42', 'avx'
|
||||
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
||||
print('disable mattrs', mattrs)
|
||||
self._template(mattrs)
|
||||
|
||||
def test_cpu_support4(self):
|
||||
features = 'sse42', 'avx'
|
||||
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
||||
print('disable mattrs', mattrs)
|
||||
self._template(mattrs)
|
||||
|
||||
def test_cpu_support5(self):
|
||||
features = 'avx',
|
||||
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
||||
print('disable mattrs', mattrs)
|
||||
self._template(mattrs)
|
||||
|
||||
@skip_if(not detect_avx_support(), msg="no AVX support")
|
||||
def test_cpu_support6(self):
|
||||
features = []
|
||||
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
||||
print('disable mattrs', mattrs)
|
||||
self._template(mattrs)
|
||||
|
||||
tests.append(TestCPUSupport)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type, Builder, Constant
|
||||
from llvm.ee import EngineBuilder
|
||||
import llvm.ee as le
|
||||
import llvmpy
|
||||
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestEngineBuilder(TestCase):
|
||||
|
||||
def make_test_module(self):
|
||||
module = Module.new("testmodule")
|
||||
fnty = Type.function(Type.int(), [])
|
||||
function = module.add_function(fnty, 'foo')
|
||||
bb_entry = function.append_basic_block('entry')
|
||||
builder = Builder.new(bb_entry)
|
||||
builder.ret(Constant.int(Type.int(), 0xcafe))
|
||||
module.verify()
|
||||
return module
|
||||
|
||||
def run_foo(self, ee, module):
|
||||
function = module.get_function_named('foo')
|
||||
retval = ee.run_function(function, [])
|
||||
self.assertEqual(retval.as_int(), 0xcafe)
|
||||
|
||||
|
||||
def test_enginebuilder_basic(self):
|
||||
module = self.make_test_module()
|
||||
self.assertTrue(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
||||
ee = EngineBuilder.new(module).create()
|
||||
self.assertFalse(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
||||
self.run_foo(ee, module)
|
||||
|
||||
|
||||
def test_enginebuilder_with_tm(self):
|
||||
tm = le.TargetMachine.new()
|
||||
module = self.make_test_module()
|
||||
self.assertTrue(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
||||
ee = EngineBuilder.new(module).create(tm)
|
||||
self.assertFalse(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
||||
self.run_foo(ee, module)
|
||||
|
||||
def test_enginebuilder_force_jit(self):
|
||||
module = self.make_test_module()
|
||||
ee = EngineBuilder.new(module).force_jit().create()
|
||||
|
||||
self.run_foo(ee, module)
|
||||
#
|
||||
# def test_enginebuilder_force_interpreter(self):
|
||||
# module = self.make_test_module()
|
||||
# ee = EngineBuilder.new(module).force_interpreter().create()
|
||||
#
|
||||
# self.run_foo(ee, module)
|
||||
|
||||
def test_enginebuilder_opt(self):
|
||||
module = self.make_test_module()
|
||||
ee = EngineBuilder.new(module).opt(3).create()
|
||||
|
||||
self.run_foo(ee, module)
|
||||
|
||||
tests.append(TestEngineBuilder)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder)
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestExact(TestCase):
|
||||
def make_module(self):
|
||||
mod = Module.new('asdfa')
|
||||
fnty = Type.function(Type.void(), [Type.int()] * 2)
|
||||
func = mod.add_function(fnty, 'foo')
|
||||
bldr = Builder.new(func.append_basic_block(''))
|
||||
return mod, func, bldr
|
||||
|
||||
def has_exact(self, inst, op):
|
||||
self.assertTrue(('%s exact' % op) in str(inst), "exact flag does not work")
|
||||
|
||||
def _test_template(self, opf, opname):
|
||||
mod, func, bldr = self.make_module()
|
||||
a, b = func.args
|
||||
self.has_exact(opf(bldr, a, b, exact=True), opname)
|
||||
|
||||
def test_udiv_exact(self):
|
||||
self._test_template(Builder.udiv, 'udiv')
|
||||
|
||||
def test_sdiv_exact(self):
|
||||
self._test_template(Builder.sdiv, 'sdiv')
|
||||
|
||||
def test_lshr_exact(self):
|
||||
self._test_template(Builder.lshr, 'lshr')
|
||||
|
||||
def test_ashr_exact(self):
|
||||
self._test_template(Builder.ashr, 'ashr')
|
||||
|
||||
tests.append(TestExact)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Type
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestExecutionEngine(TestCase):
|
||||
def test_get_pointer_to_global(self):
|
||||
module = lc.Module.new(str(self))
|
||||
gvar = module.add_global_variable(Type.int(), 'hello')
|
||||
X = 1234
|
||||
gvar.initializer = lc.Constant.int(Type.int(), X)
|
||||
|
||||
ee = le.ExecutionEngine.new(module)
|
||||
ptr = ee.get_pointer_to_global(gvar)
|
||||
from ctypes import c_void_p, cast, c_int, POINTER
|
||||
casted = cast(c_void_p(ptr), POINTER(c_int))
|
||||
self.assertEqual(X, casted[0])
|
||||
|
||||
def test_add_global_mapping(self):
|
||||
module = lc.Module.new(str(self))
|
||||
gvar = module.add_global_variable(Type.int(), 'hello')
|
||||
|
||||
fnty = lc.Type.function(Type.int(), [])
|
||||
foo = module.add_function(fnty, name='foo')
|
||||
bldr = lc.Builder.new(foo.append_basic_block('entry'))
|
||||
bldr.ret(bldr.load(gvar))
|
||||
|
||||
ee = le.ExecutionEngine.new(module)
|
||||
from ctypes import c_int, addressof, CFUNCTYPE
|
||||
value = 0xABCD
|
||||
value_ctype = c_int(value)
|
||||
value_pointer = addressof(value_ctype)
|
||||
|
||||
ee.add_global_mapping(gvar, value_pointer)
|
||||
|
||||
foo_addr = ee.get_pointer_to_function(foo)
|
||||
prototype = CFUNCTYPE(c_int)
|
||||
foo_callable = prototype(foo_addr)
|
||||
self.assertEqual(foo_callable(), value)
|
||||
|
||||
tests.append(TestExecutionEngine)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder, Constant, inline_function)
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestInlining(TestCase):
|
||||
def test_inline_call(self):
|
||||
mod = Module.new(__name__)
|
||||
callee = mod.add_function(Type.function(Type.int(), [Type.int()]),
|
||||
name='bar')
|
||||
|
||||
builder = Builder.new(callee.append_basic_block('entry'))
|
||||
builder.ret(builder.add(callee.args[0], callee.args[0]))
|
||||
|
||||
caller = mod.add_function(Type.function(Type.int(), []),
|
||||
name='foo')
|
||||
|
||||
builder = Builder.new(caller.append_basic_block('entry'))
|
||||
callinst = builder.call(callee, [Constant.int(Type.int(), 1234)])
|
||||
builder.ret(callinst)
|
||||
|
||||
pre_inlining = str(caller)
|
||||
self.assertIn('call', pre_inlining)
|
||||
|
||||
self.assertTrue(inline_function(callinst))
|
||||
|
||||
post_inlining = str(caller)
|
||||
self.assertNotIn('call', post_inlining)
|
||||
self.assertIn('2468', post_inlining)
|
||||
|
||||
tests.append(TestInlining)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
import unittest
|
||||
from llvm.core import Builder, Module, Type
|
||||
import llvm.core as lc
|
||||
from .support import TestCase, skip_if_not_intel_cpu, isolated_tests
|
||||
|
||||
@skip_if_not_intel_cpu
|
||||
class TestNativeAsm(TestCase):
|
||||
|
||||
def test_asm(self):
|
||||
m = Module.new('module1')
|
||||
|
||||
foo = m.add_function(Type.function(Type.int(),
|
||||
[Type.int(), Type.int()]),
|
||||
name="foo")
|
||||
bldr = Builder.new(foo.append_basic_block('entry'))
|
||||
x = bldr.add(foo.args[0], foo.args[1])
|
||||
bldr.ret(x)
|
||||
|
||||
att_syntax = m.to_native_assembly()
|
||||
os.environ["LLVMPY_OPTIONS"] = "-x86-asm-syntax=intel"
|
||||
lc.parse_environment_options(sys.argv[0], "LLVMPY_OPTIONS")
|
||||
intel_syntax = m.to_native_assembly()
|
||||
|
||||
self.assertNotEqual(att_syntax, intel_syntax)
|
||||
|
||||
isolated_tests.append(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
import unittest
|
||||
import sys
|
||||
import math
|
||||
from llvm.core import (Module, Type, Function, Builder, Constant)
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
from .support import TestCase, tests, BITS
|
||||
|
||||
class TestIntrinsic(TestCase):
|
||||
def test_bswap(self):
|
||||
# setup a function and a builder
|
||||
mod = Module.new('test')
|
||||
functy = Type.function(Type.int(), [])
|
||||
func = mod.add_function(functy, "showme")
|
||||
block = func.append_basic_block("entry")
|
||||
b = Builder.new(block)
|
||||
|
||||
# let's do bswap on a 32-bit integer using llvm.bswap
|
||||
val = Constant.int(Type.int(), 0x42)
|
||||
bswap = Function.intrinsic(mod, lc.INTR_BSWAP, [Type.int()])
|
||||
|
||||
bswap_res = b.call(bswap, [val])
|
||||
b.ret(bswap_res)
|
||||
|
||||
# logging.debug(mod)
|
||||
|
||||
# the output is:
|
||||
#
|
||||
# ; ModuleID = 'test'
|
||||
#
|
||||
# define void @showme() {
|
||||
# entry:
|
||||
# %0 = call i32 @llvm.bswap.i32(i32 42)
|
||||
# ret i32 %0
|
||||
# }
|
||||
|
||||
# let's run the function
|
||||
ee = le.ExecutionEngine.new(mod)
|
||||
retval = ee.run_function(func, [])
|
||||
self.assertEqual(retval.as_int(), 0x42000000)
|
||||
|
||||
def test_mysin(self):
|
||||
if sys.platform == 'win32' and BITS == 32:
|
||||
# float32 support is known to fail on 32-bit Windows
|
||||
return
|
||||
|
||||
# mysin(x) = sqrt(1.0 - pow(cos(x), 2))
|
||||
mod = Module.new('test')
|
||||
|
||||
float = Type.float()
|
||||
mysinty = Type.function( float, [float] )
|
||||
mysin = mod.add_function(mysinty, "mysin")
|
||||
block = mysin.append_basic_block("entry")
|
||||
b = Builder.new(block)
|
||||
|
||||
sqrt = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
||||
pow = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
||||
cos = Function.intrinsic(mod, lc.INTR_COS, [float])
|
||||
|
||||
mysin.args[0].name = "x"
|
||||
x = mysin.args[0]
|
||||
one = Constant.real(float, "1")
|
||||
cosx = b.call(cos, [x], "cosx")
|
||||
cos2 = b.call(pow, [cosx, Constant.int(Type.int(), 2)], "cos2")
|
||||
onemc2 = b.fsub(one, cos2, "onemc2") # Should use fsub
|
||||
sin = b.call(sqrt, [onemc2], "sin")
|
||||
b.ret(sin)
|
||||
#logging.debug(mod)
|
||||
|
||||
# ; ModuleID = 'test'
|
||||
#
|
||||
# define void @showme() {
|
||||
# entry:
|
||||
# call i32 @llvm.bswap.i32( i32 42 ) ; <i32>:0 [#uses
|
||||
# }
|
||||
#
|
||||
# declare i32 @llvm.bswap.i32(i32) nounwind readnone
|
||||
#
|
||||
# define float @mysin(float %x) {
|
||||
# entry:
|
||||
# %cosx = call float @llvm.cos.f32( float %x ) ; <float
|
||||
# %cos2 = call float @llvm.powi.f32( float %cosx, i32 2 )
|
||||
# %onemc2 = sub float 1.000000e+00, %cos2 ; <float> [#uses
|
||||
# %sin = call float @llvm.sqrt.f32( float %onemc2 )
|
||||
# ret float %sin
|
||||
# }
|
||||
#
|
||||
# declare float @llvm.sqrt.f32(float) nounwind readnone
|
||||
#
|
||||
# declare float @llvm.powi.f32(float, i32) nounwind readnone
|
||||
#
|
||||
# declare float @llvm.cos.f32(float) nounwind readnone
|
||||
|
||||
# let's run the function
|
||||
|
||||
from llvm.workaround.avx_support import detect_avx_support
|
||||
if not detect_avx_support():
|
||||
ee = le.EngineBuilder.new(mod).mattrs("-avx").create()
|
||||
else:
|
||||
ee = le.EngineBuilder.new(mod).create()
|
||||
|
||||
arg = le.GenericValue.real(Type.float(), 1.234)
|
||||
retval = ee.run_function(mysin, [arg])
|
||||
|
||||
golden = math.sin(1.234)
|
||||
answer = retval.as_real(Type.float())
|
||||
self.assertTrue(abs(answer-golden)/golden < 1e-5)
|
||||
|
||||
tests.append(TestIntrinsic)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
import unittest
|
||||
import sys
|
||||
import math
|
||||
from llvm.core import (Module, Type, Function, Builder)
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
from .support import TestCase, BITS, tests
|
||||
|
||||
class TestIntrinsicBasic(TestCase):
|
||||
|
||||
def _build_module(self, float):
|
||||
mod = Module.new('test')
|
||||
functy = Type.function(float, [float])
|
||||
func = mod.add_function(functy, "mytest%s" % float)
|
||||
block = func.append_basic_block("entry")
|
||||
b = Builder.new(block)
|
||||
return mod, func, b
|
||||
|
||||
def _template(self, mod, func, pyfunc):
|
||||
float = func.type.pointee.return_type
|
||||
|
||||
from llvm.workaround.avx_support import detect_avx_support
|
||||
if not detect_avx_support():
|
||||
ee = le.EngineBuilder.new(mod).mattrs("-avx").create()
|
||||
else:
|
||||
ee = le.EngineBuilder.new(mod).create()
|
||||
arg = le.GenericValue.real(float, 1.234)
|
||||
retval = ee.run_function(func, [arg])
|
||||
golden = pyfunc(1.234)
|
||||
answer = retval.as_real(float)
|
||||
self.assertTrue(abs(answer - golden) / golden < 1e-7)
|
||||
|
||||
def test_sqrt_f32(self):
|
||||
float = Type.float()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.sqrt)
|
||||
|
||||
def test_sqrt_f64(self):
|
||||
float = Type.double()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.sqrt)
|
||||
|
||||
def test_cos_f32(self):
|
||||
if sys.platform == 'win32' and BITS == 32:
|
||||
# float32 support is known to fail on 32-bit Windows
|
||||
return
|
||||
float = Type.float()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_COS, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.cos)
|
||||
|
||||
def test_cos_f64(self):
|
||||
float = Type.double()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_COS, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.cos)
|
||||
|
||||
def test_sin_f32(self):
|
||||
if sys.platform == 'win32' and BITS == 32:
|
||||
# float32 support is known to fail on 32-bit Windows
|
||||
return
|
||||
float = Type.float()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_SIN, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.sin)
|
||||
|
||||
def test_sin_f64(self):
|
||||
float = Type.double()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_SIN, [float])
|
||||
b.ret(b.call(intr, func.args))
|
||||
self._template(mod, func, math.sin)
|
||||
|
||||
def test_powi_f32(self):
|
||||
float = Type.float()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
||||
b.ret(b.call(intr, [func.args[0], lc.Constant.int(Type.int(), 2)]))
|
||||
self._template(mod, func, lambda x: x**2)
|
||||
|
||||
def test_powi_f64(self):
|
||||
float = Type.double()
|
||||
mod, func, b = self._build_module(float)
|
||||
intr = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
||||
b.ret(b.call(intr, [func.args[0], lc.Constant.int(Type.int(), 2)]))
|
||||
self._template(mod, func, lambda x: x**2)
|
||||
|
||||
tests.append(TestIntrinsicBasic)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder)
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestIssue10(TestCase):
|
||||
def test_issue10(self):
|
||||
m = Module.new('a')
|
||||
ti = Type.int()
|
||||
tf = Type.function(ti, [ti, ti])
|
||||
|
||||
f = m.add_function(tf, "func1")
|
||||
|
||||
bb = f.append_basic_block('entry')
|
||||
|
||||
b = Builder.new(bb)
|
||||
|
||||
# There are no instructions in bb. Positioning of the
|
||||
# builder at beginning (or end) should succeed (trivially).
|
||||
b.position_at_end(bb)
|
||||
b.position_at_beginning(bb)
|
||||
|
||||
tests.append(TestIssue10)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import unittest
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestLLRT(TestCase):
|
||||
def test_llrt_divmod(self):
|
||||
from llvm import llrt
|
||||
m = lc.Module.new('testllrt')
|
||||
longlong = lc.Type.int(64)
|
||||
lfunc = m.add_function(lc.Type.function(longlong, [longlong, longlong]), 'foo')
|
||||
bldr = lc.Builder.new(lfunc.append_basic_block(''))
|
||||
bldr.ret(bldr.udiv(*lfunc.args))
|
||||
|
||||
llrt.replace_divmod64(lfunc)
|
||||
|
||||
rt = llrt.LLRT()
|
||||
rt.install_symbols()
|
||||
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
pointer = engine.get_pointer_to_function(lfunc)
|
||||
|
||||
from ctypes import CFUNCTYPE, c_uint64
|
||||
func = CFUNCTYPE(c_uint64, c_uint64, c_uint64)(pointer)
|
||||
a, b = 98342, 2231
|
||||
self.assertEqual(func(98342, 2231), 98342 // 2231)
|
||||
|
||||
rt.uninstall_symbols()
|
||||
|
||||
tests.append(TestLLRT)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import unittest
|
||||
import sys
|
||||
import llvm
|
||||
from llvm.core import (Module, Type, Builder)
|
||||
from llvm.ee import EngineBuilder
|
||||
|
||||
import llvm.ee as le
|
||||
from .support import TestCase, tests, BITS
|
||||
|
||||
class TestMCJIT(TestCase):
|
||||
def test_mcjit(self):
|
||||
m = Module.new('oidfjs')
|
||||
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
||||
func = m.add_function(fnty, 'foo')
|
||||
bb = func.append_basic_block('')
|
||||
bldr = Builder.new(bb)
|
||||
bldr.ret(bldr.add(*func.args))
|
||||
|
||||
func.verify()
|
||||
|
||||
engine = EngineBuilder.new(m).mcjit(True).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
from ctypes import c_int, CFUNCTYPE
|
||||
callee = CFUNCTYPE(c_int, c_int, c_int)(ptr)
|
||||
self.assertEqual(321 + 123, callee(321, 123))
|
||||
|
||||
def test_multi_module_linking(self):
|
||||
# generate external library module
|
||||
m = Module.new('external-library-module')
|
||||
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
||||
libfname = 'myadd'
|
||||
func = m.add_function(fnty, libfname)
|
||||
bb = func.append_basic_block('')
|
||||
bldr = Builder.new(bb)
|
||||
bldr.ret(bldr.add(*func.args))
|
||||
func.verify()
|
||||
|
||||
# JIT the lib module and bind dynamic symbol
|
||||
libengine = EngineBuilder.new(m).mcjit(True).create()
|
||||
myadd_ptr = libengine.get_pointer_to_function(func)
|
||||
le.dylib_add_symbol(libfname, myadd_ptr)
|
||||
|
||||
# reference external library
|
||||
m = Module.new('user')
|
||||
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
||||
func = m.add_function(fnty, 'foo')
|
||||
bb = func.append_basic_block('')
|
||||
bldr = Builder.new(bb)
|
||||
extadd = m.get_or_insert_function(fnty, name=libfname)
|
||||
bldr.ret(bldr.call(extadd, func.args))
|
||||
func.verify()
|
||||
|
||||
# JIT the user module
|
||||
engine = EngineBuilder.new(m).mcjit(True).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
self.assertEqual(myadd_ptr,
|
||||
engine.get_pointer_to_named_function(libfname))
|
||||
|
||||
from ctypes import c_int, CFUNCTYPE
|
||||
callee = CFUNCTYPE(c_int, c_int, c_int)(ptr)
|
||||
self.assertEqual(321 + 123, callee(321, 123))
|
||||
|
||||
|
||||
if (llvm.version >= (3, 3) and
|
||||
not (sys.platform.startswith('win32') and BITS == 64)):
|
||||
# MCJIT broken in 3.2, the test will segfault in OSX?
|
||||
# Compatbility problem on windows 7 64-bit?
|
||||
tests.append(TestMCJIT)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Constant, MetaData, MetaDataString)
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestMetaData(TestCase):
|
||||
# test module metadata
|
||||
def test_metadata(self):
|
||||
m = Module.new('a')
|
||||
t = Type.int()
|
||||
metadata = MetaData.get(m, [Constant.int(t, 100),
|
||||
MetaDataString.get(m, 'abcdef'),
|
||||
None])
|
||||
MetaData.add_named_operand(m, 'foo', metadata)
|
||||
self.assertEqual(MetaData.get_named_operands(m, 'foo'), [metadata])
|
||||
self.assertEqual(MetaData.get_named_operands(m, 'bar'), [])
|
||||
self.assertEqual(len(metadata.operands), 3)
|
||||
self.assertEqual(metadata.operands[0].z_ext_value, 100)
|
||||
self.assertEqual(metadata.operands[1].string, 'abcdef')
|
||||
self.assertTrue(metadata.operands[2] is None)
|
||||
|
||||
tests.append(TestMetaData)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Constant, MetaData)
|
||||
from .support import TestCase, tests
|
||||
|
||||
|
||||
class TestNamedMetaData(TestCase):
|
||||
def test_named_md(self):
|
||||
m = Module.new('test_named_md')
|
||||
nmd = m.get_or_insert_named_metadata('something')
|
||||
md = MetaData.get(m, [Constant.int(Type.int(), 0xbeef)])
|
||||
nmd.add(md)
|
||||
self.assertTrue(str(nmd).startswith('!something'))
|
||||
ir = str(m)
|
||||
self.assertTrue('!something' in ir)
|
||||
|
||||
tests.append(TestNamedMetaData)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
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, skip_if
|
||||
|
||||
@skip_if(sys.platform in ('win32', 'darwin'))
|
||||
class TestNative(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
|
||||
|
||||
def _make_module(self):
|
||||
m = Module.new('module1')
|
||||
m.add_global_variable(Type.int(), 'i')
|
||||
|
||||
fty = Type.function(Type.int(), [])
|
||||
f = m.add_function(fty, name='main')
|
||||
|
||||
bldr = Builder.new(f.append_basic_block('entry'))
|
||||
bldr.ret(Constant.int(Type.int(), 0xab))
|
||||
|
||||
return m
|
||||
|
||||
def _compile(self, src):
|
||||
cc = find_executable('cc')
|
||||
if not cc:
|
||||
return
|
||||
|
||||
dst = os.path.join(self.tmpdir, 'llvmobj.out')
|
||||
s = subprocess.call([cc, '-o', dst, src])
|
||||
if s != 0:
|
||||
raise Exception("Cannot compile")
|
||||
|
||||
s = subprocess.call([dst])
|
||||
self.assertEqual(s, 0xab)
|
||||
|
||||
def test_assembly(self):
|
||||
# if sys.platform == 'darwin':
|
||||
# # skip this test on MacOSX for now
|
||||
# return
|
||||
|
||||
m = self._make_module()
|
||||
output = m.to_native_assembly()
|
||||
|
||||
src = os.path.join(self.tmpdir, 'llvmasm.s')
|
||||
with open(src, 'wb') as fout:
|
||||
if IS_PY3K:
|
||||
fout.write(output.encode('utf-8'))
|
||||
else:
|
||||
fout.write(output)
|
||||
|
||||
self._compile(src)
|
||||
|
||||
def test_object(self):
|
||||
'''
|
||||
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()
|
||||
|
||||
src = os.path.join(self.tmpdir, 'llvmobj.o')
|
||||
with open(src, 'wb') as fout:
|
||||
fout.write(output)
|
||||
|
||||
self._compile(src)
|
||||
|
||||
tests.append(TestNative)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type, Builder
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestNUWNSW(TestCase):
|
||||
def make_module(self):
|
||||
mod = Module.new('asdfa')
|
||||
fnty = Type.function(Type.void(), [Type.int()] * 2)
|
||||
func = mod.add_function(fnty, 'foo')
|
||||
bldr = Builder.new(func.append_basic_block(''))
|
||||
return mod, func, bldr
|
||||
|
||||
def has_nsw(self, inst, op):
|
||||
self.assertTrue(('%s nsw' % op) in str(inst), "NSW flag does not work")
|
||||
|
||||
def has_nuw(self, inst, op):
|
||||
self.assertTrue(('%s nuw' % op) in str(inst), "NUW flag does not work")
|
||||
|
||||
def _test_template(self, opf, opname):
|
||||
mod, func, bldr = self.make_module()
|
||||
a, b = func.args
|
||||
self.has_nsw(opf(bldr, a, b, nsw=True), opname)
|
||||
self.has_nuw(opf(bldr, a, b, nuw=True), opname)
|
||||
|
||||
def test_add_nuw_nsw(self):
|
||||
self._test_template(Builder.add, 'add')
|
||||
|
||||
def test_sub_nuw_nsw(self):
|
||||
self._test_template(Builder.sub, 'sub')
|
||||
|
||||
def test_mul_nuw_nsw(self):
|
||||
self._test_template(Builder.mul, 'mul')
|
||||
|
||||
def test_shl_nuw_nsw(self):
|
||||
self._test_template(Builder.shl, 'shl')
|
||||
|
||||
def test_neg_nuw_nsw(self):
|
||||
mod, func, bldr = self.make_module()
|
||||
a, b = func.args
|
||||
self.has_nsw(bldr.neg(a, nsw=True), 'sub')
|
||||
self.has_nuw(bldr.neg(a, nuw=True), 'sub')
|
||||
|
||||
|
||||
tests.append(TestNUWNSW)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type, GlobalVariable, Function, Builder
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestObjCache(TestCase):
|
||||
|
||||
def test_objcache(self):
|
||||
# Testing module aliasing
|
||||
m1 = Module.new('a')
|
||||
t = Type.int()
|
||||
ft = Type.function(t, [t])
|
||||
f1 = m1.add_function(ft, "func")
|
||||
m2 = f1.module
|
||||
self.assert_(m1 is m2)
|
||||
|
||||
# Testing global vairable aliasing 1
|
||||
gv1 = GlobalVariable.new(m1, t, "gv")
|
||||
gv2 = GlobalVariable.get(m1, "gv")
|
||||
self.assert_(gv1 is gv2)
|
||||
|
||||
# Testing global vairable aliasing 2
|
||||
gv3 = m1.global_variables[0]
|
||||
self.assert_(gv1 is gv3)
|
||||
|
||||
# Testing global vairable aliasing 3
|
||||
gv2 = None
|
||||
gv3 = None
|
||||
|
||||
gv1.delete()
|
||||
|
||||
gv4 = GlobalVariable.new(m1, t, "gv")
|
||||
|
||||
self.assert_(gv1 is not gv4)
|
||||
|
||||
# Testing function aliasing 1
|
||||
b1 = f1.append_basic_block('entry')
|
||||
f2 = b1.function
|
||||
self.assert_(f1 is f2)
|
||||
|
||||
# Testing function aliasing 2
|
||||
f3 = m1.get_function_named("func")
|
||||
self.assert_(f1 is f3)
|
||||
|
||||
# Testing function aliasing 3
|
||||
f4 = Function.get_or_insert(m1, ft, "func")
|
||||
self.assert_(f1 is f4)
|
||||
|
||||
# Testing function aliasing 4
|
||||
f5 = Function.get(m1, "func")
|
||||
self.assert_(f1 is f5)
|
||||
|
||||
# Testing function aliasing 5
|
||||
f6 = m1.get_or_insert_function(ft, "func")
|
||||
self.assert_(f1 is f6)
|
||||
|
||||
# Testing function aliasing 6
|
||||
f7 = m1.functions[0]
|
||||
self.assert_(f1 is f7)
|
||||
|
||||
# Testing argument aliasing
|
||||
a1 = f1.args[0]
|
||||
a2 = f1.args[0]
|
||||
self.assert_(a1 is a2)
|
||||
|
||||
# Testing basic block aliasing 1
|
||||
b2 = f1.basic_blocks[0]
|
||||
self.assert_(b1 is b2)
|
||||
|
||||
# Testing basic block aliasing 2
|
||||
b3 = f1.entry_basic_block
|
||||
self.assert_(b1 is b3)
|
||||
|
||||
# Testing basic block aliasing 3
|
||||
b31 = f1.entry_basic_block
|
||||
self.assert_(b1 is b31)
|
||||
|
||||
# Testing basic block aliasing 4
|
||||
bldr = Builder.new(b1)
|
||||
b4 = bldr.basic_block
|
||||
self.assert_(b1 is b4)
|
||||
|
||||
# Testing basic block aliasing 5
|
||||
i1 = bldr.ret_void()
|
||||
b5 = i1.basic_block
|
||||
self.assert_(b1 is b5)
|
||||
|
||||
# Testing instruction aliasing 1
|
||||
i2 = b5.instructions[0]
|
||||
self.assert_(i1 is i2)
|
||||
|
||||
# phi node
|
||||
phi = bldr.phi(t)
|
||||
phi.add_incoming(f1.args[0], b1)
|
||||
v2 = phi.get_incoming_value(0)
|
||||
b6 = phi.get_incoming_block(0)
|
||||
|
||||
# Testing PHI / basic block aliasing 5
|
||||
self.assert_(b1 is b6)
|
||||
|
||||
# Testing PHI / value aliasing
|
||||
self.assert_(f1.args[0] is v2)
|
||||
|
||||
tests.append(TestObjCache)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import unittest
|
||||
import llvm
|
||||
from llvm.core import Type
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestOpaque(TestCase):
|
||||
|
||||
def test_opaque(self):
|
||||
# Create an opaque type
|
||||
ts = Type.opaque('mystruct')
|
||||
self.assertTrue('type opaque' in str(ts))
|
||||
self.assertTrue(ts.is_opaque)
|
||||
self.assertTrue(ts.is_identified)
|
||||
self.assertFalse(ts.is_literal)
|
||||
#print(ts)
|
||||
|
||||
# Create a recursive type
|
||||
ts.set_body([Type.int(), Type.pointer(ts)])
|
||||
|
||||
self.assertEqual(ts.elements[0], Type.int())
|
||||
self.assertEqual(ts.elements[1], Type.pointer(ts))
|
||||
self.assertEqual(ts.elements[1].pointee, ts)
|
||||
self.assertFalse(ts.is_opaque) # is not longer a opaque type
|
||||
#print(ts)
|
||||
|
||||
with self.assertRaises(llvm.LLVMException):
|
||||
# Cannot redefine
|
||||
ts.set_body([])
|
||||
|
||||
def test_opaque_with_no_name(self):
|
||||
with self.assertRaises(llvm.LLVMException):
|
||||
Type.opaque('')
|
||||
|
||||
tests.append(TestOpaque)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import unittest
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
from llvm.core import Module
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestOperands(TestCase):
|
||||
# implement a test function
|
||||
test_module = """
|
||||
define i32 @prod(i32, i32) {
|
||||
entry:
|
||||
%2 = mul i32 %0, %1
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
define i32 @test_func(i32, i32, i32) {
|
||||
entry:
|
||||
%tmp1 = call i32 @prod(i32 %0, i32 %1)
|
||||
%tmp2 = add i32 %tmp1, %2
|
||||
%tmp3 = add i32 %tmp2, 1
|
||||
%tmp4 = add i32 %tmp3, -1
|
||||
%tmp5 = add i64 -81985529216486895, 12297829382473034410
|
||||
ret i32 %tmp4
|
||||
}
|
||||
"""
|
||||
def test_operands(self):
|
||||
m = Module.from_assembly(StringIO(self.test_module))
|
||||
|
||||
test_func = m.get_function_named("test_func")
|
||||
prod = m.get_function_named("prod")
|
||||
|
||||
# test operands
|
||||
i1 = test_func.basic_blocks[0].instructions[0]
|
||||
i2 = test_func.basic_blocks[0].instructions[1]
|
||||
i3 = test_func.basic_blocks[0].instructions[2]
|
||||
i4 = test_func.basic_blocks[0].instructions[3]
|
||||
i5 = test_func.basic_blocks[0].instructions[4]
|
||||
|
||||
self.assertEqual(i1.operand_count, 3)
|
||||
self.assertEqual(i2.operand_count, 2)
|
||||
|
||||
self.assertEqual(i3.operands[1].z_ext_value, 1)
|
||||
self.assertEqual(i3.operands[1].s_ext_value, 1)
|
||||
self.assertEqual(i4.operands[1].z_ext_value, 0xffffffff)
|
||||
self.assertEqual(i4.operands[1].s_ext_value, -1)
|
||||
self.assertEqual(i5.operands[0].s_ext_value, -81985529216486895)
|
||||
self.assertEqual(i5.operands[1].z_ext_value, 12297829382473034410)
|
||||
|
||||
self.assert_(i1.operands[-1] is prod)
|
||||
self.assert_(i1.operands[0] is test_func.args[0])
|
||||
self.assert_(i1.operands[1] is test_func.args[1])
|
||||
self.assert_(i2.operands[0] is i1)
|
||||
self.assert_(i2.operands[1] is test_func.args[2])
|
||||
self.assertEqual(len(i1.operands), 3)
|
||||
self.assertEqual(len(i2.operands), 2)
|
||||
|
||||
self.assert_(i1.called_function is prod)
|
||||
|
||||
tests.append(TestOperands)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
import unittest
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
import llvm
|
||||
from llvm.core import Module
|
||||
import llvm.passes as lp
|
||||
import llvm.ee as le
|
||||
from .support import TestCase, tests
|
||||
|
||||
|
||||
class TestPasses(TestCase):
|
||||
# Create a module.
|
||||
asm = """
|
||||
|
||||
define i32 @test() nounwind {
|
||||
ret i32 42
|
||||
}
|
||||
|
||||
define i32 @test1() nounwind {
|
||||
entry:
|
||||
%tmp = alloca i32
|
||||
store i32 42, i32* %tmp, align 4
|
||||
%tmp1 = load i32* %tmp, align 4
|
||||
%tmp2 = call i32 @test()
|
||||
%tmp3 = load i32* %tmp, align 4
|
||||
%tmp4 = load i32* %tmp, align 4
|
||||
ret i32 %tmp1
|
||||
}
|
||||
|
||||
define i32 @test2() nounwind {
|
||||
entry:
|
||||
%tmp = call i32 @test()
|
||||
ret i32 %tmp
|
||||
}
|
||||
"""
|
||||
def test_passes(self):
|
||||
m = Module.from_assembly(StringIO(self.asm))
|
||||
|
||||
fn_test1 = m.get_function_named('test1')
|
||||
fn_test2 = m.get_function_named('test2')
|
||||
|
||||
original_test1 = str(fn_test1)
|
||||
original_test2 = str(fn_test2)
|
||||
|
||||
# Let's run a module-level inlining pass. First, create a pass manager.
|
||||
pm = lp.PassManager.new()
|
||||
|
||||
# Add the target data as the first "pass". This is mandatory.
|
||||
pm.add(le.TargetData.new(''))
|
||||
|
||||
# Add the inlining pass.
|
||||
pm.add(lp.PASS_INLINE)
|
||||
|
||||
# Run it!
|
||||
pm.run(m)
|
||||
|
||||
# Done with the pass manager.
|
||||
del pm
|
||||
|
||||
# Make sure test2 is inlined
|
||||
self.assertNotEqual(str(fn_test2).strip(), original_test2.strip())
|
||||
|
||||
bb_entry = fn_test2.basic_blocks[0]
|
||||
|
||||
self.assertEqual(len(bb_entry.instructions), 1)
|
||||
self.assertEqual(bb_entry.instructions[0].opcode_name, 'ret')
|
||||
|
||||
# Let's run a DCE pass on the the function 'test1' now. First create a
|
||||
# function pass manager.
|
||||
fpm = lp.FunctionPassManager.new(m)
|
||||
|
||||
# Add the target data as first "pass". This is mandatory.
|
||||
fpm.add(le.TargetData.new(''))
|
||||
|
||||
# Add a DCE pass
|
||||
fpm.add(lp.PASS_ADCE)
|
||||
|
||||
# Run the pass on the function 'test1'
|
||||
fpm.run(m.get_function_named('test1'))
|
||||
|
||||
# Make sure test1 is modified
|
||||
self.assertNotEqual(str(fn_test1).strip(), original_test1.strip())
|
||||
|
||||
def test_passes_with_pmb(self):
|
||||
m = Module.from_assembly(StringIO(self.asm))
|
||||
|
||||
fn_test1 = m.get_function_named('test1')
|
||||
fn_test2 = m.get_function_named('test2')
|
||||
|
||||
original_test1 = str(fn_test1)
|
||||
original_test2 = str(fn_test2)
|
||||
|
||||
# Try out the PassManagerBuilder
|
||||
|
||||
pmb = lp.PassManagerBuilder.new()
|
||||
|
||||
self.assertEqual(pmb.opt_level, 2) # ensure default is level 2
|
||||
pmb.opt_level = 3
|
||||
self.assertEqual(pmb.opt_level, 3) # make sure it works
|
||||
|
||||
self.assertEqual(pmb.size_level, 0) # ensure default is level 0
|
||||
pmb.size_level = 2
|
||||
self.assertEqual(pmb.size_level, 2) # make sure it works
|
||||
|
||||
self.assertFalse(pmb.vectorize) # ensure default is False
|
||||
pmb.vectorize = True
|
||||
self.assertTrue(pmb.vectorize) # make sure it works
|
||||
|
||||
# make sure the default is False
|
||||
self.assertFalse(pmb.disable_unit_at_a_time)
|
||||
self.assertFalse(pmb.disable_unroll_loops)
|
||||
if llvm.version <= (3, 3):
|
||||
self.assertFalse(pmb.disable_simplify_lib_calls)
|
||||
|
||||
pmb.disable_unit_at_a_time = True
|
||||
self.assertTrue(pmb.disable_unit_at_a_time)
|
||||
|
||||
# Do function pass
|
||||
fpm = lp.FunctionPassManager.new(m)
|
||||
pmb.populate(fpm)
|
||||
fpm.run(fn_test1)
|
||||
|
||||
# Make sure test1 has changed
|
||||
self.assertNotEqual(str(fn_test1).strip(), original_test1.strip())
|
||||
|
||||
# Do module pass
|
||||
pm = lp.PassManager.new()
|
||||
pmb.populate(pm)
|
||||
pm.run(m)
|
||||
|
||||
# Make sure test2 has changed
|
||||
self.assertNotEqual(str(fn_test2).strip(), original_test2.strip())
|
||||
|
||||
def test_dump_passes(self):
|
||||
self.assertTrue(len(lp.PASSES)>0, msg="Cannot have no passes")
|
||||
|
||||
tests.append(TestPasses)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Type, Module, Builder, Constant
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestStruct(TestCase):
|
||||
def test_struct_identical(self):
|
||||
ta = Type.struct([Type.int(32), Type.float()], name='ta')
|
||||
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__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,685 +0,0 @@
|
|||
from __future__ import print_function
|
||||
from . import tests
|
||||
import sys
|
||||
import unittest
|
||||
from ctypes import Structure, c_float, c_double, c_uint8, CFUNCTYPE
|
||||
from llvm import core as lc
|
||||
from llvm import ee as le
|
||||
|
||||
from .support import (skip_if_win32, skip_if_not_win32, skip_if_not_32bits,
|
||||
skip_if_not_64bits, skip_if_not_intel_cpu, TestCase)
|
||||
|
||||
class TwoDoubleOneByte(Structure):
|
||||
_fields_ = ('x', c_double), ('y', c_double), ('z', c_uint8)
|
||||
|
||||
def __repr__(self):
|
||||
return '<x=%f y=%f z=%d>' % (self.x, self.y, self.z)
|
||||
|
||||
class TwoDouble(Structure):
|
||||
_fields_ = ('x', c_double), ('y', c_double)
|
||||
|
||||
def __repr__(self):
|
||||
return '<x=%f y=%f>' % (self.x, self.y)
|
||||
|
||||
class TwoFloat(Structure):
|
||||
_fields_ = ('x', c_float), ('y', c_float)
|
||||
|
||||
def __repr__(self):
|
||||
return '<x=%f y=%f>' % (self.x, self.y)
|
||||
|
||||
class OneByte(Structure):
|
||||
_fields_ = [('x', c_uint8)]
|
||||
|
||||
def __repr__(self):
|
||||
return '<x=%d>' % (self.x,)
|
||||
|
||||
@skip_if_not_intel_cpu
|
||||
@skip_if_win32
|
||||
class TestStructSystemVABI(TestCase):
|
||||
'''
|
||||
Non microsoft convention
|
||||
'''
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 64 bits
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_bigger_than_two_words_64(self):
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
ret = builder.insert_value(ret, e3, 2)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
self.assertEqual(arg.z, ret.z)
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_just_two_words_64(self):
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
struct_type = lc.Type.struct([double_type, double_type])
|
||||
func_type = lc.Type.function(struct_type, [struct_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = func.args[0]
|
||||
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
builder.ret(ret)
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDouble, TwoDouble)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDouble(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_two_halfwords(self):
|
||||
'''Arguments smaller or equal to a word is packed into a word.
|
||||
|
||||
Passing as struct { float, float } occupies two XMM registers instead
|
||||
of one.
|
||||
The output must be in XMM.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
float_type = lc.Type.float()
|
||||
struct_type = lc.Type.vector(float_type, 2)
|
||||
func_type = lc.Type.function(struct_type, [struct_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = func.args[0]
|
||||
constint = lambda x: lc.Constant.int(lc.Type.int(), x)
|
||||
e1, e2 = [builder.extract_element(arg, constint(i))
|
||||
for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_element(lc.Constant.undef(struct_type), se1,
|
||||
constint(0))
|
||||
ret = builder.insert_element(ret, se2, constint(1))
|
||||
builder.ret(ret)
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 32 bits
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_structure_abi_32_1(self):
|
||||
'''x86 is simple. Always pass structure as memory.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
ret = builder.insert_value(ret, e3, 2)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
self.assertEqual(arg.z, ret.z)
|
||||
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_structure_abi_32_2(self):
|
||||
'''x86 is simple. Always pass structure as memory.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
float_type = lc.Type.float()
|
||||
struct_type = lc.Type.struct([float_type, float_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_structure_abi_32_3(self):
|
||||
'''x86 is simple. Always pass structure as memory.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1 = builder.extract_value(arg, 0)
|
||||
se1 = builder.mul(e1, e1)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(OneByte, OneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = OneByte(x=8)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertEqual(arg.x * arg.x, ret.x)
|
||||
|
||||
tests.append(TestStructSystemVABI)
|
||||
|
||||
@skip_if_not_intel_cpu
|
||||
@skip_if_not_win32
|
||||
class TestStructMicrosoftABI(TestCase):
|
||||
'''
|
||||
Microsoft convention
|
||||
'''
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 64 bits
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_bigger_than_two_words_64(self):
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
ret = builder.insert_value(ret, e3, 2)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
self.assertEqual(arg.z, ret.z)
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_just_two_words_64(self):
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
struct_type = lc.Type.struct([double_type, double_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDouble, TwoDouble)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDouble(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
@skip_if_not_64bits
|
||||
def test_two_halfwords(self):
|
||||
'''Arguments smaller or equal to a word is packed into a word.
|
||||
|
||||
Floats structure are not passed on the XMM.
|
||||
Treat it as a i64.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
float_type = lc.Type.float()
|
||||
struct_type = lc.Type.struct([float_type, float_type])
|
||||
abi_type = lc.Type.int(64)
|
||||
|
||||
func_type = lc.Type.function(abi_type, [abi_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = func.args[0]
|
||||
|
||||
struct_ptr = builder.alloca(struct_type)
|
||||
struct_int_ptr = builder.bitcast(struct_ptr, lc.Type.pointer(abi_type))
|
||||
builder.store(arg, struct_int_ptr)
|
||||
|
||||
arg = builder.load(struct_ptr)
|
||||
|
||||
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
|
||||
builder.store(ret, struct_ptr)
|
||||
ret = builder.load(struct_int_ptr)
|
||||
|
||||
builder.ret(ret)
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 32 bits
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_one_word_register(self):
|
||||
'''Argument is passed by memory.
|
||||
Return value is passed by register.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(struct_type, [struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# pass structure by value
|
||||
func.args[0].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[0])
|
||||
e1 = builder.extract_value(arg, 0)
|
||||
se1 = builder.mul(e1, e1)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
|
||||
builder.ret(ret)
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(OneByte, OneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = OneByte(x=8)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertEqual(arg.x * arg.x, ret.x)
|
||||
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_two_floats(self):
|
||||
'''Argument is passed by register.
|
||||
Return in 2 registers
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
float_type = lc.Type.float()
|
||||
struct_type = lc.Type.struct([float_type, float_type])
|
||||
|
||||
abi_type = lc.Type.int(64)
|
||||
func_type = lc.Type.function(abi_type, [struct_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
out_ptr = builder.alloca(struct_type)
|
||||
|
||||
arg = func.args[0]
|
||||
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
builder.store(ret, out_ptr)
|
||||
|
||||
out_int_ptr = builder.bitcast(out_ptr, lc.Type.pointer(abi_type))
|
||||
|
||||
builder.ret(builder.load(out_int_ptr))
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
|
||||
@skip_if_not_32bits
|
||||
def test_bigger_than_two_words(self):
|
||||
'''Pass in memory.
|
||||
'''
|
||||
m = lc.Module.new('test_struct_arg')
|
||||
|
||||
double_type = lc.Type.double()
|
||||
uint8_type = lc.Type.int(8)
|
||||
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||
func_type = lc.Type.function(lc.Type.void(),
|
||||
[struct_ptr_type, struct_ptr_type])
|
||||
func = m.add_function(func_type, name='foo')
|
||||
|
||||
# return value pointer
|
||||
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||
|
||||
# pass structure by value
|
||||
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||
|
||||
# define function body
|
||||
builder = lc.Builder.new(func.append_basic_block(''))
|
||||
|
||||
arg = builder.load(func.args[1])
|
||||
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||
se1 = builder.fmul(e1, e2)
|
||||
se2 = builder.fdiv(e1, e2)
|
||||
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||
ret = builder.insert_value(ret, se2, 1)
|
||||
ret = builder.insert_value(ret, e3, 2)
|
||||
builder.store(ret, func.args[0])
|
||||
builder.ret_void()
|
||||
|
||||
del builder
|
||||
|
||||
# verify
|
||||
m.verify()
|
||||
|
||||
print(m)
|
||||
# use with ctypes
|
||||
engine = le.EngineBuilder.new(m).create()
|
||||
ptr = engine.get_pointer_to_function(func)
|
||||
|
||||
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||
cfunc = cfunctype(ptr)
|
||||
|
||||
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||
ret = cfunc(arg)
|
||||
print(arg)
|
||||
print(ret)
|
||||
|
||||
self.assertClose(arg.x * arg.y, ret.x)
|
||||
self.assertClose(arg.x / arg.y, ret.y)
|
||||
self.assertEqual(arg.z, ret.z)
|
||||
|
||||
tests.append(TestStructMicrosoftABI)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import (Module, Type, Builder, Constant)
|
||||
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestSwitch(TestCase):
|
||||
def test_arg_attr(self):
|
||||
m = Module.new('oifjda')
|
||||
fnty = Type.function(Type.void(), [Type.int()])
|
||||
func = m.add_function(fnty, 'foo')
|
||||
bb = func.append_basic_block('')
|
||||
bbdef = func.append_basic_block('')
|
||||
bbsw1 = func.append_basic_block('')
|
||||
bbsw2 = func.append_basic_block('')
|
||||
bldr = Builder.new(bb)
|
||||
|
||||
swt = bldr.switch(func.args[0], bbdef, n=2)
|
||||
swt.add_case(Constant.int(Type.int(), 0), bbsw1)
|
||||
swt.add_case(Constant.int(Type.int(), 1), bbsw2)
|
||||
|
||||
bldr.position_at_end(bbsw1)
|
||||
bldr.ret_void()
|
||||
|
||||
bldr.position_at_end(bbsw2)
|
||||
bldr.ret_void()
|
||||
|
||||
bldr.position_at_end(bbdef)
|
||||
bldr.ret_void()
|
||||
|
||||
func.verify()
|
||||
|
||||
tests.append(TestSwitch)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import unittest
|
||||
import llvm.core as lc
|
||||
import llvm.ee as le
|
||||
from llvm.core import Type, Builder
|
||||
from .support import TestCase, tests, skip_if
|
||||
|
||||
# Check PTX backend
|
||||
if le.initialize_target('PTX', noraise=True):
|
||||
PTX_ARCH = 'ptx64'
|
||||
elif le.initialize_target('NVPTX', noraise=True):
|
||||
PTX_ARCH = 'nvptx64'
|
||||
else:
|
||||
PTX_ARCH = None
|
||||
|
||||
class TestTargetMachines(TestCase):
|
||||
'''Exercise target machines
|
||||
|
||||
Require PTX backend
|
||||
'''
|
||||
def test_native(self):
|
||||
m, _ = self._build_module()
|
||||
tm = le.EngineBuilder.new(m).select_target()
|
||||
|
||||
self.assertTrue(tm.target_name)
|
||||
self.assertTrue(tm.target_data)
|
||||
self.assertTrue(tm.target_short_description)
|
||||
self.assertTrue(tm.triple)
|
||||
self.assertIn('foo', tm.emit_assembly(m))
|
||||
self.assertTrue(le.get_host_cpu_name())
|
||||
|
||||
@skip_if(not PTX_ARCH, msg='LLVM is not compiled with PTX enabled')
|
||||
def test_ptx(self):
|
||||
arch = PTX_ARCH
|
||||
print(arch)
|
||||
m, func = self._build_module()
|
||||
func.calling_convention = lc.CC_PTX_KERNEL # set calling conv
|
||||
ptxtm = le.TargetMachine.lookup(arch=arch, cpu='sm_20')
|
||||
self.assertTrue(ptxtm.triple)
|
||||
self.assertTrue(ptxtm.cpu)
|
||||
ptxasm = ptxtm.emit_assembly(m)
|
||||
self.assertIn('foo', ptxasm)
|
||||
if arch == 'nvptx64':
|
||||
self.assertIn('.address_size 64', ptxasm)
|
||||
self.assertIn('sm_20', ptxasm)
|
||||
|
||||
def _build_module(self):
|
||||
m = lc.Module.new('TestTargetMachines')
|
||||
|
||||
fnty = Type.function(Type.void(), [])
|
||||
func = m.add_function(fnty, name='foo')
|
||||
|
||||
bldr = Builder.new(func.append_basic_block('entry'))
|
||||
bldr.ret_void()
|
||||
m.verify()
|
||||
return m, func
|
||||
|
||||
|
||||
tests.append(TestTargetMachines)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Type
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestTypeHash(TestCase):
|
||||
def test_scalar_type(self):
|
||||
i32a = Type.int(32)
|
||||
i32b = Type.int(32)
|
||||
i64a = Type.int(64)
|
||||
i64b = Type.int(64)
|
||||
ts = set([i32a, i32b, i64a, i64b])
|
||||
self.assertTrue(len(ts))
|
||||
self.assertTrue(i32a in ts)
|
||||
self.assertTrue(i64b in ts)
|
||||
|
||||
def test_struct_type(self):
|
||||
ta = Type.struct([Type.int(32), Type.float()])
|
||||
tb = Type.struct([Type.int(32), Type.float()])
|
||||
tc = Type.struct([Type.int(32), Type.int(32), Type.float()])
|
||||
ts = set([ta, tb, tc])
|
||||
self.assertTrue(len(ts) == 2)
|
||||
self.assertTrue(ta in ts)
|
||||
self.assertTrue(tb in ts)
|
||||
self.assertTrue(tc in ts)
|
||||
|
||||
tests.append(TestTypeHash)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type, Builder, Constant
|
||||
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestUses(TestCase):
|
||||
|
||||
def test_uses(self):
|
||||
m = Module.new('a')
|
||||
t = Type.int()
|
||||
ft = Type.function(t, [t, t, t])
|
||||
f = m.add_function(ft, "func")
|
||||
b = f.append_basic_block('entry')
|
||||
bld = Builder.new(b)
|
||||
tmp1 = bld.add(Constant.int(t, 100), f.args[0], "tmp1")
|
||||
tmp2 = bld.add(tmp1, f.args[1], "tmp2")
|
||||
tmp3 = bld.add(tmp1, f.args[2], "tmp3")
|
||||
bld.ret(tmp3)
|
||||
|
||||
# Testing use count
|
||||
self.assertEqual(f.args[0].use_count, 1)
|
||||
self.assertEqual(f.args[1].use_count, 1)
|
||||
self.assertEqual(f.args[2].use_count, 1)
|
||||
self.assertEqual(tmp1.use_count, 2)
|
||||
self.assertEqual(tmp2.use_count, 0)
|
||||
self.assertEqual(tmp3.use_count, 1)
|
||||
|
||||
# Testing uses
|
||||
self.assert_(f.args[0].uses[0] is tmp1)
|
||||
self.assertEqual(len(f.args[0].uses), 1)
|
||||
self.assert_(f.args[1].uses[0] is tmp2)
|
||||
self.assertEqual(len(f.args[1].uses), 1)
|
||||
self.assert_(f.args[2].uses[0] is tmp3)
|
||||
self.assertEqual(len(f.args[2].uses), 1)
|
||||
self.assertEqual(len(tmp1.uses), 2)
|
||||
self.assertEqual(len(tmp2.uses), 0)
|
||||
self.assertEqual(len(tmp3.uses), 1)
|
||||
|
||||
tests.append(TestUses)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import unittest
|
||||
from llvm.core import Module, Type, Builder
|
||||
from .support import TestCase, tests
|
||||
|
||||
class TestVolatile(TestCase):
|
||||
|
||||
def test_volatile(self):
|
||||
mod = Module.new('mod')
|
||||
functype = Type.function(Type.void(), [])
|
||||
func = mod.add_function(functype, name='foo')
|
||||
bb = func.append_basic_block('entry')
|
||||
bldr = Builder.new(bb)
|
||||
ptr = bldr.alloca(Type.int())
|
||||
|
||||
# test load inst
|
||||
val = bldr.load(ptr)
|
||||
self.assertFalse(val.is_volatile, "default must be non-volatile")
|
||||
val.set_volatile(True)
|
||||
self.assertTrue(val.is_volatile, "fail to set volatile")
|
||||
val.set_volatile(False)
|
||||
self.assertFalse(val.is_volatile, "fail to unset volatile")
|
||||
|
||||
# test store inst
|
||||
store_inst = bldr.store(val, ptr)
|
||||
self.assertFalse(store_inst.is_volatile, "default must be non-volatile")
|
||||
store_inst.set_volatile(True)
|
||||
self.assertTrue(store_inst.is_volatile, "fail to set volatile")
|
||||
store_inst.set_volatile(False)
|
||||
self.assertFalse(store_inst.is_volatile, "fail to unset volatile")
|
||||
|
||||
def test_volatile_another(self):
|
||||
mod = Module.new('mod')
|
||||
functype = Type.function(Type.void(), [])
|
||||
func = mod.add_function(functype, name='foo')
|
||||
bb = func.append_basic_block('entry')
|
||||
bldr = Builder.new(bb)
|
||||
ptr = bldr.alloca(Type.int())
|
||||
|
||||
# test load inst
|
||||
val = bldr.load(ptr, volatile=True)
|
||||
self.assertTrue(val.is_volatile, "volatile kwarg does not work")
|
||||
val.set_volatile(False)
|
||||
self.assertFalse(val.is_volatile, "fail to unset volatile")
|
||||
val.set_volatile(True)
|
||||
self.assertTrue(val.is_volatile, "fail to set volatile")
|
||||
|
||||
# test store inst
|
||||
store_inst = bldr.store(val, ptr, volatile=True)
|
||||
self.assertTrue(store_inst.is_volatile, "volatile kwarg does not work")
|
||||
store_inst.set_volatile(False)
|
||||
self.assertFalse(store_inst.is_volatile, "fail to unset volatile")
|
||||
store_inst.set_volatile(True)
|
||||
self.assertTrue(store_inst.is_volatile, "fail to set volatile")
|
||||
|
||||
tests.append(TestVolatile)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
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:])
|
||||
|
|
@ -12,11 +12,7 @@ http://software.intel.com/sites/default/files/m/a/b/3/4/d/41604-319433-012a.pdf
|
|||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import contextlib
|
||||
|
||||
import sys, os, subprocess
|
||||
|
||||
def detect_avx_support(option='detect'):
|
||||
'''Detect AVX support'''
|
||||
|
|
@ -40,7 +36,6 @@ def detect_unix_like():
|
|||
except IOError:
|
||||
return False
|
||||
|
||||
with contextlib.closing(info):
|
||||
for line in info:
|
||||
if line.lstrip().startswith('flags'):
|
||||
features = line.split()
|
||||
|
|
@ -49,25 +44,12 @@ def detect_unix_like():
|
|||
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:
|
||||
info = subprocess.Popen(['sysctl', '-n', 'machdep.cpu.features'],
|
||||
stdout=subprocess.PIPE)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ class CBuilder(object):
|
|||
elif not isinstance(count, lc.Value):
|
||||
count = self.constant(types.int, count).value
|
||||
|
||||
ptr = self.builder.alloca(ty, size=count, name=name)
|
||||
ptr = self.builder.alloca_array(ty, count, name=name)
|
||||
return CArray(self, ptr)
|
||||
|
||||
def ret(self, val=None):
|
||||
|
|
|
|||
|
|
@ -1,95 +1,55 @@
|
|||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
#include <python3adapt.h>
|
||||
#include <capsulethunk.h>
|
||||
#include <llvm_binding/capsule_context.h>
|
||||
#include <llvm_binding/auto_pyobject.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
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* GetAPIModule(){
|
||||
if (NULL == TheAPIModule)
|
||||
TheAPIModule = PyImport_ImportModule("llvmpy._api");
|
||||
return TheAPIModule;
|
||||
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* GetCapsuleModule(){
|
||||
if (NULL == TheCapsuleModule)
|
||||
TheCapsuleModule = PyImport_ImportModule("llvmpy.capsule");
|
||||
return TheCapsuleModule;
|
||||
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* GetCapsuleClass() {
|
||||
if (NULL == TheCapsuleClass)
|
||||
TheCapsuleClass = PyObject_GetAttrString(GetCapsuleModule(),
|
||||
"Capsule");
|
||||
return TheCapsuleClass;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
PyObject* GetWrapperClass() {
|
||||
if (NULL == TheWrapperClass)
|
||||
TheWrapperClass = PyObject_GetAttrString(GetCapsuleModule(),
|
||||
"Wrapper");
|
||||
return TheWrapperClass;
|
||||
}
|
||||
// ------------------
|
||||
// PyCapsule Context
|
||||
// ------------------
|
||||
|
||||
static
|
||||
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) {
|
||||
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.");
|
||||
|
|
@ -99,8 +59,9 @@ CapsuleContext* GetContext(PyObject *obj) {
|
|||
}
|
||||
|
||||
static
|
||||
PyObject* GetClassName(PyObject* obj) {
|
||||
CapsuleContext* context = GetContext(obj);
|
||||
PyObject* getClassName(PyObject* self, PyObject* args) {
|
||||
CapsuleContext* context = getContext(self, args);
|
||||
//Assert(context->_magic == 0xdead);
|
||||
if (!context) {
|
||||
return NULL;
|
||||
} else {
|
||||
|
|
@ -108,476 +69,6 @@ PyObject* GetClassName(PyObject* obj) {
|
|||
}
|
||||
}
|
||||
|
||||
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 <sizeof(pointer); ++i) {
|
||||
hash ^= ((unsigned char*)&pointer)[i];
|
||||
hash = RotR(hash, 11);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static
|
||||
bool Capsule_eq(PyObject *self, PyObject *other) {
|
||||
if (PyObject_Type(self) == PyObject_Type(other)) {
|
||||
CapsuleObject *a = (CapsuleObject*)self;
|
||||
CapsuleObject *b = (CapsuleObject*)other;
|
||||
|
||||
void* pa = PyCapsule_GetPointer(a->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 }
|
||||
|
|
@ -585,10 +76,6 @@ static PyMethodDef core_methods[] = {
|
|||
declmethod(getPointer),
|
||||
declmethod(check),
|
||||
declmethod(getClassName),
|
||||
declmethod(unwrap),
|
||||
declmethod(wrap),
|
||||
declmethod(has_ownership),
|
||||
declmethod(downcast),
|
||||
{ NULL },
|
||||
#undef declmethod
|
||||
};
|
||||
|
|
@ -619,21 +106,10 @@ 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,19 @@
|
|||
from weakref import WeakValueDictionary
|
||||
from weakref import WeakKeyDictionary, WeakValueDictionary, ref
|
||||
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.
|
||||
|
|
@ -36,156 +28,9 @@ def set_debug(enabled):
|
|||
else:
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
|
||||
_addr2refct = defaultdict(lambda: 0)
|
||||
#_capsule2weak = WeakKeyDictionary()
|
||||
_addr2dtor = {}
|
||||
_pyclasses = {}
|
||||
|
||||
# Cache {cls: {addr: obj}}
|
||||
# NOTE: The same 'addr' may appear in multiple class bins.
|
||||
_cache = defaultdict(WeakValueDictionary)
|
||||
|
||||
|
||||
def release_ownership(old):
|
||||
logger.debug('Release %s', old)
|
||||
addr = getPointer(old)
|
||||
name = getName(old)
|
||||
if _addr2dtor.get((name, addr)) is None:
|
||||
clsname = getClassName(old)
|
||||
if not _pyclasses[clsname]._has_dtor():
|
||||
return
|
||||
# Guard duplicated release
|
||||
raise Exception("Already released")
|
||||
_addr2dtor[(name, addr)] = None
|
||||
|
||||
|
||||
def obtain_ownership(cap):
|
||||
cls = cap.get_class()
|
||||
if cls._has_dtor():
|
||||
addr = cap.pointer
|
||||
name = cap.name
|
||||
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
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
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
|
||||
def _capsule_weakref_dtor(item):
|
||||
addr = item.pointer
|
||||
name = item.name
|
||||
|
||||
_addr2refct[addr] -= 1
|
||||
refct = _addr2refct[addr]
|
||||
assert refct >= 0, "RefCt drop below 0"
|
||||
|
|
@ -199,6 +44,143 @@ class Wrapper(object):
|
|||
logger.debug('Destroy %s %s', name, hex(addr))
|
||||
dtor(item.capsule)
|
||||
|
||||
class Capsule(object):
|
||||
"Wraps PyCapsule so that we can build weakref of it."
|
||||
|
||||
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()
|
||||
_addr2dtor = {}
|
||||
_pyclasses = {}
|
||||
|
||||
# Cache {cls: {addr: obj}}
|
||||
# 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)
|
||||
if _addr2dtor.get((name, addr)) is None:
|
||||
clsname = Capsule.getClassName(old)
|
||||
if not _pyclasses[clsname]._has_dtor():
|
||||
return
|
||||
# Guard duplicated release
|
||||
raise Exception("Already released")
|
||||
_addr2dtor[(name, addr)] = None
|
||||
|
||||
|
||||
def obtain_ownership(cap):
|
||||
cls = cap.get_class()
|
||||
if cls._has_dtor():
|
||||
addr = cap.pointer
|
||||
name = cap.name
|
||||
assert _addr2dtor[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
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def _capsule(self):
|
||||
return self.__capsule
|
||||
|
|
@ -211,11 +193,10 @@ class Wrapper(object):
|
|||
return hash(self._capsule)
|
||||
|
||||
def __eq__(self, other):
|
||||
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)
|
||||
|
|
@ -224,25 +205,24 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import inspect, textwrap
|
||||
import functools
|
||||
import codegen as cg
|
||||
import os
|
||||
|
||||
|
|
@ -7,12 +8,10 @@ namespaces = {}
|
|||
|
||||
RESERVED = frozenset(['None'])
|
||||
|
||||
|
||||
def makedir(directory):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
|
||||
class SubModule(object):
|
||||
def __init__(self):
|
||||
self.methods = []
|
||||
|
|
@ -294,10 +293,6 @@ 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:
|
||||
|
|
@ -404,7 +399,6 @@ class Enum(object):
|
|||
writer.println(fmt % locals())
|
||||
writer.println()
|
||||
|
||||
|
||||
class Method(object):
|
||||
_kind_ = 'meth'
|
||||
|
||||
|
|
@ -522,7 +516,6 @@ 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)
|
||||
|
|
@ -601,7 +594,6 @@ class CustomFunction(Function):
|
|||
def fullname(self):
|
||||
return self.realname
|
||||
|
||||
|
||||
class Destructor(Method):
|
||||
_kind_ = 'dtor'
|
||||
|
||||
|
|
@ -633,7 +625,6 @@ class Constructor(StaticMethod):
|
|||
ret = writer.declare(retty.fullname, stmt)
|
||||
return ret
|
||||
|
||||
|
||||
class ref(_Type):
|
||||
def __init__(self, element):
|
||||
assert isinstance(element, Class), type(element)
|
||||
|
|
@ -695,16 +686,13 @@ 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'
|
||||
|
||||
|
|
@ -769,7 +757,6 @@ class CustomPythonMethod(object):
|
|||
for line in self.sourcelines:
|
||||
writer.println(line)
|
||||
|
||||
|
||||
class CustomPythonStaticMethod(CustomPythonMethod):
|
||||
def compile_py(self, writer):
|
||||
writer.println('@staticmethod')
|
||||
|
|
@ -858,7 +845,6 @@ 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)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ extern "C" {
|
|||
|
||||
#if (PY_MAJOR_VERSION >= 3)
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyObject *
|
||||
PyInit_%(module)s(void)
|
||||
{
|
||||
PyObject *module = create_python_module("%(module)s", meth_%(ns)s);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include <Python.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#if LLVM_VERSION_MAJOR >= 3 && LLVM_VERSION_MINOR >= 3
|
||||
#if LLVM_VERSION_MAJOR >= 3 and LLVM_VERSION_MINOR >= 3
|
||||
#include <llvm/IR/Value.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
|
|
@ -8,10 +8,6 @@
|
|||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/Intrinsics.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#if LLVM_VERSION_MINOR >= 4
|
||||
#include <llvm/Support/MemoryObject.h>
|
||||
#include <llvm/MC/MCDisassembler.h>
|
||||
#endif
|
||||
#else
|
||||
#include <llvm/Value.h>
|
||||
#include <llvm/DerivedTypes.h>
|
||||
|
|
@ -186,8 +182,8 @@ PyObject* iterator_to_pylist_deref(iterator begin, iterator end,
|
|||
{
|
||||
PyObject* list = PyList_New(0);
|
||||
for(; begin != end; ++begin) {
|
||||
auto_pyobject cap = pycapsule_new(&*begin, capsuleName, className);
|
||||
PyList_Append(list, *cap);
|
||||
PyObject* cap = pycapsule_new(&*begin, capsuleName, className);
|
||||
PyList_Append(list, cap);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
@ -198,8 +194,8 @@ PyObject* iterator_to_pylist(iterator begin, iterator end,
|
|||
{
|
||||
PyObject* list = PyList_New(0);
|
||||
for(; begin != end; ++begin) {
|
||||
auto_pyobject cap = pycapsule_new(*begin, capsuleName, className);
|
||||
PyList_Append(list, *cap);
|
||||
PyObject* cap = pycapsule_new(*begin, capsuleName, className);
|
||||
PyList_Append(list, cap);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
@ -575,11 +571,11 @@ PyObject* TargetMachine_addPassesToEmitFile(
|
|||
bool status = TM->addPassesToEmitFile(PM, fso, FTy, disableVerify);
|
||||
if (status) {
|
||||
StringRef sr = rso.str();
|
||||
auto_pyobject buf = PyString_FromStringAndSize(sr.data(), sr.size());
|
||||
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;
|
||||
|
|
@ -861,27 +857,6 @@ 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;
|
||||
|
|
@ -891,8 +866,7 @@ public:
|
|||
inline virtual void passEnumerate(const llvm::PassInfo * pass_info){
|
||||
PyObject* passArg = PyString_FromString(pass_info->getPassArgument());
|
||||
PyObject* passName = PyString_FromString(pass_info->getPassName());
|
||||
auto_pyobject pair = Py_BuildValue("(OO)", passArg, passName);
|
||||
PyList_Append(List, *pair);
|
||||
PyList_Append(List, Py_BuildValue("(OO)", passArg, passName));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -947,6 +921,7 @@ PyObject* TargetRegistry_lookupTarget(const std::string &Arch,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
PyObject* TargetRegistry_getClosestTargetForJIT(PyObject* Error)
|
||||
{
|
||||
|
|
@ -967,66 +942,6 @@ PyObject* TargetRegistry_getClosestTargetForJIT(PyObject* Error)
|
|||
|
||||
}
|
||||
|
||||
static
|
||||
PyObject* TargetRegistry_targets_list()
|
||||
{
|
||||
using namespace llvm;
|
||||
|
||||
return iterator_to_pylist_deref<TargetRegistry::iterator>(
|
||||
TargetRegistry::begin(), TargetRegistry::end(),
|
||||
"llvm::Target", "llvm::Target");
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 3 && LLVM_VERSION_MINOR >= 4
|
||||
static
|
||||
PyObject* MemoryObject_readBytes(const llvm::MemoryObject *mobj,
|
||||
uint64_t addr,
|
||||
uint64_t size
|
||||
)
|
||||
{
|
||||
int status;
|
||||
uint8_t *bytes;
|
||||
PyObject* po;
|
||||
|
||||
if(size < 1)
|
||||
goto fail;
|
||||
|
||||
bytes = new uint8_t[size];
|
||||
if(bytes == NULL)
|
||||
goto fail;
|
||||
|
||||
status = mobj->readBytes(addr, size, bytes);
|
||||
if(status != 0) {
|
||||
delete bytes;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
po = PyBytes_FromStringAndSize((const char *) bytes, size);
|
||||
delete bytes;
|
||||
|
||||
return po;
|
||||
fail:
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static
|
||||
PyObject* MCDisassembler_getInstruction(llvm::MCDisassembler *disasm,
|
||||
llvm::MCInst &instr,
|
||||
const llvm::MemoryObject ®ion,
|
||||
uint64_t address
|
||||
)
|
||||
{
|
||||
uint64_t size;
|
||||
llvm::MCDisassembler::DecodeStatus status;
|
||||
|
||||
size = 0;
|
||||
status = disasm->getInstruction(instr, size, region, address,
|
||||
llvm::nulls(), llvm::nulls());
|
||||
return Py_BuildValue("(i,i)", int(status), size);
|
||||
}
|
||||
|
||||
#endif /* llvm >= 3.4 */
|
||||
|
||||
static
|
||||
PyObject* llvm_sys_getHostCPUFeatures(PyObject* Features)
|
||||
{
|
||||
|
|
@ -1069,4 +984,3 @@ PyObject* llvm_sys_isBigEndianHost()
|
|||
Py_RETURN_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ components = ['core', 'analysis', 'scalaropts',
|
|||
'interpreter', 'bitreader',
|
||||
'bitwriter', 'instrumentation', 'ipa',
|
||||
'ipo', 'transformutils', 'asmparser',
|
||||
'linker', 'support', 'vectorize', 'all-targets'
|
||||
'linker', 'support', 'vectorize',
|
||||
]
|
||||
|
||||
nvptx = ['nvptx',
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
from binding import *
|
||||
from .namespace import llvm
|
||||
from .Value import Function, BasicBlock, Value
|
||||
from .Value import Function, BasicBlock
|
||||
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),
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
from binding import *
|
||||
from .namespace import llvm
|
||||
|
||||
ccs = '''
|
||||
CallingConv = llvm.Namespace('CallingConv')
|
||||
ID = CallingConv.Enum('ID', '''
|
||||
C, Fast, Cold, GHC, FirstTargetCC, X86_StdCall, X86_FastCall,
|
||||
ARM_APCS, ARM_AAPCS, ARM_AAPCS_VFP, MSP430_INTR, X86_ThisCall,
|
||||
PTX_Kernel, PTX_Device,
|
||||
'''
|
||||
|
||||
if LLVM_VERSION <= (3, 3):
|
||||
ccs += "MBLAZE_INTR, MBLAZE_SVOL,"
|
||||
|
||||
ccs += 'SPIR_FUNC, SPIR_KERNEL, Intel_OCL_BI'
|
||||
|
||||
CallingConv = llvm.Namespace('CallingConv')
|
||||
ID = CallingConv.Enum('ID', ccs) # HiPE
|
||||
PTX_Kernel, PTX_Device, MBLAZE_INTR, MBLAZE_SVOL, SPIR_FUNC,
|
||||
SPIR_KERNEL, Intel_OCL_BI
|
||||
''') # HiPE
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ DIBuilder = llvm.Class()
|
|||
from .Module import Module
|
||||
from .Value import Value, MDNode, Function, BasicBlock
|
||||
from .Instruction import Instruction
|
||||
from .DebugInfo import DIFile, DIEnumerator, DIType, DIBasicType, DIDerivedType, DICompositeType
|
||||
from .DebugInfo import DIFile, DIEnumerator, DIType, DIBasicType, DIDerivedType
|
||||
from .DebugInfo import DIDescriptor, DIArray, DISubrange, DIGlobalVariable
|
||||
from .DebugInfo import DIVariable, DISubprogram, DINameSpace, DILexicalBlockFile
|
||||
from .DebugInfo import DILexicalBlock
|
||||
|
|
@ -25,7 +25,6 @@ class DIBuilder:
|
|||
new = Constructor(ref(Module))
|
||||
delete = Destructor()
|
||||
|
||||
if LLVM_VERSION <= (3, 3):
|
||||
getCU = Method(const(ptr(MDNode)))
|
||||
finalize = Method()
|
||||
|
||||
|
|
@ -47,16 +46,12 @@ class DIBuilder:
|
|||
|
||||
createEnumerator = Method(DIEnumerator,
|
||||
stringref_arg, # Name
|
||||
uint64_arg if LLVM_VERSION <= (3, 3) else int64_arg, # Val
|
||||
uint64_arg, # Val
|
||||
)
|
||||
|
||||
if LLVM_VERSION <= (3, 3):
|
||||
createNullPtrType = Method(DIType,
|
||||
stringref_arg, # Name
|
||||
)
|
||||
else:
|
||||
createNullPtrType = Method(DIBasicType)
|
||||
|
||||
|
||||
createBasicType = Method(DIType,
|
||||
stringref_arg, # Name
|
||||
|
|
@ -90,7 +85,7 @@ class DIBuilder:
|
|||
ref(DIDescriptor), # Context
|
||||
)
|
||||
|
||||
createFriend = Method(DIType if LLVM_VERSION <= (3, 3) else DIDerivedType,
|
||||
createFriend = Method(DIType,
|
||||
ref(DIType), # Ty
|
||||
ref(DIType), # FriendTy
|
||||
)
|
||||
|
|
@ -174,7 +169,7 @@ class DIBuilder:
|
|||
ref(DIArray), # Subscripts
|
||||
)
|
||||
|
||||
createVectorType = Method(DIType if LLVM_VERSION <= (3, 3) else DICompositeType,
|
||||
createVectorType = Method(DIType,
|
||||
uint64_arg, # Size
|
||||
uint64_arg, # AlignInBits
|
||||
ref(DIType), # Ty
|
||||
|
|
@ -280,7 +275,7 @@ class DIBuilder:
|
|||
stringref_arg, # LinkageName
|
||||
ref(DIFile), # File
|
||||
unsigned_arg, # LineNo
|
||||
ref(DIType if LLVM_VERSION <= (3, 3) else DICompositeType), # Ty
|
||||
ref(DIType), # Ty
|
||||
bool_arg, # isLocalToUnit
|
||||
bool_arg, # isDefinition
|
||||
unsigned_arg, # ScopeLine
|
||||
|
|
@ -298,7 +293,7 @@ class DIBuilder:
|
|||
stringref_arg, # LinkageName
|
||||
ref(DIFile), # File
|
||||
unsigned_arg, # LineNo
|
||||
ref(DIType if LLVM_VERSION <= (3, 3) else DICompositeType), # Ty
|
||||
ref(DIType), # Ty
|
||||
bool_arg, # isLocalToUnit
|
||||
bool_arg, # isDefinition
|
||||
unsigned_arg, # Virtuality=0
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class DIFile:
|
|||
@DIEnumerator
|
||||
class DIEnumerator:
|
||||
getName = Method(return_stringref)
|
||||
getEnumValue = Method(cast(Uint64 if LLVM_VERSION <= (3, 3) else Int64, int))
|
||||
getEnumValue = Method(cast(Uint64, int))
|
||||
Verify = Method(return_bool)
|
||||
|
||||
@DIType
|
||||
|
|
|
|||
|
|
@ -346,9 +346,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
#this file is not processed unless the llvm library is
|
||||
#version 3.4 or higher. see llvmpy/__init__.py for details.
|
||||
from binding import *
|
||||
from ..namespace import llvm
|
||||
from ..Support.StringRefMemoryObject import MemoryObject
|
||||
from ..Support.raw_ostream import raw_ostream
|
||||
from src.ADT.StringRef import StringRef
|
||||
|
||||
MCSubtargetInfo = llvm.Class()
|
||||
MCDisassembler = llvm.Class()
|
||||
MCInst = llvm.Class()
|
||||
MCOperand = llvm.Class()
|
||||
MCExpr = llvm.Class()
|
||||
MCAsmInfo = llvm.Class()
|
||||
MCRegisterInfo = llvm.Class()
|
||||
MCInstrInfo = llvm.Class()
|
||||
MCInstrAnalysis = llvm.Class()
|
||||
MCInstPrinter = llvm.Class()
|
||||
MCInstrDesc = llvm.Class()
|
||||
|
||||
TargetSubtargetInfo = llvm.Class(MCSubtargetInfo)
|
||||
TargetInstrInfo = llvm.Class(MCInstrInfo)
|
||||
TargetRegisterInfo = llvm.Class(MCRegisterInfo)
|
||||
|
||||
@MCInstrDesc
|
||||
class MCInstrDesc:
|
||||
_include_ = "llvm/MC/MCInstrDesc.h"
|
||||
|
||||
TSFlags = Attr(getter=cast(Uint64, int), setter=cast(int, Uint64))
|
||||
getFlags = Method(cast(Unsigned, int))
|
||||
getOpcode = Method(cast(Unsigned, int))
|
||||
|
||||
def _ret_bool():
|
||||
return Method(cast(Bool, bool))
|
||||
|
||||
isReturn = _ret_bool()
|
||||
isCall = _ret_bool()
|
||||
isBarrier = _ret_bool()
|
||||
isBranch = _ret_bool()
|
||||
isTerminator = _ret_bool()
|
||||
isIndirectBranch = _ret_bool()
|
||||
isConditionalBranch = _ret_bool()
|
||||
isUnconditionalBranch = _ret_bool()
|
||||
|
||||
|
||||
@MCSubtargetInfo
|
||||
class MCSubtargetInfo:
|
||||
pass
|
||||
|
||||
@TargetSubtargetInfo
|
||||
class TargetSubtargetInfo:
|
||||
_include_ = 'llvm/Target/TargetSubtargetInfo.h'
|
||||
|
||||
@MCExpr
|
||||
class MCExpr:
|
||||
_include_ = "llvm/MC/MCExpr.h"
|
||||
|
||||
ExprKind = Enum('Binary', 'Constant', 'SymbolRef', 'Unary', 'Target')
|
||||
getKind = Method(ExprKind)
|
||||
|
||||
@MCOperand
|
||||
class MCOperand:
|
||||
_include_ = "llvm/MC/MCInst.h"
|
||||
|
||||
isValid = Method(cast(Bool, bool))
|
||||
isReg = Method(cast(Bool, bool))
|
||||
isImm = Method(cast(Bool, bool))
|
||||
isFPImm = Method(cast(Bool, bool))
|
||||
isExpr = Method(cast(Bool, bool))
|
||||
isInst = Method(cast(Bool, bool))
|
||||
|
||||
getReg = Method(cast(Unsigned, int))
|
||||
getImm = Method(cast(Int64, int))
|
||||
getFPImm = Method(cast(Double, float))
|
||||
getExpr = Method(const(ownedptr(MCExpr)))
|
||||
|
||||
@MCInst
|
||||
class MCInst:
|
||||
_include_ = "llvm/MC/MCInst.h"
|
||||
new = Constructor()
|
||||
|
||||
size = Method(cast(Size_t, int))
|
||||
getNumOperands = Method(cast(Unsigned, int))
|
||||
|
||||
getOperand = Method(const(ref(MCOperand)), cast(int, Unsigned))
|
||||
|
||||
getOpcode = Method(cast(Unsigned, int))
|
||||
|
||||
MCOperand.getInst = Method(const(ownedptr(MCInst)))
|
||||
|
||||
@MCAsmInfo
|
||||
class MCAsmInfo:
|
||||
_include_ = "llvm/MC/MCAsmInfo.h"
|
||||
|
||||
getAssemblerDialect = Method(cast(Unsigned, int))
|
||||
getMinInstAlignment = Method(cast(Unsigned, int))
|
||||
isLittleEndian = Method(cast(Bool, bool))
|
||||
|
||||
@MCRegisterInfo
|
||||
class MCRegisterInfo:
|
||||
_include_ = "llvm/MC/MCRegisterInfo.h"
|
||||
|
||||
getName = Method(cast(ConstCharPtr, str), cast(int, Unsigned))
|
||||
|
||||
@TargetRegisterInfo
|
||||
class TargetRegisterInfo:
|
||||
_include_ = "llvm/Target/TargetRegisterInfo.h"
|
||||
|
||||
@MCInstrInfo
|
||||
class MCInstrInfo:
|
||||
_include_ = "llvm/MC/MCInstrInfo.h"
|
||||
|
||||
get = Method(const(ref(MCInstrDesc)), cast(int, Unsigned))
|
||||
|
||||
@TargetInstrInfo
|
||||
class TargetInstrInfo:
|
||||
_include_ = 'llvm/Target/TargetInstrInfo.h'
|
||||
|
||||
@MCInstrAnalysis
|
||||
class MCInstrAnalysis:
|
||||
_include_ = "llvm/MC/MCInstrAnalysis.h"
|
||||
|
||||
|
||||
def _take_mcinst_ret_bool():
|
||||
return Method(cast(Bool, bool), const(ref(MCInst)))
|
||||
|
||||
isBranch = _take_mcinst_ret_bool()
|
||||
isConditionalBranch = _take_mcinst_ret_bool()
|
||||
isUnconditionalBranch = _take_mcinst_ret_bool()
|
||||
isIndirectBranch = _take_mcinst_ret_bool()
|
||||
isCall = _take_mcinst_ret_bool()
|
||||
isReturn = _take_mcinst_ret_bool()
|
||||
isTerminator = _take_mcinst_ret_bool()
|
||||
|
||||
@MCInstPrinter
|
||||
class MCInstPrinter:
|
||||
_include_ = "llvm/MC/MCInstPrinter.h"
|
||||
|
||||
printInst = Method(Void,
|
||||
const(ptr(MCInst)), #MI
|
||||
ref(raw_ostream), #OS
|
||||
cast(str, StringRef) #Annot
|
||||
)
|
||||
|
||||
@MCDisassembler
|
||||
class MCDisassembler:
|
||||
_include_ = "llvm/MC/MCDisassembler.h"
|
||||
|
||||
DecodeStatus = Enum('Fail', 'SoftFail', 'Success')
|
||||
|
||||
getInstruction = CustomMethod('MCDisassembler_getInstruction',
|
||||
PyObjectPtr,
|
||||
ref(MCInst),
|
||||
ref(MemoryObject),
|
||||
cast(int, Uint64)
|
||||
)
|
||||
|
||||
|
|
@ -26,10 +26,3 @@ 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)
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
from binding import *
|
||||
from ..namespace import llvm
|
||||
from ..ADT.StringRef import StringRef
|
||||
|
||||
if LLVM_VERSION >= (3, 4):
|
||||
MemoryObject = llvm.Class()
|
||||
StringRefMemoryObject = llvm.Class(MemoryObject)
|
||||
|
||||
@MemoryObject
|
||||
class MemoryObject:
|
||||
_include_ = "llvm/Support/MemoryObject.h"
|
||||
|
||||
getBase = Method(cast(Uint64, int))
|
||||
getExtent = Method(cast(Uint64, int))
|
||||
|
||||
readBytes = CustomMethod('MemoryObject_readBytes',
|
||||
PyObjectPtr,
|
||||
cast(int, Uint64), #address
|
||||
cast(int, Uint64) #size
|
||||
)
|
||||
@CustomPythonMethod
|
||||
def readAll(self):
|
||||
result = self.readBytes(self.getBase(), self.getExtent())
|
||||
if not result:
|
||||
raise Exception("expected readBytes to be successful!")
|
||||
return result
|
||||
|
||||
@StringRefMemoryObject
|
||||
class StringRefMemoryObject:
|
||||
_include_ = "llvm/Support/StringRefMemoryObject.h"
|
||||
|
||||
new = Constructor(cast(bytes, StringRef), cast(int, Uint64))
|
||||
|
|
@ -12,15 +12,6 @@ from src.Target.TargetMachine import TargetMachine
|
|||
from src.Target.TargetOptions import TargetOptions
|
||||
from src.Support.CodeGen import Reloc, CodeModel, CodeGenOpt
|
||||
|
||||
if LLVM_VERSION >= (3, 4):
|
||||
from src.MC import MCSubtargetInfo
|
||||
from src.MC import MCDisassembler
|
||||
from src.MC import MCRegisterInfo
|
||||
from src.MC import MCAsmInfo
|
||||
from src.MC import MCInstrInfo
|
||||
from src.MC import MCInstrAnalysis
|
||||
from src.MC import MCInstPrinter
|
||||
|
||||
@Target
|
||||
class Target:
|
||||
getNext = Method(const(ownedptr(Target)))
|
||||
|
|
@ -52,35 +43,7 @@ class Target:
|
|||
CodeGenOpt.Level, # = CodeGenOpt.Default
|
||||
).require_only(4)
|
||||
|
||||
if LLVM_VERSION >= (3, 4):
|
||||
createMCSubtargetInfo = Method(ptr(MCSubtargetInfo),
|
||||
cast(str, StringRef), #triple
|
||||
cast(str, StringRef), #cpu
|
||||
cast(str, StringRef) #features
|
||||
)
|
||||
|
||||
createMCDisassembler = Method(ptr(MCDisassembler), ref(MCSubtargetInfo))
|
||||
|
||||
createMCRegInfo = Method(ptr(MCRegisterInfo),
|
||||
cast(str, StringRef) #Triple
|
||||
)
|
||||
|
||||
createMCAsmInfo = Method(ptr(MCAsmInfo),
|
||||
const(ref(MCRegisterInfo)), #MRI
|
||||
cast(str, StringRef) #Triple
|
||||
)
|
||||
|
||||
createMCInstrInfo = Method(ptr(MCInstrInfo))
|
||||
|
||||
createMCInstrAnalysis = Method(ptr(MCInstrAnalysis), const(ptr(MCInstrInfo)))
|
||||
|
||||
createMCInstPrinter = Method(ptr(MCInstPrinter),
|
||||
cast(int, Unsigned), #SyntaxVariant
|
||||
const(ref(MCAsmInfo)), #MAI
|
||||
const(ref(MCInstrInfo)), #MII
|
||||
const(ref(MCRegisterInfo)), #MRI
|
||||
const(ref(MCSubtargetInfo)) #STI
|
||||
)
|
||||
@TargetRegistry
|
||||
class TargetRegistry:
|
||||
printRegisteredTargetsForVersion = StaticMethod()
|
||||
|
|
@ -103,6 +66,3 @@ class TargetRegistry:
|
|||
PyObjectPtr, # const Target*
|
||||
PyObjectPtr, # std::string &Error
|
||||
)
|
||||
|
||||
targetsList = CustomStaticMethod('TargetRegistry_targets_list', PyObjectPtr)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,10 @@ InitializeNativeTargetAsmParser = llvm.Function(
|
|||
InitializeNativeTargetDisassembler = llvm.Function(
|
||||
'InitializeNativeTargetDisassembler', cast(Bool, bool))
|
||||
|
||||
InitializeAllTargets = llvm.Function('InitializeAllTargets')
|
||||
InitializeAllTargetInfos = llvm.Function('InitializeAllTargetInfos')
|
||||
InitializeAllTargetMCs = llvm.Function('InitializeAllTargetMCs')
|
||||
InitializeAllAsmPrinters = llvm.Function('InitializeAllAsmPrinters')
|
||||
InitializeAllDisassemblers = llvm.Function('InitializeAllDisassemblers')
|
||||
InitializeAllAsmParsers = llvm.Function('InitializeAllAsmParsers')
|
||||
|
||||
#InitializeAllTargets = llvm.Function('InitializeAllTargets')
|
||||
#InitializeAllTargetInfos = llvm.Function('InitializeAllTargetInfos')
|
||||
#InitializeAllTargetMCs = llvm.Function('InitializeAllTargetMCs')
|
||||
#InitializeAllAsmPrinters = llvm.Function('InitializeAllAsmPrinters')
|
||||
|
||||
for target in TARGETS_BUILT:
|
||||
decls = 'Target', 'TargetInfo', 'TargetMC', 'AsmPrinter'
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ from src.ADT.StringRef import StringRef
|
|||
from src.Support.CodeGen import CodeModel, TLSModel, CodeGenOpt, Reloc
|
||||
from src.GlobalValue import GlobalValue
|
||||
from src.DataLayout import DataLayout
|
||||
if LLVM_VERSION >= (3, 4):
|
||||
from src.MC import MCAsmInfo, \
|
||||
TargetInstrInfo, \
|
||||
TargetSubtargetInfo, \
|
||||
TargetRegisterInfo
|
||||
|
||||
if LLVM_VERSION < (3, 3):
|
||||
from src.TargetTransformInfo import (ScalarTargetTransformInfo,
|
||||
|
|
@ -53,9 +48,6 @@ class TargetMachine:
|
|||
getVectorTargetTransformInfo = Method(const(
|
||||
ownedptr(VectorTargetTransformInfo)))
|
||||
|
||||
else:
|
||||
addAnalysisPasses = Method(Void, ref(PassManagerBase))
|
||||
|
||||
addPassesToEmitFile = Method(cast(bool, Bool),
|
||||
ref(PassManagerBase),
|
||||
ref(formatted_raw_ostream),
|
||||
|
|
@ -63,12 +55,4 @@ class TargetMachine:
|
|||
cast(bool, Bool)
|
||||
).require_only(3)
|
||||
|
||||
if LLVM_VERSION >= (3, 4):
|
||||
getSubtargetImpl = Method(const(ownedptr(TargetSubtargetInfo)))
|
||||
|
||||
getMCAsmInfo = Method(const(ownedptr(MCAsmInfo)))
|
||||
|
||||
getInstrInfo = Method(const(ownedptr(TargetInstrInfo)))
|
||||
|
||||
getRegisterInfo = Method(const(ownedptr(TargetRegisterInfo)))
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@ class PassManagerBuilder:
|
|||
return Attr(getter=cast(Bool, bool),
|
||||
setter=cast(bool, Bool))
|
||||
|
||||
if LLVM_VERSION <= (3, 3):
|
||||
DisableSimplifyLibCalls = _attr_bool()
|
||||
|
||||
DisableUnitAtATime = _attr_bool()
|
||||
DisableUnrollLoops = _attr_bool()
|
||||
if LLVM_VERSION >= (3, 3):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import os.path
|
||||
from binding import LLVM_VERSION
|
||||
|
||||
above_33 = ("MC")
|
||||
|
||||
def _init(root=__name__, file=__file__):
|
||||
base = os.path.dirname(file)
|
||||
|
|
@ -12,10 +9,6 @@ def _init(root=__name__, file=__file__):
|
|||
is_python_module = is_directory and not fname.startswith('__')
|
||||
if (is_python_module or is_python_script) and not is_init_script:
|
||||
print(fname)
|
||||
if fname in above_33 and LLVM_VERSION <= (3, 3):
|
||||
print("skip %s because llvm version is not above 3.3" % fname)
|
||||
continue
|
||||
|
||||
modname = os.path.basename(fname).rsplit('.', 1)[0]
|
||||
#importlib.import_module('.' + modname, __name__)
|
||||
__import__('.'.join([root, modname]))
|
||||
|
|
|
|||
9
setup.py
9
setup.py
|
|
@ -63,7 +63,7 @@ def auto_intrinsic_gen(incdir):
|
|||
print("Generate intrinsic IDs")
|
||||
from tools import intrgen
|
||||
|
||||
if llvm_version.startswith('3.3') or llvm_version.startswith('3.4'):
|
||||
if llvm_version.startswith('3.3'):
|
||||
path = "%s/llvm/IR/Intrinsics.gen" % incdir
|
||||
else:
|
||||
path = "%s/llvm/Intrinsics.gen" % incdir
|
||||
|
|
@ -128,7 +128,7 @@ else:
|
|||
['core', 'analysis', 'scalaropts', 'executionengine', 'mcjit',
|
||||
'jit', 'native', 'interpreter', 'bitreader', 'bitwriter',
|
||||
'instrumentation', 'ipa', 'ipo', 'transformutils',
|
||||
'asmparser', 'linker', 'support', 'vectorize', 'all-targets']
|
||||
'asmparser', 'linker', 'support', 'vectorize']
|
||||
+ extra_components)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
|
|
@ -184,13 +184,10 @@ setup(
|
|||
maintainer_email = 'llvmpy@continuum.io',
|
||||
url = 'http://www.llvmpy.org/',
|
||||
packages = ['llvm', 'llvm.workaround',
|
||||
'llvm.mc',
|
||||
'llvm_cbuilder',
|
||||
'llpython',
|
||||
'llvm_array',
|
||||
'llvmpy.api', 'llvmpy.api.llvm',
|
||||
'llvm.tests',
|
||||
'llvm.utils',],
|
||||
'llvmpy.api', 'llvmpy.api.llvm',],
|
||||
package_data = {'llvm': ['llrt/*.ll']},
|
||||
py_modules = ['llvmpy',
|
||||
'llvmpy._capsule',
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import llvm
|
||||
|
||||
if llvm.version >= (3, 4):
|
||||
|
||||
from llvm.target import TargetMachine
|
||||
from llvm import mc
|
||||
from llvm.mc import Disassembler
|
||||
|
||||
llvm.target.initialize_all()
|
||||
|
||||
def print_instructions(dasm, bs, align=None):
|
||||
branch_properties = [
|
||||
'is_branch',
|
||||
'is_cond_branch',
|
||||
'is_uncond_branch',
|
||||
'is_indirect_branch',
|
||||
'is_call',
|
||||
'is_return',
|
||||
'is_terminator',
|
||||
'is_barrier'
|
||||
]
|
||||
|
||||
print("print instructions")
|
||||
for (addr, data, inst) in dasm.decode(bs, 0x4000, align):
|
||||
|
||||
if inst is None:
|
||||
print("\t0x%x => (bad)" % (addr))
|
||||
else:
|
||||
ops = ", ".join(map(lambda op: repr(op), inst.operands()))
|
||||
if isinstance(inst, mc.BadInstr):
|
||||
print("\t0x%x (bad) ops = %s" % (addr, ops))
|
||||
else:
|
||||
print("\t0x%x ops = %s" % (addr, ops))
|
||||
|
||||
print("\t\topcode = 0x%x, flags = 0x%x, tsflags = 0x%x" % (inst.opcode, inst.flags, inst.ts_flags))
|
||||
for line in str(inst).split("\n"):
|
||||
print("\t\t%-24s %s" % ("".join(map(lambda b: "%02x" % b, data))+":", line.strip()))
|
||||
|
||||
for bp in branch_properties:
|
||||
print("\t\t%-22s%r" % (bp+":", getattr(inst, bp)() ))
|
||||
|
||||
|
||||
x86 = TargetMachine.x86()
|
||||
print("x86: LE=%s" % x86.is_little_endian())
|
||||
print_instructions(Disassembler(x86), "\x01\xc3\xc3\xcc\x90")
|
||||
|
||||
x86_64 = TargetMachine.x86_64()
|
||||
print("x86-64: LE=%s" % x86_64.is_little_endian())
|
||||
print_instructions(Disassembler(x86_64), "\x55\x48\x89\xe8")
|
||||
|
||||
arm = TargetMachine.arm()
|
||||
print("arm: LE=%s" % arm.is_little_endian())
|
||||
code = [
|
||||
"\xe9\x2d\x48\x00",
|
||||
"\xea\x00\x00\x06",
|
||||
"\xe2\x4d\xd0\x20",
|
||||
"\xe2\x8d\xb0\x04",
|
||||
"\xe5\x0b\x00\x20",
|
||||
"\x03\x30\x22\xe0", #bad instruction to test alignment
|
||||
"\x73\x20\xef\xe6", #bad instruction to test alignment
|
||||
"\x18\x00\x1b\xe5",
|
||||
"\x10\x30\xa0\xe3"
|
||||
]
|
||||
print_instructions(Disassembler(arm), "".join(map(lambda s: s[::-1], code)), 4)
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import llvm.target
|
||||
from llvmpy import api, extra
|
||||
|
||||
|
||||
def main():
|
||||
if llvm.version < (3, 4):
|
||||
return 0
|
||||
|
||||
triple = "i386--"
|
||||
|
||||
print("init start")
|
||||
api.llvm.InitializeAllTargets()
|
||||
api.llvm.InitializeAllTargetInfos()
|
||||
api.llvm.InitializeAllTargetMCs()
|
||||
api.llvm.InitializeAllAsmParsers()
|
||||
api.llvm.InitializeAllAsmPrinters()
|
||||
api.llvm.InitializeAllDisassemblers()
|
||||
print("init done\n")
|
||||
|
||||
tm = llvm.target.TargetMachine.x86()
|
||||
if not tm:
|
||||
print("error: failed to lookup target x86 \n")
|
||||
return 1
|
||||
|
||||
print("created target machine\n")
|
||||
|
||||
MII = tm.instr_info
|
||||
if not MII:
|
||||
print("error: no instruction info for target " + triple + "\n")
|
||||
return 1
|
||||
|
||||
print("created instr info\n")
|
||||
MID = MII.get(919) #int3
|
||||
print("INT3(%d): flags=0x%x, tsflags=0x%x\n" % (MID.getOpcode(), MID.getFlags(), MID.TSFlags))
|
||||
|
||||
return 0
|
||||
|
||||
exit(main())
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
# Import the llvm-py modules.
|
||||
from llvm import *
|
||||
from llvm.core import *
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from llvm.core import *
|
|||
from llvm.passes import *
|
||||
from llvm.ee import *
|
||||
import llvm
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
from os.path import dirname, join as join_path
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
from llvm.core import *
|
||||
|
||||
class TestMetaData(TestCase):
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class TestPass(unittest.TestCase):
|
|||
self.assertTrue(tli.description)
|
||||
pm.add(tli)
|
||||
|
||||
if llvm.version >= (3, 2) and llvm.version < (3, 3):
|
||||
if llvm.version >= (3, 2):
|
||||
tti = TargetTransformInfo.new(tm)
|
||||
self.assertFalse(tti.name)
|
||||
self.assertTrue(tti.description)
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
from llvmpy.api import llvm;
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
llvm.InitializeAllDisassemblers()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
|
||||
mthds = (
|
||||
("description:", "getShortDescription"),
|
||||
("has JIT:", "hasJIT" ),
|
||||
("has target machine:", "hasTargetMachine" ),
|
||||
("has asm backend:", "hasMCAsmBackend" ),
|
||||
("has asm parser:", "hasMCAsmParser" ),
|
||||
("has asm printer:", "hasAsmPrinter" ),
|
||||
("has disassembler:", "hasMCDisassembler" ),
|
||||
("has inst printer:", "hasMCInstPrinter" ),
|
||||
("has code emitter:", "hasMCCodeEmitter" ),
|
||||
("has object streamer:", "hasMCObjectStreamer"),
|
||||
("has asm streamer:", "hasAsmStreamer" )
|
||||
)
|
||||
|
||||
for target in llvm.TargetRegistry.targetsList():
|
||||
print("target %s" % target.getName())
|
||||
fmt = "%3s%-25s%r"
|
||||
for (desc, mthd) in mthds:
|
||||
print(fmt % ("", desc, getattr(target, mthd)()))
|
||||
|
||||
print("")
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from llvm.core import *
|
||||
from llvm.tbaa import *
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
import unittest
|
||||
|
||||
class TestTBAABuilder(TestCase):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import unittest, sys, logging
|
|||
|
||||
from llvm import *
|
||||
from llvm.core import *
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
|
||||
class TestModule(TestCase):
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import unittest
|
|||
import llvm.ee
|
||||
from llvm.core import *
|
||||
from llvm import _dwarf, debuginfo
|
||||
from llvm.tests.support import TestCase
|
||||
from llvm.test_llvmpy import TestCase
|
||||
|
||||
class TestDebugInfo(TestCase):
|
||||
|
||||
|
|
|
|||
|
|
@ -266,8 +266,8 @@ def do_argument():
|
|||
ft = Type.function(tip, [tip])
|
||||
f = Function.new(m, ft, 'func')
|
||||
a = f.args[0]
|
||||
a.add_attribute(ATTR_NEST)
|
||||
a.remove_attribute(ATTR_NEST)
|
||||
a.add_attribute(ATTR_ZEXT)
|
||||
a.remove_attribute(ATTR_ZEXT)
|
||||
a.alignment = 16
|
||||
a1 = a.alignment
|
||||
|
||||
|
|
@ -299,8 +299,6 @@ def do_function():
|
|||
g = list(f.basic_blocks)
|
||||
f.add_attribute(ATTR_NO_RETURN)
|
||||
f.add_attribute(ATTR_ALWAYS_INLINE)
|
||||
#for some reason removeFnAttr is just gone in 3.3
|
||||
if version <= (3, 2):
|
||||
f.remove_attribute(ATTR_NO_RETURN)
|
||||
|
||||
# LLVM misbehaves:
|
||||
|
|
@ -333,7 +331,6 @@ def do_callorinvokeinstruction():
|
|||
i = bb.invoke(f, [Constant.int(ti, 10)], b, b)
|
||||
a = i.calling_convention
|
||||
i.calling_convention = CC_FASTCALL
|
||||
if version <= (3, 2):
|
||||
i.add_parameter_attribute(0, ATTR_SEXT)
|
||||
i.remove_parameter_attribute(0, ATTR_SEXT)
|
||||
i.set_parameter_alignment(0, 8)
|
||||
|
|
@ -566,10 +563,10 @@ def do_executionengine():
|
|||
ee2 = ExecutionEngine.new(m3, False)
|
||||
m4 = Module.new('d')
|
||||
m5 = Module.new('e')
|
||||
#ee3 = ExecutionEngine.new(m4, False)
|
||||
#ee3.add_module(m5)
|
||||
#x = ee3.remove_module(m5)
|
||||
#isinstance(x, Module)
|
||||
ee3 = ExecutionEngine.new(m4, False)
|
||||
ee3.add_module(m5)
|
||||
x = ee3.remove_module(m5)
|
||||
isinstance(x, Module)
|
||||
|
||||
|
||||
def do_llvm_ee():
|
||||
|
|
@ -619,51 +616,6 @@ def do_llvm_passes():
|
|||
do_passmanager()
|
||||
do_functionpassmanager()
|
||||
|
||||
def do_llvm_target():
|
||||
print(" Testing module llvm.target")
|
||||
from llvm import target
|
||||
|
||||
target.initialize_all()
|
||||
target.print_registered_targets()
|
||||
target.get_host_cpu_name()
|
||||
target.get_default_triple()
|
||||
|
||||
tm = TargetMachine.new()
|
||||
tm = TargetMachine.lookup("arm")
|
||||
tm = TargetMachine.arm()
|
||||
tm = TargetMachine.thumb()
|
||||
tm = TargetMachine.x86()
|
||||
tm = TargetMachine.x86_64()
|
||||
tm.target_data
|
||||
tm.target_name
|
||||
tm.target_short_description
|
||||
tm.triple
|
||||
tm.cpu
|
||||
tm.feature_string
|
||||
tm.target
|
||||
|
||||
if llvm.version >= (3, 4):
|
||||
tm.reg_info
|
||||
tm.subtarget_info
|
||||
tm.asm_info
|
||||
tm.instr_info
|
||||
tm.instr_analysis
|
||||
tm.disassembler
|
||||
tm.is_little_endian()
|
||||
|
||||
def do_llvm_mc():
|
||||
if llvm.version < (3, 4):
|
||||
return
|
||||
|
||||
from llvm import target
|
||||
from llvm import mc
|
||||
|
||||
target.initialize_all()
|
||||
tm = TargetMachine.x86()
|
||||
dasm = mc.Disassembler(tm)
|
||||
|
||||
for (offset, data, instr) in dasm.decode("c3", 0):
|
||||
pass
|
||||
|
||||
def main():
|
||||
print("Testing package llvm")
|
||||
|
|
@ -671,8 +623,7 @@ def main():
|
|||
do_llvm_core()
|
||||
do_llvm_ee()
|
||||
do_llvm_passes()
|
||||
do_llvm_target()
|
||||
do_llvm_mc()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue