Esempio n. 1
0
def insert_ll_stackcheck(translator):
    from rpython.translator.backendopt.support import find_calls_from
    from rpython.rlib.rstack import stack_check
    from rpython.tool.algo.graphlib import Edge, make_edge_dict, break_cycles_v
    rtyper = translator.rtyper
    graph = rtyper.annotate_helper(stack_check, [])
    rtyper.specialize_more_blocks()
    stack_check_ptr = rtyper.getcallable(graph)
    stack_check_ptr_const = Constant(stack_check_ptr,
                                     lltype.typeOf(stack_check_ptr))
    edges = set()
    insert_in = set()
    block2graph = {}
    for caller in translator.graphs:
        pyobj = getattr(caller, 'func', None)
        if pyobj is not None:
            if getattr(pyobj, '_dont_insert_stackcheck_', False):
                continue
        for block, callee in find_calls_from(translator, caller):
            if getattr(getattr(callee, 'func', None),
                       'insert_stack_check_here', False):
                insert_in.add(callee.startblock)
                block2graph[callee.startblock] = callee
                continue
            if block is not caller.startblock:
                edges.add((caller.startblock, block))
                block2graph[caller.startblock] = caller
            edges.add((block, callee.startblock))
            block2graph[block] = caller

    edgelist = [Edge(block1, block2) for (block1, block2) in edges]
    edgedict = make_edge_dict(edgelist)
    for block in break_cycles_v(edgedict, edgedict):
        insert_in.add(block)

    for block in insert_in:
        v = Variable()
        v.concretetype = lltype.Void
        unwind_op = SpaceOperation('direct_call', [stack_check_ptr_const], v)
        block.operations.insert(0, unwind_op)
        # prevents cycles of tail calls from occurring -- such cycles would
        # not consume any stack, so would turn into potentially infinite loops
        graph = block2graph[block]
        graph.inhibit_tail_call = True
    return len(insert_in)
Esempio n. 2
0
def insert_ll_stackcheck(translator):
    from rpython.translator.backendopt.support import find_calls_from
    from rpython.rlib.rstack import stack_check
    from rpython.tool.algo.graphlib import Edge, make_edge_dict, break_cycles_v
    rtyper = translator.rtyper
    graph = rtyper.annotate_helper(stack_check, [])
    rtyper.specialize_more_blocks()
    stack_check_ptr = rtyper.getcallable(graph)
    stack_check_ptr_const = Constant(stack_check_ptr, lltype.typeOf(stack_check_ptr))
    edges = set()
    insert_in = set()
    block2graph = {}
    for caller in translator.graphs:
        pyobj = getattr(caller, 'func', None)
        if pyobj is not None:
            if getattr(pyobj, '_dont_insert_stackcheck_', False):
                continue
        for block, callee in find_calls_from(translator, caller):
            if getattr(getattr(callee, 'func', None),
                       'insert_stack_check_here', False):
                insert_in.add(callee.startblock)
                block2graph[callee.startblock] = callee
                continue
            if block is not caller.startblock:
                edges.add((caller.startblock, block))
                block2graph[caller.startblock] = caller
            edges.add((block, callee.startblock))
            block2graph[block] = caller

    edgelist = [Edge(block1, block2) for (block1, block2) in edges]
    edgedict = make_edge_dict(edgelist)
    for block in break_cycles_v(edgedict, edgedict):
        insert_in.add(block)

    for block in insert_in:
        v = Variable()
        v.concretetype = lltype.Void
        unwind_op = SpaceOperation('direct_call', [stack_check_ptr_const], v)
        block.operations.insert(0, unwind_op)
        # prevents cycles of tail calls from occurring -- such cycles would
        # not consume any stack, so would turn into potentially infinite loops
        graph = block2graph[block]
        graph.inhibit_tail_call = True
    return len(insert_in)
