def storesink_graph(graph): """ remove superfluous getfields. use a super-local method: all non-join blocks inherit the heap information from their (single) predecessor """ added_some_same_as = False entrymap = mkentrymap(graph) # all merge blocks are starting points todo = [(block, None, None) for (block, prev_blocks) in entrymap.iteritems() if len(prev_blocks) > 1 or block is graph.startblock] visited = 0 while todo: block, cache, inputlink = todo.pop() visited += 1 if cache is None: cache = {} if block.operations: changed_block = _storesink_block(block, cache, inputlink) added_some_same_as = changed_block or added_some_same_as for link in block.exits: if len(entrymap[link.target]) == 1: new_cache = _translate_cache(cache, link) todo.append((link.target, new_cache, link)) assert visited == len(entrymap) if added_some_same_as: removenoops.remove_same_as(graph) simplify.transform_dead_op_vars(graph)
def test_isinstance(): class A: pass class B(A): pass def g(n): if n > 10: return A() else: b = B() b.value = 321 return b def fn(n): x = g(n) assert isinstance(x, B) return x.value t, graph = check(fn, [5], 321) assert summary(graph)['debug_assert'] == 1 from rpython.translator.backendopt.removenoops import remove_debug_assert remove_debug_assert(graph) assert "debug_assert" not in summary(graph) from rpython.translator.simplify import transform_dead_op_vars transform_dead_op_vars(graph, t) assert summary(graph)["direct_call"] == 1
def remove_obvious_noops(): for graph in graphs: removenoops.remove_same_as(graph) simplify.eliminate_empty_blocks(graph) simplify.transform_dead_op_vars(graph, translator) removenoops.remove_duplicate_casts(graph, translator) if config.print_statistics: print "after no-op removal:" print_statistics(translator.graphs[0], translator)
def test_transform_dead_op_vars_bug(): from rpython.rtyper.llinterp import LLInterpreter, LLException exc = ValueError() def f1(): raise exc # this function used to be considered side-effects-free def f2(): f1() # <- so this call was removed graph, t = translate(f2, [], backend_optimize=False) transform_dead_op_vars(graph, t) interp = LLInterpreter(t.rtyper) e = py.test.raises(LLException, 'interp.eval_graph(graph, [])') assert 'ValueError' in str(e.value)
def remove_mallocs(translator, graphs=None): if graphs is None: graphs = translator.graphs tot = 0 for graph in graphs: count = remove_simple_mallocs(graph, verbose=translator.config.translation.verbose) if count: # remove typical leftovers from malloc removal removenoops.remove_same_as(graph) simplify.eliminate_empty_blocks(graph) simplify.transform_dead_op_vars(graph, translator) tot += count log.malloc("removed %d simple mallocs in total" % tot) return tot
def test_simple_melting_away(): def fn(n): assert n >= 1 return n - 1 graph, t = get_graph(fn, [int]) assert summary(graph) == {'int_ge': 1, 'int_sub': 1} remove_asserts(t, [graph]) assert summary(graph) == {'int_ge': 1, 'debug_assert': 1, 'int_sub': 1} check_graph(graph, [1], 0, t) from rpython.translator.backendopt.removenoops import remove_debug_assert remove_debug_assert(graph) assert summary(graph) == {'int_ge': 1, 'int_sub': 1} from rpython.translator.simplify import transform_dead_op_vars transform_dead_op_vars(graph) assert summary(graph) == {'int_sub': 1}
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