Merge branch 'llpython'
This commit is contained in:
commit
affc4c016f
15 changed files with 2412 additions and 0 deletions
0
llpython/__init__.py
Normal file
0
llpython/__init__.py
Normal file
110
llpython/byte_control.py
Normal file
110
llpython/byte_control.py
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import opcode
|
||||||
|
import opcode_util
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from bytecode_visitor import BytecodeFlowVisitor, BenignBytecodeVisitorMixin
|
||||||
|
from control_flow import ControlFlowGraph
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class ControlFlowBuilder (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
||||||
|
'''Visitor responsible for traversing a bytecode flow object and
|
||||||
|
building a control flow graph (CFG).
|
||||||
|
|
||||||
|
The primary purpose of this transformation is to create a CFG,
|
||||||
|
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:`numba.llnumba.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
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_flow_object (self, flow):
|
||||||
|
super(ControlFlowBuilder, self).enter_flow_object(flow)
|
||||||
|
self.flow = flow
|
||||||
|
self.cfg = ControlFlowGraph()
|
||||||
|
for block in flow.keys():
|
||||||
|
self.cfg.add_block(block, flow[block])
|
||||||
|
|
||||||
|
def exit_flow_object (self, flow):
|
||||||
|
super(ControlFlowBuilder, self).exit_flow_object(flow)
|
||||||
|
assert self.flow == flow
|
||||||
|
self.cfg.compute_dataflow()
|
||||||
|
self.cfg.update_for_ssa()
|
||||||
|
ret_val = self.cfg
|
||||||
|
del self.cfg
|
||||||
|
del self.flow
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
self.block = block
|
||||||
|
assert block in self.cfg.blocks
|
||||||
|
if block == 0:
|
||||||
|
for local_index in range(self.nargs):
|
||||||
|
self.op_STORE_FAST(0, opcode.opmap['STORE_FAST'], local_index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_next_block (self, block):
|
||||||
|
return self.block_list[self.block_list.index(block) + 1]
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
assert block == self.block
|
||||||
|
del self.block
|
||||||
|
i, op, opname, arg, args = self.flow[block][-1]
|
||||||
|
if 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':
|
||||||
|
self.cfg.add_edge(block, arg)
|
||||||
|
elif opname != 'RETURN_VALUE':
|
||||||
|
self.cfg.add_edge(block, self._get_next_block(block))
|
||||||
|
if op in opcode_util.hascbranch:
|
||||||
|
self.cfg.add_edge(block, self._get_next_block(block))
|
||||||
|
|
||||||
|
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,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
def op_STORE_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
self.cfg.writes_local(self.block, i, arg)
|
||||||
|
return super(ControlFlowBuilder, self).op_STORE_FAST(i, op, arg, *args,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_cfg (func):
|
||||||
|
'''Given a Python function, create a bytecode flow, visit the flow
|
||||||
|
object, and return a control flow graph.'''
|
||||||
|
import byte_flow
|
||||||
|
return ControlFlowBuilder().visit(
|
||||||
|
byte_flow.build_flow(func),
|
||||||
|
opcode_util.get_code_object(func).co_argcount)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_control.py
|
||||||
234
llpython/byte_flow.py
Normal file
234
llpython/byte_flow.py
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import dis
|
||||||
|
import opcode
|
||||||
|
|
||||||
|
from bytecode_visitor import BytecodeIterVisitor
|
||||||
|
import opcode_util
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeFlowBuilder (BytecodeIterVisitor):
|
||||||
|
'''Transforms a bytecode vector into a bytecode "flow tree".
|
||||||
|
|
||||||
|
The flow tree is a Python dictionary, described loosely by the
|
||||||
|
following set of productions:
|
||||||
|
|
||||||
|
* `flow_tree` ``:=`` ``{`` `blocks` ``*`` ``}``
|
||||||
|
* `blocks` ``:=`` `block_index` ``:`` ``[`` `bytecode_tree` ``*`` ``]``
|
||||||
|
* `bytecode_tree` ``:=`` ``(`` `opcode_index` ``,`` `opcode` ``,``
|
||||||
|
`opname` ``,`` `arg` ``,`` ``[`` `bytecode_tree` ``*`` ``]`` ``)``
|
||||||
|
|
||||||
|
The primary purpose of this transformation is to simulate the
|
||||||
|
value stack, removing it and any stack-specific opcodes.'''
|
||||||
|
|
||||||
|
def __init__ (self, *args, **kws):
|
||||||
|
super(BytecodeFlowBuilder, self).__init__(*args, **kws)
|
||||||
|
om_items = opcode_util.OPCODE_MAP.items()
|
||||||
|
self.opmap = dict((opcode.opmap[opname], (opname, pops, pushes, stmt))
|
||||||
|
for opname, (pops, pushes, stmt) in om_items
|
||||||
|
if opname in opcode.opmap)
|
||||||
|
|
||||||
|
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
|
||||||
|
stk_args = self.stack[-pops:]
|
||||||
|
del self.stack[-pops:]
|
||||||
|
else:
|
||||||
|
stk_args = []
|
||||||
|
ret_val = (i, op, opname, arg, stk_args)
|
||||||
|
if pushes:
|
||||||
|
self.stack.append(ret_val)
|
||||||
|
if appends:
|
||||||
|
self.block.append(ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def _op (self, i, op, arg):
|
||||||
|
opname, pops, pushes, appends = self.opmap[op]
|
||||||
|
return self._visit_op(i, op, arg, opname, pops, pushes, appends)
|
||||||
|
|
||||||
|
def enter_code_object (self, co_obj):
|
||||||
|
labels = dis.findlabels(co_obj.co_code)
|
||||||
|
labels = opcode_util.extendlabels(co_obj.co_code, labels)
|
||||||
|
self.blocks = dict((index, [])
|
||||||
|
for index in labels)
|
||||||
|
self.stack = []
|
||||||
|
self.loop_stack = []
|
||||||
|
self.blocks[0] = self.block = []
|
||||||
|
|
||||||
|
def exit_code_object (self, co_obj):
|
||||||
|
ret_val = self.blocks
|
||||||
|
del self.stack
|
||||||
|
del self.loop_stack
|
||||||
|
del self.block
|
||||||
|
del self.blocks
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def visit_op (self, i, op, arg):
|
||||||
|
if i in self.blocks:
|
||||||
|
self.block = self.blocks[i]
|
||||||
|
return super(BytecodeFlowBuilder, self).visit_op(i, op, arg)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def op_BREAK_LOOP (self, i, op, arg):
|
||||||
|
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||||
|
assert arg is None
|
||||||
|
return self._op(i, op, loop_i + loop_arg + 3)
|
||||||
|
|
||||||
|
#op_BUILD_CLASS = _op
|
||||||
|
op_BUILD_LIST = _op
|
||||||
|
op_BUILD_MAP = _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_FAST = _op
|
||||||
|
op_DELETE_GLOBAL = _op
|
||||||
|
op_DELETE_NAME = _op
|
||||||
|
op_DELETE_SLICE = _op
|
||||||
|
op_DELETE_SUBSCR = _op
|
||||||
|
|
||||||
|
def op_DUP_TOP (self, i, op, arg):
|
||||||
|
self.stack.append(self.stack[-1])
|
||||||
|
|
||||||
|
def op_DUP_TOPX (self, i, op, arg):
|
||||||
|
self.stack += self.stack[-arg:]
|
||||||
|
|
||||||
|
#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
|
||||||
|
op_JUMP_ABSOLUTE = _op
|
||||||
|
op_JUMP_FORWARD = _op
|
||||||
|
op_JUMP_IF_FALSE = _op
|
||||||
|
op_JUMP_IF_TRUE = _op
|
||||||
|
op_LIST_APPEND = _op
|
||||||
|
op_LOAD_ATTR = _op
|
||||||
|
op_LOAD_CLOSURE = _op
|
||||||
|
op_LOAD_CONST = _op
|
||||||
|
op_LOAD_DEREF = _op
|
||||||
|
op_LOAD_FAST = _op
|
||||||
|
op_LOAD_GLOBAL = _op
|
||||||
|
op_LOAD_LOCALS = _op
|
||||||
|
op_LOAD_NAME = _op
|
||||||
|
op_MAKE_CLOSURE = _op
|
||||||
|
op_MAKE_FUNCTION = _op
|
||||||
|
op_NOP = _op
|
||||||
|
|
||||||
|
def op_POP_BLOCK (self, i, op, arg):
|
||||||
|
self.loop_stack.pop()
|
||||||
|
return self._op(i, op, arg)
|
||||||
|
|
||||||
|
op_POP_JUMP_IF_FALSE = _op
|
||||||
|
op_POP_JUMP_IF_TRUE = _op
|
||||||
|
op_POP_TOP = _op
|
||||||
|
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
|
||||||
|
op_RETURN_VALUE = _op
|
||||||
|
|
||||||
|
def op_ROT_FOUR (self, i, op, arg):
|
||||||
|
self.stack[-4:] = (self.stack[-1], self.stack[-4], self.stack[-3],
|
||||||
|
self.stack[-2])
|
||||||
|
|
||||||
|
def op_ROT_THREE (self, i, op, arg):
|
||||||
|
self.stack[-3:] = (self.stack[-1], self.stack[-3], self.stack[-2])
|
||||||
|
|
||||||
|
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_LOOP (self, i, op, arg):
|
||||||
|
self.loop_stack.append((i, op, arg))
|
||||||
|
self.block.append((i, op, self.opnames[op], arg, []))
|
||||||
|
|
||||||
|
op_SLICE = _op
|
||||||
|
#op_STOP_CODE = _op
|
||||||
|
op_STORE_ATTR = _op
|
||||||
|
op_STORE_DEREF = _op
|
||||||
|
op_STORE_FAST = _op
|
||||||
|
op_STORE_GLOBAL = _op
|
||||||
|
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_SEQUENCE = _op
|
||||||
|
#op_WITH_CLEANUP = _op
|
||||||
|
op_YIELD_VALUE = _op
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_flow (func):
|
||||||
|
'''Given a Python function, return a bytecode flow tree for that
|
||||||
|
function.'''
|
||||||
|
return BytecodeFlowBuilder().visit(opcode_util.get_code_object(func))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# 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)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_flow.py
|
||||||
602
llpython/byte_translator.py
Normal file
602
llpython/byte_translator.py
Normal file
|
|
@ -0,0 +1,602 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
'''Defines a bytecode based LLVM translator for llnumba code.
|
||||||
|
'''
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module imports
|
||||||
|
|
||||||
|
import opcode
|
||||||
|
import types
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
import opcode_util
|
||||||
|
import bytetype
|
||||||
|
from bytecode_visitor import BytecodeFlowVisitor
|
||||||
|
from byte_flow import BytecodeFlowBuilder
|
||||||
|
from byte_control import ControlFlowBuilder
|
||||||
|
from phi_injector import PhiInjector, synthetic_opname
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module data
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# XXX Stolen from numba.translate:
|
||||||
|
|
||||||
|
_compare_mapping_float = {'>':lc.FCMP_OGT,
|
||||||
|
'<':lc.FCMP_OLT,
|
||||||
|
'==':lc.FCMP_OEQ,
|
||||||
|
'>=':lc.FCMP_OGE,
|
||||||
|
'<=':lc.FCMP_OLE,
|
||||||
|
'!=':lc.FCMP_ONE}
|
||||||
|
|
||||||
|
_compare_mapping_sint = {'>':lc.ICMP_SGT,
|
||||||
|
'<':lc.ICMP_SLT,
|
||||||
|
'==':lc.ICMP_EQ,
|
||||||
|
'>=':lc.ICMP_SGE,
|
||||||
|
'<=':lc.ICMP_SLE,
|
||||||
|
'!=':lc.ICMP_NE}
|
||||||
|
|
||||||
|
# XXX Stolen from numba.llvm_types:
|
||||||
|
|
||||||
|
class LLVMCaster (object):
|
||||||
|
def build_pointer_cast(_, builder, lval1, lty2):
|
||||||
|
return builder.bitcast(lval1, lty2)
|
||||||
|
|
||||||
|
def build_int_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
width1 = lval1.type.width
|
||||||
|
width2 = lty2.width
|
||||||
|
ret_val = lval1
|
||||||
|
if width2 > width1:
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.zext(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.sext(lval1, lty2)
|
||||||
|
elif width2 < width1:
|
||||||
|
ret_val = builder.trunc(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_float_ext(_, builder, lval1, lty2):
|
||||||
|
return builder.fpext(lval1, lty2)
|
||||||
|
|
||||||
|
def build_float_trunc(_, builder, lval1, lty2):
|
||||||
|
return builder.fptrunc(lval1, lty2)
|
||||||
|
|
||||||
|
def build_int_to_float_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
ret_val = None
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.uitofp(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.sitofp(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_int_to_ptr_cast(_, builder, lval1, lty2):
|
||||||
|
return builder.inttoptr(lval1, lty2)
|
||||||
|
|
||||||
|
def build_float_to_int_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
ret_val = None
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.fptoui(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.fptosi(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
CAST_MAP = {
|
||||||
|
lc.TYPE_POINTER : build_pointer_cast,
|
||||||
|
lc.TYPE_INTEGER: build_int_cast,
|
||||||
|
(lc.TYPE_FLOAT, lc.TYPE_DOUBLE) : build_float_ext,
|
||||||
|
(lc.TYPE_DOUBLE, lc.TYPE_FLOAT) : build_float_trunc,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_FLOAT) : build_int_to_float_cast,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_DOUBLE) : build_int_to_float_cast,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_POINTER) : build_int_to_ptr_cast,
|
||||||
|
(lc.TYPE_FLOAT, lc.TYPE_INTEGER) : build_float_to_int_cast,
|
||||||
|
(lc.TYPE_DOUBLE, lc.TYPE_INTEGER) : build_float_to_int_cast,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_cast(cls, builder, lval1, lty2, *args, **kws):
|
||||||
|
ret_val = lval1
|
||||||
|
lty1 = lval1.type
|
||||||
|
lkind1 = lty1.kind
|
||||||
|
lkind2 = lty2.kind
|
||||||
|
|
||||||
|
if lkind1 == lkind2:
|
||||||
|
|
||||||
|
if lkind1 in cls.CAST_MAP:
|
||||||
|
ret_val = cls.CAST_MAP[lkind1](cls, builder, lval1, lty2,
|
||||||
|
*args, **kws)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(lkind1)
|
||||||
|
else:
|
||||||
|
map_index = (lkind1, lkind2)
|
||||||
|
if map_index in cls.CAST_MAP:
|
||||||
|
ret_val = cls.CAST_MAP[map_index](cls, builder, lval1, lty2,
|
||||||
|
*args, **kws)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(lkind1, lkind2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Class definitions
|
||||||
|
|
||||||
|
class LLVMTranslator (BytecodeFlowVisitor):
|
||||||
|
'''Transformer responsible for visiting a set of bytecode flow
|
||||||
|
trees, emitting LLVM code.
|
||||||
|
|
||||||
|
Unlike other translators in :py:mod:`numba.llnumba`, this
|
||||||
|
incorporates the full transformation chain, starting with
|
||||||
|
:py:class:`numba.llnumba.byte_flow.BytecodeFlowBuilder`, then
|
||||||
|
:py:class:`numba.llnumba.byte_control.ControlFlowBuilder`, and
|
||||||
|
then :py:class:`numba.llnumba.phi_injector.PhiInjector`.'''
|
||||||
|
|
||||||
|
def __init__ (self, llvm_module = None, *args, **kws):
|
||||||
|
'''Constructor for LLVMTranslator.'''
|
||||||
|
super(LLVMTranslator, self).__init__(*args, **kws)
|
||||||
|
if llvm_module is None:
|
||||||
|
llvm_module = lc.Module.new('Translated_Module_%d' % (id(self),))
|
||||||
|
self.llvm_module = llvm_module
|
||||||
|
self.bytecode_flow_builder = BytecodeFlowBuilder()
|
||||||
|
self.control_flow_builder = ControlFlowBuilder()
|
||||||
|
self.phi_injector = PhiInjector()
|
||||||
|
|
||||||
|
def translate (self, function, llvm_type = None, llvm_function = None,
|
||||||
|
env = None):
|
||||||
|
'''Translate a function to the given LLVM function type.
|
||||||
|
|
||||||
|
If no type is given, then assume the function is of LLVM type
|
||||||
|
"void ()".
|
||||||
|
|
||||||
|
The optional env parameter allows extension of the global
|
||||||
|
environment.'''
|
||||||
|
if llvm_type is None:
|
||||||
|
if llvm_function is None:
|
||||||
|
llvm_type = lc.Type.function(lvoid, ())
|
||||||
|
else:
|
||||||
|
llvm_type = llvm_function.type.pointee
|
||||||
|
if env is None:
|
||||||
|
env = {}
|
||||||
|
else:
|
||||||
|
env = env.copy()
|
||||||
|
env.update((name, method)
|
||||||
|
for name, method in lc.Builder.__dict__.items()
|
||||||
|
if not name.startswith('_'))
|
||||||
|
env.update((name, value)
|
||||||
|
for name, value in bytetype.__dict__.items()
|
||||||
|
if not name.startswith('_'))
|
||||||
|
self.loop_stack = []
|
||||||
|
self.llvm_type = llvm_type
|
||||||
|
self.target_function_name = env.get('target_function_name',
|
||||||
|
function.__name__)
|
||||||
|
self.function = function
|
||||||
|
self.code_obj = opcode_util.get_code_object(function)
|
||||||
|
func_globals = getattr(function, 'func_globals',
|
||||||
|
getattr(function, '__globals__', {})).copy()
|
||||||
|
func_globals.update(env)
|
||||||
|
self.globals = func_globals
|
||||||
|
nargs = self.code_obj.co_argcount
|
||||||
|
self.cfg = self.control_flow_builder.visit(
|
||||||
|
self.bytecode_flow_builder.visit(self.code_obj), nargs)
|
||||||
|
self.llvm_function = llvm_function
|
||||||
|
flow = self.phi_injector.visit_cfg(self.cfg, nargs)
|
||||||
|
ret_val = self.visit(flow)
|
||||||
|
del self.cfg
|
||||||
|
del self.globals
|
||||||
|
del self.code_obj
|
||||||
|
del self.target_function_name
|
||||||
|
del self.function
|
||||||
|
del self.llvm_type
|
||||||
|
del self.loop_stack
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_flow_object (self, flow):
|
||||||
|
super(LLVMTranslator, self).enter_flow_object(flow)
|
||||||
|
if self.llvm_function is None:
|
||||||
|
self.llvm_function = self.llvm_module.add_function(
|
||||||
|
self.llvm_type, self.target_function_name)
|
||||||
|
self.llvm_blocks = {}
|
||||||
|
self.llvm_definitions = {}
|
||||||
|
self.pending_phis = {}
|
||||||
|
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
|
||||||
|
|
||||||
|
def exit_flow_object (self, flow):
|
||||||
|
super(LLVMTranslator, self).exit_flow_object(flow)
|
||||||
|
ret_val = self.llvm_function
|
||||||
|
del self.pending_phis
|
||||||
|
del self.llvm_definitions
|
||||||
|
del self.llvm_blocks
|
||||||
|
if __debug__ and logger.getEffectiveLevel() < logging.DEBUG:
|
||||||
|
logger.debug(str(ret_val))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
ret_val = False
|
||||||
|
if block in self.llvm_blocks:
|
||||||
|
self.llvm_block = self.llvm_blocks[block]
|
||||||
|
self.builder = lc.Builder.new(self.llvm_block)
|
||||||
|
ret_val = True
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
del self.llvm_block
|
||||||
|
del self.builder
|
||||||
|
|
||||||
|
def visit_synthetic_op (self, i, op, arg, *args, **kws):
|
||||||
|
method = getattr(self, 'op_%s' % (synthetic_opname[op],))
|
||||||
|
return method(i, op, arg, *args, **kws)
|
||||||
|
|
||||||
|
def op_REF_ARG (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.llvm_function.args[arg]]
|
||||||
|
|
||||||
|
def op_BUILD_PHI (self, i, op, arg, *args, **kws):
|
||||||
|
phi_type = None
|
||||||
|
incoming = []
|
||||||
|
pending = []
|
||||||
|
for child_arg in arg:
|
||||||
|
child_block, _, child_opname, child_arg, _ = child_arg
|
||||||
|
assert child_opname == 'REF_DEF'
|
||||||
|
if child_arg in self.llvm_definitions:
|
||||||
|
child_def = self.llvm_definitions[child_arg]
|
||||||
|
if phi_type is None:
|
||||||
|
phi_type = child_def.type
|
||||||
|
incoming.append((child_block, child_def))
|
||||||
|
else:
|
||||||
|
pending.append((child_arg, child_block))
|
||||||
|
phi = self.builder.phi(phi_type)
|
||||||
|
for block_index, defn in incoming:
|
||||||
|
phi.add_incoming(defn, self.llvm_blocks[block_index])
|
||||||
|
for defn_index, block_index in pending:
|
||||||
|
if defn_index not in self.pending_phis:
|
||||||
|
self.pending_phis[defn_index] = []
|
||||||
|
self.pending_phis[defn_index].append((phi, block_index))
|
||||||
|
return [phi]
|
||||||
|
|
||||||
|
def op_DEFINITION (self, i, op, def_index, *args, **kws):
|
||||||
|
assert len(args) == 1
|
||||||
|
arg = args[0]
|
||||||
|
if def_index in self.pending_phis:
|
||||||
|
for phi, block_index in self.pending_phis[def_index]:
|
||||||
|
phi.add_incoming(arg, self.llvm_blocks[block_index])
|
||||||
|
self.llvm_definitions[def_index] = arg
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_REF_DEF (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.llvm_definitions[arg]]
|
||||||
|
|
||||||
|
def op_BINARY_ADD (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.add(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fadd(arg1, arg2)]
|
||||||
|
elif arg1.type.kind == lc.TYPE_POINTER:
|
||||||
|
ret_val = [self.builder.gep(arg1, [arg2])]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_ADD for %r" %
|
||||||
|
(args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_AND (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.and_(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_DIVIDE (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.sdiv(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fdiv(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_DIVIDE for %r"
|
||||||
|
% (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_FLOOR_DIVIDE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_FLOOR_DIVIDE")
|
||||||
|
|
||||||
|
def op_BINARY_LSHIFT (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.shl(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_MODULO (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.srem(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.frem(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_MODULO for %r"
|
||||||
|
% (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_MULTIPLY (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.mul(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fmul(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_MULTIPLY for "
|
||||||
|
"%r" % (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_OR (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.or_(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_POWER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_POWER")
|
||||||
|
|
||||||
|
def op_BINARY_RSHIFT (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.lshr(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_SUBSCR (self, i, op, arg, *args, **kws):
|
||||||
|
arr_val = args[0]
|
||||||
|
index_vals = args[1:]
|
||||||
|
ret_val = gep_result = self.builder.gep(arr_val, index_vals)
|
||||||
|
if (gep_result.type.kind == lc.TYPE_POINTER and
|
||||||
|
gep_result.type.pointee.kind != lc.TYPE_POINTER):
|
||||||
|
ret_val = self.builder.load(gep_result)
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_BINARY_SUBTRACT (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.sub(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fsub(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_SUBTRACT for "
|
||||||
|
"%r" % (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
op_BINARY_TRUE_DIVIDE = op_BINARY_DIVIDE
|
||||||
|
|
||||||
|
def op_BINARY_XOR (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.xor(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BREAK_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_BUILD_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BUILD_SLICE")
|
||||||
|
|
||||||
|
def op_BUILD_TUPLE (self, i, op, arg, *args, **kws):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION (self, i, op, arg, *args, **kws):
|
||||||
|
fn = args[0]
|
||||||
|
args = args[1:]
|
||||||
|
fn_name = getattr(fn, '__name__', None)
|
||||||
|
if isinstance(fn, (types.FunctionType, types.MethodType)):
|
||||||
|
ret_val = [fn(self.builder, *args)]
|
||||||
|
elif isinstance(fn, lc.Value):
|
||||||
|
ret_val = [self.builder.call(fn, args)]
|
||||||
|
elif isinstance(fn, lc.Type):
|
||||||
|
if isinstance(fn, lc.FunctionType):
|
||||||
|
ret_val = [self.builder.call(
|
||||||
|
self.llvm_module.get_or_insert_function(fn, fn_name),
|
||||||
|
args)]
|
||||||
|
else:
|
||||||
|
assert len(args) == 1
|
||||||
|
ret_val = [LLVMCaster.build_cast(self.builder, args[0], fn)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Don't know how to call %s() (%r @ %d)!"
|
||||||
|
% (fn_name, fn, i))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_KW (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CALL_FUNCTION_KW")
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_VAR (self, i, op, arg, *args, **kws):
|
||||||
|
args = list(args)
|
||||||
|
var_args = list(args.pop())
|
||||||
|
args.extend(var_args)
|
||||||
|
return self.op_CALL_FUNCTION(i, op, arg, *args, **kws)
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_VAR_KW (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CALL_FUNCTION_VAR_KW")
|
||||||
|
|
||||||
|
def op_COMPARE_OP (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
cmp_kind = opcode.cmp_op[arg]
|
||||||
|
if isinstance(arg1.type, lc.IntegerType):
|
||||||
|
ret_val = [self.builder.icmp(_compare_mapping_sint[cmp_kind],
|
||||||
|
arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fcmp(_compare_mapping_float[cmp_kind],
|
||||||
|
arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('Comparison of type %r' % (arg1.type,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_CONTINUE_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CONTINUE_LOOP")
|
||||||
|
|
||||||
|
def op_DELETE_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_DELETE_ATTR")
|
||||||
|
|
||||||
|
def op_DELETE_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_DELETE_SLICE")
|
||||||
|
|
||||||
|
def op_FOR_ITER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_FOR_ITER")
|
||||||
|
|
||||||
|
def op_GET_ITER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_GET_ITER")
|
||||||
|
|
||||||
|
op_INPLACE_ADD = op_BINARY_ADD
|
||||||
|
op_INPLACE_AND = op_BINARY_AND
|
||||||
|
op_INPLACE_DIVIDE = op_BINARY_DIVIDE
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = op_BINARY_FLOOR_DIVIDE
|
||||||
|
op_INPLACE_LSHIFT = op_BINARY_LSHIFT
|
||||||
|
op_INPLACE_MODULO = op_BINARY_MODULO
|
||||||
|
op_INPLACE_MULTIPLY = op_BINARY_MULTIPLY
|
||||||
|
op_INPLACE_OR = op_BINARY_OR
|
||||||
|
op_INPLACE_POWER = op_BINARY_POWER
|
||||||
|
op_INPLACE_RSHIFT = op_BINARY_RSHIFT
|
||||||
|
op_INPLACE_SUBTRACT = op_BINARY_SUBTRACT
|
||||||
|
op_INPLACE_TRUE_DIVIDE = op_BINARY_TRUE_DIVIDE
|
||||||
|
op_INPLACE_XOR = op_BINARY_XOR
|
||||||
|
|
||||||
|
def op_JUMP_ABSOLUTE (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_JUMP_FORWARD (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + arg + 3])]
|
||||||
|
|
||||||
|
def op_JUMP_IF_FALSE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_FALSE")
|
||||||
|
|
||||||
|
def op_JUMP_IF_FALSE_OR_POP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_FALSE_OR_POP")
|
||||||
|
|
||||||
|
def op_JUMP_IF_TRUE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_TRUE")
|
||||||
|
|
||||||
|
def op_JUMP_IF_TRUE_OR_POP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_TRUE_OR_POP")
|
||||||
|
|
||||||
|
def op_LOAD_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
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
|
||||||
|
|
||||||
|
def op_LOAD_DEREF (self, i, op, arg, *args, **kws):
|
||||||
|
name = self.code_obj.co_freevars[arg]
|
||||||
|
ret_val = self.globals[name]
|
||||||
|
if isinstance(ret_val, lc.Type) and not hasattr(ret_val, '__name__'):
|
||||||
|
ret_val.__name__ = name
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_LOAD_GLOBAL (self, i, op, arg, *args, **kws):
|
||||||
|
name = self.code_obj.co_names[arg]
|
||||||
|
ret_val = self.globals[name]
|
||||||
|
if isinstance(ret_val, lc.Type) and not hasattr(ret_val, '__name__'):
|
||||||
|
ret_val.__name__ = name
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.pop()
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + 1])]
|
||||||
|
|
||||||
|
def op_POP_JUMP_IF_FALSE (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.cbranch(args[0], self.llvm_blocks[i + 3],
|
||||||
|
self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_POP_JUMP_IF_TRUE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_POP_JUMP_IF_TRUE")
|
||||||
|
|
||||||
|
def op_POP_TOP (self, i, op, arg, *args, **kws):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_RETURN_VALUE (self, i, op, arg, *args, **kws):
|
||||||
|
if args[0] is None:
|
||||||
|
ret_val = [self.builder.ret_void()]
|
||||||
|
else:
|
||||||
|
ret_val = [self.builder.ret(args[0])]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_SETUP_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.append((i, arg))
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + 3])]
|
||||||
|
|
||||||
|
def op_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_SLICE")
|
||||||
|
|
||||||
|
def op_STORE_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_STORE_ATTR")
|
||||||
|
|
||||||
|
def op_STORE_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_STORE_SLICE")
|
||||||
|
|
||||||
|
def op_STORE_SUBSCR (self, i, op, arg, *args, **kws):
|
||||||
|
store_val, arr_val, index_val = args
|
||||||
|
dest_addr = self.builder.gep(arr_val, [index_val])
|
||||||
|
return [self.builder.store(store_val, dest_addr)]
|
||||||
|
|
||||||
|
def op_UNARY_CONVERT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_CONVERT")
|
||||||
|
|
||||||
|
def op_UNARY_INVERT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_INVERT")
|
||||||
|
|
||||||
|
def op_UNARY_NEGATIVE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_NEGATIVE")
|
||||||
|
|
||||||
|
def op_UNARY_NOT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_NOT")
|
||||||
|
|
||||||
|
def op_UNARY_POSITIVE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_POSITIVE")
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def translate_function (func, lltype, llvm_module = None, **kws):
|
||||||
|
'''Given a function and an LLVM function type, emit LLVM code for
|
||||||
|
that function using a new LLVMTranslator instance.'''
|
||||||
|
translator = LLVMTranslator(llvm_module)
|
||||||
|
ret_val = translator.translate(func, lltype, env = kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def translate_into_function (py_function, llvm_function, **kws):
|
||||||
|
translator = LLVMTranslator(llvm_function.module)
|
||||||
|
ret_val = translator.translate(py_function, llvm_function = llvm_function,
|
||||||
|
env = kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def llnumba (lltype, llvm_module = None, **kws):
|
||||||
|
'''Decorator version of translate_function().'''
|
||||||
|
def _llnumba (func):
|
||||||
|
return translate_function(func, lltype, llvm_module, **kws)
|
||||||
|
return _llnumba
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def llnumba_into (llvm_function, **kws):
|
||||||
|
def _llnumba_into (func):
|
||||||
|
return translate_into_function(llvm_function, func, **kws)
|
||||||
|
return _llnumba_into
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
from tests import llfuncs, llfunctys
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
elif 'all' in args:
|
||||||
|
args = [llfunc
|
||||||
|
for llfunc in dir(llfuncs) if not llfunc.startswith('_')]
|
||||||
|
llvm_module = lc.Module.new('test_module')
|
||||||
|
for arg in args:
|
||||||
|
translate_function(getattr(llfuncs, arg), getattr(llfunctys, arg),
|
||||||
|
llvm_module)
|
||||||
|
print(llvm_module)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_translator.py
|
||||||
335
llpython/bytecode_visitor.py
Normal file
335
llpython/bytecode_visitor.py
Normal file
|
|
@ -0,0 +1,335 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
import opcode
|
||||||
|
from opcode_util import itercode
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeVisitor (object):
|
||||||
|
opnames = [name.split('+')[0] for name in opcode.opname]
|
||||||
|
|
||||||
|
def visit_op (self, i, op, arg, *args, **kws):
|
||||||
|
if op < 0:
|
||||||
|
ret_val = self.visit_synthetic_op(i, op, arg, *args, **kws)
|
||||||
|
else:
|
||||||
|
method = getattr(self, 'op_' + self.opnames[op])
|
||||||
|
ret_val = method(i, op, arg, *args, **kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def visit_synthetic_op (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'BytecodeVisitor.visit_synthetic_op() must be overloaded if using '
|
||||||
|
'synthetic opcodes.')
|
||||||
|
|
||||||
|
def _not_implemented (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("BytecodeVisitor.op_%s (@ bytecode index %d)"
|
||||||
|
% (self.opnames[op], i))
|
||||||
|
|
||||||
|
op_BINARY_ADD = _not_implemented
|
||||||
|
op_BINARY_AND = _not_implemented
|
||||||
|
op_BINARY_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_FLOOR_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_LSHIFT = _not_implemented
|
||||||
|
op_BINARY_MODULO = _not_implemented
|
||||||
|
op_BINARY_MULTIPLY = _not_implemented
|
||||||
|
op_BINARY_OR = _not_implemented
|
||||||
|
op_BINARY_POWER = _not_implemented
|
||||||
|
op_BINARY_RSHIFT = _not_implemented
|
||||||
|
op_BINARY_SUBSCR = _not_implemented
|
||||||
|
op_BINARY_SUBTRACT = _not_implemented
|
||||||
|
op_BINARY_TRUE_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_XOR = _not_implemented
|
||||||
|
op_BREAK_LOOP = _not_implemented
|
||||||
|
op_BUILD_CLASS = _not_implemented
|
||||||
|
op_BUILD_LIST = _not_implemented
|
||||||
|
op_BUILD_MAP = _not_implemented
|
||||||
|
op_BUILD_SET = _not_implemented
|
||||||
|
op_BUILD_SLICE = _not_implemented
|
||||||
|
op_BUILD_TUPLE = _not_implemented
|
||||||
|
op_CALL_FUNCTION = _not_implemented
|
||||||
|
op_CALL_FUNCTION_KW = _not_implemented
|
||||||
|
op_CALL_FUNCTION_VAR = _not_implemented
|
||||||
|
op_CALL_FUNCTION_VAR_KW = _not_implemented
|
||||||
|
op_COMPARE_OP = _not_implemented
|
||||||
|
op_CONTINUE_LOOP = _not_implemented
|
||||||
|
op_DELETE_ATTR = _not_implemented
|
||||||
|
op_DELETE_DEREF = _not_implemented
|
||||||
|
op_DELETE_FAST = _not_implemented
|
||||||
|
op_DELETE_GLOBAL = _not_implemented
|
||||||
|
op_DELETE_NAME = _not_implemented
|
||||||
|
op_DELETE_SLICE = _not_implemented
|
||||||
|
op_DELETE_SUBSCR = _not_implemented
|
||||||
|
op_DUP_TOP = _not_implemented
|
||||||
|
op_DUP_TOPX = _not_implemented
|
||||||
|
op_DUP_TOP_TWO = _not_implemented
|
||||||
|
op_END_FINALLY = _not_implemented
|
||||||
|
op_EXEC_STMT = _not_implemented
|
||||||
|
op_EXTENDED_ARG = _not_implemented
|
||||||
|
op_FOR_ITER = _not_implemented
|
||||||
|
op_GET_ITER = _not_implemented
|
||||||
|
op_IMPORT_FROM = _not_implemented
|
||||||
|
op_IMPORT_NAME = _not_implemented
|
||||||
|
op_IMPORT_STAR = _not_implemented
|
||||||
|
op_INPLACE_ADD = _not_implemented
|
||||||
|
op_INPLACE_AND = _not_implemented
|
||||||
|
op_INPLACE_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_LSHIFT = _not_implemented
|
||||||
|
op_INPLACE_MODULO = _not_implemented
|
||||||
|
op_INPLACE_MULTIPLY = _not_implemented
|
||||||
|
op_INPLACE_OR = _not_implemented
|
||||||
|
op_INPLACE_POWER = _not_implemented
|
||||||
|
op_INPLACE_RSHIFT = _not_implemented
|
||||||
|
op_INPLACE_SUBTRACT = _not_implemented
|
||||||
|
op_INPLACE_TRUE_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_XOR = _not_implemented
|
||||||
|
op_JUMP_ABSOLUTE = _not_implemented
|
||||||
|
op_JUMP_FORWARD = _not_implemented
|
||||||
|
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 = _not_implemented
|
||||||
|
op_LOAD_ATTR = _not_implemented
|
||||||
|
op_LOAD_BUILD_CLASS = _not_implemented
|
||||||
|
op_LOAD_CLOSURE = _not_implemented
|
||||||
|
op_LOAD_CONST = _not_implemented
|
||||||
|
op_LOAD_DEREF = _not_implemented
|
||||||
|
op_LOAD_FAST = _not_implemented
|
||||||
|
op_LOAD_GLOBAL = _not_implemented
|
||||||
|
op_LOAD_LOCALS = _not_implemented
|
||||||
|
op_LOAD_NAME = _not_implemented
|
||||||
|
op_MAKE_CLOSURE = _not_implemented
|
||||||
|
op_MAKE_FUNCTION = _not_implemented
|
||||||
|
op_MAP_ADD = _not_implemented
|
||||||
|
op_NOP = _not_implemented
|
||||||
|
op_POP_BLOCK = _not_implemented
|
||||||
|
op_POP_EXCEPT = _not_implemented
|
||||||
|
op_POP_JUMP_IF_FALSE = _not_implemented
|
||||||
|
op_POP_JUMP_IF_TRUE = _not_implemented
|
||||||
|
op_POP_TOP = _not_implemented
|
||||||
|
op_PRINT_EXPR = _not_implemented
|
||||||
|
op_PRINT_ITEM = _not_implemented
|
||||||
|
op_PRINT_ITEM_TO = _not_implemented
|
||||||
|
op_PRINT_NEWLINE = _not_implemented
|
||||||
|
op_PRINT_NEWLINE_TO = _not_implemented
|
||||||
|
op_RAISE_VARARGS = _not_implemented
|
||||||
|
op_RETURN_VALUE = _not_implemented
|
||||||
|
op_ROT_FOUR = _not_implemented
|
||||||
|
op_ROT_THREE = _not_implemented
|
||||||
|
op_ROT_TWO = _not_implemented
|
||||||
|
op_SETUP_EXCEPT = _not_implemented
|
||||||
|
op_SETUP_FINALLY = _not_implemented
|
||||||
|
op_SETUP_LOOP = _not_implemented
|
||||||
|
op_SETUP_WITH = _not_implemented
|
||||||
|
op_SET_ADD = _not_implemented
|
||||||
|
op_SLICE = _not_implemented
|
||||||
|
op_STOP_CODE = _not_implemented
|
||||||
|
op_STORE_ATTR = _not_implemented
|
||||||
|
op_STORE_DEREF = _not_implemented
|
||||||
|
op_STORE_FAST = _not_implemented
|
||||||
|
op_STORE_GLOBAL = _not_implemented
|
||||||
|
op_STORE_LOCALS = _not_implemented
|
||||||
|
op_STORE_MAP = _not_implemented
|
||||||
|
op_STORE_NAME = _not_implemented
|
||||||
|
op_STORE_SLICE = _not_implemented
|
||||||
|
op_STORE_SUBSCR = _not_implemented
|
||||||
|
op_UNARY_CONVERT = _not_implemented
|
||||||
|
op_UNARY_INVERT = _not_implemented
|
||||||
|
op_UNARY_NEGATIVE = _not_implemented
|
||||||
|
op_UNARY_NOT = _not_implemented
|
||||||
|
op_UNARY_POSITIVE = _not_implemented
|
||||||
|
op_UNPACK_EX = _not_implemented
|
||||||
|
op_UNPACK_SEQUENCE = _not_implemented
|
||||||
|
op_WITH_CLEANUP = _not_implemented
|
||||||
|
op_YIELD_VALUE = _not_implemented
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeIterVisitor (BytecodeVisitor):
|
||||||
|
def visit (self, co_obj):
|
||||||
|
self.enter_code_object(co_obj)
|
||||||
|
for i, op, arg in itercode(co_obj.co_code):
|
||||||
|
self.visit_op(i, op, arg)
|
||||||
|
return self.exit_code_object(co_obj)
|
||||||
|
|
||||||
|
def enter_code_object (self, co_obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_code_object (self, co_obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeFlowVisitor (BytecodeVisitor):
|
||||||
|
def visit (self, flow):
|
||||||
|
self.block_list = list(flow.keys())
|
||||||
|
self.block_list.sort()
|
||||||
|
self.enter_flow_object(flow)
|
||||||
|
for block in self.block_list:
|
||||||
|
prelude = self.enter_block(block)
|
||||||
|
prelude_isa_list = isinstance(prelude, list)
|
||||||
|
if prelude or prelude_isa_list:
|
||||||
|
if not prelude_isa_list:
|
||||||
|
prelude = []
|
||||||
|
new_stmts = list(self.visit_op(i, op, arg, *args)
|
||||||
|
for i, op, _, arg, args in flow[block])
|
||||||
|
self.new_flow[block] = list(itertools.chain(
|
||||||
|
prelude, *new_stmts))
|
||||||
|
self.exit_block(block)
|
||||||
|
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 = {}
|
||||||
|
|
||||||
|
def exit_flow_object (self, flow):
|
||||||
|
ret_val = self.new_flow
|
||||||
|
del self.new_flow
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BenignBytecodeVisitorMixin (object):
|
||||||
|
def _do_nothing (self, i, op, arg, *args, **kws):
|
||||||
|
return [(i, op, self.opnames[op], arg, args)]
|
||||||
|
|
||||||
|
op_BINARY_ADD = _do_nothing
|
||||||
|
op_BINARY_AND = _do_nothing
|
||||||
|
op_BINARY_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_FLOOR_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_LSHIFT = _do_nothing
|
||||||
|
op_BINARY_MODULO = _do_nothing
|
||||||
|
op_BINARY_MULTIPLY = _do_nothing
|
||||||
|
op_BINARY_OR = _do_nothing
|
||||||
|
op_BINARY_POWER = _do_nothing
|
||||||
|
op_BINARY_RSHIFT = _do_nothing
|
||||||
|
op_BINARY_SUBSCR = _do_nothing
|
||||||
|
op_BINARY_SUBTRACT = _do_nothing
|
||||||
|
op_BINARY_TRUE_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_XOR = _do_nothing
|
||||||
|
op_BREAK_LOOP = _do_nothing
|
||||||
|
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
|
||||||
|
op_COMPARE_OP = _do_nothing
|
||||||
|
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_DUP_TOP = _do_nothing
|
||||||
|
op_DUP_TOPX = _do_nothing
|
||||||
|
op_DUP_TOP_TWO = _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 = _do_nothing
|
||||||
|
op_INPLACE_AND = _do_nothing
|
||||||
|
op_INPLACE_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_LSHIFT = _do_nothing
|
||||||
|
op_INPLACE_MODULO = _do_nothing
|
||||||
|
op_INPLACE_MULTIPLY = _do_nothing
|
||||||
|
op_INPLACE_OR = _do_nothing
|
||||||
|
op_INPLACE_POWER = _do_nothing
|
||||||
|
op_INPLACE_RSHIFT = _do_nothing
|
||||||
|
op_INPLACE_SUBTRACT = _do_nothing
|
||||||
|
op_INPLACE_TRUE_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_XOR = _do_nothing
|
||||||
|
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
|
||||||
|
op_LOAD_CONST = _do_nothing
|
||||||
|
op_LOAD_DEREF = _do_nothing
|
||||||
|
op_LOAD_FAST = _do_nothing
|
||||||
|
op_LOAD_GLOBAL = _do_nothing
|
||||||
|
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 = _do_nothing
|
||||||
|
op_ROT_FOUR = _do_nothing
|
||||||
|
op_ROT_THREE = _do_nothing
|
||||||
|
op_ROT_TWO = _do_nothing
|
||||||
|
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
|
||||||
|
op_STORE_FAST = _do_nothing
|
||||||
|
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 = _do_nothing
|
||||||
|
op_UNARY_INVERT = _do_nothing
|
||||||
|
op_UNARY_NEGATIVE = _do_nothing
|
||||||
|
op_UNARY_NOT = _do_nothing
|
||||||
|
op_UNARY_POSITIVE = _do_nothing
|
||||||
|
op_UNPACK_EX = _do_nothing
|
||||||
|
op_UNPACK_SEQUENCE = _do_nothing
|
||||||
|
op_WITH_CLEANUP = _do_nothing
|
||||||
|
op_YIELD_VALUE = _do_nothing
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of bytecode_visitor.py
|
||||||
43
llpython/bytetype.py
Normal file
43
llpython/bytetype.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
lvoid = lc.Type.void()
|
||||||
|
li1 = lc.Type.int(1)
|
||||||
|
li8 = lc.Type.int(8)
|
||||||
|
li16 = lc.Type.int(16)
|
||||||
|
li32 = lc.Type.int(32)
|
||||||
|
li64 = lc.Type.int(64)
|
||||||
|
liptr = lc.Type.int(ctypes.sizeof(ctypes.c_void_p) * 8)
|
||||||
|
lc_size_t = lc.Type.int(ctypes.sizeof(
|
||||||
|
getattr(ctypes, 'c_ssize_t', getattr(ctypes, 'c_size_t'))) * 8)
|
||||||
|
lfloat = lc.Type.float()
|
||||||
|
ldouble = lc.Type.double()
|
||||||
|
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_struct = lc.Type.struct(l_pyobject_head)
|
||||||
|
l_pyobj_p = l_pyobject_head_struct_p = lc.Type.pointer(l_pyobject_head_struct)
|
||||||
|
l_pyfunc = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||||
|
|
||||||
|
strlen = lc.Type.function(lc_size_t, (li8_ptr,))
|
||||||
|
strncpy = lc.Type.function(li8_ptr, (li8_ptr, li8_ptr, lc_size_t))
|
||||||
|
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)
|
||||||
|
PyEval_SaveThread = lc.Type.function(li8_ptr, [])
|
||||||
|
PyEval_RestoreThread = lc.Type.function(lc.Type.void(), [li8_ptr])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of bytetype.py
|
||||||
218
llpython/control_flow.py
Normal file
218
llpython/control_flow.py
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class ControlFlowGraph (object):
|
||||||
|
def __init__ (self):
|
||||||
|
self.blocks = {}
|
||||||
|
self.blocks_in = {}
|
||||||
|
self.blocks_out = {}
|
||||||
|
self.blocks_reads = {}
|
||||||
|
self.blocks_writes = {}
|
||||||
|
self.blocks_writer = {}
|
||||||
|
self.blocks_dom = {}
|
||||||
|
self.blocks_reaching = {}
|
||||||
|
|
||||||
|
def add_block (self, key, value = None):
|
||||||
|
self.blocks[key] = value
|
||||||
|
if key not in self.blocks_in:
|
||||||
|
self.blocks_in[key] = set()
|
||||||
|
self.blocks_out[key] = set()
|
||||||
|
self.blocks_reads[key] = set()
|
||||||
|
self.blocks_writes[key] = set()
|
||||||
|
self.blocks_writer[key] = {}
|
||||||
|
|
||||||
|
def add_edge (self, from_block, to_block):
|
||||||
|
self.blocks_out[from_block].add(to_block)
|
||||||
|
self.blocks_in[to_block].add(from_block)
|
||||||
|
|
||||||
|
def unlink_unreachables (self):
|
||||||
|
changed = True
|
||||||
|
next_blocks = self.blocks.keys()
|
||||||
|
next_blocks.remove(0)
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
blocks = next_blocks
|
||||||
|
next_blocks = blocks[:]
|
||||||
|
for block in blocks:
|
||||||
|
if len(self.blocks_in[block]) == 0:
|
||||||
|
blocks_out = self.blocks_out[block]
|
||||||
|
for out_edge in blocks_out:
|
||||||
|
self.blocks_in[out_edge].discard(block)
|
||||||
|
blocks_out.clear()
|
||||||
|
next_blocks.remove(block)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
def compute_dataflow (self):
|
||||||
|
'''Compute the dominator and reaching dataflow relationships
|
||||||
|
in the CFG.'''
|
||||||
|
blocks = set(self.blocks.keys())
|
||||||
|
nonentry_blocks = blocks.copy()
|
||||||
|
for block in blocks:
|
||||||
|
self.blocks_dom[block] = blocks
|
||||||
|
self.blocks_reaching[block] = set((block,))
|
||||||
|
if len(self.blocks_in[block]) == 0:
|
||||||
|
self.blocks_dom[block] = set((block,))
|
||||||
|
nonentry_blocks.remove(block)
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for block in nonentry_blocks:
|
||||||
|
olddom = self.blocks_dom[block]
|
||||||
|
newdom = set.intersection(*[self.blocks_dom[pred]
|
||||||
|
for pred in self.blocks_in[block]])
|
||||||
|
newdom.add(block)
|
||||||
|
if newdom != olddom:
|
||||||
|
changed = True
|
||||||
|
self.blocks_dom[block] = newdom
|
||||||
|
oldreaching = self.blocks_reaching[block]
|
||||||
|
newreaching = set.union(
|
||||||
|
*[self.blocks_reaching[pred]
|
||||||
|
for pred in self.blocks_in[block]])
|
||||||
|
newreaching.add(block)
|
||||||
|
if newreaching != oldreaching:
|
||||||
|
changed = True
|
||||||
|
self.blocks_reaching[block] = newreaching
|
||||||
|
return self.blocks_dom, self.blocks_reaching
|
||||||
|
|
||||||
|
def update_for_ssa (self):
|
||||||
|
'''Modify the blocks_writes map to reflect phi nodes inserted
|
||||||
|
for static single assignment representations.'''
|
||||||
|
joins = [block for block in self.blocks.keys()
|
||||||
|
if len(self.blocks_in[block]) > 1]
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for block in joins:
|
||||||
|
phis_needed = self.phi_needed(block)
|
||||||
|
for affected_local in phis_needed:
|
||||||
|
if affected_local not in self.blocks_writes[block]:
|
||||||
|
changed = True
|
||||||
|
# NOTE: For this to work, we assume that basic
|
||||||
|
# blocks are indexed by their instruction
|
||||||
|
# index in the VM bytecode.
|
||||||
|
self.writes_local(block, block, affected_local)
|
||||||
|
if changed:
|
||||||
|
# Any modifications have invalidated the reaching
|
||||||
|
# definitions, so delete any memoized results.
|
||||||
|
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.
|
||||||
|
|
||||||
|
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).'''
|
||||||
|
preds = self.blocks_in[block]
|
||||||
|
npreds = len(preds)
|
||||||
|
if npreds == 0:
|
||||||
|
ret_val = None
|
||||||
|
elif npreds == 1:
|
||||||
|
ret_val = tuple(preds)[0]
|
||||||
|
else:
|
||||||
|
ret_val = [pred for pred in preds
|
||||||
|
if block not in self.blocks_dom[pred]][0]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def block_writes_to_writer_map (self, block):
|
||||||
|
ret_val = {}
|
||||||
|
for local in self.blocks_writes[block]:
|
||||||
|
ret_val[local] = block
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def get_reaching_definitions (self, block):
|
||||||
|
'''Return a nested map for the given block
|
||||||
|
s.t. ret_val[pred][local] equals the block key for the
|
||||||
|
definition of local that reaches the argument block via that
|
||||||
|
predecessor.
|
||||||
|
|
||||||
|
Useful for actually populating phi nodes, once you know you
|
||||||
|
need them.'''
|
||||||
|
has_memoized = hasattr(self, 'reaching_definitions')
|
||||||
|
if has_memoized and block in self.reaching_definitions:
|
||||||
|
ret_val = self.reaching_definitions[block]
|
||||||
|
else:
|
||||||
|
preds = self.blocks_in[block]
|
||||||
|
ret_val = {}
|
||||||
|
for pred in preds:
|
||||||
|
ret_val[pred] = self.block_writes_to_writer_map(pred)
|
||||||
|
crnt = self.idom(pred)
|
||||||
|
while crnt != None:
|
||||||
|
crnt_writer_map = self.block_writes_to_writer_map(crnt)
|
||||||
|
# This order of update favors the first definitions
|
||||||
|
# encountered in the traversal since the traversal
|
||||||
|
# visits blocks in reverse execution order.
|
||||||
|
crnt_writer_map.update(ret_val[pred])
|
||||||
|
ret_val[pred] = crnt_writer_map
|
||||||
|
crnt = self.idom(crnt)
|
||||||
|
if not has_memoized:
|
||||||
|
self.reaching_definitions = {}
|
||||||
|
self.reaching_definitions[block] = ret_val
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def nreaches (self, block):
|
||||||
|
'''For each local, find the number of unique reaching
|
||||||
|
definitions the current block has.'''
|
||||||
|
reaching_definitions = self.get_reaching_definitions(block)
|
||||||
|
definition_map = {}
|
||||||
|
for pred in self.blocks_in[block]:
|
||||||
|
reaching_from_pred = reaching_definitions[pred]
|
||||||
|
for local in reaching_from_pred.keys():
|
||||||
|
if local not in definition_map:
|
||||||
|
definition_map[local] = set()
|
||||||
|
definition_map[local].add(reaching_from_pred[local])
|
||||||
|
ret_val = {}
|
||||||
|
for local in definition_map.keys():
|
||||||
|
ret_val[local] = len(definition_map[local])
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def writes_local (self, block, write_instr_index, local_index):
|
||||||
|
self.blocks_writes[block].add(local_index)
|
||||||
|
block_writers = self.blocks_writer[block]
|
||||||
|
old_index = block_writers.get(local_index, -1)
|
||||||
|
# This checks for a corner case that would impact
|
||||||
|
# numba.translate.Translate.build_phi_nodes().
|
||||||
|
assert old_index != write_instr_index, (
|
||||||
|
"Found corner case for STORE_FAST at a CFG join!")
|
||||||
|
block_writers[local_index] = max(write_instr_index, old_index)
|
||||||
|
|
||||||
|
def phi_needed (self, join):
|
||||||
|
'''Return the set of locals that will require a phi node to be
|
||||||
|
generated at the given join.'''
|
||||||
|
nreaches = self.nreaches(join)
|
||||||
|
return set([local for local in nreaches.keys()
|
||||||
|
if nreaches[local] > 1])
|
||||||
|
|
||||||
|
def pprint (self, *args, **kws):
|
||||||
|
pprint.pprint(self.__dict__, *args, **kws)
|
||||||
|
|
||||||
|
def pformat (self, *args, **kws):
|
||||||
|
return pprint.pformat(self.__dict__, *args, **kws)
|
||||||
|
|
||||||
|
def to_dot (self, graph_name = None):
|
||||||
|
'''Return a dot (digraph visualizer in Graphviz) graph
|
||||||
|
description as a string.'''
|
||||||
|
if graph_name is None:
|
||||||
|
graph_name = 'CFG_%d' % id(self)
|
||||||
|
lines_out = []
|
||||||
|
for block_index in self.blocks:
|
||||||
|
lines_out.append(
|
||||||
|
'BLOCK_%r [shape=box, label="BLOCK_%r\\nr: %r, w: %r"];' %
|
||||||
|
(block_index, block_index,
|
||||||
|
tuple(self.blocks_reads[block_index]),
|
||||||
|
tuple(self.blocks_writes[block_index])))
|
||||||
|
for block_index in self.blocks:
|
||||||
|
for out_edge in self.blocks_out[block_index]:
|
||||||
|
lines_out.append('BLOCK_%r -> BLOCK_%r;' %
|
||||||
|
(block_index, out_edge))
|
||||||
|
return 'digraph %s {\n%s\n}\n' % (graph_name, '\n'.join(lines_out))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of control_flow.py
|
||||||
27
llpython/gen_bytecode_visitor.py
Normal file
27
llpython/gen_bytecode_visitor.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import opcode_util
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def generate_bytecode_visitor (classname = 'BytecodeVisitor',
|
||||||
|
baseclass = 'object'):
|
||||||
|
opnames = list(set((opname.split('+')[0]
|
||||||
|
for opname in opcode_util.OPCODE_MAP.keys())))
|
||||||
|
opnames.sort()
|
||||||
|
return 'class %s (%s):\n%s\n' % (
|
||||||
|
classname, baseclass,
|
||||||
|
'\n\n'.join((' def op_%s (self, i, op, arg):\n'
|
||||||
|
' raise NotImplementedError("%s.op_%s")' %
|
||||||
|
(opname, classname, opname)
|
||||||
|
for opname in opnames)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
print(generate_bytecode_visitor(*sys.argv[1:]))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of gen_bytecode_visitor.py
|
||||||
369
llpython/nobitey.py
Normal file
369
llpython/nobitey.py
Normal file
|
|
@ -0,0 +1,369 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
import imp
|
||||||
|
import io
|
||||||
|
import types
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
import llvm.ee as le
|
||||||
|
|
||||||
|
import bytetype
|
||||||
|
import byte_translator
|
||||||
|
from pyaddfunc import pyaddfunc
|
||||||
|
|
||||||
|
LLVM_TO_INT_PARSE_STR_MAP = {
|
||||||
|
8 : 'b',
|
||||||
|
16 : 'h',
|
||||||
|
32 : 'i', # Note that on 32-bit systems sizeof(int) == sizeof(long)
|
||||||
|
64 : 'L', # Seeing sizeof(long long) == 8 on both 32 and 64-bit platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_TO_PARSE_STR_MAP = {
|
||||||
|
lc.TYPE_FLOAT : 'f',
|
||||||
|
lc.TYPE_DOUBLE : 'd',
|
||||||
|
}
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
# XXX Stolen from numba.translate
|
||||||
|
|
||||||
|
def get_string_constant (module, const_str):
|
||||||
|
const_name = "__STR_%x" % (hash(const_str),)
|
||||||
|
try:
|
||||||
|
ret_val = module.get_global_variable_named(const_name)
|
||||||
|
except:
|
||||||
|
lconst_str = lc.Constant.stringz(const_str)
|
||||||
|
ret_val = module.add_global_variable(lconst_str.type, const_name)
|
||||||
|
ret_val.initializer = lconst_str
|
||||||
|
ret_val.linkage = lc.LINKAGE_INTERNAL
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class NoBitey (object):
|
||||||
|
def __init__ (self, target_module = None, type_annotations = None):
|
||||||
|
if target_module is None:
|
||||||
|
target_module = lc.Module.new('NoBitey_%d' % id(self))
|
||||||
|
if type_annotations is None:
|
||||||
|
type_annotations = {}
|
||||||
|
self.target_module = target_module
|
||||||
|
self.type_aliases = type_annotations # Reserved for future use.
|
||||||
|
|
||||||
|
def _build_parse_string (self, llvm_type):
|
||||||
|
kind = llvm_type.kind
|
||||||
|
if kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = LLVM_TO_INT_PARSE_STR_MAP[llvm_type.width]
|
||||||
|
elif kind in LLVM_TO_PARSE_STR_MAP:
|
||||||
|
ret_val = LLVM_TO_PARSE_STR_MAP[kind]
|
||||||
|
else:
|
||||||
|
raise TypeError('Unsupported LLVM type: %s' % str(llvm_type))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_parse_string (self, llvm_tys):
|
||||||
|
"""Given a set of LLVM types, return a string for parsing
|
||||||
|
them via PyArg_ParseTuple."""
|
||||||
|
return ''.join((self._build_parse_string(ty)
|
||||||
|
for ty in llvm_tys))
|
||||||
|
|
||||||
|
def handle_abi_casts (self, builder, result):
|
||||||
|
if result.type.kind == lc.TYPE_FLOAT:
|
||||||
|
# NOTE: The C ABI apparently casts floats to doubles when
|
||||||
|
# an argument must be pushed on the stack, as is the case
|
||||||
|
# when calling a variable argument function.
|
||||||
|
# XXX Is there documentation on this where I can find all
|
||||||
|
# coercion rules? Do we still need some libffi
|
||||||
|
# integration?
|
||||||
|
result = builder.fpext(result, bytetype.ldouble)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def build_wrapper_function (self, llvm_function, engine = None):
|
||||||
|
arg_types = llvm_function.type.pointee.args
|
||||||
|
return_type = llvm_function.type.pointee.return_type
|
||||||
|
li32_0 = lc.Constant.int(bytetype.li32, 0)
|
||||||
|
def get_llvm_function (builder):
|
||||||
|
if self.target_module != llvm_function.module:
|
||||||
|
llvm_function_ptr = self.target_module.add_global_variable(
|
||||||
|
llvm_function.type, llvm_function.name)
|
||||||
|
llvm_function_ptr.initializer = lc.Constant.inttoptr(
|
||||||
|
lc.Constant.int(
|
||||||
|
bytetype.liptr,
|
||||||
|
engine.get_pointer_to_function(llvm_function)),
|
||||||
|
llvm_function.type)
|
||||||
|
llvm_function_ptr.linkage = lc.LINKAGE_INTERNAL
|
||||||
|
ret_val = builder.load(llvm_function_ptr)
|
||||||
|
else:
|
||||||
|
ret_val = llvm_function
|
||||||
|
return ret_val
|
||||||
|
def build_parse_args (builder):
|
||||||
|
return [builder.alloca(arg_type) for arg_type in arg_types]
|
||||||
|
def build_parse_string (builder):
|
||||||
|
parse_str = get_string_constant(
|
||||||
|
self.target_module, self.build_parse_string(arg_types))
|
||||||
|
return builder.gep(parse_str, (li32_0, li32_0))
|
||||||
|
def load_target_args (builder, args):
|
||||||
|
return [builder.load(arg) for arg in args]
|
||||||
|
def build_build_string (builder):
|
||||||
|
build_str = get_string_constant(
|
||||||
|
self.target_module, self._build_parse_string(return_type))
|
||||||
|
return builder.gep(build_str, (li32_0, li32_0))
|
||||||
|
handle_abi_casts = self.handle_abi_casts
|
||||||
|
target_function_name = llvm_function.name + "_wrapper"
|
||||||
|
# __________________________________________________
|
||||||
|
@byte_translator.llnumba(bytetype.l_pyfunc, self.target_module,
|
||||||
|
**locals())
|
||||||
|
def _wrapper (self, args):
|
||||||
|
ret_val = l_pyobj_p(0)
|
||||||
|
parse_args = build_parse_args()
|
||||||
|
parse_result = PyArg_ParseTuple(args, build_parse_string(),
|
||||||
|
*parse_args)
|
||||||
|
if parse_result != li32(0):
|
||||||
|
thread_state = PyEval_SaveThread()
|
||||||
|
target_args = load_target_args(parse_args)
|
||||||
|
llresult = handle_abi_casts(get_llvm_function()(*target_args))
|
||||||
|
PyEval_RestoreThread(thread_state)
|
||||||
|
ret_val = Py_BuildValue(build_build_string(), llresult)
|
||||||
|
return ret_val
|
||||||
|
# __________________________________________________
|
||||||
|
return _wrapper
|
||||||
|
|
||||||
|
def wrap_llvm_module (self, llvm_module, engine = None, py_module = None):
|
||||||
|
'''
|
||||||
|
Shamefully adapted from bitey.bind.wrap_llvm_module().
|
||||||
|
'''
|
||||||
|
functions = [func for func in llvm_module.functions
|
||||||
|
if not func.name.startswith("_")
|
||||||
|
and not func.is_declaration
|
||||||
|
and func.linkage == lc.LINKAGE_EXTERNAL]
|
||||||
|
if engine is None:
|
||||||
|
engine = le.ExecutionEngine.new(llvm_module)
|
||||||
|
wrappers = [self.build_wrapper_function(func, engine)
|
||||||
|
for func in functions]
|
||||||
|
if __debug__: print(self.target_module)
|
||||||
|
if self.target_module != llvm_module:
|
||||||
|
engine.add_module(self.target_module)
|
||||||
|
py_wrappers = [pyaddfunc(wrapper.name,
|
||||||
|
engine.get_pointer_to_function(wrapper))
|
||||||
|
for wrapper in wrappers]
|
||||||
|
if py_module:
|
||||||
|
for py_wrapper in py_wrappers:
|
||||||
|
setattr(py_module, py_wrapper.__name__[:-8], py_wrapper)
|
||||||
|
setattr(py_module, '_llvm_module', llvm_module)
|
||||||
|
setattr(py_module, '_llvm_engine', engine)
|
||||||
|
if self.target_module != llvm_module:
|
||||||
|
setattr(py_module, '_llvm_wrappers', self.target_module)
|
||||||
|
return engine, py_wrappers
|
||||||
|
|
||||||
|
def wrap_llvm_module_in_python (self, llvm_module, py_module = None):
|
||||||
|
'''
|
||||||
|
Mildly reworked and abstracted bitey.bind.wrap_llvm_bitcode().
|
||||||
|
Abstracted to accept any existing LLVM Module object, and
|
||||||
|
return a Python wrapper module (even if one wasn't originally
|
||||||
|
specified).
|
||||||
|
'''
|
||||||
|
if py_module is None:
|
||||||
|
py_module = types.ModuleType(str(llvm_module.id))
|
||||||
|
engine = le.ExecutionEngine.new(llvm_module)
|
||||||
|
self.wrap_llvm_module(llvm_module, engine, py_module)
|
||||||
|
return py_module
|
||||||
|
|
||||||
|
def wrap_llvm_bitcode (self, bitcode, py_module = None):
|
||||||
|
'''
|
||||||
|
Intended to be drop-in replacement of
|
||||||
|
bitey.bind.wrap_llvm_bitcode().
|
||||||
|
'''
|
||||||
|
return self.wrap_llvm_module_in_python(
|
||||||
|
lc.Module.from_bitcode(io.BytesIO(bitcode)), py_module)
|
||||||
|
|
||||||
|
def wrap_llvm_assembly (self, llvm_asm, py_module = None):
|
||||||
|
return self.wrap_llvm_module_in_python(
|
||||||
|
lc.Module.from_assembly(io.BytesIO(llvm_asm)), py_module)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class NoBiteyLoader(object):
|
||||||
|
"""
|
||||||
|
Load LLVM compiled bitcode and autogenerate a ctypes binding.
|
||||||
|
|
||||||
|
Initially copied and adapted from bitey.loader module.
|
||||||
|
"""
|
||||||
|
def __init__(self, pkg, name, source, preload, postload):
|
||||||
|
self.package = pkg
|
||||||
|
self.name = name
|
||||||
|
self.fullname = '.'.join((pkg,name))
|
||||||
|
self.source = source
|
||||||
|
self.preload = preload
|
||||||
|
self.postload = postload
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _check_magic(cls, filename):
|
||||||
|
if os.path.exists(filename):
|
||||||
|
magic = open(filename,"rb").read(4)
|
||||||
|
if magic == b'\xde\xc0\x17\x0b':
|
||||||
|
return True
|
||||||
|
elif magic[:2] == b'\x42\x43':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_module(cls, fullname, source_path, source_data, preload=None,
|
||||||
|
postload=None):
|
||||||
|
name = fullname.split(".")[-1]
|
||||||
|
mod = imp.new_module(name)
|
||||||
|
if preload:
|
||||||
|
exec(preload, mod.__dict__, mod.__dict__)
|
||||||
|
type_annotations = getattr(mod, '_type_annotations', None)
|
||||||
|
nb = NoBitey(type_annotations = type_annotations)
|
||||||
|
if source_path.endswith(('.o', '.bc')):
|
||||||
|
nb.wrap_llvm_bitcode(source_data, mod)
|
||||||
|
elif source_path.endswith('.s'):
|
||||||
|
nb.wrap_llvm_assembly(source_data, mod)
|
||||||
|
if postload:
|
||||||
|
exec(postload, mod.__dict__, mod.__dict__)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_module(cls, fullname, paths = None):
|
||||||
|
if paths is None:
|
||||||
|
paths = sys.path
|
||||||
|
names = fullname.split('.')
|
||||||
|
modname = names[-1]
|
||||||
|
source_paths = None
|
||||||
|
for f in paths:
|
||||||
|
path = os.path.join(os.path.realpath(f), modname)
|
||||||
|
source = path + '.o'
|
||||||
|
if cls._check_magic(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
source = path + '.bc'
|
||||||
|
if os.path.exists(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
source = path + '.s'
|
||||||
|
if os.path.exists(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
if source_paths:
|
||||||
|
path, source = source_paths
|
||||||
|
return cls('.'.join(names[:-1]), modname, source,
|
||||||
|
path + ".pre.py", path + ".post.py")
|
||||||
|
|
||||||
|
def get_code(self, module):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_data(self, module):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_filename(self, name):
|
||||||
|
return self.source
|
||||||
|
|
||||||
|
def get_source(self, name):
|
||||||
|
with open(self.source, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def is_package(self, *args, **kw):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def load_module(self, fullname):
|
||||||
|
if fullname in sys.modules:
|
||||||
|
return sys.modules[fullname]
|
||||||
|
|
||||||
|
preload = None
|
||||||
|
postload = None
|
||||||
|
|
||||||
|
# Get the preload file (if any)
|
||||||
|
if os.path.exists(self.preload):
|
||||||
|
with open(self.preload) as f:
|
||||||
|
preload = f.read()
|
||||||
|
|
||||||
|
# Get the source
|
||||||
|
with open(self.source, 'rb') as f:
|
||||||
|
source_data = f.read()
|
||||||
|
|
||||||
|
# Get the postload file (if any)
|
||||||
|
if os.path.exists(self.postload):
|
||||||
|
with open(self.postload) as f:
|
||||||
|
postload = f.read()
|
||||||
|
|
||||||
|
mod = self.build_module(fullname, self.get_filename(None), source_data,
|
||||||
|
preload, postload)
|
||||||
|
sys.modules[fullname] = mod
|
||||||
|
mod.__loader__ = self
|
||||||
|
mod.__file__ = self.source
|
||||||
|
return mod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls):
|
||||||
|
if cls not in sys.meta_path:
|
||||||
|
sys.meta_path.append(cls)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove(cls):
|
||||||
|
sys.meta_path.remove(cls)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def _mk_add_42 (llvm_module, at_type = bytetype.lc_long):
|
||||||
|
f = llvm_module.add_function(
|
||||||
|
lc.Type.function(at_type, [at_type]), 'add_42_%s' % str(at_type))
|
||||||
|
block = f.append_basic_block('entry')
|
||||||
|
builder = lc.Builder.new(block)
|
||||||
|
if at_type.kind == lc.TYPE_INTEGER:
|
||||||
|
const_42 = lc.Constant.int(at_type, 42)
|
||||||
|
add = builder.add
|
||||||
|
elif at_type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
const_42 = lc.Constant.real(at_type, 42.)
|
||||||
|
add = builder.fadd
|
||||||
|
else:
|
||||||
|
raise TypeError('Unsupported type: %s' % str(at_type))
|
||||||
|
builder.ret(add(f.args[0], const_42))
|
||||||
|
return f
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_test_module ():
|
||||||
|
llvm_module = lc.Module.new('nobitey_test')
|
||||||
|
for ty in (bytetype.li32, bytetype.li64, bytetype.lfloat,
|
||||||
|
bytetype.ldouble):
|
||||||
|
fn = _mk_add_42(llvm_module, ty)
|
||||||
|
return llvm_module
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def test_wrap_module (arg = None):
|
||||||
|
# Build up a module.
|
||||||
|
m = build_test_module()
|
||||||
|
if arg and arg.lower() == 'separated':
|
||||||
|
wrap_module = NoBitey().wrap_llvm_module_in_python(m)
|
||||||
|
else:
|
||||||
|
wrap_module = NoBitey(m).wrap_llvm_module_in_python(m)
|
||||||
|
# Now try running the generated wrappers.
|
||||||
|
for py_wf_name in ('add_42_i32', 'add_42_i64', 'add_42_float',
|
||||||
|
'add_42_double'):
|
||||||
|
py_wf = getattr(wrap_module, py_wf_name)
|
||||||
|
for i in range(42):
|
||||||
|
result = py_wf(i)
|
||||||
|
expected = i + 42
|
||||||
|
assert result == expected, "%r != %r in %r" % (
|
||||||
|
result, expected, py_wf)
|
||||||
|
return wrap_module
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
if args:
|
||||||
|
for arg in args:
|
||||||
|
test_wrap_module(arg)
|
||||||
|
else:
|
||||||
|
test_wrap_module()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of nobitey.py
|
||||||
215
llpython/opcode_util.py
Normal file
215
llpython/opcode_util.py
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import dis
|
||||||
|
import opcode
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# 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')]
|
||||||
|
|
||||||
|
# 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, None, 1),
|
||||||
|
'JUMP_IF_FALSE_OR_POP': (None, None, None),
|
||||||
|
'JUMP_IF_TRUE': (1, None, 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module functions
|
||||||
|
|
||||||
|
def itercode(code):
|
||||||
|
"""Return a generator of byte-offset, opcode, and argument
|
||||||
|
from a byte-code-string
|
||||||
|
"""
|
||||||
|
i = 0
|
||||||
|
extended_arg = 0
|
||||||
|
if isinstance(code[0], str):
|
||||||
|
code = [ord(c) for c in code]
|
||||||
|
n = len(code)
|
||||||
|
while i < n:
|
||||||
|
op = code[i]
|
||||||
|
num = i
|
||||||
|
i = i + 1
|
||||||
|
oparg = None
|
||||||
|
if op >= opcode.HAVE_ARGUMENT:
|
||||||
|
oparg = code[i] + (code[i + 1] * 256) + extended_arg
|
||||||
|
extended_arg = 0
|
||||||
|
i = i + 2
|
||||||
|
if op == opcode.EXTENDED_ARG:
|
||||||
|
extended_arg = oparg * 65536
|
||||||
|
|
||||||
|
delta = yield num, op, oparg
|
||||||
|
if delta is not None:
|
||||||
|
abs_rel, dst = delta
|
||||||
|
assert abs_rel == 'abs' or abs_rel == 'rel'
|
||||||
|
i = dst if abs_rel == 'abs' else i + dst
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def extendlabels(code, labels = None):
|
||||||
|
"""Extend the set of jump target labels to account for the
|
||||||
|
passthrough targets of conditional branches.
|
||||||
|
|
||||||
|
This allows us to create a control flow graph where there is at
|
||||||
|
most one branch per basic block.
|
||||||
|
"""
|
||||||
|
if labels is None:
|
||||||
|
labels = []
|
||||||
|
if isinstance(code[0], str):
|
||||||
|
code = [ord(c) for c in code]
|
||||||
|
n = len(code)
|
||||||
|
i = 0
|
||||||
|
while i < n:
|
||||||
|
op = code[i]
|
||||||
|
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:
|
||||||
|
labels.append(i)
|
||||||
|
return labels
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def get_code_object (func):
|
||||||
|
return getattr(func, '__code__', getattr(func, 'func_code', None))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of opcode_util.py
|
||||||
153
llpython/phi_injector.py
Normal file
153
llpython/phi_injector.py
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
from bytecode_visitor import BytecodeFlowVisitor, BenignBytecodeVisitorMixin
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
synthetic_opname = []
|
||||||
|
synthetic_opmap = {}
|
||||||
|
|
||||||
|
def def_synth_op (opname):
|
||||||
|
global synthetic_opname, synthetic_opmap
|
||||||
|
ret_val = -(len(synthetic_opname) + 1)
|
||||||
|
synthetic_opname.insert(0, opname)
|
||||||
|
synthetic_opmap[opname] = ret_val
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
REF_ARG = def_synth_op('REF_ARG')
|
||||||
|
BUILD_PHI = def_synth_op('BUILD_PHI')
|
||||||
|
DEFINITION = def_synth_op('DEFINITION')
|
||||||
|
REF_DEF = def_synth_op('REF_DEF')
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class PhiInjector (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
||||||
|
'''Transformer responsible for modifying a bytecode flow, removing
|
||||||
|
LOAD_FAST and STORE_FAST opcodes, and replacing them with a static
|
||||||
|
single assignment (SSA) representation.
|
||||||
|
|
||||||
|
In order to support SSA, PhiInjector adds the following synthetic
|
||||||
|
opcodes to transformed flows:
|
||||||
|
|
||||||
|
* REF_ARG: Specifically reference an incomming argument value.
|
||||||
|
|
||||||
|
* BUILD_PHI: Build a phi node to disambiguate between several
|
||||||
|
possible definitions at a control flow join.
|
||||||
|
|
||||||
|
* DEFINITION: Unique value definition indexed by the "arg" field
|
||||||
|
in the tuple.
|
||||||
|
|
||||||
|
* REF_DEF: Reference a specific value definition.'''
|
||||||
|
|
||||||
|
def visit_cfg (self, cfg, nargs = 0, *args, **kws):
|
||||||
|
self.cfg = cfg
|
||||||
|
ret_val = self.visit(cfg.blocks, nargs)
|
||||||
|
del self.cfg
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def visit (self, flow, nargs = 0, *args, **kws):
|
||||||
|
self.nargs = nargs
|
||||||
|
self.definitions = []
|
||||||
|
self.phis = []
|
||||||
|
self.prev_blocks = []
|
||||||
|
self.blocks_locals = dict((block, {})
|
||||||
|
for block in self.cfg.blocks.keys())
|
||||||
|
ret_val = super(PhiInjector, self).visit(flow, *args, **kws)
|
||||||
|
for block, _, _, args, _ in self.phis:
|
||||||
|
local = args.pop()
|
||||||
|
reaching_definitions = self.cfg.reaching_definitions[block]
|
||||||
|
for prev in reaching_definitions.keys():
|
||||||
|
if 0 in self.cfg.blocks_reaching[prev]:
|
||||||
|
args.append((prev, REF_DEF, 'REF_DEF',
|
||||||
|
self.blocks_locals[prev][local], ()))
|
||||||
|
args.sort()
|
||||||
|
del self.blocks_locals
|
||||||
|
del self.prev_blocks
|
||||||
|
del self.phis
|
||||||
|
del self.definitions
|
||||||
|
del self.nargs
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def add_definition (self, index, local, arg):
|
||||||
|
definition_index = len(self.definitions)
|
||||||
|
definition = (index, DEFINITION, 'DEFINITION', definition_index,
|
||||||
|
(arg,))
|
||||||
|
self.definitions.append(definition)
|
||||||
|
self.blocks_locals[self.block][local] = definition_index
|
||||||
|
return definition
|
||||||
|
|
||||||
|
def add_phi (self, index, local):
|
||||||
|
ret_val = (index, BUILD_PHI, 'BUILD_PHI', [local], ())
|
||||||
|
self.phis.append(ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
ret_val = False
|
||||||
|
self.block = block
|
||||||
|
if block == 0:
|
||||||
|
if self.nargs > 0:
|
||||||
|
ret_val = [self.add_definition(-1, arg,
|
||||||
|
(-1, REF_ARG, 'REF_ARG', arg,
|
||||||
|
()))
|
||||||
|
for arg in range(self.nargs)]
|
||||||
|
else:
|
||||||
|
ret_val = True
|
||||||
|
elif 0 in self.cfg.blocks_reaching[block]:
|
||||||
|
ret_val = True
|
||||||
|
prev_block_locals = None
|
||||||
|
for pred_block in self.cfg.blocks_in[block]:
|
||||||
|
if pred_block in self.prev_blocks:
|
||||||
|
prev_block_locals = self.blocks_locals[pred_block]
|
||||||
|
break
|
||||||
|
assert prev_block_locals is not None, "Internal translation error"
|
||||||
|
self.blocks_locals[block] = prev_block_locals.copy()
|
||||||
|
phis_needed = self.cfg.phi_needed(block)
|
||||||
|
if phis_needed:
|
||||||
|
ret_val = [self.add_definition(block, local,
|
||||||
|
self.add_phi(block, local))
|
||||||
|
for local in phis_needed]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
if 0 in self.cfg.blocks_reaching[block]:
|
||||||
|
self.prev_blocks.append(block)
|
||||||
|
del self.block
|
||||||
|
|
||||||
|
def op_STORE_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
assert len(args) == 1
|
||||||
|
return [self.add_definition(i, arg, args[0])]
|
||||||
|
|
||||||
|
def op_LOAD_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
return [(i, REF_DEF, 'REF_DEF', self.blocks_locals[self.block][arg],
|
||||||
|
args)]
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def inject_phis (func):
|
||||||
|
'''Given a Python function, return a bytecode flow object that has
|
||||||
|
been transformed by a fresh PhiInjector instance.'''
|
||||||
|
import byte_control
|
||||||
|
argcount = byte_control.opcode_util.get_code_object(func).co_argcount
|
||||||
|
cfg = byte_control.build_cfg(func)
|
||||||
|
return PhiInjector().visit_cfg(cfg, argcount)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
import pprint
|
||||||
|
from tests import llfuncs
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
for arg in args:
|
||||||
|
pprint.pprint(inject_phis(getattr(llfuncs, arg)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of phi_injector.py
|
||||||
41
llpython/pyaddfunc.py
Normal file
41
llpython/pyaddfunc.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
class PyMethodDef (ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('ml_name', ctypes.c_char_p),
|
||||||
|
('ml_meth', ctypes.c_void_p),
|
||||||
|
('ml_flags', ctypes.c_int),
|
||||||
|
('ml_doc', ctypes.c_char_p),
|
||||||
|
]
|
||||||
|
|
||||||
|
PyCFunction_NewEx = ctypes.pythonapi.PyCFunction_NewEx
|
||||||
|
PyCFunction_NewEx.argtypes = (ctypes.POINTER(PyMethodDef),
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.c_void_p)
|
||||||
|
PyCFunction_NewEx.restype = ctypes.py_object
|
||||||
|
|
||||||
|
cache = {} # Unsure if this is necessary to keep the PyMethodDef
|
||||||
|
# structures from being garbage collected. Assuming so...
|
||||||
|
|
||||||
|
def pyaddfunc (func_name, func_ptr, func_doc = None):
|
||||||
|
global cache
|
||||||
|
if bytes != str:
|
||||||
|
func_name = bytes(ord(ch) for ch in func_name)
|
||||||
|
key = (func_name, func_ptr)
|
||||||
|
if key in cache:
|
||||||
|
_, ret_val = cache[key]
|
||||||
|
else:
|
||||||
|
mdef = PyMethodDef(bytes(func_name),
|
||||||
|
func_ptr,
|
||||||
|
1, # == METH_VARARGS (hopefully remains so...)
|
||||||
|
func_doc)
|
||||||
|
ret_val = PyCFunction_NewEx(ctypes.byref(mdef), 0, 0)
|
||||||
|
cache[key] = (mdef, ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of pyaddfunc.py
|
||||||
0
llpython/tests/__init__.py
Normal file
0
llpython/tests/__init__.py
Normal file
42
llpython/tests/llfuncs.py
Normal file
42
llpython/tests/llfuncs.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def doslice (in_string, lower, upper):
|
||||||
|
l = strlen(in_string)
|
||||||
|
if lower < lc_size_t(0):
|
||||||
|
lower += l
|
||||||
|
if upper < lc_size_t(0):
|
||||||
|
upper += l
|
||||||
|
temp_len = upper - lower
|
||||||
|
if temp_len < lc_size_t(0):
|
||||||
|
temp_len = lc_size_t(0)
|
||||||
|
ret_val = alloca_array(li8, temp_len + lc_size_t(1))
|
||||||
|
strncpy(ret_val, in_string + lower, temp_len)
|
||||||
|
ret_val[temp_len] = li8(0)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def ipow (val, exp):
|
||||||
|
ret_val = 1
|
||||||
|
temp = val
|
||||||
|
w = exp
|
||||||
|
while w > 0:
|
||||||
|
if (w & 1) != 0:
|
||||||
|
ret_val *= temp
|
||||||
|
# TODO: Overflow check on ret_val
|
||||||
|
w >>= 1
|
||||||
|
if w == 0: break
|
||||||
|
temp *= temp
|
||||||
|
# TODO: Overflow check on temp
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def pymod (arg1, arg2):
|
||||||
|
ret_val = arg1 % arg2
|
||||||
|
if ret_val < 0:
|
||||||
|
if arg2 > 0:
|
||||||
|
ret_val += arg2
|
||||||
|
elif arg2 < 0:
|
||||||
|
ret_val += arg2
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of llfuncs.py
|
||||||
23
llpython/tests/llfunctys.py
Normal file
23
llpython/tests/llfunctys.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
try:
|
||||||
|
from llnumba import bytetype
|
||||||
|
except ImportError:
|
||||||
|
from numba.llnumba import bytetype
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
doslice = lc.Type.function(bytetype.li8_ptr, (
|
||||||
|
bytetype.li8_ptr, bytetype.lc_size_t, bytetype.lc_size_t))
|
||||||
|
|
||||||
|
ipow = lc.Type.function(bytetype.li32, (bytetype.li32,
|
||||||
|
bytetype.li32))
|
||||||
|
|
||||||
|
pymod = lc.Type.function(bytetype.li32, (bytetype.li32,
|
||||||
|
bytetype.li32))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of llfunctys.py
|
||||||
Loading…
Add table
Add a link
Reference in a new issue