Esempio n. 3
0
def find_inner_loops(graph, check_exitswitch_type=None):
    """Enumerate what look like the innermost loops of the graph.
    Returns a list of non-overlapping Loop() instances.
    """
    # Heuristic (thanks Stakkars for the idea):
    # to find the "best" cycle to pick,
    #
    #   * look for variables that don't change over the whole cycle
    #
    #   * cycles with _more_ of them are _inside_ cycles with less of them,
    #     because any variable that doesn't change over an outer loop will
    #     not change over an inner loop either, and on the other hand the
    #     outer loop is likely to use and modify variables that remain
    #     constant over the inner loop.
    #
    #   * if the numbers are the same, fall back to a more arbitrary
    #     measure: loops involving less blocks may be more important
    #     to optimize
    #
    #   * in a cycle, which block is the head of the loop?  Somewhat
    #     arbitrarily we pick the first Bool-switching block that has
    #     two exits.  The "first" means the one closest to the
    #     startblock of the graph.
    #
    startdistance = {}     # {block: distance-from-startblock}
    pending = [graph.startblock]
    edge_list = []
    dist = 0
    while pending:
        newblocks = []
        for block in pending:
            if block not in startdistance:
                startdistance[block] = dist
                for link in block.exits:
                    newblocks.append(link.target)
                    edge = graphlib.Edge(block, link.target)
                    edge.link = link
                    edge_list.append(edge)
        dist += 1
        pending = newblocks

    vertices = startdistance
    edges = graphlib.make_edge_dict(edge_list)
    cycles = graphlib.all_cycles(graph.startblock, vertices, edges)
    loops = []
    variable_families = None

    for cycle in cycles:
        # find the headblock
        candidates = []
        for i in range(len(cycle)):
            block = cycle[i].source
            v = block.exitswitch
            if isinstance(v, Variable) and len(block.exits) == 2:
                if getattr(v, 'concretetype', None) is check_exitswitch_type:
                    dist = startdistance[block]
                    candidates.append((dist, i))
        if not candidates:
            continue
        _, i = min(candidates)
        links = [edge.link for edge in cycle[i:] + cycle[:i]]
        loop = Loop(cycle[i].source, links)

        # count the variables that remain constant across the cycle,
        # detected as having its SSA family present across all blocks.
        if variable_families is None:
            dffb = DataFlowFamilyBuilder(graph)
            variable_families = dffb.get_variable_families()

        num_loop_constants = 0
        for v in loop.headblock.inputargs:
            v = variable_families.find_rep(v)
            for link in loop.links:
                block1 = link.target
                for v1 in block1.inputargs:
                    v1 = variable_families.find_rep(v1)
                    if v1 is v:
                        break    # ok, found in this block
                else:
                    break   # not found in this block, fail
            else:
                # found in all blocks, this variable is a loop constant
                num_loop_constants += 1

        # smaller keys are "better"
        key = (-num_loop_constants,   # maximize num_loop_constants
               len(cycle))            # minimize len(cycle)
        loops.append((key, loop))

    loops.sort()

    # returns 'loops' without overlapping blocks
    result = []
    blocks_seen = {}
    for key, loop in loops:
        for link in loop.links:
            if link.target in blocks_seen:
                break     # overlapping
        else:
            # non-overlapping
            result.append(loop)
            for link in loop.links:
                blocks_seen[link.target] = True
    return result
Esempio n. 4
0
def find_inner_loops(graph, check_exitswitch_type=None):
    """Enumerate what look like the innermost loops of the graph.
    Returns a list of non-overlapping Loop() instances.
    """
    # Heuristic (thanks Stakkars for the idea):
    # to find the "best" cycle to pick,
    #
    #   * look for variables that don't change over the whole cycle
    #
    #   * cycles with _more_ of them are _inside_ cycles with less of them,
    #     because any variable that doesn't change over an outer loop will
    #     not change over an inner loop either, and on the other hand the
    #     outer loop is likely to use and modify variables that remain
    #     constant over the inner loop.
    #
    #   * if the numbers are the same, fall back to a more arbitrary
    #     measure: loops involving less blocks may be more important
    #     to optimize
    #
    #   * in a cycle, which block is the head of the loop?  Somewhat
    #     arbitrarily we pick the first Bool-switching block that has
    #     two exits.  The "first" means the one closest to the
    #     startblock of the graph.
    #
    startdistance = {}  # {block: distance-from-startblock}
    pending = [graph.startblock]
    edge_list = []
    dist = 0
    while pending:
        newblocks = []
        for block in pending:
            if block not in startdistance:
                startdistance[block] = dist
                for link in block.exits:
                    newblocks.append(link.target)
                    edge = graphlib.Edge(block, link.target)
                    edge.link = link
                    edge_list.append(edge)
        dist += 1
        pending = newblocks

    vertices = startdistance
    edges = graphlib.make_edge_dict(edge_list)
    cycles = graphlib.all_cycles(graph.startblock, vertices, edges)
    loops = []
    variable_families = None

    for cycle in cycles:
        # find the headblock
        candidates = []
        for i in range(len(cycle)):
            block = cycle[i].source
            v = block.exitswitch
            if isinstance(v, Variable) and len(block.exits) == 2:
                if getattr(v, 'concretetype', None) is check_exitswitch_type:
                    dist = startdistance[block]
                    candidates.append((dist, i))
        if not candidates:
            continue
        _, i = min(candidates)
        links = [edge.link for edge in cycle[i:] + cycle[:i]]
        loop = Loop(cycle[i].source, links)

        # count the variables that remain constant across the cycle,
        # detected as having its SSA family present across all blocks.
        if variable_families is None:
            dffb = DataFlowFamilyBuilder(graph)
            variable_families = dffb.get_variable_families()

        num_loop_constants = 0
        for v in loop.headblock.inputargs:
            v = variable_families.find_rep(v)
            for link in loop.links:
                block1 = link.target
                for v1 in block1.inputargs:
                    v1 = variable_families.find_rep(v1)
                    if v1 is v:
                        break  # ok, found in this block
                else:
                    break  # not found in this block, fail
            else:
                # found in all blocks, this variable is a loop constant
                num_loop_constants += 1

        # smaller keys are "better"
        key = (
            -num_loop_constants,  # maximize num_loop_constants
            len(cycle))  # minimize len(cycle)
        loops.append((key, loop))

    loops.sort()

    # returns 'loops' without overlapping blocks
    result = []
    blocks_seen = {}
    for key, loop in loops:
        for link in loop.links:
            if link.target in blocks_seen:
                break  # overlapping
        else:
            # non-overlapping
            result.append(loop)
            for link in loop.links:
                blocks_seen[link.target] = True
    return result