def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.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() for caller in translator.graphs: 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) continue if block is not caller.startblock: edges.add((caller.startblock, block)) edges.add((block, callee.startblock)) 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) return len(insert_in)
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.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() for caller in translator.graphs: 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) continue if block is not caller.startblock: edges.add((caller.startblock, block)) edges.add((block, callee.startblock)) 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) return len(insert_in)
def insert_stackcheck(ann): from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles edges = [] graphs_to_patch = {} for callposition, (caller, callee) in ann.translator.callgraph.items(): if getattr(getattr(callee, "func", None), "insert_stack_check_here", False): graphs_to_patch[callee] = True continue edge = Edge(caller, callee) edge.callposition = callposition edges.append(edge) for graph in graphs_to_patch: v = Variable() ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation("simple_call", [Constant(stack_check)], v) graph.startblock.operations.insert(0, unwind_op) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): caller = edge.source _, _, call_tag = edge.callposition if call_tag: caller_block, _ = call_tag else: ann.warning("cycle detected but no information on where to insert " "stack_check()") continue # caller block found, insert stack_check() v = Variable() # push annotation on v ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation("simple_call", [Constant(stack_check)], v) caller_block.operations.insert(0, unwind_op)
def insert_stackcheck(ann): from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles edges = [] graphs_to_patch = {} for callposition, (caller, callee) in ann.translator.callgraph.items(): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): graphs_to_patch[callee] = True continue edge = Edge(caller, callee) edge.callposition = callposition edges.append(edge) for graph in graphs_to_patch: v = Variable() ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v) graph.startblock.operations.insert(0, unwind_op) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): caller = edge.source _, _, call_tag = edge.callposition if call_tag: caller_block, _ = call_tag else: ann.warning("cycle detected but no information on where to insert " "stack_check()") continue # caller block found, insert stack_check() v = Variable() # push annotation on v ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v) caller_block.operations.insert(0, unwind_op)
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rpython.module.ll_stack import ll_stack_check from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles rtyper = translator.rtyper graph = rtyper.annotate_helper(ll_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 = [] graphs_to_patch = {} insert_in = {} for caller in translator.graphs: for block, callee in find_calls_from(translator, caller): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): insert_in[callee.startblock] = True continue edge = Edge(caller, callee) edge.block = block edges.append(edge) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): block = edge.block insert_in[block] = True 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)
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles 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 = [] graphs_to_patch = {} insert_in = {} for caller in translator.graphs: for block, callee in find_calls_from(translator, caller): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): insert_in[callee.startblock] = True continue edge = Edge(caller, callee) edge.block = block edges.append(edge) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): block = edge.block insert_in[block] = True 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) return len(insert_in)
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.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: 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)
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
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