Exemple #1
0
    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()
Exemple #2
0
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 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()])
Exemple #4
0
 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
Exemple #5
0
 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])
Exemple #6
0
 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
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
 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])
Exemple #10
0
 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()
Exemple #11
0
    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()
Exemple #12
0
class RegAllocator(object):
    DEBUG_REGALLOC = False

    def __init__(self, graph, kind):
        self.graph = graph
        self.kind = kind

    def make_dependencies(self):
        dg = DependencyGraph()
        for block in self.graph.iterblocks():
            # Compute die_at = {Variable: index_of_operation_with_last_usage}
            die_at = dict.fromkeys(block.inputargs, 0)
            for i, op in enumerate(block.operations):
                for v in op.args:
                    if isinstance(v, Variable):
                        die_at[v] = i
                    elif isinstance(v, ListOfKind):
                        for v1 in v:
                            if isinstance(v1, Variable):
                                die_at[v1] = i
                if op.result is not None:
                    die_at[op.result] = i + 1
            if isinstance(block.exitswitch, tuple):
                for x in block.exitswitch:
                    die_at.pop(x, None)
            else:
                die_at.pop(block.exitswitch, None)
            for link in block.exits:
                for v in link.args:
                    die_at.pop(v, None)
            die_at = [(value, key) for (key, value) in die_at.items()]
            die_at.sort()
            die_at.append((sys.maxint,))
            # Done.  XXX the code above this line runs 3 times
            # (for kind in KINDS) to produce the same result...
            livevars = [v for v in block.inputargs
                          if getkind(v.concretetype) == self.kind]
            # Add the variables of this block to the dependency graph
            for i, v in enumerate(livevars):
                dg.add_node(v)
                for j in range(i):
                    dg.add_edge(livevars[j], v)
            livevars = set(livevars)
            die_index = 0
            for i, op in enumerate(block.operations):
                while die_at[die_index][0] == i:
                    try:
                        livevars.remove(die_at[die_index][1])
                    except KeyError:
                        pass
                    die_index += 1
                if (op.result is not None and
                    getkind(op.result.concretetype) == self.kind):
                    dg.add_node(op.result)
                    for v in livevars:
                        if getkind(v.concretetype) == self.kind:
                            dg.add_edge(v, op.result)
                    livevars.add(op.result)
        self._depgraph = dg

    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 _try_coalesce(self, v, w):
        if isinstance(v, Variable) and getkind(v.concretetype) == self.kind:
            dg = self._depgraph
            uf = self._unionfind
            v0 = uf.find_rep(v)
            w0 = uf.find_rep(w)
            if v0 is not w0 and v0 not in dg.neighbours[w0]:
                _, rep, _ = uf.union(v0, w0)
                assert uf.find_rep(v0) is uf.find_rep(w0) is rep
                if rep is v0:
                    dg.coalesce(w0, v0)
                else:
                    assert rep is w0
                    dg.coalesce(v0, w0)

    def find_node_coloring(self):
        self._coloring = self._depgraph.find_node_coloring()
        if self.DEBUG_REGALLOC:
            for block in self.graph.iterblocks():
                print block
                for v in block.getvariables():
                    print '\t', v, '\t', self.getcolor(v)

    def getcolor(self, v):
        return self._coloring[self._unionfind.find_rep(v)]

    def swapcolors(self, col1, col2):
        for key, value in self._coloring.items():
            if value == col1:
                self._coloring[key] = col2
            elif value == col2:
                self._coloring[key] = col1
