Made some improvements to llpython.af_to_api, specifically attempting to normalize variable access so reference counting becomes the target API's responsibility.

This commit is contained in:
Jon Riehl 2013-06-17 18:08:36 -05:00
commit a6cf8c4f71
2 changed files with 105 additions and 37 deletions

View file

@ -13,8 +13,9 @@ import llvm.core as lc
from . import byte_control
from . import addr_flow
from . import opcode_util
from .bytetype import l_pyobj_p, lc_int
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
@ -37,7 +38,7 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
arguments. The OPCODE_NAME is the opcode name as determined by
the map in opcode.opname.
'''
def __init__(self, _prefix=None, _postfix=None):
def __init__(self, _prefix=None, _postfix=None, **kwds):
if _prefix is not None:
if inspect.isfunction(_prefix):
__prefix = _prefix
@ -67,8 +68,17 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
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, **kws):
monotype=l_pyobj_p, **kwds):
'''
Generate LLVM code for the given code object and it's control
flow graph.
@ -83,15 +93,12 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
self.null = lc.Constant.null(self.obj_type)
self.code_obj = code_obj
self.cfg = cfg
self.target_function_name = kws.get(
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.incref = self.get_op_function('INCREF', self.null)
self.decref = self.get_op_function('DECREF', self.null,
return_type = lc.Type.void())
self.visit(cfg.blocks)
del self.llvm_module
del self.cfg
@ -143,7 +150,15 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
local_index = arg_index - 1
name = self.code_obj.co_varnames[local_index]
arg.name = name
self.builder.store(arg, self.symtab[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):
'''
@ -178,18 +193,17 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
del self.builder
del self.llvm_block
def _op(self, i, op, arg, *args, **kws):
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.
target_fn = self.get_op_function(self.opnames[op], *args, **kws)
result = self.builder.call(target_fn, args)
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, **kws):
def _not_implemented(self, i, op, arg, *args, **kwds):
raise NotImplementedError(self.opnames[op])
op_BINARY_ADD = _op
@ -221,14 +235,28 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
op_CONTINUE_LOOP = _op
op_DELETE_ATTR = _op
op_DELETE_DEREF = _op
op_DELETE_FAST = _op
op_DELETE_GLOBAL = _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 = _op
op_DUP_TOPX = _op
op_DUP_TOP_TWO = _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
@ -251,10 +279,10 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
op_INPLACE_TRUE_DIVIDE = _op
op_INPLACE_XOR = _op
def op_JUMP_ABSOLUTE(self, i, op, arg, *args, **kws):
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, **kws):
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
@ -265,16 +293,40 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
op_LOAD_ATTR = _op
op_LOAD_BUILD_CLASS = _op
op_LOAD_CLOSURE = _op
op_LOAD_CONST = _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, **kws):
def op_LOAD_FAST(self, i, op, arg, *args, **kwds):
varname = self.code_obj.co_varnames[arg]
result = self.builder.load(self.symtab[varname])
args = self.symtab[varname],
result = self.call_op_function(i, 'LOAD_FAST', *args)
self.values[i] = result
return [result, self.builder.call(self.incref, [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_GLOBAL = _op
op_LOAD_LOCALS = _op
op_LOAD_NAME = _op
op_MAKE_CLOSURE = _op
@ -284,12 +336,12 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
op_POP_BLOCK = _op
op_POP_EXCEPT = _op
def _op_cbranch(self, i, op, arg, *args, **kws):
def _op_cbranch(self, i, op, arg, *args, **kwds):
branch_taken = self.llvm_blocks[arg]
branch_not_taken = self.llvm_blocks[i + 3]
_kws = kws.copy()
_kws.update(return_type=lc.Type.int(1))
test = self._op(i, op, None, *args, **_kws)[0]
_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)]
@ -305,6 +357,7 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
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
@ -324,12 +377,20 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
src_index, = args
src = self.values[src_index]
varname = self.code_obj.co_varnames[arg]
result = self.builder.store(src, self.symtab[varname])
self.values[i] = result
dest = self.symtab[varname]
result = self.call_op_function(i, 'STORE_FAST', src, dest,
return_type=lvoid)
return [result]
op_STORE_GLOBAL = _op
op_STORE_LOCALS = _op
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
@ -347,14 +408,15 @@ class AddressFlowToLLVMPyAPICalls(GenericFlowVisitor):
# ______________________________________________________________________
# Function definition(s)
def demo_translator(*args, **kws):
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(**kws).translate_cfg(obj, cfg))
print(AddressFlowToLLVMPyAPICalls(**kwds).translate_cfg(obj, cfg,
**kwds))
return opcode_util.visit_code_args(_visit, *args)
# ______________________________________________________________________

View file

@ -221,6 +221,9 @@ def visit_code_args(visitor, *args, **kws):
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:
@ -231,12 +234,15 @@ def visit_code_args(visitor, *args, **kws):
else:
args = ('pymod',)
for arg in args:
if arg.endswith('.py'):
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')
for codeobj in itercodeobjs(in_codeobj):
visitor(codeobj)
_visit_code_objs(in_codeobj)
else:
visitor(getattr(llfuncs, arg))