def merge_if_blocks(graph, verbose=True): merge = False while merge_if_blocks_once(graph): merge = True if merge: if verbose: log("merging blocks in %s" % (graph.name, )) else: log.dot()
def merge_if_blocks(graph, verbose=True): merge = False while merge_if_blocks_once(graph): merge = True if merge: if verbose: log("merging blocks in %s" % (graph.name, )) else: log.dot()
def remove_simple_mallocs(self, graph): """Iteratively remove (inline) the mallocs that can be simplified away.""" tot = 0 while True: count = self.remove_mallocs_once(graph) if count: if self.verbose: log.malloc('%d simple mallocs removed in %r' % (count, graph.name)) else: log.dot() tot += count else: break return tot
def logresult(self, op, msg, exc=None): # only for nice log outputs if self.verbose: if exc is None: exc = '' else: exc = ': %s' % (exc,) chain = [] while True: chain.append(str(op.result)) if op.opname != 'direct_call': break fobj = op.args[0].value._obj op = self.inline_and_remove[fobj.graph] log.mallocv('%s %s%s' % ('->'.join(chain), msg, exc)) elif exc is None: log.dot()
def logresult(self, op, msg, exc=None): # only for nice log outputs if self.verbose: if exc is None: exc = '' else: exc = ': %s' % (exc, ) chain = [] while True: chain.append(str(op.result)) if op.opname != 'direct_call': break fobj = op.args[0].value._obj op = self.inline_and_remove[fobj.graph] log.mallocv('%s %s%s' % ('->'.join(chain), msg, exc)) elif exc is None: log.dot()
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 always_inline(graph): weight, fixed = 0.0, True else: weight, fixed = heuristic(graph) # Don't let 'weight' be NaN past this point. If we do, # then heapify() might (sometimes, rarely) not do its job # correctly. I suspect it's because the algorithm gets # confused by the fact that both 'a < b' and 'b < a' are # false. A concrete example: [39.0, 0.0, 33.0, nan, nan] # heapifies to [33.0, nan, 39.0, nan, 0.0], but 33.0 is # not the smallest item. if not (weight < 1e9): weight = 1e9 #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] = 'initial' 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 as e: try_again[graph] = str(e) 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 invalid = [(graph, msg) for graph, msg in try_again.items() if always_inline(graph) is True] if invalid: message = '\n'.join([ "%s has _always_inline_=True but inlining failed:\n\t%s" % (graph, msg) for (graph, msg) in invalid]) raise CannotInline(message) for graph in to_cleanup: cleanup_graph(graph) return count
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 always_inline(graph): 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] = 'initial' 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, e: try_again[graph] = str(e) 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
def partial_escape(translator, graph): """ Main function. Blocks, which we'll work on, are in a dequeue, called "worklist", and are indexing link-state tuples in "statemap". """ insert_links(graph) worklist = deque([graph.startblock]) statemap = defaultdict(list) statemap[graph.startblock] = [(None, {})] finished = set() entrymap = mkentrymap(graph) backedges = find_backedges(graph) number_getfield_removed = 0 while worklist: block = worklist.popleft() must_be_materialized = block.is_final_block() for link in entrymap[block]: if link in backedges: must_be_materialized = True state = get_current_state(statemap[block], must_be_materialized=must_be_materialized) if block.is_final_block(): continue new_operations = [] # Going through the operations for op in block.operations: if op.opname == 'malloc': # Create new entry for every allocation that is not returned if can_remove(op): vobj = VirtualObject(op.result.concretetype, op.args) state[op.result] = vobj vobj.aliases.add(op.result) else: new_operations.append(op) elif op.opname == 'cast_pointer': if op.args[0] in state: # Creating something like an 'alias' for the casting state[op.result] = vobj = state[op.args[0]] vobj.aliases.add(op.result) else: new_operations.append(op) elif op.opname == 'setfield': if op.args[0] in state: state[op.args[0]].vars[op.args[1].value, op.args[0].concretetype] = op.args[2] else: materialize_object(op.args[2], state, new_operations) new_operations.append(op) elif op.opname == 'getfield': key = op.args[1].value, op.args[0].concretetype if op.args[0] in state and key in state[op.args[0]].vars: targ = state[op.args[0]].vars[key] number_getfield_removed += 1 if targ in state: state[op.result] = vobj = state[targ] state[targ].aliases.add(vobj) else: new_operations.append(SpaceOperation('same_as', [targ], op.result)) else: materialize_object(op.args[0], state, new_operations) new_operations.append(op) else: for arg in op.args: materialize_object(arg, state, new_operations) new_operations.append(op) # for all backedges, materialize all arguments (loops aren't supported # properly yet) for exit in block.exits: if exit in backedges or exit.target.is_final_block(): for arg in exit.args: materialize_object(arg, state, new_operations) block.operations = new_operations # We're done with the internals of the block. Editing the lists: finished.add(block) for exit in block.exits: # Only adding to the worklist if all its ancestors are processed for lnk in entrymap[exit.target]: if lnk.prevblock not in finished and lnk not in backedges: break else: if exit.target not in finished and exit.target not in worklist: # XXX worklist.append(exit.target) # setting statemaps: statemap[exit.target].append((exit, state)) if number_getfield_removed: if translator.config.translation.verbose: log.cse("partial escape analysis removed %s getfields in graph %s" % (number_getfield_removed, graph)) else: log.dot() # Done. Cleaning up. remove_same_as(graph) transform_dead_op_vars(graph) eliminate_empty_blocks(graph) join_blocks(graph) checkgraph(graph) return number_getfield_removed
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 always_inline(graph): 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] = 'initial' 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, e: try_again[graph] = str(e) 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