Exemple #13
0
class HintBookkeeper(object):

    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 getdesc(self, graph):
        try:
            return self.descs[graph]
        except KeyError:
            self.descs[graph] = desc = GraphDesc(self, graph)
            return desc

    def enter(self, position_key):
        """Start of an operation.
        The operation is uniquely identified by the given key."""
        res = getattr(self, 'position_key', None)
        self.position_key = position_key
        TLS.bookkeeper = self
        return res

    def leave(self, old=None):
        """End of an operation."""
        if old is None:
            del TLS.bookkeeper
            del self.position_key
        else:
            self.position_key = old

    def myinputargorigin(self, graph, i):
        try:
            origin = self.originflags[graph, i]
        except KeyError:
            origin = hintmodel.InputArgOriginFlags(self, graph, i)
            self.originflags[graph, i] = origin
        return origin

    def myorigin(self):
        try:
            origin = self.originflags[self.position_key]
        except KeyError:
            assert len(self.position_key) == 3
            graph, block, i = self.position_key
            spaceop = block.operations[i]
            spaceop = SpaceOperation(spaceop.opname,
                                     list(spaceop.args),
                                     spaceop.result)
            origin = hintmodel.OriginFlags(self, spaceop)
            self.originflags[self.position_key] = origin
        return origin

    def compute_at_fixpoint(self):
        binding = self.annotator.binding

        # for the entry point, we need to remove the 'myorigin' of
        # the input arguments (otherwise they will always be green,
        # as there is no call to the entry point to make them red)
        tsgraph = self.annotator.translator.graphs[0]
        for v in tsgraph.getargs():
            hs_arg = binding(v)
            if isinstance(hs_arg, hintmodel.SomeLLAbstractConstant):
                hs_arg.myorigin = None
        # for convenience, force the return var to be red too, as
        # the timeshifter doesn't support anything else
        if self.annotator.policy.entrypoint_returns_red:
            v = tsgraph.getreturnvar()
            hs_red = hintmodel.variableoftype(v.concretetype)
            self.annotator.setbinding(v, hs_red)

        # propagate the green/red constraints
        log.event("Computing maximal green set...")
        greenorigindependencies = {}
        callreturndependencies = {}
        for origin in self.originflags.values():
            origin.greenargs = True
            origin.record_dependencies(greenorigindependencies,
                                       callreturndependencies)

        while True:
            progress = False
            # check all calls to see if they are green calls or not
            for origin, graphs in callreturndependencies.items():
                if self.is_green_call(origin.spaceop):
                    pass   # green call => don't force spaceop.result to red
                else:
                    # non-green calls: replace the dependency with a regular
                    # dependency from graph.getreturnvar() to spaceop.result
                    del callreturndependencies[origin]
                    retdeps = greenorigindependencies.setdefault(origin, [])
                    for graph in graphs:
                        retdeps.append(graph.getreturnvar())
            # propagate normal dependencies
            for origin, deps in greenorigindependencies.items():
                for v in deps:
                    if not binding(v).is_green():
                        # not green => force the origin to be red too
                        origin.greenargs = False
                        del greenorigindependencies[origin]
                        progress = True
                        break
            if not progress:
                break

        for callfamily in self.tsgraph_maximal_call_families.infos():
            if len(callfamily.tsgraphs) > 1:
                # if at least one graph in the family returns a red,
                # we force a red as the return of all of them
                returns_red = False
                for graph in callfamily.tsgraphs:
                    if not binding(graph.getreturnvar()).is_green():
                        returns_red = True
                if returns_red:
                    for graph in callfamily.tsgraphs:
                        v = graph.getreturnvar()
                        hs_red = hintmodel.variableoftype(v.concretetype)
                        self.annotator.setbinding(v, hs_red)

        # compute and cache the signature of the graphs before they are
        # modified by further code
        ha = self.annotator
        for tsgraph in ha.translator.graphs:
            sig_hs = ([ha.binding(v) for v in tsgraph.getargs()],
                      ha.binding(tsgraph.getreturnvar()))
            self.tsgraphsigs[tsgraph] = sig_hs

    def is_pure_graph(self, graph):
        impure = self.impurity_analyzer.analyze_direct_call(graph)
        return not impure

    def is_green_call(self, callop):
        "Is the given call operation completely computable at compile-time?"
        for v in callop.args:
            hs_arg = self.annotator.binding(v)
            if not hs_arg.is_green():
                return False
        # all-green arguments.  Note that we can return True even if the
        # result appears to be red; it's not a real red result then.
        impure = self.impurity_analyzer.analyze(callop)
        return not impure

    def immutableconstant(self, const):
        res = hintmodel.SomeLLAbstractConstant(const.concretetype, {})
        res.const = const.value
        # we want null pointers to be deepfrozen!
        if isinstance(const.concretetype, (lltype.Ptr,
                                           ootype.Instance,
                                           ootype.BuiltinType,
                                           ootype.StaticMethod)):
            if not const.value:
                res.deepfrozen = True
        return res

    def immutablevalue(self, value):
        return self.immutableconstant(Constant(value, lltype.typeOf(value)))

    def current_op_concretetype(self):
        _, block, i = self.position_key
        op = block.operations[i]
        return op.result.concretetype

    def current_op_binding(self):
        _, block, i = self.position_key
        op = block.operations[i]
        hs_res = self.annotator.binding(op.result, annmodel.s_ImpossibleValue)
        return hs_res

    def getvirtualcontainerdef(self, TYPE, constructor=None):
        try:
            res = self.virtual_containers[self.position_key]
            assert res.T == TYPE
        except KeyError:
            if constructor is None:
                from pypy.jit.hintannotator.container import virtualcontainerdef
                constructor = virtualcontainerdef
            res = constructor(self, TYPE)
            self.virtual_containers[self.position_key] = res
        return res

    def warning(self, msg):
        return self.annotator.warning(msg)

    def specialization_key(self, fixed, args_hs):
        if fixed:
            return 'fixed'
        else:
            key = []
            specialize = False
            for i, arg_hs in enumerate(args_hs):
                if isinstance(arg_hs, hintmodel.SomeLLAbstractVariable):
                    key.append('v')
                    specialize = True
                    continue

                if (isinstance(arg_hs, hintmodel.SomeLLAbstractConstant)
                    and arg_hs.eager_concrete):
                    key.append('E')
                    specialize = True
                else:
                    key.append('x')

                if (isinstance(arg_hs, hintmodel.SomeLLAbstractConstant)
                    and arg_hs.deepfrozen):
                    key.append('D')
                    specialize = True
                else:
                    key.append('x')

            if specialize:
                return ''.join(key)
            else:
                return None

    def get_graph_by_key(self, graph, specialization_key):
        desc = self.getdesc(graph)
        return desc._cache[specialization_key]

    def get_graph_for_call(self, graph, fixed, args_hs):
        # this can modify args_hs in-place!
        key = self.specialization_key(fixed, args_hs)
        if key is None:
            alt_name = None
        else:
            alt_name = graph.name + '_H'+key
        desc = self.getdesc(graph)
        graph = desc.specialize(args_hs, key=key, alt_name=alt_name)
        return graph

    def graph_call(self, graph, fixed, args_hs,
                   tsgraph_accum=None, hs_callable=None):
        input_args_hs = list(args_hs)
        graph = self.get_graph_for_call(graph, fixed, input_args_hs)
        if tsgraph_accum is not None:
            tsgraph_accum.append(graph)     # save this if the caller cares

        # propagate fixing of arguments in the function to the caller
        for inp_arg_hs, arg_hs in zip(input_args_hs, args_hs):
            if isinstance(arg_hs, hintmodel.SomeLLAbstractConstant):
                assert len(inp_arg_hs.origins) == 1
                [o] = inp_arg_hs.origins.keys()
                if o.read_fixed():
                    for o in arg_hs.origins:
                        o.set_fixed()
        
        hs_res = self.annotator.recursivecall(graph,
                                              self.position_key,
                                              input_args_hs)
        # look on which input args the hs_res result depends on
        if isinstance(hs_res, hintmodel.SomeLLAbstractConstant):
            if (hs_callable is not None and
                not isinstance(hs_callable, hintmodel.SomeLLAbstractConstant)):
                hs_res = hintmodel.variableoftype(hs_res.concretetype,
                                                  hs_res.deepfrozen)
            else:
                deps_hs = []
                for hs_inputarg, hs_arg in zip(input_args_hs, args_hs):
                    if isinstance(hs_inputarg,
                                  hintmodel.SomeLLAbstractConstant):
                        assert len(hs_inputarg.origins) == 1
                        [o] = hs_inputarg.origins.keys()
                        if o in hs_res.origins:
                            deps_hs.append(hs_arg)
                if fixed:
                    deps_hs.append(hs_res)
                hs_res = hintmodel.reorigin(hs_res, hs_callable, *deps_hs)
        return hs_res

    def graph_family_call(self, graph_list, fixed, args_hs,
                          tsgraphs_accum=None, hs_callable=None):
        if tsgraphs_accum is None:
            tsgraphs = []
        else:
            tsgraphs = tsgraphs_accum
        results_hs = []
        for graph in graph_list:
            results_hs.append(self.graph_call(graph, fixed, args_hs,
                                              tsgraphs, hs_callable))
        # put the tsgraphs in the same call family
        call_families = self.tsgraph_maximal_call_families
        _, rep, callfamily = call_families.find(tsgraphs[0])
        for tsgraph in tsgraphs[1:]:
            _, rep, callfamily = call_families.union(rep, tsgraph)
        return annmodel.unionof(*results_hs)
Exemple #14
0
class RegAllocator(object):
    DEBUG_REGALLOC = False

    def __init__(self, graph, consider_var, ListOfKind):
        self.graph = graph
        self.consider_var = consider_var
        self.ListOfKind = ListOfKind

    def make_dependencies(self):
        dg = DependencyGraph()
        for block in self.graph.iterblocks():
            # Compute die_at = {Variable: index_of_operation_with_last_usage}
            die_at = dict.fromkeys(block.inputargs, 0)
            for i, op in enumerate(block.operations):
                for v in op.args:
                    if isinstance(v, Variable):
                        die_at[v] = i
                    elif isinstance(v, self.ListOfKind):
                        for v1 in v:
                            if isinstance(v1, Variable):
                                die_at[v1] = i
                if op.result is not None:
                    die_at[op.result] = i + 1
            if isinstance(block.exitswitch, tuple):
                for x in block.exitswitch:
                    die_at.pop(x, None)
            else:
                die_at.pop(block.exitswitch, None)
            for link in block.exits:
                for v in link.args:
                    die_at.pop(v, None)
            die_at = [(value, key) for (key, value) in die_at.items()]
            die_at.sort()
            die_at.append((sys.maxint, ))
            # Done.  XXX the code above this line runs 3 times
            # (for kind in KINDS) to produce the same result...
            livevars = [v for v in block.inputargs if self.consider_var(v)]
            # Add the variables of this block to the dependency graph
            for i, v in enumerate(livevars):
                dg.add_node(v)
                for j in range(i):
                    dg.add_edge(livevars[j], v)
            livevars = set(livevars)
            die_index = 0
            for i, op in enumerate(block.operations):
                while die_at[die_index][0] == i:
                    try:
                        livevars.remove(die_at[die_index][1])
                    except KeyError:
                        pass
                    die_index += 1
                if (op.result is not None and self.consider_var(op.result)):
                    dg.add_node(op.result)
                    for v in livevars:
                        if self.consider_var(v):
                            dg.add_edge(v, op.result)
                    livevars.add(op.result)
        self._depgraph = dg

    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 _try_coalesce(self, v, w):
        if isinstance(v, Variable) and self.consider_var(v):
            assert self.consider_var(w)
            dg = self._depgraph
            uf = self._unionfind
            v0 = uf.find_rep(v)
            w0 = uf.find_rep(w)
            if v0 is not w0 and v0 not in dg.neighbours[w0]:
                _, rep, _ = uf.union(v0, w0)
                assert uf.find_rep(v0) is uf.find_rep(w0) is rep
                if rep is v0:
                    dg.coalesce(w0, v0)
                else:
                    assert rep is w0
                    dg.coalesce(v0, w0)

    def find_node_coloring(self):
        self._coloring = self._depgraph.find_node_coloring()
        if self.DEBUG_REGALLOC:
            for block in self.graph.iterblocks():
                print block
                for v in block.getvariables():
                    print '\t', v, '\t', self.getcolor(v)

    def getcolor(self, v):
        return self._coloring[self._unionfind.find_rep(v)]

    def swapcolors(self, col1, col2):
        for key, value in self._coloring.items():
            if value == col1:
                self._coloring[key] = col2
            elif value == col2:
                self._coloring[key] = col1
Exemple #15
0
    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()