class LLOp(object): def __init__(self, sideeffects=True, canfold=False, canraise=(), pyobj=False, canmallocgc=False, canrun=False, oo=False, tryfold=False): # self.opname = ... (set afterwards) if canfold: sideeffects = False # The operation has no side-effects: it can be removed # if its result is not used self.sideeffects = sideeffects # Can be safely constant-folded: no side-effects # and always gives the same result for given args self.canfold = canfold # Can *try* to fold the operation, but it may raise on you self.tryfold = tryfold or canfold # Exceptions that can be raised self.canraise = canraise assert isinstance(canraise, tuple) assert not canraise or not canfold # The operation manipulates PyObjects self.pyobj = pyobj # The operation can go a GC malloc self.canmallocgc = canmallocgc if canmallocgc: if (MemoryError not in self.canraise and Exception not in self.canraise): self.canraise += (MemoryError, ) # The operation can be run directly with __call__ self.canrun = canrun or canfold # The operation belongs to the ootypesystem self.oo = oo # __________ make the LLOp instances callable from LL helpers __________ __name__ = property(lambda self: 'llop_' + self.opname) def __call__(self, RESULTTYPE, *args): # llop is meant to be rtyped and not called directly, unless it is # a canfold=True operation fold = self.fold if getattr(fold, 'need_result_type', False): val = fold(RESULTTYPE, *args) else: val = fold(*args) if RESULTTYPE is not lltype.Void: val = lltype.enforce(RESULTTYPE, val) return val def get_fold_impl(self): global lltype # <- lazy import hack, worth an XXX from pypy.rpython.lltypesystem import lltype if self.canrun: if self.oo: from pypy.rpython.ootypesystem.ooopimpl import get_op_impl else: from pypy.rpython.lltypesystem.opimpl import get_op_impl op_impl = get_op_impl(self.opname) else: error = TypeError("cannot constant-fold operation %r" % (self.opname, )) def op_impl(*args): raise error # cache the implementation function into 'self' self.fold = op_impl return op_impl fold = roproperty(get_fold_impl) def is_pure(self, args_v): if self.canfold: # canfold => pure operation return True if self is llop.debug_assert: # debug_assert is pure enough return True # reading from immutable (lltype) if self is llop.getfield or self is llop.getarrayitem: field = getattr(args_v[1], 'value', None) return args_v[0].concretetype.TO._immutable_field(field) # reading from immutable (ootype) (xxx what about arrays?) if self is llop.oogetfield: field = getattr(args_v[1], 'value', None) return args_v[0].concretetype._immutable_field(field) # default return False def __repr__(self): return '<LLOp %s>' % (getattr(self, 'opname', '?'), )
class FunctionGraph(object): __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__'] def __init__(self, name, startblock, return_var=None): self.name = name # function name (possibly mangled already) self.startblock = startblock # build default returnblock self.returnblock = Block([return_var or Variable()]) self.returnblock.operations = () self.returnblock.exits = () # block corresponding to exception results self.exceptblock = Block([ Variable('etype'), # exception class Variable('evalue') ]) # exception value self.exceptblock.operations = () self.exceptblock.exits = () self.tag = None def getargs(self): return self.startblock.inputargs def getreturnvar(self): return self.returnblock.inputargs[0] def getsource(self): from pypy.tool.sourcetools import getsource func = self.func # can raise AttributeError src = getsource(self.func) if src is None: raise AttributeError('source not found') return src source = roproperty(getsource) def getstartline(self): return self.func.func_code.co_firstlineno startline = roproperty(getstartline) def getfilename(self): return self.func.func_code.co_filename filename = roproperty(getfilename) def __str__(self): if hasattr(self, 'func'): return nice_repr_for_func(self.func, self.name) else: return self.name def __repr__(self): return '<FunctionGraph of %s at 0x%x>' % (self, uid(self)) def iterblocks(self): block = self.startblock yield block seen = {block: True} stack = list(block.exits[::-1]) while stack: block = stack.pop().target if block not in seen: yield block seen[block] = True stack += block.exits[::-1] def iterlinks(self): block = self.startblock seen = {block: True} stack = list(block.exits[::-1]) while stack: link = stack.pop() yield link block = link.target if block not in seen: seen[block] = True stack += block.exits[::-1] def iterblockops(self): for block in self.iterblocks(): for op in block.operations: yield block, op def show(self, t=None): from pypy.translator.tool.graphpage import FlowGraphPage FlowGraphPage(t, [self]).display()