def remove_asserts(translator, graphs): rtyper = translator.rtyper clsdef = translator.annotator.bookkeeper.getuniqueclassdef(AssertionError) r_AssertionError = rclass.getclassrepr(rtyper, clsdef) ll_AssertionError = r_AssertionError.convert_const(AssertionError) total_count = [0, 0] for graph in graphs: count = 0 morework = True while morework: morework = False eliminate_empty_blocks(graph) join_blocks(graph) for link in graph.iterlinks(): if (link.target is graph.exceptblock and isinstance(link.args[0], Constant) and link.args[0].value == ll_AssertionError): if kill_assertion_link(graph, link): count += 1 morework = True break else: total_count[0] += 1 if translator.config.translation.verbose: log.removeassert("cannot remove an assert from %s" % (graph.name,)) if count: # now melt away the (hopefully) dead operation that compute # the condition total_count[1] += count if translator.config.translation.verbose: log.removeassert("removed %d asserts in %s" % (count, graph.name)) checkgraph(graph) #transform_dead_op_vars(graph, translator) log.removeassert("Could not remove %d asserts, but removed %d asserts." % tuple(total_count))
def show_incremental_progress(gv_func): from pypy import conftest graph = _getgraph(gv_func) fixduplicatevars(graph) flowmodel.checkgraph(graph) if conftest.option.view: eliminate_empty_blocks(graph) graph.show()
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 _buildgraph(graph): assert graph.startblock.operations[0].opname == 'debug_assert' del graph.startblock.operations[0] # rgenop makes graphs that use the same variable in several blocks, fixduplicatevars(graph) # fix this now flowmodel.checkgraph(graph) eliminate_empty_blocks(graph) # we cannot call join_blocks(graph) here! It has a subtle problem: # it copies operations between blocks without renaming op.result. # See test_promotion.test_many_promotions for a failure. graph.rgenop = True return graph
def check_auto_inlining(self, func, sig, multiplier=None, call_count_check=False, checkvirtual=False, remove_same_as=False, heuristic=None, const_fold_first=False): t = self.translate(func, sig) if checkvirtual: check_virtual_methods() if const_fold_first: from pypy.translator.backendopt.constfold import constant_fold_graph from pypy.translator.simplify import eliminate_empty_blocks for graph in t.graphs: constant_fold_graph(graph) eliminate_empty_blocks(graph) if option.view: t.view() # inline! sanity_check(t) # also check before inlining (so we don't blame it) threshold = INLINE_THRESHOLD_FOR_TEST if multiplier is not None: threshold *= multiplier call_count_pred = None if call_count_check: call_count_pred = lambda lbl: True instrument_inline_candidates(t.graphs, threshold) if remove_same_as: for graph in t.graphs: removenoops.remove_same_as(graph) if heuristic is not None: kwargs = {"heuristic": heuristic} else: kwargs = {} auto_inlining(t, threshold, call_count_pred=call_count_pred, **kwargs) sanity_check(t) if option.view: t.view() interp = LLInterpreter(t.rtyper) def eval_func(args): return interp.eval_graph(graphof(t, func), args) return eval_func, t
def simplify(self, block_subset=None, extra_passes=None): # Generic simplifications transform.transform_graph(self, block_subset=block_subset, extra_passes=extra_passes) if block_subset is None: graphs = self.translator.graphs else: graphs = {} for block in block_subset: graph = self.annotated.get(block) if graph: graphs[graph] = True for graph in graphs: simplify.eliminate_empty_blocks(graph)
def remove_mallocs(translator, graphs=None, type_system="lltypesystem"): if graphs is None: graphs = translator.graphs tot = 0 for graph in graphs: count = remove_simple_mallocs(graph, type_system=type_system, 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 remove_mallocs(translator, graphs=None, type_system="lltypesystem"): if graphs is None: graphs = translator.graphs tot = 0 for graph in graphs: count = remove_simple_mallocs( graph, type_system=type_system, 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 remove_tail_calls_to_self(translator, graph): entrymap = mkentrymap(graph) changed = False for link in entrymap[graph.returnblock]: block = link.prevblock if (len(block.exits) == 1 and len(block.operations) > 0 and block.operations[-1].opname == 'direct_call' and block.operations[-1].result == link.args[0]): call = get_graph(block.operations[-1].args[0], translator) print "getgraph", graph if graph is graph: _remove_tail_call(translator, graph, block) changed = True if changed: from pypy.translator import simplify checkgraph(graph) simplify.remove_identical_vars(graph) simplify.eliminate_empty_blocks(graph) simplify.join_blocks(graph)
def test_ignore_breaking_transformations(): def f(): pass f._annspecialcase_ = "override:ignore" def g(i): if i == 1: return "ab" else: try: return f() except: return "hello!" t, typer, graph = gengraph(g, [int]) from pypy.translator import simplify from pypy.translator.backendopt import removenoops from pypy.objspace.flow.model import checkgraph removenoops.remove_same_as(graph) simplify.eliminate_empty_blocks(graph) #should not crash: checkgraph(graph)
def remove_asserts(translator, graphs): rtyper = translator.rtyper clsdef = translator.annotator.bookkeeper.getuniqueclassdef(AssertionError) r_AssertionError = rclass.getclassrepr(rtyper, clsdef) ll_AssertionError = r_AssertionError.convert_const(AssertionError) total_count = [0, 0] for graph in graphs: count = 0 morework = True while morework: morework = False eliminate_empty_blocks(graph) join_blocks(graph) for link in graph.iterlinks(): if (link.target is graph.exceptblock and isinstance(link.args[0], Constant) and link.args[0].value == ll_AssertionError): if kill_assertion_link(graph, link): count += 1 morework = True break else: total_count[0] += 1 if translator.config.translation.verbose: log.removeassert( "cannot remove an assert from %s" % (graph.name, )) if count: # now melt away the (hopefully) dead operation that compute # the condition total_count[1] += count if translator.config.translation.verbose: log.removeassert("removed %d asserts in %s" % (count, graph.name)) checkgraph(graph) #transform_dead_op_vars(graph, translator) log.removeassert("Could not remove %d asserts, but removed %d asserts." % tuple(total_count))
def tweak_generator_body_graph(Entry, graph): # First, always run simplify_graph in order to reduce the number of # variables passed around simplify_graph(graph) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable() v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], graph.exceptblock)) # for block in list(graph.iterblocks()): for exit in block.exits: if exit.target is graph.returnblock: exit.args = [] exit.target = stopblock assert block is not stopblock for index in range(len(block.operations) - 1, -1, -1): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, block, index) newblock = newlink.target # class Resume(AbstractPosition): _immutable_ = True block = newblock Resume.__name__ = 'Resume%d' % len(mappings) mappings.append(Resume) varnames = get_variable_names(newlink.args) # _insert_reads(newblock, varnames) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation( 'setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation( 'simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check link1 = Link([block.inputargs[0]], Resume.block) link1.exitcase = True nextblock = Block([Variable('entry')]) link2 = Link([block.inputargs[0]], nextblock) link2.exitcase = False block.closeblock(link1, link2) block = nextblock block.closeblock( Link([ Constant(AssertionError), Constant(AssertionError("bad generator class")) ], graph.exceptblock)) graph.startblock = regular_entry_block graph.signature = Signature(['entry']) graph.defaults = () checkgraph(graph) eliminate_empty_blocks(graph)
def backend_optimizations(translator, graphs=None, secondary=False, **kwds): # sensible keywords are # raisingop2direct_call, inline_threshold, mallocs # merge_if_blocks, constfold, heap2stack # clever_malloc_removal, remove_asserts config = translator.config.translation.backendopt.copy(as_default=True) config.set(**kwds) if graphs is None: graphs = translator.graphs if config.print_statistics: print "before optimizations:" print_statistics(translator.graphs[0], translator, "per-graph.txt") if config.raisingop2direct_call: raisingop2direct_call(translator, graphs) if translator.rtyper.type_system.name == 'ootypesystem': check_virtual_methods() # remove obvious no-ops 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) if config.inline or config.mallocs: heuristic = get_function(config.inline_heuristic) if config.inline: threshold = config.inline_threshold else: threshold = 0 inline_malloc_removal_phase(config, translator, graphs, threshold, inline_heuristic=heuristic) constfold(config, graphs) if config.clever_malloc_removal: threshold = config.clever_malloc_removal_threshold heuristic = get_function(config.clever_malloc_removal_heuristic) log.inlineandremove("phase with threshold factor: %s" % threshold) log.inlineandremove("heuristic: %s.%s" % (heuristic.__module__, heuristic.__name__)) count = mallocprediction.clever_inlining_and_malloc_removal( translator, graphs, threshold = threshold, heuristic=heuristic) log.inlineandremove("removed %d simple mallocs in total" % count) constfold(config, graphs) if config.print_statistics: print "after clever inlining and malloc removal" print_statistics(translator.graphs[0], translator) if config.profile_based_inline and not secondary: threshold = config.profile_based_inline_threshold heuristic = get_function(config.profile_based_inline_heuristic) inline.instrument_inline_candidates(graphs, threshold) counters = translator.driver_instrument_result( config.profile_based_inline) n = len(counters) def call_count_pred(label): if label >= n: return False return counters[label] > 250 # xxx introduce an option for this inline_malloc_removal_phase(config, translator, graphs, threshold, inline_heuristic=heuristic, call_count_pred=call_count_pred) constfold(config, graphs) if config.remove_asserts: remove_asserts(translator, graphs) if config.heap2stack: assert graphs is translator.graphs # XXX for now malloc_to_stack(translator) if config.merge_if_blocks: log.mergeifblocks("starting to merge if blocks") for graph in graphs: merge_if_blocks(graph, translator.config.translation.verbose) if config.print_statistics: print "after if-to-switch:" print_statistics(translator.graphs[0], translator) for graph in graphs: checkgraph(graph)
def tweak_generator_body_graph(Entry, graph): # First, always run simplify_graph in order to reduce the number of # variables passed around simplify_graph(graph) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable(); v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], graph.exceptblock)) # for block in list(graph.iterblocks()): for exit in block.exits: if exit.target is graph.returnblock: exit.args = [] exit.target = stopblock assert block is not stopblock for index in range(len(block.operations)-1, -1, -1): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, block, index) newblock = newlink.target # class Resume(AbstractPosition): _immutable_ = True block = newblock Resume.__name__ = 'Resume%d' % len(mappings) mappings.append(Resume) varnames = get_variable_names(newlink.args) # _insert_reads(newblock, varnames) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation('setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation('simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check link1 = Link([block.inputargs[0]], Resume.block) link1.exitcase = True nextblock = Block([Variable('entry')]) link2 = Link([block.inputargs[0]], nextblock) link2.exitcase = False block.closeblock(link1, link2) block = nextblock block.closeblock(Link([Constant(AssertionError), Constant(AssertionError("bad generator class"))], graph.exceptblock)) graph.startblock = regular_entry_block graph.signature = Signature(['entry']) graph.defaults = () checkgraph(graph) eliminate_empty_blocks(graph)