Esempio n. 1
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)
Esempio n. 2
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()
Esempio n. 3
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()