diff --git a/.github/workflows/rpython-unit-tets.yml b/.github/workflows/rpython-unit-tests.yml similarity index 90% rename from .github/workflows/rpython-unit-tets.yml rename to .github/workflows/rpython-unit-tests.yml index 9afa542fe28..e29f9f0258d 100644 --- a/.github/workflows/rpython-unit-tets.yml +++ b/.github/workflows/rpython-unit-tests.yml @@ -178,4 +178,10 @@ jobs: sudo apt-get install -y libgc-dev pypy2 -m pip install --user hypothesis==4.39.3 - name: Test Translator - run: pypy2 pytest.py rpython/translator + run: | + # we don't need to run test_typed.py, because test_backendoptimized.py + # runs the same tests, just with more optimizations + pypy2 pytest.py rpython/translator --ignore=rpython/translator/c/test/test_typed.py --ignore=rpython/translator/c/test/test_newgc.py + # only run the tests for the incminimark GC, the one that we always use + # in practice + pypy2 pytest.py rpython/translator/c/test/test_newgc.py -k TestIncrementalMiniMarkGC diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py index c0d61b7975b..3fcee967dfe 100644 --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -12,7 +12,6 @@ # and just small enough to prevend inlining of some rlist functions. DEFL_PROF_BASED_INLINE_THRESHOLD = 32.4 -DEFL_CLEVER_MALLOC_REMOVAL_INLINE_THRESHOLD = 32.4 DEFL_LOW_INLINE_THRESHOLD = DEFL_INLINE_THRESHOLD / 2.0 DEFL_GC = "incminimark" # XXX @@ -242,21 +241,6 @@ "for profile based inlining", default="rpython.translator.backendopt.inline.inlining_heuristic", ), # cmdline="--prof-based-inline-heuristic" fix me - # control clever malloc removal - BoolOption("clever_malloc_removal", - "Drives inlining to remove mallocs in a clever way", - default=False, - cmdline="--clever-malloc-removal"), - FloatOption("clever_malloc_removal_threshold", - "Threshold when to inline functions in " - "clever malloc removal", - default=DEFL_CLEVER_MALLOC_REMOVAL_INLINE_THRESHOLD, - cmdline="--clever-malloc-removal-threshold"), - StrOption("clever_malloc_removal_heuristic", - "Dotted name of an heuristic function " - "for inlining in clever malloc removal", - default="rpython.translator.backendopt.inline.inlining_heuristic", - cmdline="--clever-malloc-removal-heuristic"), BoolOption("remove_asserts", "Remove operations that look like 'raise AssertionError', " @@ -267,10 +251,6 @@ "without relying on the C compiler", default=False), - BoolOption("stack_optimization", - "Tranform graphs in SSI form into graphs tailored for " - "stack based virtual machines (only for backends that support it)", - default=True), BoolOption("storesink", "Perform store sinking", default=True), BoolOption("replace_we_are_jitted", "Replace we_are_jitted() calls by False", diff --git a/rpython/memory/gctransform/test/test_framework.py b/rpython/memory/gctransform/test/test_framework.py index 506d84d19f0..666d7fdf486 100644 --- a/rpython/memory/gctransform/test/test_framework.py +++ b/rpython/memory/gctransform/test/test_framework.py @@ -398,7 +398,7 @@ def f(): return len(l) t = rtype(f, []) - backend_optimizations(t, clever_malloc_removal=False, storesink=True) + backend_optimizations(t, storesink=True) etrafo = ExceptionTransformer(t) graph = etrafo.transform_completely() collect_analyzer = CollectAnalyzer(t) diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py index 5871d3a321c..72736a802a8 100644 --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -1882,6 +1882,10 @@ def _check_range(self, n): # checks that it's ok to make an array of size 'n', and returns # range(n). Explicitly overridden by some tests. try: + # first check it's a reasonable amount of memory + # because range lists don't take much space on pypy + if n > 2 ** 30: + raise MemoryError("8 GiB should be enough for tests") return range(n) except OverflowError: raise MemoryError("definitely too many items") diff --git a/rpython/translator/backendopt/all.py b/rpython/translator/backendopt/all.py index 3286baca135..af0a36a171f 100644 --- a/rpython/translator/backendopt/all.py +++ b/rpython/translator/backendopt/all.py @@ -6,7 +6,6 @@ from rpython.translator.backendopt.stat import print_statistics from rpython.translator.backendopt.merge_if_blocks import merge_if_blocks from rpython.translator import simplify -from rpython.translator.backendopt import mallocprediction from rpython.translator.backendopt.removeassert import remove_asserts from rpython.translator.backendopt.support import log from rpython.translator.backendopt.storesink import storesink_graph @@ -36,7 +35,7 @@ def backend_optimizations(translator, graphs=None, secondary=False, # sensible keywords are # inline_threshold, mallocs # merge_if_blocks, constfold, heap2stack - # clever_malloc_removal, remove_asserts + # remove_asserts # replace_we_are_jitted config = translator.config.translation.backendopt.copy(as_default=True) @@ -90,22 +89,6 @@ def remove_obvious_noops(): inline_graph_from_anywhere=inline_graph_from_anywhere) 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.storesink: for graph in graphs: storesink_graph(graph) diff --git a/rpython/translator/backendopt/escape.py b/rpython/translator/backendopt/escape.py deleted file mode 100644 index 6470f0e16a6..00000000000 --- a/rpython/translator/backendopt/escape.py +++ /dev/null @@ -1,356 +0,0 @@ -from rpython.flowspace.model import Variable -from rpython.rtyper.lltypesystem import lltype -from rpython.translator.simplify import get_graph -from rpython.tool.uid import uid - - -class CreationPoint(object): - def __init__(self, creation_method, TYPE, op=None): - self.escapes = False - self.returns = False - self.creation_method = creation_method - if creation_method == "constant": - self.escapes = True - self.TYPE = TYPE - self.op = op - - def __repr__(self): - return ("CreationPoint(<0x%x>, %r, %s, esc=%s)" % - (uid(self), self.TYPE, self.creation_method, self.escapes)) - -class VarState(object): - def __init__(self, *creps): - self.creation_points = set() - for crep in creps: - self.creation_points.add(crep) - - def contains(self, other): - return other.creation_points.issubset(self.creation_points) - - def merge(self, other): - creation_points = self.creation_points.union(other.creation_points) - return VarState(*creation_points) - - def setescapes(self): - changed = [] - for crep in self.creation_points: - if not crep.escapes: - changed.append(crep) - crep.escapes = True - return changed - - def setreturns(self): - changed = [] - for crep in self.creation_points: - if not crep.returns: - changed.append(crep) - crep.returns = True - return changed - - def does_escape(self): - for crep in self.creation_points: - if crep.escapes: - return True - return False - - def does_return(self): - for crep in self.creation_points: - if crep.returns: - return True - return False - - def __repr__(self): - return "" % (self.creation_points, ) - -class AbstractDataFlowInterpreter(object): - def __init__(self, translation_context): - self.translation_context = translation_context - self.scheduled = {} # block: graph containing it - self.varstates = {} # var-or-const: state - self.creationpoints = {} # var: creationpoint - self.constant_cps = {} # const: creationpoint - self.dependencies = {} # creationpoint: {block: graph containing it} - self.functionargs = {} # graph: list of state of args - self.flown_blocks = {} # block: True - - def seen_graphs(self): - return self.functionargs.keys() - - def getstate(self, var_or_const): - if not isonheap(var_or_const): - return None - if var_or_const in self.varstates: - return self.varstates[var_or_const] - if isinstance(var_or_const, Variable): - varstate = VarState() - else: - if var_or_const not in self.constant_cps: - crep = CreationPoint("constant", var_or_const.concretetype) - self.constant_cps[var_or_const] = crep - else: - crep = self.constant_cps[var_or_const] - varstate = VarState(crep) - self.varstates[var_or_const] = varstate - return varstate - - def getstates(self, varorconstlist): - return [self.getstate(var) for var in varorconstlist] - - def setstate(self, var, state): - self.varstates[var] = state - - def get_creationpoint(self, var, method="?", op=None): - if var in self.creationpoints: - return self.creationpoints[var] - crep = CreationPoint(method, var.concretetype, op) - self.creationpoints[var] = crep - return crep - - def schedule_function(self, graph): - startblock = graph.startblock - if graph in self.functionargs: - args = self.functionargs[graph] - else: - args = [] - for var in startblock.inputargs: - if not isonheap(var): - varstate = None - else: - crep = self.get_creationpoint(var, "arg") - varstate = VarState(crep) - self.setstate(var, varstate) - args.append(varstate) - self.scheduled[startblock] = graph - self.functionargs[graph] = args - resultstate = self.getstate(graph.returnblock.inputargs[0]) - return resultstate, args - - def flow_block(self, block, graph): - self.flown_blocks[block] = True - if block is graph.returnblock: - if isonheap(block.inputargs[0]): - self.returns(self.getstate(block.inputargs[0])) - return - if block is graph.exceptblock: - if isonheap(block.inputargs[0]): - self.escapes(self.getstate(block.inputargs[0])) - if isonheap(block.inputargs[1]): - self.escapes(self.getstate(block.inputargs[1])) - return - self.curr_block = block - self.curr_graph = graph - - for op in block.operations: - self.flow_operation(op) - for exit in block.exits: - args = self.getstates(exit.args) - targetargs = self.getstates(exit.target.inputargs) - # flow every block at least once - if (multicontains(targetargs, args) and - exit.target in self.flown_blocks): - continue - for prevstate, origstate, var in zip(args, targetargs, - exit.target.inputargs): - if not isonheap(var): - continue - newstate = prevstate.merge(origstate) - self.setstate(var, newstate) - self.scheduled[exit.target] = graph - - def flow_operation(self, op): - args = self.getstates(op.args) - opimpl = getattr(self, 'op_' + op.opname, None) - if opimpl is not None: - res = opimpl(op, *args) - if res is not NotImplemented: - self.setstate(op.result, res) - return - - if isonheap(op.result) or filter(None, args): - for arg in args: - if arg is not None: - self.escapes(arg) - - def complete(self): - while self.scheduled: - block, graph = self.scheduled.popitem() - self.flow_block(block, graph) - - def escapes(self, arg): - changed = arg.setescapes() - self.handle_changed(changed) - - def returns(self, arg): - changed = arg.setreturns() - self.handle_changed(changed) - - def handle_changed(self, changed): - for crep in changed: - if crep not in self.dependencies: - continue - self.scheduled.update(self.dependencies[crep]) - - def register_block_dependency(self, state, block=None, graph=None): - if block is None: - block = self.curr_block - graph = self.curr_graph - for crep in state.creation_points: - self.dependencies.setdefault(crep, {})[block] = graph - - def register_state_dependency(self, state1, state2): - "state1 depends on state2: if state2 does escape/change, so does state1" - # change state1 according to how state2 is now - if state2.does_escape(): - self.escapes(state1) - if state2.does_return(): - self.returns(state1) - # register a dependency of the current block on state2: - # that means that if state2 changes the current block will be reflown - # triggering this function again and thus updating state1 - self.register_block_dependency(state2) - - # _____________________________________________________________________ - # operation implementations - - def op_malloc(self, op, typestate, flagsstate): - assert flagsstate is None - flags = op.args[1].value - if flags != {'flavor': 'gc'}: - return NotImplemented - return VarState(self.get_creationpoint(op.result, "malloc", op)) - - def op_malloc_varsize(self, op, typestate, flagsstate, lengthstate): - assert flagsstate is None - flags = op.args[1].value - if flags != {'flavor': 'gc'}: - return NotImplemented - return VarState(self.get_creationpoint(op.result, "malloc_varsize", op)) - - def op_cast_pointer(self, op, state): - return state - - def op_setfield(self, op, objstate, fieldname, valuestate): - if valuestate is not None: - # be pessimistic for now: - # everything that gets stored into a structure escapes - self.escapes(valuestate) - return None - - def op_setarrayitem(self, op, objstate, indexstate, valuestate): - if valuestate is not None: - # everything that gets stored into a structure escapes - self.escapes(valuestate) - return None - - def op_getarrayitem(self, op, objstate, indexstate): - if isonheap(op.result): - return VarState(self.get_creationpoint(op.result, "getarrayitem", op)) - - def op_getfield(self, op, objstate, fieldname): - if isonheap(op.result): - # assume that getfield creates a new value - return VarState(self.get_creationpoint(op.result, "getfield", op)) - - def op_getarraysize(self, op, arraystate): - pass - - def op_direct_call(self, op, function, *args): - graph = get_graph(op.args[0], self.translation_context) - if graph is None: - for arg in args: - if arg is None: - continue - # an external function can escape every parameter: - self.escapes(arg) - funcargs = [None] * len(args) - else: - result, funcargs = self.schedule_function(graph) - assert len(args) == len(funcargs) - for localarg, funcarg in zip(args, funcargs): - if localarg is None: - assert funcarg is None - continue - if funcarg is not None: - self.register_state_dependency(localarg, funcarg) - if isonheap(op.result): - # assume that a call creates a new value - return VarState(self.get_creationpoint(op.result, "direct_call", op)) - - def op_indirect_call(self, op, function, *args): - graphs = op.args[-1].value - args = args[:-1] - if graphs is None: - for localarg in args: - if localarg is None: - continue - self.escapes(localarg) - else: - for graph in graphs: - result, funcargs = self.schedule_function(graph) - assert len(args) == len(funcargs) - for localarg, funcarg in zip(args, funcargs): - if localarg is None: - assert funcarg is None - continue - self.register_state_dependency(localarg, funcarg) - if isonheap(op.result): - # assume that a call creates a new value - return VarState(self.get_creationpoint(op.result, "indirect_call", op)) - - def op_ptr_iszero(self, op, ptrstate): - return None - - op_cast_ptr_to_int = op_keepalive = op_ptr_nonzero = op_ptr_iszero - - def op_ptr_eq(self, op, ptr1state, ptr2state): - return None - - op_ptr_ne = op_ptr_eq - - def op_same_as(self, op, objstate): - return objstate - -def isonheap(var_or_const): - return isinstance(var_or_const.concretetype, lltype.Ptr) - -def multicontains(l1, l2): - assert len(l1) == len(l2) - for a, b in zip(l1, l2): - if a is None: - assert b is None - elif not a.contains(b): - return False - return True - - -def is_malloc_like(adi, graph, seen): - if graph in seen: - return seen[graph] - return_state = adi.getstate(graph.getreturnvar()) - if return_state is None or len(return_state.creation_points) != 1: - seen[graph] = False - return False - crep, = return_state.creation_points - if crep.escapes: - seen[graph] = False - return False - if crep.creation_method in ["malloc", "malloc_varsize"]: - assert crep.returns - seen[graph] = True - return True - if crep.creation_method == "direct_call": - subgraph = get_graph(crep.op.args[0], adi.translation_context) - if subgraph is None: - seen[graph] = False - return False - res = is_malloc_like(adi, subgraph, seen) - seen[graph] = res - return res - seen[graph] = False - return False - - -def malloc_like_graphs(adi): - seen = {} - return [graph for graph in adi.seen_graphs() - if is_malloc_like(adi, graph, seen)] diff --git a/rpython/translator/backendopt/mallocprediction.py b/rpython/translator/backendopt/mallocprediction.py deleted file mode 100644 index 3e785758dba..00000000000 --- a/rpython/translator/backendopt/mallocprediction.py +++ /dev/null @@ -1,181 +0,0 @@ -from rpython.translator.backendopt.escape import AbstractDataFlowInterpreter -from rpython.translator.backendopt.escape import malloc_like_graphs -from rpython.translator.backendopt.all import remove_mallocs -from rpython.translator.backendopt import inline -from rpython.rtyper.lltypesystem import lltype -from rpython.translator.simplify import get_graph -from rpython.translator.backendopt import removenoops -from rpython.translator.backendopt.support import log - -SMALL_THRESHOLD = 15 -BIG_THRESHOLD = 50 - -def find_malloc_creps(graph, adi, translator, malloc_graphs): - # mapping from malloc creation point to graphs that it flows into - malloc_creps = {} - # find all mallocs that don't escape - for block, op in graph.iterblockops(): - if op.opname == 'malloc': - STRUCT = op.args[0].value - # must not remove mallocs of structures that have a RTTI with a destructor - flags = op.args[1].value - if flags != {'flavor': 'gc'}: - continue - try: - destr_ptr = lltype.getRuntimeTypeInfo( - STRUCT)._obj.destructor_funcptr - if destr_ptr: - continue - except (ValueError, AttributeError): - pass - varstate = adi.getstate(op.result) - assert len(varstate.creation_points) == 1 - crep, = varstate.creation_points - if not crep.escapes and not crep.returns: - malloc_creps[crep] = {} - if op.opname == 'direct_call': - called_graph = get_graph(op.args[0], translator) - if called_graph not in malloc_graphs: - continue - varstate = adi.getstate(op.result) - assert len(varstate.creation_points) == 1 - crep, = varstate.creation_points - if not crep.escapes and not crep.returns: - malloc_creps[crep] = {} - return malloc_creps - -def find_calls_where_creps_go(interesting_creps, graph, adi, - translator, seen): - #print "find_calls_where_creps_go", interesting_creps, graph.name - #print seen - # drop creps that are merged with another creation point - for block in graph.iterblocks(): - for var in block.getvariables(): - varstate = adi.getstate(var) - if varstate is None: - continue - for crep in varstate.creation_points: - if crep in interesting_creps: - if len(varstate.creation_points) != 1: - del interesting_creps[crep] - break - - # drop creps that are passed into an indirect_call - for block, op in graph.iterblockops(): - if not interesting_creps: - return - if op.opname == "indirect_call": - for var in op.args[:-1]: - varstate = adi.getstate(var) - if varstate is None: - continue - for crep in varstate.creation_points: - if crep in interesting_creps: - del interesting_creps[crep] - elif op.opname == "direct_call": - #print op, interesting_creps - called_graph = get_graph(op.args[0], translator) - interesting = {} - if called_graph is None: - graphvars = [None] * len(op.args) - else: - graphvars = called_graph.getargs() + [called_graph.getreturnvar()] - for var, graphvar in zip(op.args[1:] + [op.result], graphvars): - varstate = adi.getstate(var) - if varstate is None: - #print "no varstate" - continue - if len(varstate.creation_points) == 1: - crep, = varstate.creation_points - if crep not in interesting_creps: - #print "not interesting" - continue - if called_graph is None: - del interesting_creps[crep] - #print "graph not found" - continue - if called_graph in seen: - seen[called_graph][graph] = True - #print "seen already" - else: - #print "taking", crep - seen[called_graph] = {graph: True} - argstate = adi.getstate(graphvar) - argcrep, = argstate.creation_points - interesting[argcrep] = True - #print interesting - if interesting: - find_calls_where_creps_go(interesting, called_graph, - adi, translator, seen) - return interesting_creps - -def find_malloc_removal_candidates(t, graphs): - adi = AbstractDataFlowInterpreter(t) - for graph in graphs: - if graph.startblock not in adi.flown_blocks: - adi.schedule_function(graph) - adi.complete() - malloc_graphs = malloc_like_graphs(adi) - targetset = dict.fromkeys(graphs) - caller_candidates = {} - seen = {} - for graph in adi.seen_graphs(): - creps = find_malloc_creps(graph, adi, t, malloc_graphs) - if creps: - find_calls_where_creps_go(creps, graph, adi, t, seen) - if creps: - if graph in targetset: - caller_candidates[graph] = True - callgraph = [] - for called_graph, callers in seen.iteritems(): - for caller in callers: - if caller in targetset and called_graph in targetset: - callgraph.append((caller, called_graph)) - else: - log.inlineandremove.WARNING("would like to inline into" - " out of target set: %r" - % caller) - return callgraph, caller_candidates - -def inline_and_remove(t, graphs, threshold=BIG_THRESHOLD, - heuristic=inline.inlining_heuristic): - callgraph, caller_candidates = find_malloc_removal_candidates(t, graphs) - log.inlineandremove("found %s malloc removal candidates" % - len(caller_candidates)) - if callgraph: - count = inline.auto_inlining(t, callgraph=callgraph, - threshold=threshold, - heuristic=heuristic) - if not count: - return False - log.inlineandremove('inlined %d callsites.'% (count,)) - count = remove_mallocs(t, caller_candidates.keys()) - return count - else: - return False - -def preparation(translator, graphs, threshold=SMALL_THRESHOLD, - heuristic=inline.inlining_heuristic): - count = 0 - inline.auto_inline_graphs(translator, graphs, threshold, - heuristic=heuristic) - count += remove_mallocs(translator, graphs) - log.inlineandremove("preparation removed %s mallocs in total" % count) - return count - -def clever_inlining_and_malloc_removal(translator, graphs=None, - threshold=BIG_THRESHOLD, - heuristic=inline.inlining_heuristic): - if graphs is None: - graphs = translator.graphs - count = 0 - while 1: - newcount = inline_and_remove(translator, graphs, threshold=threshold, - heuristic=heuristic) - if not newcount: - break - count += newcount - for graph in graphs: - removenoops.remove_duplicate_casts(graph, translator) - return count - diff --git a/rpython/translator/backendopt/tailrecursion.py b/rpython/translator/backendopt/tailrecursion.py deleted file mode 100644 index 94f7b3a4144..00000000000 --- a/rpython/translator/backendopt/tailrecursion.py +++ /dev/null @@ -1,33 +0,0 @@ -from rpython.flowspace.model import mkentrymap, checkgraph - -# this transformation is very academical -- I had too much time - -def _remove_tail_call(translator, graph, block): - print "removing tail call" - assert len(block.exits) == 1 - assert block.exits[0].target is graph.returnblock - assert block.operations[-1].result == block.exits[0].args[0] - op = block.operations[-1] - block.operations = block.operations[:-1] - block.exits[0].args = op.args[1:] - block.exits[0].target = graph.startblock - -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]): - print "getgraph", graph - if graph is graph: - _remove_tail_call(translator, graph, block) - changed = True - if changed: - from rpython.translator import simplify - checkgraph(graph) - simplify.remove_identical_vars(graph) - simplify.eliminate_empty_blocks(graph) - simplify.join_blocks(graph) diff --git a/rpython/translator/backendopt/test/test_escape.py b/rpython/translator/backendopt/test/test_escape.py deleted file mode 100644 index aa70983b38e..00000000000 --- a/rpython/translator/backendopt/test/test_escape.py +++ /dev/null @@ -1,370 +0,0 @@ -from rpython.translator.interactive import Translation -from rpython.translator.translator import graphof -from rpython.translator.backendopt.escape import AbstractDataFlowInterpreter -from rpython.translator.backendopt.escape import malloc_like_graphs -from rpython.rlib.objectmodel import instantiate -from rpython.conftest import option - -def build_adi(function, types): - t = Translation(function, types) - t.rtype() - if option.view: - t.view() - adi = AbstractDataFlowInterpreter(t.context) - graph = graphof(t.context, function) - adi.schedule_function(graph) - adi.complete() - return t.context, adi, graph - -def test_simple(): - class A(object): - pass - def f(): - a = A() - a.x = 1 - return a.x - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - crep, = state.creation_points - assert not crep.escapes - -def test_branch(): - class T: - pass - def fn2(x, y): - t = T() - t.x = x - t.y = y - if x > 0: - return t.x + t.y - else: - return t.x - t.y - t, adi, graph = build_adi(fn2, [int, int]) - tvar = graph.startblock.operations[0].result - state = adi.getstate(tvar) - crep, = state.creation_points - assert not crep.escapes - -def test_loop(): - class A(object): - pass - def f(): - a = A() - i = 0 - while i < 3: - a.x = i - a = A() - i += 1 - return a.x - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - crep, = state.creation_points - assert not crep.escapes - avarinloop = graph.startblock.exits[0].target.inputargs[1] - state1 = adi.getstate(avarinloop) - assert crep in state1.creation_points - assert len(state1.creation_points) == 2 - -def test_global(): - class A(object): - pass - globala = A() - def f(): - a = A() - a.next = None - globala.next = a - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - assert len(state.creation_points) == 1 - crep, = state.creation_points - assert crep.escapes - -def test_classattrs(): - class A: - attr = 666 - class B(A): - attr = 42 - def fn5(): - b = B() - return b.attr - t, adi, graph = build_adi(fn5, []) - bvar = graph.startblock.operations[0].result - state = adi.getstate(bvar) - crep, = state.creation_points - assert not crep.escapes - -def test_aliasing(): - class A: - pass - def fn6(n): - a1 = A() - a1.x = 5 - a2 = A() - a2.x = 6 - if n > 0: - a = a1 - else: - a = a2 - a.x = 12 - return a1.x - t, adi, graph = build_adi(fn6, [int]) - op = graph.startblock.exits[0].target.operations[0] - assert op.opname == 'setfield' - avar = op.args[0] - state = adi.getstate(avar) - assert len(state.creation_points) == 2 - for crep in state.creation_points: - assert not crep.escapes - -def test_call(): - class A(object): - pass - def g(b): - return b.i + 2 - def f(): - a = A() - a.i = 2 - return g(a) - t, adi, graph = build_adi(f, []) - g_graph = graphof(t, g) - bvar = g_graph.startblock.inputargs[0] - bstate = adi.getstate(bvar) - bcrep, = bstate.creation_points - assert not bcrep.escapes - avar = graph.startblock.operations[0].result - astate = adi.getstate(avar) - acrep, = astate.creation_points - assert not acrep.escapes - -def test_dependencies(): - class A(object): - pass - globala = A() - globala.l = [1] - def g(a): - a.append(1) - globala.l = a - def f(): - a = [0] - a.append(1) - # g(a) - return globala.l[0] - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - astate = adi.getstate(avar) - appendgraph = graph.startblock.operations[3].args[0].value._obj.graph - appendarg0 = appendgraph.startblock.inputargs[0] - appendstate = adi.getstate(appendarg0) - resizegraph = [op for op in appendgraph.startblock.operations if op.opname != "same_as"][2].args[0].value._obj.graph - resizearg0 = resizegraph.startblock.inputargs[0] - resizestate = adi.getstate(resizearg0) - reallygraph = resizegraph.startblock.exits[0].target.operations[0].args[0].value._obj.graph - reallyarg0 = reallygraph.startblock.inputargs[0] - reallystate = adi.getstate(reallyarg0) - -def test_substruct(): - class A(object): - pass - class B(object): - pass - def g(a, b): - a.b = b - a.b.x = 1 - return a.b - def f(): - a0 = A() - b0 = B() - return g(a0, b0).x - t, adi, graph = build_adi(f, []) - g_graph = graphof(t, g) - a0var = graph.startblock.operations[0].result - b0var = graph.startblock.operations[3].result - a0state = adi.getstate(a0var) - b0state = adi.getstate(b0var) - a0crep, = a0state.creation_points - assert not a0crep.escapes - b0crep, = b0state.creation_points - assert b0crep.escapes - -def test_multiple_calls(): - class A(object): - pass - def h(a, b): - a.x = 1 - return b - def g(a, b): - return h(b, a) - def f(): - a1 = A() - a2 = A() - a3 = A() - a4 = h(a1, a2) - a5 = g(a3, a4) - t, adi, graph = build_adi(f, []) - a1var = graph.startblock.operations[0].result - a2var = graph.startblock.operations[3].result - a3var = graph.startblock.operations[6].result - a1state = adi.getstate(a1var) - a2state = adi.getstate(a2var) - a3state = adi.getstate(a3var) - a1crep, = a1state.creation_points - a2crep, = a2state.creation_points - a3crep, = a3state.creation_points - assert not a1crep.escapes and not a2crep.escapes and not a3crep.escapes - assert not a1crep.returns and a2crep.returns and a3crep.returns - -def test_indirect_call(): - class A(object): - pass - def f1(a): - return a.x - def f2(a): - return a.x + 1 - def g1(a): - return a - def g2(a): - return None - def f(i): - a1 = A() - a2 = A() - a1.x = 1 - a2.x = 2 - if i: - f = f1 - g = g1 - else: - f = f2 - g = g2 - x = f(a1) - a0 = g(a2) - if a0 is not None: - return x - else: - return 42 - t, adi, graph = build_adi(f, [int]) - a1var = graph.startblock.operations[0].result - a2var = graph.startblock.operations[3].result - a1state = adi.getstate(a1var) - a2state = adi.getstate(a2var) - a1crep, = a1state.creation_points - a2crep, = a2state.creation_points - assert not a1crep.escapes and a2crep.returns - -def test_indirect_call_unknown_graphs(): - class A: - pass - class B: - pass - def f(i): - if i: - klass = A - else: - klass = B - a = instantiate(klass) - # does not crash - t, adi, graph = build_adi(f, [int]) - -def test_getarray(): - class A(object): - pass - def f(): - a = A() - l = [None] - l[0] = a - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - crep, = state.creation_points - assert crep.escapes - -def test_flow_blocksonce(): - class A(object): - pass - def f(): - a = 0 - for i in range(10): - a += i - b = A() - b.x = 1 - return b.x + 2 - t, adi, graph = build_adi(f, []) - avar = graph.startblock.exits[0].target.exits[1].target.operations[0].result - state = adi.getstate(avar) - assert not state.does_escape() - -def test_call_external(): - import time - def f(): - return time.time() - #does not crash - t, adi, graph = build_adi(f, []) - -def test_getsubstruct(): - def f(i): - s = "hello" - return s[i] - # does not crash - t, adi, graph = build_adi(f, [int]) - -def test_getarraysubstruct(): - def createdict(i, j): - d = {2 : 23, - 3 : 21} - return d[i] + d[j] - # does not crash, for now - t, adi, graph = build_adi(createdict, [int, int]) - dvar = graph.startblock.operations[0].result - dstate = adi.getstate(dvar) - assert not dstate.does_escape() - -def test_raise_escapes(): - def f(): - a = ValueError() - raise a - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - assert state.does_escape() - -def test_return(): - class A(object): - pass - def f(): - a = A() - return a - t, adi, graph = build_adi(f, []) - avar = graph.startblock.operations[0].result - state = adi.getstate(avar) - assert not state.does_escape() - assert state.does_return() - - -def test_big(): - from rpython.translator.goal.targetrpystonex import make_target_definition - entrypoint, _, _ = make_target_definition(10) - # does not crash - t, adi, graph = build_adi(entrypoint, [int]) - -def test_find_malloc_like_graphs(): - class A(object): - pass - def f(x): - a = A() - a.x = x - return a - - def g(a): - return a - - def h(x): - return f(x + 1) - def main(x): - return f(x).x + g(h(x)).x - - t, adi, graph = build_adi(main, [int]) - graphs = malloc_like_graphs(adi) - assert set([g.name for g in graphs]) == set(["f", "h"]) - diff --git a/rpython/translator/backendopt/test/test_mallocprediction.py b/rpython/translator/backendopt/test/test_mallocprediction.py deleted file mode 100644 index cfb681df419..00000000000 --- a/rpython/translator/backendopt/test/test_mallocprediction.py +++ /dev/null @@ -1,195 +0,0 @@ -import py -from rpython.translator.translator import TranslationContext -from rpython.translator.backendopt import inline -from rpython.translator.backendopt.all import backend_optimizations -from rpython.translator.translator import TranslationContext, graphof -from rpython.rtyper.llinterp import LLInterpreter -from rpython.flowspace.model import checkgraph, Block -from rpython.conftest import option -import sys - -from rpython.translator.backendopt.mallocprediction import * - -def rtype(fn, signature): - t = TranslationContext() - t.buildannotator().build_types(fn, signature) - t.buildrtyper().specialize() - graph = graphof(t, fn) - if option.view: - t.view() - return t, graph - - -def check_inlining(t, graph, args, result): - callgraph, caller_candidates = find_malloc_removal_candidates(t, t.graphs) - nice_callgraph = {} - for caller, callee in callgraph: - nice_callgraph.setdefault(caller, {})[callee] = True - inline_and_remove(t, t.graphs) - if option.view: - t.view() - interp = LLInterpreter(t.rtyper) - res = interp.eval_graph(graph, args) - assert res == result - return nice_callgraph, caller_candidates - -def test_fn(): - class A: - pass - class B(A): - pass - def g(a, b, i): - a.b = b - b.i = i - return a.b.i - def h(x): - return x + 42 - def fn(i): - a = A() - b = B() - x = h(i) - return g(a, b, x) - t, graph = rtype(fn, [int]) - callgraph, caller_candidates = check_inlining(t, graph, [0], 42) - assert caller_candidates == {graph: True} - assert len(callgraph) == 1 - ggraph = graphof(t, g) - assert callgraph == {graph: {ggraph: True}} - -def test_multiple_calls(): - class A: - pass - class B(A): - pass - def g2(b, i): - b.i = h(i) - def g1(a, b, i): - a.b = b - g2(b, h(i)) - return a.b.i - def h(x): - return x + 42 - def fn(i): - a = A() - b = B() - x = h(i) - return g1(a, b, x) - t, graph = rtype(fn, [int]) - callgraph, caller_candidates = check_inlining(t, graph, [0], 3 * 42) - print callgraph - assert caller_candidates == {graph: True} - assert len(callgraph) == 1 - g1graph = graphof(t, g1) - g2graph = graphof(t, g2) - assert callgraph == {graph: {g1graph: True}} - callgraph, caller_candidates = check_inlining(t, graph, [0], 3 * 42) - assert callgraph == {graph: {g2graph: True}} - -def test_malloc_returns(): - class A: - pass - def g(a): - return a.x - def h(x): - return x + 42 - def make_a(x): - a = A() - a.x = x - return a - def fn(i): - a = make_a(h(i)) - return g(a) - t, graph = rtype(fn, [int]) - callgraph, caller_candidates = check_inlining(t, graph, [0], 42) - assert caller_candidates == {graph: True} - assert len(callgraph) == 1 - ggraph = graphof(t, g) - makegraph = graphof(t, make_a) - assert callgraph == {graph: {ggraph: True, makegraph: True}} - -def test_tuple(): - def f(x, y): - return h(x + 1, x * y) - def h(x, y): - return x, y - - def g(x): - a, b = f(x, x*5) - return a + b - t, graph = rtype(g, [int]) - callgraph, caller_candidates = check_inlining(t, graph, [2], 23) - assert caller_candidates == {graph: True} - assert len(callgraph) == 2 - fgraph = graphof(t, f) - hgraph = graphof(t, h) - assert callgraph == {graph: {fgraph: True}, fgraph: {hgraph: True}} - -def test_indirect_call(): - class A(object): - pass - def f1(a, i): - return a.x - def f2(a, i): - return a.x + 1 - def g1(a, i): - return a - def g2(a, i): - return None - def f(i): - a1 = A() - a2 = A() - a1.x = 1 - a2.x = 2 - if i: - f = f1 - g = g1 - else: - f = f2 - g = g2 - x = f(a1, 0) - a0 = g(a2, 1) - if a0 is not None: - return 43 - else: - return 42 - t, graph = rtype(f, [int]) - callgraph, caller_candidate = check_inlining(t, graph, [0], 42) - assert caller_candidate == {} - -def test_pystone(): - from rpython.translator.goal.targetrpystonex import make_target_definition - entrypoint, _, _ = make_target_definition(10) - def heuristic(graph): - for block in graph.iterblocks(): - for op in block.operations: - if op.opname in ('malloc',): - return inline.inlining_heuristic(graph) - return sys.maxint, False - # does not crash - t, graph = rtype(entrypoint, [int]) - total0 = preparation(t, t.graphs, heuristic=heuristic) - total = clever_inlining_and_malloc_removal(t) - assert total in (6, 7) # XXX total0 appears to vary - # we get 6 before fbace1f687b0, but 7 afterwards on some - # platforms, probably because rtime.clock() now contains - # a fall-back path - -def test_richards(): - from rpython.translator.goal.richards import entry_point - t, graph = rtype(entry_point, [int]) - total0 = preparation(t, t.graphs) - total = clever_inlining_and_malloc_removal(t) - assert total0 + total == 9 - -def test_loop(): - l = [10, 12, 15, 1] - def f(x): - res = 0 - for i in range(x): - res += i - for i in l: - res += i - return res - t, graph = rtype(f, [int]) - total = clever_inlining_and_malloc_removal(t) - assert total == 3 # range, two iterators diff --git a/rpython/translator/backendopt/test/test_tailrecursion.py b/rpython/translator/backendopt/test/test_tailrecursion.py deleted file mode 100644 index 84b8550ec80..00000000000 --- a/rpython/translator/backendopt/test/test_tailrecursion.py +++ /dev/null @@ -1,20 +0,0 @@ -from rpython.rtyper.llinterp import LLInterpreter -from rpython.translator.backendopt.tailrecursion import remove_tail_calls_to_self -from rpython.translator.translator import TranslationContext, graphof - - -def test_recursive_gcd(): - def gcd(a, b): - if a == 1 or a == 0: - return b - if a > b: - return gcd(b, a) - return gcd(b % a, a) - t = TranslationContext() - t.buildannotator().build_types(gcd, [int, int]) - t.buildrtyper().specialize() - gcd_graph = graphof(t, gcd) - remove_tail_calls_to_self(t, gcd_graph) - lli = LLInterpreter(t.rtyper) - res = lli.eval_graph(gcd_graph, (15, 25)) - assert res == 5 diff --git a/rpython/translator/c/test/test_genc.py b/rpython/translator/c/test/test_genc.py index a82aba8d20b..0395eefbd52 100644 --- a/rpython/translator/c/test/test_genc.py +++ b/rpython/translator/c/test/test_genc.py @@ -408,33 +408,39 @@ def test_nan_and_special_values(): assert math.isinf(inf) nan = inf/inf assert math.isnan(nan) - - for value, checker in [ - (inf, lambda x: math.isinf(x) and x > 0.0), - (-inf, lambda x: math.isinf(x) and x < 0.0), - (nan, math.isnan), - (42.0, isfinite), - (0.0, lambda x: not x and math.copysign(1., x) == 1.), - (-0.0, lambda x: not x and math.copysign(1., x) == -1.), - ]: - def f(): - return value - f1 = compile(f, []) - res = f1() + values_and_checkers = [ + (inf, lambda x: math.isinf(x) and x > 0.0), + (-inf, lambda x: math.isinf(x) and x < 0.0), + (nan, math.isnan), + (42.0, isfinite), + (0.0, lambda x: not x and math.copysign(1., x) == 1.), + (-0.0, lambda x: not x and math.copysign(1., x) == -1.), + ] + unrolling_values = unrolling_iterable(enumerate(values_and_checkers)) + l = [[value] for value, _ in values_and_checkers] + l2 = [[(-value, -value), (value, value)] for value, _ in values_and_checkers] + + def f(variant, index, x): + if variant == 0: + for i, (value, _) in unrolling_values: + if index == i: + return value + assert 0 + if variant == 1: + return l[index][x] + if variant == 2: + return l2[index][x][1] + assert 0 + f1 = compile(f, [int, int, int]) + + for index, (value, checker) in enumerate(values_and_checkers): + res = f1(0, index, -1) assert checker(res) - l = [value] - def g(x): - return l[x] - g2 = compile(g, [int]) - res = g2(0) + res = f1(1, index, 0) assert checker(res) - l2 = [(-value, -value), (value, value)] - def h(x): - return l2[x][1] - h3 = compile(h, [int]) - res = h3(1) + res = f1(2, index, 1) assert checker(res) def test_prebuilt_instance_with_dict(): diff --git a/rpython/translator/platform/test/test_platform.py b/rpython/translator/platform/test/test_platform.py index 7285e9dfa28..c06906ee772 100644 --- a/rpython/translator/platform/test/test_platform.py +++ b/rpython/translator/platform/test/test_platform.py @@ -120,7 +120,7 @@ def test_environment_inheritance(self): assert 'xyz' in res.out os.environ['_SOME_VARIABLE_2'] = 'zyz' try: - res = self.platform.execute('python', ['-c', cmd % 2]) + res = self.platform.execute(sys.executable, ['-c', cmd % 2]) assert 'zyz' in res.out finally: del os.environ['_SOME_VARIABLE_2']