Author: Alex Gaynor <[email protected]> Branch: numpy-dtype-refactor Changeset: r50069:6de4a284490c Date: 2011-12-02 12:40 -0500 http://bitbucket.org/pypy/pypy/changeset/6de4a284490c/
Log: merged default diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -15,7 +15,7 @@ * `FAQ`_: some frequently asked questions. -* `Release 1.6`_: the latest official release +* `Release 1.7`_: the latest official release * `PyPy Blog`_: news and status info about PyPy @@ -75,7 +75,7 @@ .. _`Getting Started`: getting-started.html .. _`Papers`: extradoc.html .. _`Videos`: video-index.html -.. _`Release 1.6`: http://pypy.org/download.html +.. _`Release 1.7`: http://pypy.org/download.html .. _`speed.pypy.org`: http://speed.pypy.org .. _`RPython toolchain`: translation.html .. _`potential project ideas`: project-ideas.html @@ -120,9 +120,9 @@ Windows, on top of .NET, and on top of Java. To dig into PyPy it is recommended to try out the current Mercurial default branch, which is always working or mostly working, -instead of the latest release, which is `1.6`__. +instead of the latest release, which is `1.7`__. -.. __: release-1.6.0.html +.. __: release-1.7.0.html PyPy is mainly developed on Linux and Mac OS X. Windows is supported, but platform-specific bugs tend to take longer before we notice and fix diff --git a/pypy/jit/backend/llsupport/test/test_regalloc.py b/pypy/jit/backend/llsupport/test/test_regalloc.py --- a/pypy/jit/backend/llsupport/test/test_regalloc.py +++ b/pypy/jit/backend/llsupport/test/test_regalloc.py @@ -2,6 +2,8 @@ from pypy.jit.metainterp.history import BoxInt, ConstInt, BoxFloat, INT, FLOAT from pypy.jit.backend.llsupport.regalloc import FrameManager from pypy.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan +from pypy.jit.tool.oparser import parse +from pypy.jit.backend.detect_cpu import getcpuclass def newboxes(*values): return [BoxInt(v) for v in values] diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py --- a/pypy/jit/backend/x86/regalloc.py +++ b/pypy/jit/backend/x86/regalloc.py @@ -167,26 +167,22 @@ operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations, allgcrefs) # compute longevity of variables - longevity = self._compute_vars_longevity(inputargs, operations) + longevity, useful = self._compute_vars_longevity(inputargs, operations) self.longevity = longevity self.rm = gpr_reg_mgr_cls(longevity, frame_manager = self.fm, assembler = self.assembler) self.xrm = xmm_reg_mgr_cls(longevity, frame_manager = self.fm, assembler = self.assembler) - return operations + return operations, useful def prepare_loop(self, inputargs, operations, looptoken, allgcrefs): - operations = self._prepare(inputargs, operations, allgcrefs) - jump = operations[-1] - loop_consts = self._compute_loop_consts(inputargs, jump, looptoken) - self.loop_consts = loop_consts - return self._process_inputargs(inputargs), operations + operations, useful = self._prepare(inputargs, operations, allgcrefs) + return self._process_inputargs(inputargs, useful), operations def prepare_bridge(self, prev_depths, inputargs, arglocs, operations, allgcrefs): - operations = self._prepare(inputargs, operations, allgcrefs) - self.loop_consts = {} + operations, _ = self._prepare(inputargs, operations, allgcrefs) self._update_bindings(arglocs, inputargs) self.fm.frame_depth = prev_depths[0] self.param_depth = prev_depths[1] @@ -195,7 +191,7 @@ def reserve_param(self, n): self.param_depth = max(self.param_depth, n) - def _process_inputargs(self, inputargs): + def _process_inputargs(self, inputargs, useful): # XXX we can sort out here by longevity if we need something # more optimal floatlocs = [None] * len(inputargs) @@ -211,7 +207,7 @@ arg = inputargs[i] assert not isinstance(arg, Const) reg = None - if arg not in self.loop_consts and self.longevity[arg][1] > -1: + if self.longevity[arg][1] > -1 and arg in useful: if arg.type == FLOAT: # xxx is it really a good idea? at the first CALL they # will all be flushed anyway @@ -287,15 +283,15 @@ else: return self.xrm.make_sure_var_in_reg(var, forbidden_vars) - def _compute_loop_consts(self, inputargs, jump, looptoken): - if jump.getopnum() != rop.JUMP or jump.getdescr() is not looptoken: - loop_consts = {} - else: - loop_consts = {} - for i in range(len(inputargs)): - if inputargs[i] is jump.getarg(i): - loop_consts[inputargs[i]] = i - return loop_consts + #def _compute_loop_consts(self, inputargs, jump, looptoken): + # if jump.getopnum() != rop.JUMP or jump.getdescr() is not looptoken: + # loop_consts = {} + # else: + # loop_consts = {} + # for i in range(len(inputargs)): + # if inputargs[i] is jump.getarg(i): + # loop_consts[inputargs[i]] = i + # return loop_consts def _update_bindings(self, locs, inputargs): # XXX this should probably go to llsupport/regalloc.py @@ -450,8 +446,14 @@ def _compute_vars_longevity(self, inputargs, operations): # compute a dictionary that maps variables to index in # operations that is a "last-time-seen" + + # returns a pair longevity/useful. Non-useful variables are ones that + # never appear in the assembler or it does not matter if they appear on + # stack or in registers. Main example is loop arguments that go + # only to guard operations or to jump or to finish produced = {} last_used = {} + useful = {} for i in range(len(operations)-1, -1, -1): op = operations[i] if op.result: @@ -459,8 +461,11 @@ continue assert op.result not in produced produced[op.result] = i + opnum = op.getopnum() for j in range(op.numargs()): arg = op.getarg(j) + if opnum != rop.JUMP and opnum != rop.FINISH: + useful[arg] = None if isinstance(arg, Box) and arg not in last_used: last_used[arg] = i if op.is_guard(): @@ -486,7 +491,7 @@ longevity[arg] = (0, last_used[arg]) del last_used[arg] assert len(last_used) == 0 - return longevity + return longevity, useful def loc(self, v): if v is None: # xxx kludgy diff --git a/pypy/jit/backend/x86/test/test_regalloc.py b/pypy/jit/backend/x86/test/test_regalloc.py --- a/pypy/jit/backend/x86/test/test_regalloc.py +++ b/pypy/jit/backend/x86/test/test_regalloc.py @@ -149,6 +149,13 @@ self.cpu.execute_token(loop.token) return loop + def prepare_loop(self, ops): + loop = self.parse(ops) + regalloc = RegAlloc(self.cpu.assembler, False) + regalloc.prepare_loop(loop.inputargs, loop.operations, + loop.token, []) + return regalloc + def getint(self, index): return self.cpu.get_latest_value_int(index) @@ -422,6 +429,35 @@ self.run(loop) assert self.getints(9) == range(9) + def test_loopargs(self): + ops = """ + [i0, i1, i2, i3] + i4 = int_add(i0, i1) + jump(i4, i1, i2, i3) + """ + regalloc = self.prepare_loop(ops) + assert len(regalloc.rm.reg_bindings) == 2 + + def test_loopargs_2(self): + ops = """ + [i0, i1, i2, i3] + i4 = int_add(i0, i1) + finish(i4, i1, i2, i3) + """ + regalloc = self.prepare_loop(ops) + assert len(regalloc.rm.reg_bindings) == 2 + + def test_loopargs_3(self): + ops = """ + [i0, i1, i2, i3] + i4 = int_add(i0, i1) + guard_true(i4) [i0, i1, i2, i3, i4] + jump(i4, i1, i2, i3) + """ + regalloc = self.prepare_loop(ops) + assert len(regalloc.rm.reg_bindings) == 2 + + class TestRegallocCompOps(BaseTestRegalloc): def test_cmp_op_0(self): diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py --- a/pypy/jit/codewriter/jtransform.py +++ b/pypy/jit/codewriter/jtransform.py @@ -15,6 +15,8 @@ from pypy.translator.simplify import get_funcobj from pypy.translator.unsimplify import varoftype +class UnsupportedMallocFlags(Exception): + pass def transform_graph(graph, cpu=None, callcontrol=None, portal_jd=None): """Transform a control flow graph to make it suitable for @@ -205,7 +207,24 @@ if op.args[0] in self.vable_array_vars: self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]] - rewrite_op_cast_pointer = rewrite_op_same_as + def rewrite_op_cast_pointer(self, op): + newop = self.rewrite_op_same_as(op) + assert newop is None + return + # disabled for now + if (self._is_rclass_instance(op.args[0]) and + self._is_rclass_instance(op.result)): + FROM = op.args[0].concretetype.TO + TO = op.result.concretetype.TO + if lltype._castdepth(TO, FROM) > 0: + vtable = heaptracker.get_vtable_for_gcstruct(self.cpu, TO) + const_vtable = Constant(vtable, lltype.typeOf(vtable)) + return [None, # hack, do the right renaming from op.args[0] to op.result + SpaceOperation("record_known_class", [op.args[0], const_vtable], None)] + + def rewrite_op_jit_record_known_class(self, op): + return SpaceOperation("record_known_class", [op.args[0], op.args[1]], None) + def rewrite_op_cast_bool_to_int(self, op): pass def rewrite_op_cast_bool_to_uint(self, op): pass def rewrite_op_cast_char_to_int(self, op): pass @@ -481,8 +500,22 @@ def rewrite_op_malloc_varsize(self, op): if op.args[1].value['flavor'] == 'raw': + d = op.args[1].value.copy() + d.pop('flavor') + add_memory_pressure = d.pop('add_memory_pressure', False) + zero = d.pop('zero', False) + track_allocation = d.pop('track_allocation', True) + if d: + raise UnsupportedMallocFlags(d) ARRAY = op.args[0].value - return self._do_builtin_call(op, 'raw_malloc', + name = 'raw_malloc' + if zero: + name += '_zero' + if add_memory_pressure: + name += '_add_memory_pressure' + if not track_allocation: + name += '_no_track_allocation' + return self._do_builtin_call(op, name, [op.args[2]], extra = (ARRAY,), extrakey = ARRAY) diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py --- a/pypy/jit/codewriter/support.py +++ b/pypy/jit/codewriter/support.py @@ -599,10 +599,21 @@ return p return _ll_0_alloc_with_del - def build_ll_1_raw_malloc(ARRAY): - def _ll_1_raw_malloc(n): - return lltype.malloc(ARRAY, n, flavor='raw') - return _ll_1_raw_malloc + def build_raw_malloc_builder(zero=False, add_memory_pressure=False, track_allocation=True): + def build_ll_1_raw_malloc(ARRAY): + def _ll_1_raw_malloc(n): + return lltype.malloc(ARRAY, n, flavor='raw', zero=zero, add_memory_pressure=add_memory_pressure) + return _ll_1_raw_malloc + return build_ll_1_raw_malloc + + build_ll_1_raw_malloc = build_raw_malloc_builder() + build_ll_1_raw_malloc_zero = build_raw_malloc_builder(zero=True) + build_ll_1_raw_malloc_zero_add_memory_pressure = build_raw_malloc_builder(zero=True, add_memory_pressure=True) + build_ll_1_raw_malloc_add_memory_pressure = build_raw_malloc_builder(add_memory_pressure=True) + build_ll_1_raw_malloc_no_track_allocation = build_raw_malloc_builder(track_allocation=False) + build_ll_1_raw_malloc_zero_no_track_allocation = build_raw_malloc_builder(zero=True, track_allocation=False) + build_ll_1_raw_malloc_zero_add_memory_pressure_no_track_allocation = build_raw_malloc_builder(zero=True, add_memory_pressure=True, track_allocation=False) + build_ll_1_raw_malloc_add_memory_pressure_no_track_allocation = build_raw_malloc_builder(add_memory_pressure=True, track_allocation=False) def build_ll_1_raw_free(ARRAY): def _ll_1_raw_free(p): diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py --- a/pypy/jit/codewriter/test/test_jtransform.py +++ b/pypy/jit/codewriter/test/test_jtransform.py @@ -1,3 +1,5 @@ + +import py import random try: from itertools import product @@ -15,12 +17,12 @@ from pypy.objspace.flow.model import FunctionGraph, Block, Link from pypy.objspace.flow.model import SpaceOperation, Variable, Constant -from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr +from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr, rffi from pypy.rpython.lltypesystem.module import ll_math from pypy.translator.unsimplify import varoftype from pypy.jit.codewriter import heaptracker, effectinfo from pypy.jit.codewriter.flatten import ListOfKind -from pypy.jit.codewriter.jtransform import Transformer +from pypy.jit.codewriter.jtransform import Transformer, UnsupportedMallocFlags from pypy.jit.metainterp.history import getkind def const(x): @@ -538,6 +540,44 @@ assert op1.opname == '-live-' assert op1.args == [] +def test_raw_malloc(): + S = rffi.CArray(lltype.Signed) + v1 = varoftype(lltype.Signed) + v = varoftype(lltype.Ptr(S)) + flags = Constant({'flavor': 'raw'}, lltype.Void) + op = SpaceOperation('malloc_varsize', [Constant(S, lltype.Void), flags, + v1], v) + tr = Transformer(FakeCPU(), FakeResidualCallControl()) + op0, op1 = tr.rewrite_operation(op) + assert op0.opname == 'residual_call_ir_i' + assert op0.args[0].value == 'raw_malloc' # pseudo-function as a str + assert op1.opname == '-live-' + assert op1.args == [] + +def test_raw_malloc_zero(): + S = rffi.CArray(lltype.Signed) + v1 = varoftype(lltype.Signed) + v = varoftype(lltype.Ptr(S)) + flags = Constant({'flavor': 'raw', 'zero': True}, lltype.Void) + op = SpaceOperation('malloc_varsize', [Constant(S, lltype.Void), flags, + v1], v) + tr = Transformer(FakeCPU(), FakeResidualCallControl()) + op0, op1 = tr.rewrite_operation(op) + assert op0.opname == 'residual_call_ir_i' + assert op0.args[0].value == 'raw_malloc_zero' # pseudo-function as a str + assert op1.opname == '-live-' + assert op1.args == [] + +def test_raw_malloc_unsupported_flag(): + S = rffi.CArray(lltype.Signed) + v1 = varoftype(lltype.Signed) + v = varoftype(lltype.Ptr(S)) + flags = Constant({'flavor': 'raw', 'unsupported_flag': True}, lltype.Void) + op = SpaceOperation('malloc_varsize', [Constant(S, lltype.Void), flags, + v1], v) + tr = Transformer(FakeCPU(), FakeResidualCallControl()) + py.test.raises(UnsupportedMallocFlags, tr.rewrite_operation, op) + def test_rename_on_links(): v1 = Variable() v2 = Variable(); v2.concretetype = llmemory.Address @@ -1140,4 +1180,4 @@ assert op1.opname == 'mark_opaque_ptr' assert op1.args == [v1] assert op1.result is None - assert op2 is None \ No newline at end of file + assert op2 is None diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py --- a/pypy/jit/metainterp/blackhole.py +++ b/pypy/jit/metainterp/blackhole.py @@ -518,6 +518,9 @@ @arguments("r") def bhimpl_mark_opaque_ptr(a): pass + @arguments("r", "i") + def bhimpl_record_known_class(a, b): + pass @arguments("i", returns="i") def bhimpl_int_copy(a): diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py --- a/pypy/jit/metainterp/optimizeopt/rewrite.py +++ b/pypy/jit/metainterp/optimizeopt/rewrite.py @@ -260,6 +260,16 @@ def optimize_GUARD_FALSE(self, op): self.optimize_guard(op, CONST_0) + def optimize_RECORD_KNOWN_CLASS(self, op): + value = self.getvalue(op.getarg(0)) + expectedclassbox = op.getarg(1) + assert isinstance(expectedclassbox, Const) + realclassbox = value.get_constant_class(self.optimizer.cpu) + if realclassbox is not None: + assert realclassbox.same_constant(expectedclassbox) + return + value.make_constant_class(expectedclassbox, None) + def optimize_GUARD_CLASS(self, op): value = self.getvalue(op.getarg(0)) expectedclassbox = op.getarg(1) @@ -481,6 +491,9 @@ self.pure(rop.CAST_PTR_TO_INT, [op.result], op.getarg(0)) self.emit_operation(op) + def optimize_SAME_AS(self, op): + self.make_equal_to(op.result, self.getvalue(op.getarg(0))) + dispatch_opt = make_dispatcher_method(OptRewrite, 'optimize_', default=OptRewrite.emit_operation) optimize_guards = _findall(OptRewrite, 'optimize_', 'GUARD') diff --git a/pypy/jit/metainterp/optimizeopt/simplify.py b/pypy/jit/metainterp/optimizeopt/simplify.py --- a/pypy/jit/metainterp/optimizeopt/simplify.py +++ b/pypy/jit/metainterp/optimizeopt/simplify.py @@ -28,6 +28,9 @@ def optimize_MARK_OPAQUE_PTR(self, op): pass + def optimize_RECORD_KNOWN_CLASS(self, op): + pass + dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_', default=OptSimplify.emit_operation) diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -6482,6 +6482,21 @@ # not obvious, because of the exception UnicodeDecodeError that # can be raised by ll_str2unicode() + def test_record_known_class(self): + ops = """ + [p0] + p1 = getfield_gc(p0, descr=nextdescr) + record_known_class(p1, ConstClass(node_vtable)) + guard_class(p1, ConstClass(node_vtable)) [] + jump(p1) + """ + expected = """ + [p0] + p1 = getfield_gc(p0, descr=nextdescr) + jump(p1) + """ + self.optimize_loop(ops, expected) + def test_quasi_immut(self): ops = """ [p0, p1, i0] diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py --- a/pypy/jit/metainterp/pyjitpl.py +++ b/pypy/jit/metainterp/pyjitpl.py @@ -243,6 +243,18 @@ def opimpl_mark_opaque_ptr(self, box): return self.execute(rop.MARK_OPAQUE_PTR, box) + @arguments("box", "box") + def opimpl_record_known_class(self, box, clsbox): + from pypy.rpython.lltypesystem import llmemory + if self.metainterp.heapcache.is_class_known(box): + return + adr = clsbox.getaddr() + bounding_class = llmemory.cast_adr_to_ptr(adr, rclass.CLASSTYPE) + if bounding_class.subclassrange_max - bounding_class.subclassrange_min == 1: + # precise class knowledge, this can be used + self.execute(rop.RECORD_KNOWN_CLASS, box, clsbox) + self.metainterp.heapcache.class_now_known(box) + @arguments("box") def _opimpl_any_return(self, box): self.metainterp.finishframe(box) diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py --- a/pypy/jit/metainterp/resoperation.py +++ b/pypy/jit/metainterp/resoperation.py @@ -494,6 +494,7 @@ 'COPYSTRCONTENT/5', # src, dst, srcstart, dststart, length 'COPYUNICODECONTENT/5', 'QUASIIMMUT_FIELD/1d', # [objptr], descr=SlowMutateDescr + 'RECORD_KNOWN_CLASS/2', # [objptr, clsptr] '_CANRAISE_FIRST', # ----- start of can_raise operations ----- '_CALL_FIRST', diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py --- a/pypy/jit/metainterp/test/test_ajit.py +++ b/pypy/jit/metainterp/test/test_ajit.py @@ -14,7 +14,7 @@ from pypy.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside, loop_invariant, elidable, promote, jit_debug, assert_green, AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff, - isconstant, isvirtual, promote_string, set_param) + isconstant, isvirtual, promote_string, set_param, record_known_class) from pypy.rlib.rarithmetic import ovfcheck from pypy.rpython.lltypesystem import lltype, llmemory, rffi from pypy.rpython.ootypesystem import ootype @@ -3585,6 +3585,132 @@ self.interp_operations(f, [5], translationoptions=translationoptions) + def test_annotation_gives_class_knowledge_to_tracer(self): + py.test.skip("disabled") + class Base(object): + pass + class A(Base): + def f(self): + return self.a + def g(self): + return self.a + 1 + class B(Base): + def f(self): + return self.b + def g(self): + return self.b + 1 + class C(B): + def f(self): + self.c += 1 + return self.c + def g(self): + return self.c + 1 + @dont_look_inside + def make(x): + if x > 0: + a = A() + a.a = x + 1 + elif x < 0: + a = B() + a.b = -x + else: + a = C() + a.c = 10 + return a + def f(x): + a = make(x) + if x > 0: + assert isinstance(a, A) + z = a.f() + elif x < 0: + assert isinstance(a, B) + z = a.f() + else: + assert isinstance(a, C) + z = a.f() + return z + a.g() + res1 = f(6) + res2 = self.interp_operations(f, [6]) + assert res1 == res2 + self.check_operations_history(guard_class=0, record_known_class=1) + + res1 = f(-6) + res2 = self.interp_operations(f, [-6]) + assert res1 == res2 + # cannot use record_known_class here, because B has a subclass + self.check_operations_history(guard_class=1) + + res1 = f(0) + res2 = self.interp_operations(f, [0]) + assert res1 == res2 + # here it works again + self.check_operations_history(guard_class=0, record_known_class=1) + + def test_give_class_knowledge_to_tracer_explicitly(self): + from pypy.rpython.lltypesystem.lloperation import llop + class Base(object): + def f(self): + raise NotImplementedError + def g(self): + raise NotImplementedError + class A(Base): + def f(self): + return self.a + def g(self): + return self.a + 1 + class B(Base): + def f(self): + return self.b + def g(self): + return self.b + 1 + class C(B): + def f(self): + self.c += 1 + return self.c + def g(self): + return self.c + 1 + @dont_look_inside + def make(x): + if x > 0: + a = A() + a.a = x + 1 + elif x < 0: + a = B() + a.b = -x + else: + a = C() + a.c = 10 + return a + def f(x): + a = make(x) + if x > 0: + record_known_class(a, A) + z = a.f() + elif x < 0: + record_known_class(a, B) + z = a.f() + else: + record_known_class(a, C) + z = a.f() + return z + a.g() + res1 = f(6) + res2 = self.interp_operations(f, [6]) + assert res1 == res2 + self.check_operations_history(guard_class=0, record_known_class=1) + + res1 = f(-6) + res2 = self.interp_operations(f, [-6]) + assert res1 == res2 + # cannot use record_known_class here, because B has a subclass + self.check_operations_history(guard_class=1) + + res1 = f(0) + res2 = self.interp_operations(f, [0]) + assert res1 == res2 + # here it works again + self.check_operations_history(guard_class=0, record_known_class=1) + + class TestLLtype(BaseLLtypeTests, LLJitMixin): def test_tagged(self): from pypy.rlib.objectmodel import UnboxedValue diff --git a/pypy/jit/metainterp/test/test_virtual.py b/pypy/jit/metainterp/test/test_virtual.py --- a/pypy/jit/metainterp/test/test_virtual.py +++ b/pypy/jit/metainterp/test/test_virtual.py @@ -612,7 +612,7 @@ return node.value res = self.meta_interp(f, [48, 3], policy=StopAtXPolicy(externfn)) assert res == f(48, 3) - self.check_loop_count(3) + self.check_loop_count(5) res = self.meta_interp(f, [40, 3], policy=StopAtXPolicy(externfn)) assert res == f(40, 3) self.check_loop_count(3) @@ -761,6 +761,27 @@ res = self.meta_interp(f, [0x1F, 0x11]) assert res == f(0x1F, 0x11) + def test_duplicated_virtual(self): + myjitdriver = JitDriver(greens = [], reds = ['n', 'node1', 'node2']) + def f(n): + node1 = self._new() + node1.value = 0 + node2 = self._new() + node2.value = 1 + while n > 0: + myjitdriver.jit_merge_point(n=n, node1=node1, node2=node2) + next = self._new() + next.value = node1.value + node2.value + n + node1 = next + node2 = next + n -= 1 + return node1.value + res = self.meta_interp(f, [10]) + assert res == f(10) + self.check_resops(new_with_vtable=0, new=0) + + + class VirtualMiscTests: def test_multiple_equal_virtuals(self): diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -27,6 +27,7 @@ 'builtinify' : 'interp_magic.builtinify', 'lookup_special' : 'interp_magic.lookup_special', 'do_what_I_mean' : 'interp_magic.do_what_I_mean', + 'list_strategy' : 'interp_magic.list_strategy', } submodules = { diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -5,7 +5,6 @@ from pypy.objspace.std.typeobject import MethodCache from pypy.objspace.std.mapdict import IndexCache - def internal_repr(space, w_object): return space.wrap('%r' % (w_object,)) @@ -73,3 +72,11 @@ def do_what_I_mean(space): return space.wrap(42) + +def list_strategy(space, w_list): + from pypy.objspace.std.listobject import W_ListObject + if isinstance(w_list, W_ListObject): + return space.wrap(w_list.strategy._applevel_repr) + else: + w_msg = space.wrap("Can only get the list strategy of a list") + raise OperationError(space.w_TypeError, w_msg) diff --git a/pypy/module/__pypy__/test/test_special.py b/pypy/module/__pypy__/test/test_special.py --- a/pypy/module/__pypy__/test/test_special.py +++ b/pypy/module/__pypy__/test/test_special.py @@ -5,7 +5,7 @@ def setup_class(cls): if option.runappdirect: py.test.skip("does not make sense on pypy-c") - cls.space = gettestobjspace(**{"objspace.usemodules.select": False}) + cls.space = gettestobjspace(**{"objspace.usemodules.select": False, "objspace.std.withrangelist": True}) def test__isfake(self): from __pypy__ import isfake @@ -54,3 +54,21 @@ from __pypy__ import do_what_I_mean x = do_what_I_mean() assert x == 42 + + def test_list_strategy(self): + from __pypy__ import list_strategy + + l = [1, 2, 3] + assert list_strategy(l) == "int" + l = ["a", "b", "c"] + assert list_strategy(l) == "str" + l = [1.1, 2.2, 3.3] + assert list_strategy(l) == "float" + l = range(3) + assert list_strategy(l) == "range" + l = [1, "b", 3] + assert list_strategy(l) == "object" + l = [] + assert list_strategy(l) == "empty" + o = 5 + raises(TypeError, list_strategy, 5) diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -326,6 +326,8 @@ to the added item. W_Lists do not switch back to EmptyListStrategy when becoming empty again.""" + _applevel_repr = "empty" + def __init__(self, space): ListStrategy.__init__(self, space) # cache an empty list that is used whenever getitems is called (i.e. sorting) @@ -426,6 +428,8 @@ On any operation destroying the range (inserting, appending non-ints) the strategy is switched to IntegerListStrategy.""" + _applevel_repr = "range" + def switch_to_integer_strategy(self, w_list): items = self._getitems_range(w_list, False) strategy = w_list.strategy = self.space.fromcache(IntegerListStrategy) @@ -864,6 +868,7 @@ class ObjectListStrategy(AbstractUnwrappedStrategy, ListStrategy): _none_value = None + _applevel_repr = "object" def unwrap(self, w_obj): return w_obj @@ -892,6 +897,7 @@ class IntegerListStrategy(AbstractUnwrappedStrategy, ListStrategy): _none_value = 0 + _applevel_repr = "int" def wrap(self, intval): return self.space.wrap(intval) @@ -918,6 +924,7 @@ class FloatListStrategy(AbstractUnwrappedStrategy, ListStrategy): _none_value = 0.0 + _applevel_repr = "float" def wrap(self, floatval): return self.space.wrap(floatval) @@ -944,6 +951,7 @@ class StringListStrategy(AbstractUnwrappedStrategy, ListStrategy): _none_value = None + _applevel_repr = "str" def wrap(self, stringval): return self.space.wrap(stringval) diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py --- a/pypy/rlib/jit.py +++ b/pypy/rlib/jit.py @@ -738,3 +738,29 @@ return hop.genop('jit_marker', vlist, resulttype=lltype.Void) +def record_known_class(value, cls): + """ + Assure the JIT that value is an instance of cls. This is not a precise + class check, unlike a guard_class. + """ + assert isinstance(value, cls) + + +class Entry(ExtRegistryEntry): + _about_ = record_known_class + + def compute_result_annotation(self, s_inst, s_cls): + from pypy.annotation import model as annmodel + assert s_cls.is_constant() + assert not s_inst.can_be_none() + assert isinstance(s_inst, annmodel.SomeInstance) + + def specialize_call(self, hop): + from pypy.rpython.lltypesystem import lltype, rclass + classrepr = rclass.get_type_repr(hop.rtyper) + + hop.exception_cannot_occur() + v_inst = hop.inputarg(hop.args_r[0], arg=0) + v_cls = hop.inputarg(classrepr, arg=1) + return hop.genop('jit_record_known_class', [v_inst, v_cls], + resulttype=lltype.Void) diff --git a/pypy/rpython/llinterp.py b/pypy/rpython/llinterp.py --- a/pypy/rpython/llinterp.py +++ b/pypy/rpython/llinterp.py @@ -548,6 +548,9 @@ def op_jit_marker(self, *args): pass + def op_jit_record_known_class(self, *args): + pass + def op_get_exception_addr(self, *args): raise NotImplementedError diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py --- a/pypy/rpython/lltypesystem/lloperation.py +++ b/pypy/rpython/lltypesystem/lloperation.py @@ -430,6 +430,7 @@ 'jit_force_virtual': LLOp(canrun=True), 'jit_is_virtual': LLOp(canrun=True), 'jit_force_quasi_immutable': LLOp(canrun=True), + 'jit_record_known_class' : LLOp(canrun=True), 'get_exception_addr': LLOp(), 'get_exc_value_addr': LLOp(), 'do_malloc_fixedsize_clear':LLOp(canmallocgc=True), diff --git a/pypy/rpython/lltypesystem/opimpl.py b/pypy/rpython/lltypesystem/opimpl.py --- a/pypy/rpython/lltypesystem/opimpl.py +++ b/pypy/rpython/lltypesystem/opimpl.py @@ -548,6 +548,9 @@ def op_jit_force_quasi_immutable(*args): pass +def op_jit_record_known_class(x, y): + pass + def op_get_group_member(TYPE, grpptr, memberoffset): from pypy.rpython.lltypesystem import llgroup assert isinstance(memberoffset, llgroup.GroupMemberOffset) diff --git a/pypy/translator/c/src/support.h b/pypy/translator/c/src/support.h --- a/pypy/translator/c/src/support.h +++ b/pypy/translator/c/src/support.h @@ -11,6 +11,7 @@ #endif /* MIN */ #define RUNNING_ON_LLINTERP 0 +#define OP_JIT_RECORD_KNOWN_CLASS(i, c, r) /* nothing */ #define FAIL_EXCEPTION(exc, msg) \ { \ _______________________________________________ pypy-commit mailing list [email protected] http://mail.python.org/mailman/listinfo/pypy-commit
