def create_exception_handling(self, graph, always_exc_clear=False): """After an exception in a direct_call (or indirect_call), that is not caught by an explicit except statement, we need to reraise the exception. So after this direct_call we need to test if an exception had occurred. If so, we return from the current graph with a special value (False/-1/-1.0/null). Because of the added exitswitch we need an additional block. """ if hasattr(graph, 'exceptiontransformed'): assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed) return else: self.raise_analyzer.analyze_direct_call(graph) graph.exceptiontransformed = self.exc_data_ptr self.always_exc_clear = always_exc_clear join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 for block in list(graph.iterblocks()): self.replace_stack_unwind(block) self.replace_fetch_restore_operations(block) need_exc_matching, gen_exc_checks = self.transform_block(graph, block) n_need_exc_matching_blocks += need_exc_matching n_gen_exc_checks += gen_exc_checks self.transform_except_block(graph, graph.exceptblock) cleanup_graph(graph) removenoops.remove_superfluous_keep_alive(graph) return n_need_exc_matching_blocks, n_gen_exc_checks
def create_exception_handling(self, graph): """After an exception in a direct_call (or indirect_call), that is not caught by an explicit except statement, we need to reraise the exception. So after this direct_call we need to test if an exception had occurred. If so, we return from the current graph with a special value (False/-1/-1.0/null). Because of the added exitswitch we need an additional block. """ if hasattr(graph, 'exceptiontransformed'): assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed) return else: self.raise_analyzer.analyze_direct_call(graph) graph.exceptiontransformed = self.exc_data_ptr join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 # entrymap = mkentrymap(graph) if graph.exceptblock in entrymap: for link in entrymap[graph.exceptblock]: self.transform_jump_to_except_block(graph, entrymap, link) # for block in list(graph.iterblocks()): self.replace_fetch_restore_operations(block) need_exc_matching, gen_exc_checks = self.transform_block( graph, block) n_need_exc_matching_blocks += need_exc_matching n_gen_exc_checks += gen_exc_checks cleanup_graph(graph) return n_need_exc_matching_blocks, n_gen_exc_checks
def create_exception_handling(self, graph, always_exc_clear=False): """After an exception in a direct_call (or indirect_call), that is not caught by an explicit except statement, we need to reraise the exception. So after this direct_call we need to test if an exception had occurred. If so, we return from the current graph with a special value (False/-1/-1.0/null). Because of the added exitswitch we need an additional block. """ if hasattr(graph, 'exceptiontransformed'): assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed) return else: self.raise_analyzer.analyze_direct_call(graph) graph.exceptiontransformed = self.exc_data_ptr self.always_exc_clear = always_exc_clear join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 for block in list(graph.iterblocks()): self.replace_stack_unwind(block) self.replace_fetch_restore_operations(block) need_exc_matching, gen_exc_checks = self.transform_block( graph, block) n_need_exc_matching_blocks += need_exc_matching n_gen_exc_checks += gen_exc_checks self.transform_except_block(graph, graph.exceptblock) cleanup_graph(graph) removenoops.remove_superfluous_keep_alive(graph) return n_need_exc_matching_blocks, n_gen_exc_checks
def create_exception_handling(self, graph): """After an exception in a direct_call (or indirect_call), that is not caught by an explicit except statement, we need to reraise the exception. So after this direct_call we need to test if an exception had occurred. If so, we return from the current graph with a special value (False/-1/-1.0/null). Because of the added exitswitch we need an additional block. """ if hasattr(graph, "exceptiontransformed"): assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed) return else: self.raise_analyzer.analyze_direct_call(graph) graph.exceptiontransformed = self.exc_data_ptr join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 # entrymap = mkentrymap(graph) if graph.exceptblock in entrymap: for link in entrymap[graph.exceptblock]: self.transform_jump_to_except_block(graph, entrymap, link) # for block in list(graph.iterblocks()): self.replace_fetch_restore_operations(block) need_exc_matching, gen_exc_checks = self.transform_block(graph, block) n_need_exc_matching_blocks += need_exc_matching n_gen_exc_checks += gen_exc_checks cleanup_graph(graph) return n_need_exc_matching_blocks, n_gen_exc_checks
def inline_helpers(self, graph): if not self.prepared: raise Exception("Need to call prepare_inline_helpers first") if self.inline: raise_analyzer = RaiseAnalyzer(self.translator) to_enum = self.graph_dependencies.get(graph, self.graphs_to_inline) for inline_graph in to_enum: try: inline.inline_function(self.translator, inline_graph, graph, self.lltype_to_classdef, raise_analyzer, cleanup=False) except inline.CannotInline, e: print 'CANNOT INLINE:', e print '\t%s into %s' % (inline_graph, graph) cleanup_graph(graph)
def cleanup(self): """ cleaning up -- makes sense to be done after inlining, because the inliner inserted quite some empty blocks and blocks that can be joined. """ cleanup_graph(self.graph)
def auto_inlining(translator, threshold=None, callgraph=None, call_count_pred=None, heuristic=inlining_heuristic): assert threshold is not None and threshold != 1 to_cleanup = {} from heapq import heappush, heappop, heapreplace, heapify callers = {} # {graph: {graphs-that-call-it}} callees = {} # {graph: {graphs-that-it-calls}} if callgraph is None: callgraph = inlinable_static_callers(translator.graphs) for graph1, graph2 in callgraph: callers.setdefault(graph2, {})[graph1] = True callees.setdefault(graph1, {})[graph2] = True # the -len(callers) change is OK heap = [(0.0, -len(callers[graph]), graph) for graph in callers] valid_weight = {} try_again = {} lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping() raise_analyzer = RaiseAnalyzer(translator) count = 0 while heap: weight, _, graph = heap[0] if not valid_weight.get(graph): if hasattr(graph, 'func') and \ getattr(graph.func, '_always_inline_', None): weight, fixed = 0.0, True else: weight, fixed = heuristic(graph) #print ' + cost %7.2f %50s' % (weight, graph.name) heapreplace(heap, (weight, -len(callers[graph]), graph)) valid_weight[graph] = True if not fixed: try_again[graph] = True continue if weight >= threshold: # finished... unless some graphs not in valid_weight would now # have a weight below the threshold. Re-insert such graphs # at the start of the heap finished = True for i in range(len(heap)): graph = heap[i][2] if not valid_weight.get(graph): heap[i] = (0.0, heap[i][1], graph) finished = False if finished: break else: heapify(heap) continue heappop(heap) if callers[graph]: if translator.config.translation.verbose: log.inlining('%7.2f %50s' % (weight, graph.name)) else: log.dot() for parentgraph in callers[graph]: if parentgraph == graph: continue subcount = 0 try: subcount = inline_function(translator, graph, parentgraph, lltype_to_classdef, raise_analyzer, call_count_pred, cleanup=False) to_cleanup[parentgraph] = True res = bool(subcount) except CannotInline: try_again[graph] = True res = CannotInline if res is True: count += subcount # the parentgraph should now contain all calls that were # done by 'graph' for graph2 in callees.get(graph, {}): callees[parentgraph][graph2] = True callers[graph2][parentgraph] = True if parentgraph in try_again: # the parentgraph was previously uninlinable, but it has # been modified. Maybe now we can inline it into further # parents? del try_again[parentgraph] heappush(heap, (0.0, -len(callers[parentgraph]), parentgraph)) valid_weight[parentgraph] = False for graph in to_cleanup: cleanup_graph(graph) return count