def __init__(self, annotator): self.annotator = annotator self.policy = annotator.policy self.descs = {} # map Python objects to their XxxDesc wrappers self.methoddescs = {} # map (funcdesc, classdef) to the MethodDesc self.classdefs = [] # list of all ClassDefs self.pbctypes = {} self.seen_mutable = {} self.listdefs = {} # map position_keys to ListDefs self.dictdefs = {} # map position_keys to DictDefs self.immutable_cache = {} self.classpbc_attr_families = { } # {'attr': UnionFind(ClassAttrFamily)} self.frozenpbc_attr_families = UnionFind(description.FrozenAttrFamily) self.pbc_maximal_call_families = UnionFind(description.CallFamily) self.emulated_pbc_calls = {} self.all_specializations = {} # {FuncDesc: specialization-info} self.pending_specializations = [] # list of callbacks self.external_class_cache = {} # cache of ExternalType classes self.needs_generic_instantiate = {} self.stats = Stats(self) # used in SomeObject.__new__ for keeping debugging info self._isomeobject_coming_from = identity_dict() delayed_imports()
def memo(funcdesc, arglist_s): from pypy.annotation.model import SomePBC, SomeImpossibleValue, SomeBool from pypy.annotation.model import unionof # call the function now, and collect possible results argvalues = [] for s in arglist_s: if s.is_constant(): values = [s.const] elif isinstance(s, SomePBC): values = [] assert not s.can_be_None, "memo call: cannot mix None and PBCs" for desc in s.descriptions: if desc.pyobj is None: raise Exception( "memo call with a class or PBC that has no " "corresponding Python object (%r)" % (desc, )) values.append(desc.pyobj) elif isinstance(s, SomeImpossibleValue): return s # we will probably get more possible args later elif isinstance(s, SomeBool): values = [False, True] else: raise Exception("memo call: argument must be a class or a frozen " "PBC, got %r" % (s, )) argvalues.append(values) # the list of all possible tuples of arguments to give to the memo function possiblevalues = cartesian_product(argvalues) # a MemoTable factory -- one MemoTable per family of arguments that can # be called together, merged via a UnionFind. bookkeeper = funcdesc.bookkeeper try: memotables = bookkeeper.all_specializations[funcdesc] except KeyError: func = funcdesc.pyobj if func is None: raise Exception("memo call: no Python function object to call " "(%r)" % (funcdesc, )) def compute_one_result(args): value = func(*args) memotable = MemoTable(funcdesc, args, value) memotable.register_finish() return memotable memotables = UnionFind(compute_one_result) bookkeeper.all_specializations[funcdesc] = memotables # merge the MemoTables for the individual argument combinations firstvalues = possiblevalues.next() _, _, memotable = memotables.find(firstvalues) for values in possiblevalues: _, _, memotable = memotables.union(firstvalues, values) if memotable.graph is not None: return memotable.graph # if already computed else: # otherwise, for now, return the union of each possible result return unionof( *[bookkeeper.immutablevalue(v) for v in memotable.table.values()])
def get_classpbc_attr_families(self, attrname): """Return the UnionFind for the ClassAttrFamilies corresponding to attributes of the given name. """ map = self.classpbc_attr_families try: access_sets = map[attrname] except KeyError: access_sets = map[attrname] = UnionFind(description.ClassAttrFamily) return access_sets
def __init__(self, hannotator): self.pending_specializations = [] self.originflags = {} self.virtual_containers = {} self.descs = {} self.tsgraph_maximal_call_families = UnionFind(TsGraphCallFamily) self.annotator = hannotator self.tsgraphsigs = {} self.nonstuboriggraphcount = 0 self.stuboriggraphcount = 0 if hannotator is not None: # for tests t = hannotator.base_translator self.impurity_analyzer = ImpurityAnalyzer(t) # circular imports hack global hintmodel from pypy.jit.hintannotator import model as hintmodel
def coalesce_variables(self): self._unionfind = UnionFind() pendingblocks = list(self.graph.iterblocks()) while pendingblocks: block = pendingblocks.pop() # Aggressively try to coalesce each source variable with its # target. We start from the end of the graph instead of # from the beginning. This is a bit arbitrary, but the idea # is that the end of the graph runs typically more often # than the start, given that we resume execution from the # middle during blackholing. for link in block.exits: if link.last_exception is not None: self._depgraph.add_node(link.last_exception) if link.last_exc_value is not None: self._depgraph.add_node(link.last_exc_value) for i, v in enumerate(link.args): self._try_coalesce(v, link.target.inputargs[i])
def test_cleanup(): state = [] class ReferencedByExternalState(object): def __init__(self, obj): state.append(self) self.obj = obj def absorb(self, other): state.remove(other) uf = UnionFind(ReferencedByExternalState) uf.find(1) for i in xrange(1, 10, 2): uf.union(i, 1) uf.find(2) for i in xrange(2, 20, 2): uf.union(i, 2) assert len(state) == 2 # we have exactly 2 partitions
def __init__(self, graph): # Build a list of "unification opportunities": for each block and each # 'n', an "opportunity" groups the block's nth input variable with # the nth output variable from each of the incoming links, in a list: # [Block, blockvar, linkvar, linkvar, linkvar...] opportunities = [] opportunities_with_const = [] for block, links in mkinsideentrymap(graph).items(): assert links for n, inputvar in enumerate(block.inputargs): vars = [block, inputvar] put_in = opportunities for link in links: var = link.args[n] if not isinstance(var, Variable): put_in = opportunities_with_const vars.append(var) # if any link provides a Constant, record this in # the opportunities_with_const list instead put_in.append(vars) self.opportunities = opportunities self.opportunities_with_const = opportunities_with_const self.variable_families = UnionFind()
def compute_lifetimes(self, graph): """Compute the static data flow of the graph: returns a list of LifeTime instances, each of which corresponds to a set of Variables from the graph. The variables are grouped in the same LifeTime if a value can pass from one to the other by following the links. Each LifeTime also records all places where a Variable in the set is used (read) or build (created). """ lifetimes = UnionFind(LifeTime) def set_creation_point(block, var, *cp): _, _, info = lifetimes.find((block, var)) info.creationpoints[cp] = True def set_use_point(block, var, *up): _, _, info = lifetimes.find((block, var)) info.usepoints[up] = True def union(block1, var1, block2, var2): if isinstance(var1, Variable): lifetimes.union((block1, var1), (block2, var2)) elif isinstance(var1, Constant): set_creation_point(block2, var2, "constant", var1) else: raise TypeError(var1) for var in graph.startblock.inputargs: set_creation_point(graph.startblock, var, "inputargs") set_use_point(graph.returnblock, graph.returnblock.inputargs[0], "return") set_use_point(graph.exceptblock, graph.exceptblock.inputargs[0], "except") set_use_point(graph.exceptblock, graph.exceptblock.inputargs[1], "except") def visit(node): if isinstance(node, Block): for op in node.operations: if op.opname in self.IDENTITY_OPS: # special-case these operations to identify their input # and output variables union(node, op.args[0], node, op.result) continue if op.opname in self.SUBSTRUCT_OPS: if self.visit_substruct_op(node, union, op): continue for i in range(len(op.args)): if isinstance(op.args[i], Variable): set_use_point(node, op.args[i], "op", node, op, i) set_creation_point(node, op.result, "op", node, op) if isinstance(node.exitswitch, Variable): set_use_point(node, node.exitswitch, "exitswitch", node) if isinstance(node, Link): if isinstance(node.last_exception, Variable): set_creation_point(node.prevblock, node.last_exception, "last_exception") if isinstance(node.last_exc_value, Variable): set_creation_point(node.prevblock, node.last_exc_value, "last_exc_value") d = {} for i, arg in enumerate(node.args): union(node.prevblock, arg, node.target, node.target.inputargs[i]) if isinstance(arg, Variable): if arg in d: # same variable present several times in link.args # consider it as a 'use' of the variable, which # will disable malloc optimization (aliasing problems) set_use_point(node.prevblock, arg, "dup", node, i) else: d[arg] = True traverse(visit, graph) return lifetimes.infos()