Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: better-storesink Changeset: r87174:6efe86960ad3 Date: 2016-09-16 22:49 +0200 http://bitbucket.org/pypy/pypy/changeset/6efe86960ad3/
Log: don't optimize array fields of virtualizables (otherwise they can end up being passed around too much) diff --git a/rpython/translator/backendopt/cse.py b/rpython/translator/backendopt/cse.py --- a/rpython/translator/backendopt/cse.py +++ b/rpython/translator/backendopt/cse.py @@ -255,11 +255,27 @@ self._clear_heapcache_for(concretetype, fieldname) self.heapcache[target, concretetype, fieldname] = op.args[2] continue + if op.opname == "jit_force_virtualizable": + T = op.args[0].concretetype + FIELD = getattr(T.TO, op.args[1].value) + if hasattr(FIELD, 'TO') and isinstance(FIELD.TO, lltype.GcArray): + # clear the cache for the virtualizable array fields, as + # they run the risk of being passed around too much + self._clear_heapcache_for_effects( + {('struct', T, op.args[1].value)}) + if has_side_effects(op): self._clear_heapcache_for_effects_of_op(op) continue # foldable operations + if op.opname == "cast_pointer": + # cast_pointer is a pretty strange operation! it introduces + # more aliases, that confuse the CSE pass. Therefore we unify + # the two variables in new_unions, to improve the folding. + self.new_unions.union(op.args[0], op.result) + # don't do anything further + continue if not can_fold(op): continue key = (op.opname, op.result.concretetype, @@ -270,11 +286,6 @@ added_same_as += 1 else: self.purecache[key] = op.result - if op.opname == "cast_pointer": - # cast_pointer is a pretty strange operation! it introduces - # more aliases, that confuse the CSE pass. Therefore we unify - # the two variables in new_unions, to improve the folding. - self.new_unions.union(op.args[0], op.result) return added_same_as def _merge(tuples, variable_families, analyzer, loop_blocks, backedges): diff --git a/rpython/translator/backendopt/stat.py b/rpython/translator/backendopt/stat.py --- a/rpython/translator/backendopt/stat.py +++ b/rpython/translator/backendopt/stat.py @@ -1,65 +1,99 @@ from rpython.translator.simplify import get_graph from hashlib import md5 +from collections import defaultdict + +def find_reachable_graphs(graph, translator, ignore_stack_checks=False): + seen_graphs = set() + stack = [graph] + while stack: + graph = stack.pop() + if graph in seen_graphs: + continue + seen_graphs.add(graph) + yield graph + for block, op in graph.iterblockops(): + if op.opname == "direct_call": + called_graph = get_graph(op.args[0], translator) + if called_graph is not None and ignore_stack_checks: + if called_graph.name.startswith('ll_stack_check'): + continue + if called_graph is not None: + stack.append(called_graph) + elif op.opname == "indirect_call": + called_graphs = op.args[-1].value + if called_graphs is not None: + stack.extend(called_graphs) + def get_statistics(graph, translator, save_per_graph_details=None, ignore_stack_checks=False): - seen_graphs = {} - stack = [graph] num_graphs = 0 num_blocks = 0 num_ops = 0 num_mallocs = 0 + num_memory = 0 per_graph = {} - while stack: - graph = stack.pop() - if graph in seen_graphs: - continue - seen_graphs[graph] = True + for graph in find_reachable_graphs(graph, translator, ignore_stack_checks): num_graphs += 1 old_num_blocks = num_blocks old_num_ops = num_ops old_num_mallocs = num_mallocs + old_num_memory = num_memory for block in graph.iterblocks(): num_blocks += 1 for op in block.operations: - if op.opname == "direct_call": - called_graph = get_graph(op.args[0], translator) - if called_graph is not None and ignore_stack_checks: - if called_graph.name.startswith('ll_stack_check'): - continue - if called_graph is not None: - stack.append(called_graph) - elif op.opname == "indirect_call": - called_graphs = op.args[-1].value - if called_graphs is not None: - stack.extend(called_graphs) - elif op.opname.startswith("malloc"): + if op.opname.startswith("malloc"): num_mallocs += 1 + elif op.opname.startswith(("get", "set")): + num_memory += 1 num_ops += 1 - per_graph[graph] = (num_blocks-old_num_blocks, num_ops-old_num_ops, num_mallocs-old_num_mallocs) + per_graph[graph] = (num_blocks-old_num_blocks, num_ops-old_num_ops, num_mallocs-old_num_mallocs, num_memory-old_num_memory) if save_per_graph_details: details = [] - for graph, (nblocks, nops, nmallocs) in per_graph.iteritems(): + for graph, (nblocks, nops, nmallocs, nmemory) in per_graph.iteritems(): try: code = graph.func.func_code.co_code except AttributeError: code = "None" hash = md5(code).hexdigest() - details.append((hash, graph.name, nblocks, nops, nmallocs)) + details.append((hash, graph.name, nblocks, nops, nmallocs, nmemory)) details.sort() f = open(save_per_graph_details, "w") try: - for hash, name, nblocks, nops, nmallocs in details: - print >>f, hash, name, nblocks, nops, nmallocs + for hash, name, nblocks, nops, nmallocs, nmemory in details: + print >>f, hash, name, nblocks, nops, nmallocs, nmemory finally: f.close() - return num_graphs, num_blocks, num_ops, num_mallocs + return num_graphs, num_blocks, num_ops, num_mallocs, num_memory def print_statistics(graph, translator, save_per_graph_details=None, ignore_stack_checks=False): - num_graphs, num_blocks, num_ops, num_mallocs = get_statistics( + num_graphs, num_blocks, num_ops, num_mallocs, num_memory = get_statistics( graph, translator, save_per_graph_details, ignore_stack_checks=ignore_stack_checks) print ("Statistics:\nnumber of graphs %s\n" "number of blocks %s\n" "number of operations %s\n" "number of mallocs %s\n" - ) % (num_graphs, num_blocks, num_ops, num_mallocs) + "number of memory operations %s\n" + ) % (num_graphs, num_blocks, num_ops, num_mallocs, num_memory) + calls = defaultdict(int) + opnames = defaultdict(int) + for graph in find_reachable_graphs(graph, translator): + for block, op in graph.iterblockops(): + opnames[op.opname] += 1 + if op.opname == "direct_call": + called_graph = get_graph(op.args[0], translator) + if called_graph is not None and ignore_stack_checks: + if called_graph.name.startswith('ll_stack_check'): + continue + if called_graph is not None: + calls[called_graph] += 1 + elif op.opname == "indirect_call": + called_graphs = op.args[-1].value + if called_graphs is not None: + for called_graph in called_graphs: + calls[called_graph] += 1 + for num, name in sorted((num, name) for (name, num) in opnames.iteritems()): + print name, num + print + for num, graph in sorted((num, graph) for (graph, num) in calls.iteritems())[-100:]: + print graph.name, num diff --git a/rpython/translator/backendopt/test/test_cse.py b/rpython/translator/backendopt/test/test_cse.py --- a/rpython/translator/backendopt/test/test_cse.py +++ b/rpython/translator/backendopt/test/test_cse.py @@ -465,6 +465,28 @@ return cls.user_overridden_class self.check(f, [int], getfield=0) + def test_dont_fold_virtualizable(self): + class A(object): + _virtualizable_ = ["x[*]", "y"] + + a1 = A() + a1.x = [1, 2, 3] + a1.y = 2 + a2 = A() + a2.x = [65, 4, 3] + a2.y = 8 + def f(i): + if i: + a = a1 + else: + a = a2 + res = a.x[0] + res += a.y + if i == 10: + res += a.x[1] + res += a.y + return res + self.check(f, [int], getfield=3) def fakevar(name='v'): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit