[pypy-svn] r69793 - in pypy/trunk/pypy: . annotation jit/backend jit/backend/cli/test jit/backend/llgraph jit/backend/llgraph/test jit/backend/llsupport jit/backend/llsupport/test jit/backend/test jit/backend/x86 jit/backend/x86/test jit/metainterp jit/metainterp/doc jit/metainterp/test rlib rpython rpython/lltypesystem rpython/lltypesystem/test rpython/memory/gctransform rpython/memory/gctransform/test rpython/ootypesystem rpython/test translator/backendopt translator/backendopt/test translator/stackless
arigo at codespeak.net
arigo at codespeak.net
Tue Dec 1 11:56:37 CET 2009
Author: arigo
Date: Tue Dec 1 11:56:34 2009
New Revision: 69793
Added:
pypy/trunk/pypy/jit/backend/cli/test/conftest.py
- copied unchanged from r69788, pypy/branch/virtual-forcing/pypy/jit/backend/cli/test/conftest.py
Removed:
pypy/trunk/pypy/jit/metainterp/doc/linking.txt
pypy/trunk/pypy/jit/metainterp/doc/matching_rules.txt
pypy/trunk/pypy/jit/metainterp/doc/virtualizables.txt
Modified:
pypy/trunk/pypy/annotation/description.py
pypy/trunk/pypy/jit/backend/llgraph/llimpl.py
pypy/trunk/pypy/jit/backend/llgraph/runner.py
pypy/trunk/pypy/jit/backend/llgraph/test/test_llgraph.py
pypy/trunk/pypy/jit/backend/llsupport/regalloc.py
pypy/trunk/pypy/jit/backend/llsupport/test/test_regalloc.py
pypy/trunk/pypy/jit/backend/model.py
pypy/trunk/pypy/jit/backend/test/runner_test.py
pypy/trunk/pypy/jit/backend/x86/assembler.py
pypy/trunk/pypy/jit/backend/x86/regalloc.py
pypy/trunk/pypy/jit/backend/x86/runner.py
pypy/trunk/pypy/jit/backend/x86/test/test_gc_integration.py
pypy/trunk/pypy/jit/metainterp/codewriter.py
pypy/trunk/pypy/jit/metainterp/compile.py
pypy/trunk/pypy/jit/metainterp/doc/jitpl5.txt
pypy/trunk/pypy/jit/metainterp/doc/loop.txt
pypy/trunk/pypy/jit/metainterp/effectinfo.py
pypy/trunk/pypy/jit/metainterp/pyjitpl.py
pypy/trunk/pypy/jit/metainterp/resoperation.py
pypy/trunk/pypy/jit/metainterp/resume.py
pypy/trunk/pypy/jit/metainterp/test/test_basic.py
pypy/trunk/pypy/jit/metainterp/test/test_codewriter.py
pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
pypy/trunk/pypy/jit/metainterp/test/test_resume.py
pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
pypy/trunk/pypy/jit/metainterp/typesystem.py
pypy/trunk/pypy/jit/metainterp/virtualizable.py
pypy/trunk/pypy/jit/metainterp/warmstate.py
pypy/trunk/pypy/rlib/jit.py
pypy/trunk/pypy/rpython/llinterp.py
pypy/trunk/pypy/rpython/lltypesystem/ll2ctypes.py
pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
pypy/trunk/pypy/rpython/lltypesystem/rffi.py
pypy/trunk/pypy/rpython/lltypesystem/rvirtualizable2.py
pypy/trunk/pypy/rpython/lltypesystem/test/test_rffi.py
pypy/trunk/pypy/rpython/memory/gctransform/framework.py
pypy/trunk/pypy/rpython/memory/gctransform/test/test_framework.py
pypy/trunk/pypy/rpython/ootypesystem/rvirtualizable2.py
pypy/trunk/pypy/rpython/test/test_rvirtualizable2.py
pypy/trunk/pypy/testrunner_cfg.py
pypy/trunk/pypy/translator/backendopt/canraise.py
pypy/trunk/pypy/translator/backendopt/graphanalyze.py
pypy/trunk/pypy/translator/backendopt/test/test_canraise.py
pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py
pypy/trunk/pypy/translator/backendopt/writeanalyze.py
pypy/trunk/pypy/translator/stackless/transform.py
Log:
Merge the branch/virtual-forcing so far, introducing proper forcing of
virtualizables followed by a guard failure when we eventually return to
assembler. This also changes the front-end so that it always aborts a
trace after the virtualizable was forced.
The changes are not very ootype-friendly. The CLI tests are disabled
for now.
Fix the write analyzer to give the correct answer even when calling
external functions that may call back.
svn merge -r69626:69792 svn+ssh://codespeak.net/svn/pypy/branch/virtual-forcing .
Modified: pypy/trunk/pypy/annotation/description.py
==============================================================================
--- pypy/trunk/pypy/annotation/description.py (original)
+++ pypy/trunk/pypy/annotation/description.py Tue Dec 1 11:56:34 2009
@@ -202,6 +202,9 @@
graph.name = alt_name
return graph
+ def getgraphs(self):
+ return self._cache.values()
+
def getuniquegraph(self):
if len(self._cache) != 1:
raise NoStandardGraph(self)
Modified: pypy/trunk/pypy/jit/backend/llgraph/llimpl.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llgraph/llimpl.py (original)
+++ pypy/trunk/pypy/jit/backend/llgraph/llimpl.py Tue Dec 1 11:56:34 2009
@@ -35,8 +35,13 @@
_TO_OPAQUE = {}
def _to_opaque(value):
- return lltype.opaqueptr(_TO_OPAQUE[value.__class__], 'opaque',
- externalobj=value)
+ try:
+ return value._the_opaque_pointer
+ except AttributeError:
+ op = lltype.opaqueptr(_TO_OPAQUE[value.__class__], 'opaque',
+ externalobj=value)
+ value._the_opaque_pointer = op
+ return op
def from_opaque_string(s):
if isinstance(s, str):
@@ -145,6 +150,9 @@
'unicodesetitem' : (('ref', 'int', 'int'), 'int'),
'cast_ptr_to_int' : (('ref',), 'int'),
'debug_merge_point': (('ref',), None),
+ 'force_token' : ((), 'int'),
+ 'call_may_force' : (('int', 'varargs'), 'intorptr'),
+ 'guard_not_forced': ((), None)
#'getitem' : (('void', 'ref', 'int'), 'int'),
#'setitem' : (('void', 'ref', 'int', 'int'), None),
#'newlist' : (('void', 'varargs'), 'ref'),
@@ -384,6 +392,9 @@
def __init__(self, memocast):
self.verbose = False
self.memocast = memocast
+ self.opindex = 1
+ self._forced = False
+ self._may_force = -1
def getenv(self, v):
if isinstance(v, Constant):
@@ -391,6 +402,19 @@
else:
return self.env[v]
+ def _populate_fail_args(self, op, skip=None):
+ fail_args = []
+ if op.fail_args:
+ for fail_arg in op.fail_args:
+ if fail_arg is None:
+ fail_args.append(None)
+ elif fail_arg is skip:
+ fail_args.append(fail_arg.concretetype._defl())
+ else:
+ fail_args.append(self.getenv(fail_arg))
+ self.fail_args = fail_args
+ self.fail_index = op.fail_index
+
def execute(self):
"""Execute all operations in a loop,
possibly following to other loops as well.
@@ -401,6 +425,7 @@
operations = self.loop.operations
opindex = 0
while True:
+ self.opindex = opindex
op = operations[opindex]
args = [self.getenv(v) for v in op.args]
if not op.is_final():
@@ -419,18 +444,11 @@
opindex = 0
continue
else:
- fail_args = []
- if op.fail_args:
- for fail_arg in op.fail_args:
- if fail_arg is None:
- fail_args.append(None)
- else:
- fail_args.append(self.getenv(fail_arg))
+ self._populate_fail_args(op)
# a non-patched guard
if self.verbose:
log.trace('failed: %s' % (
', '.join(map(str, fail_args)),))
- self.fail_args = fail_args
return op.fail_index
#verbose = self.verbose
assert (result is None) == (op.result is None)
@@ -453,7 +471,8 @@
if op.opnum == rop.JUMP:
assert len(op.jump_target.inputargs) == len(args)
self.env = dict(zip(op.jump_target.inputargs, args))
- operations = op.jump_target.operations
+ self.loop = op.jump_target
+ operations = self.loop.operations
opindex = 0
_stats.exec_jumps += 1
elif op.opnum == rop.FINISH:
@@ -484,7 +503,7 @@
try:
res = ophandler(self, descr, *values)
finally:
- if verbose:
+ if 0: # if verbose:
argtypes, restype = TYPES[opname]
if res is None:
resdata = ''
@@ -493,9 +512,9 @@
else:
resdata = '-> ' + repr1(res, restype, self.memocast)
# fish the types
- #log.cpu('\t%s %s %s' % (opname, repr_list(values, argtypes,
- # self.memocast),
- # resdata))
+ log.cpu('\t%s %s %s' % (opname, repr_list(values, argtypes,
+ self.memocast),
+ resdata))
return res
def as_int(self, x):
@@ -776,6 +795,24 @@
def op_uint_xor(self, descr, arg1, arg2):
return arg1 ^ arg2
+ def op_force_token(self, descr):
+ opaque_frame = _to_opaque(self)
+ return llmemory.cast_ptr_to_adr(opaque_frame)
+
+ def op_call_may_force(self, calldescr, func, *args):
+ assert not self._forced
+ self._may_force = self.opindex
+ try:
+ return self.op_call(calldescr, func, *args)
+ finally:
+ self._may_force = -1
+
+ def op_guard_not_forced(self, descr):
+ forced = self._forced
+ self._forced = False
+ if forced:
+ raise GuardFailed
+
class OOFrame(Frame):
@@ -1042,6 +1079,25 @@
return lltype.cast_opaque_ptr(llmemory.GCREF,
_get_error(ZeroDivisionError).args[1])
+def force(opaque_frame):
+ frame = _from_opaque(opaque_frame)
+ assert not frame._forced
+ frame._forced = True
+ assert frame._may_force >= 0
+ call_op = frame.loop.operations[frame._may_force]
+ guard_op = frame.loop.operations[frame._may_force+1]
+ assert call_op.opnum == rop.CALL_MAY_FORCE
+ frame._populate_fail_args(guard_op, skip=call_op.result)
+ return frame.fail_index
+
+def get_forced_token_frame(force_token):
+ opaque_frame = llmemory.cast_adr_to_ptr(force_token,
+ lltype.Ptr(_TO_OPAQUE[Frame]))
+ return opaque_frame
+
+def get_frame_forced_token(opaque_frame):
+ return llmemory.cast_ptr_to_adr(opaque_frame)
+
class MemoCast(object):
def __init__(self):
self.addresses = [llmemory.NULL]
@@ -1411,6 +1467,9 @@
setannotation(get_overflow_error_value, annmodel.SomePtr(llmemory.GCREF))
setannotation(get_zero_division_error, annmodel.SomeAddress())
setannotation(get_zero_division_error_value, annmodel.SomePtr(llmemory.GCREF))
+setannotation(force, annmodel.SomeInteger())
+setannotation(get_forced_token_frame, s_Frame)
+setannotation(get_frame_forced_token, annmodel.SomeAddress())
setannotation(new_memo_cast, s_MemoCast)
setannotation(cast_adr_to_int, annmodel.SomeInteger())
Modified: pypy/trunk/pypy/jit/backend/llgraph/runner.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llgraph/runner.py (original)
+++ pypy/trunk/pypy/jit/backend/llgraph/runner.py Tue Dec 1 11:56:34 2009
@@ -4,6 +4,7 @@
import sys
from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib.objectmodel import we_are_translated
from pypy.rpython.lltypesystem import lltype, llmemory, rclass
from pypy.rpython.ootypesystem import ootype
from pypy.rpython.llinterp import LLInterpreter
@@ -20,31 +21,16 @@
class Descr(history.AbstractDescr):
- name = None
- ofs = -1
- typeinfo = '?'
-
- def __init__(self, ofs, typeinfo='?', extrainfo=None):
+
+ def __init__(self, ofs, typeinfo, extrainfo=None, name=None):
self.ofs = ofs
self.typeinfo = typeinfo
self.extrainfo = extrainfo
+ self.name = name
def get_extra_info(self):
return self.extrainfo
- def __hash__(self):
- return hash((self.ofs, self.typeinfo))
-
- def __eq__(self, other):
- if not isinstance(other, Descr):
- return NotImplemented
- return self.ofs == other.ofs and self.typeinfo == other.typeinfo
-
- def __ne__(self, other):
- if not isinstance(other, Descr):
- return NotImplemented
- return self.ofs != other.ofs or self.typeinfo != other.typeinfo
-
def sort_key(self):
return self.ofs
@@ -75,9 +61,12 @@
raise TypeError("cannot use comparison on Descrs")
def __repr__(self):
+ args = [repr(self.ofs), repr(self.typeinfo)]
if self.name is not None:
- return '<Descr %r, %r, %r>' % (self.ofs, self.typeinfo, self.name)
- return '<Descr %r, %r>' % (self.ofs, self.typeinfo)
+ args.append(repr(self.name))
+ if self.extrainfo is not None:
+ args.append('E')
+ return '<Descr %r>' % (', '.join(args),)
history.TreeLoop._compiled_version = lltype.nullptr(llimpl.COMPILEDLOOP.TO)
@@ -99,11 +88,21 @@
llimpl._stats = self.stats
llimpl._llinterp = LLInterpreter(self.rtyper)
self._future_values = []
+ self._descrs = {}
def _freeze_(self):
assert self.translate_support_code
return False
+ def getdescr(self, ofs, typeinfo='?', extrainfo=None, name=None):
+ key = (ofs, typeinfo, extrainfo, name)
+ try:
+ return self._descrs[key]
+ except KeyError:
+ descr = Descr(ofs, typeinfo, extrainfo, name)
+ self._descrs[key] = descr
+ return descr
+
def set_class_sizes(self, class_sizes):
self.class_sizes = class_sizes
for vtable, size in class_sizes.items():
@@ -233,6 +232,10 @@
def get_latest_value_float(self, index):
return llimpl.frame_float_getvalue(self.latest_frame, index)
+ def get_latest_force_token(self):
+ token = llimpl.get_frame_forced_token(self.latest_frame)
+ return self.cast_adr_to_int(token)
+
# ----------
def get_exception(self):
@@ -252,16 +255,9 @@
return (self.cast_adr_to_int(llimpl.get_zero_division_error()),
llimpl.get_zero_division_error_value())
- @staticmethod
- def sizeof(S):
+ def sizeof(self, S):
assert not isinstance(S, lltype.Ptr)
- return Descr(symbolic.get_size(S))
-
- @staticmethod
- def numof(S):
- return 4
-
- ##addresssuffix = '4'
+ return self.getdescr(symbolic.get_size(S))
def cast_adr_to_int(self, adr):
return llimpl.cast_adr_to_int(self.memo_cast, adr)
@@ -282,18 +278,14 @@
BaseCPU.__init__(self, *args, **kwds)
self.fielddescrof_vtable = self.fielddescrof(rclass.OBJECT, 'typeptr')
- @staticmethod
- def fielddescrof(S, fieldname):
+ def fielddescrof(self, S, fieldname):
ofs, size = symbolic.get_field_token(S, fieldname)
token = history.getkind(getattr(S, fieldname))
- res = Descr(ofs, token[0])
- res.name = fieldname
- return res
+ return self.getdescr(ofs, token[0], name=fieldname)
- @staticmethod
- def calldescrof(FUNC, ARGS, RESULT, extrainfo=None):
+ def calldescrof(self, FUNC, ARGS, RESULT, extrainfo=None):
token = history.getkind(RESULT)
- return Descr(0, token[0], extrainfo=extrainfo)
+ return self.getdescr(0, token[0], extrainfo=extrainfo)
def get_exception(self):
return self.cast_adr_to_int(llimpl.get_exception())
@@ -301,13 +293,12 @@
def get_exc_value(self):
return llimpl.get_exc_value()
- @staticmethod
- def arraydescrof(A):
+ def arraydescrof(self, A):
assert isinstance(A, lltype.GcArray)
assert A.OF != lltype.Void
size = symbolic.get_size(A)
token = history.getkind(A.OF)
- return Descr(size, token[0])
+ return self.getdescr(size, token[0])
# ---------- the backend-dependent operations ----------
@@ -498,6 +489,14 @@
return history.BoxInt(llimpl.cast_to_int(ptrbox.getref_base(),
self.memo_cast))
+ def force(self, force_token):
+ token = self.cast_int_to_adr(force_token)
+ frame = llimpl.get_forced_token_frame(token)
+ fail_index = llimpl.force(frame)
+ self.latest_frame = frame
+ return self.get_fail_descr_from_number(fail_index)
+
+
class OOtypeCPU(BaseCPU):
is_oo = True
ts = oohelper
Modified: pypy/trunk/pypy/jit/backend/llgraph/test/test_llgraph.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llgraph/test/test_llgraph.py (original)
+++ pypy/trunk/pypy/jit/backend/llgraph/test/test_llgraph.py Tue Dec 1 11:56:34 2009
@@ -10,6 +10,9 @@
from pypy.jit.backend.test.runner_test import LLtypeBackendTest
class TestLLTypeLLGraph(LLtypeBackendTest):
+ # for individual tests see:
+ # ====> ../../test/runner_test.py
+
from pypy.jit.backend.llgraph.runner import LLtypeCPU as cpu_type
def setup_method(self, _):
Modified: pypy/trunk/pypy/jit/backend/llsupport/regalloc.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llsupport/regalloc.py (original)
+++ pypy/trunk/pypy/jit/backend/llsupport/regalloc.py Tue Dec 1 11:56:34 2009
@@ -304,7 +304,7 @@
self.assembler.regalloc_mov(reg, to)
# otherwise it's clean
- def before_call(self, force_store=[]):
+ def before_call(self, force_store=[], save_all_regs=False):
""" Spill registers before a call, as described by
'self.save_around_call_regs'. Registers are not spilled if
they don't survive past the current operation, unless they
@@ -316,8 +316,8 @@
del self.reg_bindings[v]
self.free_regs.append(reg)
continue
- if reg not in self.save_around_call_regs:
- # we don't need to
+ if not save_all_regs and reg not in self.save_around_call_regs:
+ # we don't have to
continue
self._sync_var(v)
del self.reg_bindings[v]
@@ -327,12 +327,12 @@
""" Adjust registers according to the result of the call,
which is in variable v.
"""
- if v is not None:
- self._check_type(v)
- r = self.call_result_location(v)
- self.reg_bindings[v] = r
- self.free_regs = [fr for fr in self.free_regs if fr is not r]
-
+ self._check_type(v)
+ r = self.call_result_location(v)
+ self.reg_bindings[v] = r
+ self.free_regs = [fr for fr in self.free_regs if fr is not r]
+ return r
+
# abstract methods, override
def convert_to_imm(self, c):
Modified: pypy/trunk/pypy/jit/backend/llsupport/test/test_regalloc.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llsupport/test/test_regalloc.py (original)
+++ pypy/trunk/pypy/jit/backend/llsupport/test/test_regalloc.py Tue Dec 1 11:56:34 2009
@@ -278,6 +278,30 @@
assert len(rm.reg_bindings) == 3
rm._check_invariants()
+ def test_call_support_save_all_regs(self):
+ class XRegisterManager(RegisterManager):
+ save_around_call_regs = [r1, r2]
+
+ def call_result_location(self, v):
+ return r1
+
+ sm = TStackManager()
+ asm = MockAsm()
+ boxes, longevity = boxes_and_longevity(5)
+ rm = XRegisterManager(longevity, stack_manager=sm,
+ assembler=asm)
+ for b in boxes[:-1]:
+ rm.force_allocate_reg(b)
+ rm.before_call(save_all_regs=True)
+ assert len(rm.reg_bindings) == 0
+ assert sm.stack_depth == 4
+ assert len(asm.moves) == 4
+ rm._check_invariants()
+ rm.after_call(boxes[-1])
+ assert len(rm.reg_bindings) == 1
+ rm._check_invariants()
+
+
def test_different_stack_width(self):
class XRegisterManager(RegisterManager):
reg_width = 2
Modified: pypy/trunk/pypy/jit/backend/model.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/model.py (original)
+++ pypy/trunk/pypy/jit/backend/model.py Tue Dec 1 11:56:34 2009
@@ -78,6 +78,11 @@
or from 'args' if it was a FINISH). Returns a ptr or an obj."""
raise NotImplementedError
+ def get_latest_force_token(self):
+ """After a GUARD_NOT_FORCED fails, this function returns the
+ same FORCE_TOKEN result as the one in the just-failed loop."""
+ raise NotImplementedError
+
def get_exception(self):
raise NotImplementedError
@@ -205,6 +210,16 @@
def do_cast_ptr_to_int(self, ptrbox):
raise NotImplementedError
+ def do_force_token(self):
+ # this should not be implemented at all by the backends
+ raise NotImplementedError
+
+ def do_call_may_force(self, args, calldescr):
+ return self.do_call(args, calldescr)
+
+ def force(self, force_token):
+ raise NotImplementedError
+
# ootype specific operations
# --------------------------
Modified: pypy/trunk/pypy/jit/backend/test/runner_test.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/test/runner_test.py (original)
+++ pypy/trunk/pypy/jit/backend/test/runner_test.py Tue Dec 1 11:56:34 2009
@@ -1221,6 +1221,139 @@
else:
assert record == []
+ def test_force_operations_returning_void(self):
+ values = []
+ def maybe_force(token, flag):
+ if flag:
+ descr = self.cpu.force(token)
+ values.append(descr)
+ values.append(self.cpu.get_latest_value_int(0))
+ values.append(self.cpu.get_latest_value_int(1))
+
+ FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Void)
+ func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
+ funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
+ calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+ cpu = self.cpu
+ i0 = BoxInt()
+ i1 = BoxInt()
+ tok = BoxInt()
+ faildescr = BasicFailDescr(1)
+ ops = [
+ ResOperation(rop.FORCE_TOKEN, [], tok),
+ ResOperation(rop.CALL_MAY_FORCE, [funcbox, tok, i1], None,
+ descr=calldescr),
+ ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+ ResOperation(rop.FINISH, [i0], None, descr=BasicFailDescr(0))
+ ]
+ ops[2].fail_args = [i1, i0]
+ looptoken = LoopToken()
+ self.cpu.compile_loop([i0, i1], ops, looptoken)
+ self.cpu.set_future_value_int(0, 20)
+ self.cpu.set_future_value_int(1, 0)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 0
+ assert self.cpu.get_latest_value_int(0) == 20
+ assert values == []
+
+ self.cpu.set_future_value_int(0, 10)
+ self.cpu.set_future_value_int(1, 1)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 1
+ assert self.cpu.get_latest_value_int(0) == 1
+ assert self.cpu.get_latest_value_int(1) == 10
+ assert values == [faildescr, 1, 10]
+
+ def test_force_operations_returning_int(self):
+ values = []
+ def maybe_force(token, flag):
+ if flag:
+ self.cpu.force(token)
+ values.append(self.cpu.get_latest_value_int(0))
+ values.append(self.cpu.get_latest_value_int(2))
+ return 42
+
+ FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Signed)
+ func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
+ funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
+ calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+ cpu = self.cpu
+ i0 = BoxInt()
+ i1 = BoxInt()
+ i2 = BoxInt()
+ tok = BoxInt()
+ faildescr = BasicFailDescr(1)
+ ops = [
+ ResOperation(rop.FORCE_TOKEN, [], tok),
+ ResOperation(rop.CALL_MAY_FORCE, [funcbox, tok, i1], i2,
+ descr=calldescr),
+ ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+ ResOperation(rop.FINISH, [i2], None, descr=BasicFailDescr(0))
+ ]
+ ops[2].fail_args = [i1, i2, i0]
+ looptoken = LoopToken()
+ self.cpu.compile_loop([i0, i1], ops, looptoken)
+ self.cpu.set_future_value_int(0, 20)
+ self.cpu.set_future_value_int(1, 0)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 0
+ assert self.cpu.get_latest_value_int(0) == 42
+ assert values == []
+
+ self.cpu.set_future_value_int(0, 10)
+ self.cpu.set_future_value_int(1, 1)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 1
+ assert self.cpu.get_latest_value_int(0) == 1
+ assert self.cpu.get_latest_value_int(1) == 42
+ assert self.cpu.get_latest_value_int(2) == 10
+ assert values == [1, 10]
+
+ def test_force_operations_returning_float(self):
+ values = []
+ def maybe_force(token, flag):
+ if flag:
+ self.cpu.force(token)
+ values.append(self.cpu.get_latest_value_int(0))
+ values.append(self.cpu.get_latest_value_int(2))
+ return 42.5
+
+ FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Float)
+ func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
+ funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
+ calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+ cpu = self.cpu
+ i0 = BoxInt()
+ i1 = BoxInt()
+ f2 = BoxFloat()
+ tok = BoxInt()
+ faildescr = BasicFailDescr(1)
+ ops = [
+ ResOperation(rop.FORCE_TOKEN, [], tok),
+ ResOperation(rop.CALL_MAY_FORCE, [funcbox, tok, i1], f2,
+ descr=calldescr),
+ ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+ ResOperation(rop.FINISH, [f2], None, descr=BasicFailDescr(0))
+ ]
+ ops[2].fail_args = [i1, f2, i0]
+ looptoken = LoopToken()
+ self.cpu.compile_loop([i0, i1], ops, looptoken)
+ self.cpu.set_future_value_int(0, 20)
+ self.cpu.set_future_value_int(1, 0)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 0
+ assert self.cpu.get_latest_value_float(0) == 42.5
+ assert values == []
+
+ self.cpu.set_future_value_int(0, 10)
+ self.cpu.set_future_value_int(1, 1)
+ fail = self.cpu.execute_token(looptoken)
+ assert fail.identifier == 1
+ assert self.cpu.get_latest_value_int(0) == 1
+ assert self.cpu.get_latest_value_float(1) == 42.5
+ assert self.cpu.get_latest_value_int(2) == 10
+ assert values == [1, 10]
+
# pure do_ / descr features
def test_do_operations(self):
Modified: pypy/trunk/pypy/jit/backend/x86/assembler.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/assembler.py (original)
+++ pypy/trunk/pypy/jit/backend/x86/assembler.py Tue Dec 1 11:56:34 2009
@@ -9,7 +9,8 @@
from pypy.rpython.annlowlevel import llhelper
from pypy.tool.uid import fixid
from pypy.jit.backend.x86.regalloc import RegAlloc, WORD, lower_byte,\
- X86RegisterManager, X86XMMRegisterManager, get_ebp_ofs
+ X86RegisterManager, X86XMMRegisterManager, get_ebp_ofs, FRAME_FIXED_SIZE,\
+ FORCE_INDEX_OFS
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.jit.backend.x86 import codebuf
from pypy.jit.backend.x86.ri386 import *
@@ -22,8 +23,6 @@
# our calling convention - we pass first 6 args in registers
# and the rest stays on the stack
-RET_BP = 5 # ret ip + bp + bx + esi + edi = 5 words
-
if sys.platform == 'darwin':
# darwin requires the stack to be 16 bytes aligned on calls
CALL_ALIGN = 4
@@ -90,6 +89,7 @@
self.fail_boxes_int = NonmovableGrowableArraySigned()
self.fail_boxes_ptr = NonmovableGrowableArrayGCREF()
self.fail_boxes_float = NonmovableGrowableArrayFloat()
+ self.fail_ebp = 0
self.setup_failure_recovery()
def leave_jitted_hook(self):
@@ -200,7 +200,11 @@
# patch stack adjustment LEA
# possibly align, e.g. for Mac OS X
mc = codebuf.InMemoryCodeBuilder(adr_lea, adr_lea + 4)
- mc.write(packimm32(-(stack_depth + RET_BP - 2) * WORD))
+ # Compute the correct offset for the instruction LEA ESP, [EBP-4*words].
+ # Given that [EBP] is where we saved EBP, i.e. in the last word
+ # of our fixed frame, then the 'words' value is:
+ words = (FRAME_FIXED_SIZE - 1) + stack_depth
+ mc.write(packimm32(-WORD * words))
mc.done()
def _assemble_bootstrap_code(self, inputargs, arglocs):
@@ -210,8 +214,8 @@
self.mc.PUSH(ebx)
self.mc.PUSH(esi)
self.mc.PUSH(edi)
- # NB. exactly 4 pushes above; if this changes, fix stack_pos().
- # You must also keep _get_callshape() in sync.
+ # NB. the shape of the frame is hard-coded in get_basic_shape() too.
+ # Also, make sure this is consistent with FRAME_FIXED_SIZE.
adr_stackadjust = self._patchable_stackadjust()
tmp = X86RegisterManager.all_regs[0]
xmmtmp = X86XMMRegisterManager.all_regs[0]
@@ -266,9 +270,6 @@
regalloc_mov = mov # legacy interface
- def regalloc_fstp(self, loc):
- self.mc.FSTP(loc)
-
def regalloc_push(self, loc):
if isinstance(loc, XMMREG):
self.mc.SUB(esp, imm(2*WORD))
@@ -758,7 +759,8 @@
def implement_guard_recovery(self, guard_opnum, faildescr, failargs,
fail_locs):
exc = (guard_opnum == rop.GUARD_EXCEPTION or
- guard_opnum == rop.GUARD_NO_EXCEPTION)
+ guard_opnum == rop.GUARD_NO_EXCEPTION or
+ guard_opnum == rop.GUARD_NOT_FORCED)
return self.generate_quick_failure(faildescr, failargs, fail_locs, exc)
def generate_quick_failure(self, faildescr, failargs, fail_locs, exc):
@@ -876,75 +878,79 @@
arglocs.append(loc)
return arglocs[:]
+ def grab_frame_values(self, bytecode, frame_addr, allregisters):
+ # no malloc allowed here!!
+ self.fail_ebp = allregisters[16 + ebp.op]
+ num = 0
+ value_hi = 0
+ while 1:
+ # decode the next instruction from the bytecode
+ code = rffi.cast(lltype.Signed, bytecode[0])
+ bytecode = rffi.ptradd(bytecode, 1)
+ if code >= 4*self.DESCR_FROMSTACK:
+ if code > 0x7F:
+ shift = 7
+ code &= 0x7F
+ while True:
+ nextcode = rffi.cast(lltype.Signed, bytecode[0])
+ bytecode = rffi.ptradd(bytecode, 1)
+ code |= (nextcode & 0x7F) << shift
+ shift += 7
+ if nextcode <= 0x7F:
+ break
+ # load the value from the stack
+ kind = code & 3
+ code = (code >> 2) - self.DESCR_FROMSTACK
+ stackloc = frame_addr + get_ebp_ofs(code)
+ value = rffi.cast(rffi.LONGP, stackloc)[0]
+ if kind == self.DESCR_FLOAT:
+ value_hi = value
+ value = rffi.cast(rffi.LONGP, stackloc - 4)[0]
+ else:
+ # 'code' identifies a register: load its value
+ kind = code & 3
+ if kind == self.DESCR_SPECIAL:
+ if code == self.DESCR_HOLE:
+ num += 1
+ continue
+ assert code == self.DESCR_STOP
+ break
+ code >>= 2
+ if kind == self.DESCR_FLOAT:
+ value = allregisters[2*code]
+ value_hi = allregisters[2*code + 1]
+ else:
+ value = allregisters[16 + code]
+
+ # store the loaded value into fail_boxes_<type>
+ if kind == self.DESCR_INT:
+ tgt = self.fail_boxes_int.get_addr_for_num(num)
+ elif kind == self.DESCR_REF:
+ tgt = self.fail_boxes_ptr.get_addr_for_num(num)
+ elif kind == self.DESCR_FLOAT:
+ tgt = self.fail_boxes_float.get_addr_for_num(num)
+ rffi.cast(rffi.LONGP, tgt)[1] = value_hi
+ else:
+ assert 0, "bogus kind"
+ rffi.cast(rffi.LONGP, tgt)[0] = value
+ num += 1
+ #
+ if not we_are_translated():
+ assert bytecode[4] == 0xCC
+ fail_index = rffi.cast(rffi.LONGP, bytecode)[0]
+ return fail_index
+
def setup_failure_recovery(self):
def failure_recovery_func(registers):
- # no malloc allowed here!!
# 'registers' is a pointer to a structure containing the
# original value of the registers, optionally the original
# value of XMM registers, and finally a reference to the
# recovery bytecode. See _build_failure_recovery() for details.
stack_at_ebp = registers[ebp.op]
bytecode = rffi.cast(rffi.UCHARP, registers[8])
- num = 0
- value_hi = 0
- while 1:
- # decode the next instruction from the bytecode
- code = rffi.cast(lltype.Signed, bytecode[0])
- bytecode = rffi.ptradd(bytecode, 1)
- if code >= 4*self.DESCR_FROMSTACK:
- if code > 0x7F:
- shift = 7
- code &= 0x7F
- while True:
- nextcode = rffi.cast(lltype.Signed, bytecode[0])
- bytecode = rffi.ptradd(bytecode, 1)
- code |= (nextcode & 0x7F) << shift
- shift += 7
- if nextcode <= 0x7F:
- break
- # load the value from the stack
- kind = code & 3
- code = (code >> 2) - self.DESCR_FROMSTACK
- stackloc = stack_at_ebp + get_ebp_ofs(code)
- value = rffi.cast(rffi.LONGP, stackloc)[0]
- if kind == self.DESCR_FLOAT:
- value_hi = value
- value = rffi.cast(rffi.LONGP, stackloc - 4)[0]
- else:
- # 'code' identifies a register: load its value
- kind = code & 3
- if kind == self.DESCR_SPECIAL:
- if code == self.DESCR_HOLE:
- num += 1
- continue
- assert code == self.DESCR_STOP
- break
- code >>= 2
- if kind == self.DESCR_FLOAT:
- xmmregisters = rffi.ptradd(registers, -16)
- value = xmmregisters[2*code]
- value_hi = xmmregisters[2*code + 1]
- else:
- value = registers[code]
-
- # store the loaded value into fail_boxes_<type>
- if kind == self.DESCR_INT:
- tgt = self.fail_boxes_int.get_addr_for_num(num)
- elif kind == self.DESCR_REF:
- tgt = self.fail_boxes_ptr.get_addr_for_num(num)
- elif kind == self.DESCR_FLOAT:
- tgt = self.fail_boxes_float.get_addr_for_num(num)
- rffi.cast(rffi.LONGP, tgt)[1] = value_hi
- else:
- assert 0, "bogus kind"
- rffi.cast(rffi.LONGP, tgt)[0] = value
- num += 1
- #
- if not we_are_translated():
- assert bytecode[4] == 0xCC
- fail_index = rffi.cast(rffi.LONGP, bytecode)[0]
- return fail_index
+ allregisters = rffi.ptradd(registers, -16)
+ return self.grab_frame_values(bytecode, stack_at_ebp, allregisters)
self.failure_recovery_func = failure_recovery_func
self.failure_recovery_code = [0, 0, 0, 0]
@@ -997,11 +1003,11 @@
# now we return from the complete frame, which starts from
# _assemble_bootstrap_code(). The LEA below throws away most
# of the frame, including all the PUSHes that we did just above.
- mc.LEA(esp, addr_add(ebp, imm((-RET_BP + 2) * WORD)))
- mc.POP(edi)
- mc.POP(esi)
- mc.POP(ebx)
- mc.POP(ebp)
+ mc.LEA(esp, addr_add(ebp, imm(-3 * WORD)))
+ mc.POP(edi) # [ebp-12]
+ mc.POP(esi) # [ebp-8]
+ mc.POP(ebx) # [ebp-4]
+ mc.POP(ebp) # [ebp]
mc.RET()
self.mc2.done()
self.failure_recovery_code[exc + 2 * withfloats] = recovery_addr
@@ -1042,14 +1048,14 @@
addr = self.cpu.get_on_leave_jitted_int(save_exception=exc)
mc.CALL(rel32(addr))
- # don't break the following code sequence!
+ # don't break the following code sequence! xxx no reason any more?
mc = mc._mc
- mc.LEA(esp, addr_add(ebp, imm((-RET_BP + 2) * WORD)))
+ mc.LEA(esp, addr_add(ebp, imm(-3 * WORD)))
mc.MOV(eax, imm(fail_index))
- mc.POP(edi)
- mc.POP(esi)
- mc.POP(ebx)
- mc.POP(ebp)
+ mc.POP(edi) # [ebp-12]
+ mc.POP(esi) # [ebp-8]
+ mc.POP(ebx) # [ebp-4]
+ mc.POP(ebp) # [ebp]
mc.RET()
@specialize.arg(2)
@@ -1098,12 +1104,23 @@
self.mc.CALL(x)
self.mark_gc_roots()
self.mc.ADD(esp, imm(extra_on_stack))
- if size == 1:
+ if isinstance(resloc, MODRM64):
+ self.mc.FSTP(resloc)
+ elif size == 1:
self.mc.AND(eax, imm(0xff))
elif size == 2:
self.mc.AND(eax, imm(0xffff))
genop_call_pure = genop_call
+
+ def genop_guard_call_may_force(self, op, guard_op, addr,
+ arglocs, result_loc):
+ faildescr = guard_op.descr
+ fail_index = self.cpu.get_fail_descr_number(faildescr)
+ self.mc.MOV(mem(ebp, FORCE_INDEX_OFS), imm(fail_index))
+ self.genop_call(op, arglocs, result_loc)
+ self.mc.CMP(mem(ebp, FORCE_INDEX_OFS), imm(0))
+ return self.implement_guard(addr, self.mc.JL)
def genop_discard_cond_call_gc_wb(self, op, arglocs):
# use 'mc._mc' directly instead of 'mc', to avoid
@@ -1134,6 +1151,9 @@
assert 0 < offset <= 127
mc.overwrite(jz_location-1, [chr(offset)])
+ def genop_force_token(self, op, arglocs, resloc):
+ self.mc.LEA(resloc, mem(ebp, FORCE_INDEX_OFS))
+
def not_implemented_op_discard(self, op, arglocs):
msg = "not implemented operation: %s" % op.getopname()
print msg
Modified: pypy/trunk/pypy/jit/backend/x86/regalloc.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/regalloc.py (original)
+++ pypy/trunk/pypy/jit/backend/x86/regalloc.py Tue Dec 1 11:56:34 2009
@@ -19,6 +19,8 @@
TempBox
WORD = 4
+FRAME_FIXED_SIZE = 5 # ebp + ebx + esi + edi + force_index = 5 words
+FORCE_INDEX_OFS = -4*WORD
width_of_type = {
INT : 1,
@@ -98,10 +100,9 @@
def after_call(self, v):
# the result is stored in st0, but we don't have this around,
- # so we move it to some stack location
- if v is not None:
- loc = self.stack_manager.loc(v, 2)
- self.assembler.regalloc_fstp(loc)
+ # so genop_call will move it to some stack location immediately
+ # after the call
+ return self.stack_manager.loc(v, 2)
class X86StackManager(StackManager):
@@ -287,7 +288,8 @@
self.assembler.regalloc_perform_with_guard(op, guard_op, faillocs,
arglocs, result_loc,
self.sm.stack_depth)
- self.rm.possibly_free_var(op.result)
+ if op.result is not None:
+ self.possibly_free_var(op.result)
self.possibly_free_vars(guard_op.fail_args)
def perform_guard(self, guard_op, arglocs, result_loc):
@@ -308,7 +310,10 @@
self.assembler.dump('%s(%s)' % (op, arglocs))
self.assembler.regalloc_perform_discard(op, arglocs)
- def can_optimize_cmp_op(self, op, i, operations):
+ def can_merge_with_next_guard(self, op, i, operations):
+ if op.opnum == rop.CALL_MAY_FORCE:
+ assert operations[i + 1].opnum == rop.GUARD_NOT_FORCED
+ return True
if not op.is_comparison():
return False
if (operations[i + 1].opnum != rop.GUARD_TRUE and
@@ -332,7 +337,7 @@
i += 1
self.possibly_free_vars(op.args)
continue
- if self.can_optimize_cmp_op(op, i, operations):
+ if self.can_merge_with_next_guard(op, i, operations):
oplist[op.opnum](self, op, operations[i + 1])
i += 1
else:
@@ -604,25 +609,38 @@
self.Perform(op, [loc0], loc1)
self.rm.possibly_free_var(op.args[0])
- def _call(self, op, arglocs, force_store=[]):
- self.rm.before_call(force_store)
- self.xrm.before_call(force_store)
- self.Perform(op, arglocs, eax)
+ def _call(self, op, arglocs, force_store=[], guard_not_forced_op=None):
+ save_all_regs = guard_not_forced_op is not None
+ self.rm.before_call(force_store, save_all_regs=save_all_regs)
+ self.xrm.before_call(force_store, save_all_regs=save_all_regs)
if op.result is not None:
if op.result.type == FLOAT:
- self.xrm.after_call(op.result)
+ resloc = self.xrm.after_call(op.result)
else:
- self.rm.after_call(op.result)
+ resloc = self.rm.after_call(op.result)
+ else:
+ resloc = None
+ if guard_not_forced_op is not None:
+ self.perform_with_guard(op, guard_not_forced_op, arglocs, resloc)
+ else:
+ self.Perform(op, arglocs, resloc)
- def consider_call(self, op, ignored):
+ def _consider_call(self, op, guard_not_forced_op=None):
calldescr = op.descr
assert isinstance(calldescr, BaseCallDescr)
assert len(calldescr.arg_classes) == len(op.args) - 1
size = calldescr.get_result_size(self.translate_support_code)
- self._call(op, [imm(size)] + [self.loc(arg) for arg in op.args])
+ self._call(op, [imm(size)] + [self.loc(arg) for arg in op.args],
+ guard_not_forced_op=guard_not_forced_op)
+ def consider_call(self, op, ignored):
+ self._consider_call(op)
consider_call_pure = consider_call
+ def consider_call_may_force(self, op, guard_op):
+ assert guard_op is not None
+ self._consider_call(op, guard_op)
+
def consider_cond_call_gc_wb(self, op, ignored):
assert op.result is None
arglocs = [self.loc(arg) for arg in op.args]
@@ -927,6 +945,10 @@
assert reg is eax # ok to ignore this one
return gcrootmap.compress_callshape(shape)
+ def consider_force_token(self, op, ignored):
+ loc = self.rm.force_allocate_reg(op.result)
+ self.Perform(op, [], loc)
+
def not_implemented_op(self, op, ignored):
msg = "[regalloc] Not implemented operation: %s" % op.getopname()
print msg
@@ -942,10 +964,9 @@
def get_ebp_ofs(position):
# Argument is a stack position (0, 1, 2...).
- # Returns (ebp-16), (ebp-20), (ebp-24)...
- # This depends on the fact that our function prologue contains
- # exactly 4 PUSHes.
- return -WORD * (4 + position)
+ # Returns (ebp-20), (ebp-24), (ebp-28)...
+ # i.e. the n'th word beyond the fixed frame size.
+ return -WORD * (FRAME_FIXED_SIZE + position)
def lower_byte(reg):
# argh, kill, use lowest8bits instead
Modified: pypy/trunk/pypy/jit/backend/x86/runner.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/runner.py (original)
+++ pypy/trunk/pypy/jit/backend/x86/runner.py Tue Dec 1 11:56:34 2009
@@ -6,9 +6,9 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.jit.metainterp import history
from pypy.jit.backend.x86.assembler import Assembler386
+from pypy.jit.backend.x86.regalloc import FORCE_INDEX_OFS
from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU
-
class CPU386(AbstractLLCPU):
debug = True
supports_floats = True
@@ -59,6 +59,9 @@
llmemory.GCREF.TO))
return ptrvalue
+ def get_latest_force_token(self):
+ return self.assembler.fail_ebp + FORCE_INDEX_OFS
+
def execute_token(self, executable_token):
addr = executable_token._x86_bootstrap_code
func = rffi.cast(lltype.Ptr(self.BOOTSTRAP_TP), addr)
@@ -87,6 +90,27 @@
adr = llmemory.cast_ptr_to_adr(x)
return CPU386.cast_adr_to_int(adr)
+ all_null_registers = lltype.malloc(rffi.LONGP.TO, 24,
+ flavor='raw', zero=True)
+
+ def force(self, addr_of_force_index):
+ TP = rffi.CArrayPtr(lltype.Signed)
+ fail_index = rffi.cast(TP, addr_of_force_index)[0]
+ assert fail_index >= 0, "already forced!"
+ faildescr = self.get_fail_descr_from_number(fail_index)
+ rffi.cast(TP, addr_of_force_index)[0] = -1
+ bytecode = rffi.cast(rffi.UCHARP,
+ faildescr._x86_failure_recovery_bytecode)
+ # start of "no gc operation!" block
+ fail_index_2 = self.assembler.grab_frame_values(
+ bytecode,
+ addr_of_force_index - FORCE_INDEX_OFS,
+ self.all_null_registers)
+ self.assembler.leave_jitted_hook()
+ # end of "no gc operation!" block
+ assert fail_index == fail_index_2
+ return faildescr
+
class CPU386_NO_SSE2(CPU386):
supports_floats = False
Modified: pypy/trunk/pypy/jit/backend/x86/test/test_gc_integration.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/test/test_gc_integration.py (original)
+++ pypy/trunk/pypy/jit/backend/x86/test/test_gc_integration.py Tue Dec 1 11:56:34 2009
@@ -9,7 +9,7 @@
from pypy.jit.backend.llsupport.descr import GcCache
from pypy.jit.backend.llsupport.gc import GcLLDescription
from pypy.jit.backend.x86.runner import CPU
-from pypy.jit.backend.x86.regalloc import RegAlloc, WORD
+from pypy.jit.backend.x86.regalloc import RegAlloc, WORD, FRAME_FIXED_SIZE
from pypy.jit.metainterp.test.oparser import parse
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.annlowlevel import llhelper
@@ -83,7 +83,8 @@
#
mark = regalloc.get_mark_gc_roots(cpu.gc_ll_descr.gcrootmap)
assert mark[0] == 'compressed'
- expected = ['ebx', 'esi', 'edi', -16, -20, -24]
+ base = -WORD * FRAME_FIXED_SIZE
+ expected = ['ebx', 'esi', 'edi', base, base-4, base-8]
assert dict.fromkeys(mark[1:]) == dict.fromkeys(expected)
class TestRegallocGcIntegration(BaseTestRegalloc):
Modified: pypy/trunk/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/codewriter.py (original)
+++ pypy/trunk/pypy/jit/metainterp/codewriter.py Tue Dec 1 11:56:34 2009
@@ -14,6 +14,7 @@
from pypy.translator.backendopt.writeanalyze import WriteAnalyzer
from pypy.jit.metainterp.typesystem import deref, arrayItem, fieldType
from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
+from pypy.jit.metainterp.effectinfo import VirtualizableAnalyzer
import py, sys
from pypy.tool.ansi_print import ansi_log
@@ -182,8 +183,10 @@
self.metainterp_sd = metainterp_sd
self.cpu = metainterp_sd.cpu
self.portal_runner_ptr = portal_runner_ptr
- self.raise_analyzer = RaiseAnalyzer(self.rtyper.annotator.translator)
- self.write_analyzer = WriteAnalyzer(self.rtyper.annotator.translator)
+ translator = self.rtyper.annotator.translator
+ self.raise_analyzer = RaiseAnalyzer(translator)
+ self.write_analyzer = WriteAnalyzer(translator)
+ self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
def make_portal_bytecode(self, graph):
log.info("making JitCodes...")
@@ -323,7 +326,9 @@
# ok
if consider_effects_of is not None:
effectinfo = effectinfo_from_writeanalyze(
- self.write_analyzer.analyze(consider_effects_of), self.cpu)
+ self.write_analyzer.analyze(consider_effects_of),
+ self.cpu,
+ self.virtualizable_analyzer.analyze(consider_effects_of))
calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT, effectinfo)
else:
calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT)
@@ -1203,12 +1208,19 @@
if op.opname == "direct_call":
func = getattr(get_funcobj(op.args[0].value), '_callable', None)
pure = getattr(func, "_pure_function_", False)
+ all_promoted_args = getattr(func,
+ "_pure_function_with_all_promoted_args_", False)
+ if pure and not all_promoted_args:
+ effectinfo = calldescr.get_extra_info()
+ assert (effectinfo is not None and
+ not effectinfo.promotes_virtualizables)
try:
canraise = self.codewriter.raise_analyzer.can_raise(op)
except lltype.DelayedPointer:
canraise = True # if we need to look into the delayed ptr that is
# the portal, then it's certainly going to raise
if pure:
+ # XXX check what to do about exceptions (also MemoryError?)
self.emit('residual_call_pure')
elif canraise:
self.emit('residual_call')
@@ -1236,9 +1248,8 @@
def handle_regular_indirect_call(self, op):
self.codewriter.register_indirect_call_targets(op)
args = op.args[1:-1]
- calldescr, non_void_args = self.codewriter.getcalldescr(op.args[0],
- args,
- op.result)
+ calldescr, non_void_args = self.codewriter.getcalldescr(
+ op.args[0], args, op.result, consider_effects_of=op)
self.minimize_variables()
self.emit('indirect_call')
self.emit(self.get_position(calldescr))
Modified: pypy/trunk/pypy/jit/metainterp/compile.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/compile.py (original)
+++ pypy/trunk/pypy/jit/metainterp/compile.py Tue Dec 1 11:56:34 2009
@@ -221,6 +221,7 @@
if box:
fail_arg_types[i] = box.type
self.fail_arg_types = fail_arg_types
+ # XXX ^^^ kill this attribute
def handle_fail(self, metainterp_sd):
from pypy.jit.metainterp.pyjitpl import MetaInterp
@@ -236,6 +237,41 @@
send_bridge_to_backend(metainterp.staticdata, self, inputargs,
new_loop.operations)
+
+class ResumeGuardForcedDescr(ResumeGuardDescr):
+
+ def handle_fail(self, metainterp_sd):
+ from pypy.jit.metainterp.pyjitpl import MetaInterp
+ metainterp = MetaInterp(metainterp_sd)
+ token = metainterp_sd.cpu.get_latest_force_token()
+ metainterp._already_allocated_resume_virtuals = self.fetch_data(token)
+ self.counter = -2 # never compile
+ return metainterp.handle_guard_failure(self)
+
+ def force_virtualizable(self, vinfo, virtualizable, force_token):
+ from pypy.jit.metainterp.pyjitpl import MetaInterp
+ from pypy.jit.metainterp.resume import force_from_resumedata
+ metainterp = MetaInterp(self.metainterp_sd)
+ metainterp.history = None # blackholing
+ liveboxes = metainterp.load_values_from_failure(self)
+ virtualizable_boxes, data = force_from_resumedata(metainterp,
+ liveboxes, self)
+ vinfo.write_boxes(virtualizable, virtualizable_boxes)
+ self.save_data(force_token, data)
+
+ def save_data(self, key, value):
+ globaldata = self.metainterp_sd.globaldata
+ assert key not in globaldata.resume_virtuals
+ globaldata.resume_virtuals[key] = value
+
+ def fetch_data(self, key):
+ globaldata = self.metainterp_sd.globaldata
+ assert key in globaldata.resume_virtuals
+ data = globaldata.resume_virtuals[key]
+ del globaldata.resume_virtuals[key]
+ return data
+
+
class ResumeFromInterpDescr(ResumeDescr):
def __init__(self, original_greenkey, redkey):
ResumeDescr.__init__(self, original_greenkey)
Modified: pypy/trunk/pypy/jit/metainterp/doc/jitpl5.txt
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/doc/jitpl5.txt (original)
+++ pypy/trunk/pypy/jit/metainterp/doc/jitpl5.txt Tue Dec 1 11:56:34 2009
@@ -78,16 +78,11 @@
matches the real data -- but this is delicate because of the
non-escaping flag.
-Instead, this is done by doing tracing from the start of the loop again.
-At the end, we don't do perfect specialization (for now), but simply
-check that the already-computed specialization still applies, and then
-jump to the already-compiled loop. (If it does not match, for now we
-just cancel everything.)
-
-If the loop is not only executed but *entered* often enough, then after
-this tracing, we generate a second copy of the loop (a "bridge") that
-starts with all variables unspecialized, and ends with a jump to the
-real loop. From this point on, we can just jump directly to the bridge
+Instead, this is done by "entry bridges": we do tracing from
+the start of the loop again, and at the end, we try to compile
+the recorded trace as a "bridge" that comes from the
+interpreter (i.e. with no virtuals at all) and goes to the old
+loop. Later on, we can just jump directly to the entry bridge
from the JUMP_ABSOLUTE bytecode.
@@ -105,10 +100,7 @@
take the set of live values and put them back into boxes, and proceed
with tracing for the rest of the loop.
-For now, we just check at the end of the loop that it matches the
-already-computed specialization. If not, we cancel creating the
-compiled version of it (and mark the guard so that future failures
-always fall back to interpretation). To do this, when we created the
-original loop, at every guard, we needed to record the set of live
-values (mostly in which register or stack location they are) as well as
-an "escaped-so-far" flag for each pointer.
+At the end of the loop, we check that it matches an already-computed
+specialization. If not, we go on tracing. This might unroll the loop
+once. (Note that there is a global limit on the length of the recorded
+trace, to avoid tracing forever.)
Modified: pypy/trunk/pypy/jit/metainterp/doc/loop.txt
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/doc/loop.txt (original)
+++ pypy/trunk/pypy/jit/metainterp/doc/loop.txt Tue Dec 1 11:56:34 2009
@@ -36,32 +36,11 @@
. VirtualSpec(cls, name1=spec1, ...)
|
- VirtualizableSpec(cls, name1=spec1, ...)
- |
- FixedClassSpec(cls)
- |
NotSpec
-For (a simplified) example, ``VirtualizableSpec(PyFrame, x =
-VirtualSpec(W_IntObject, value = NotSpec))`` describes the virtualizable
-frame for a loop in which the only used variable is ``x``, which is a
-virtual ``W_IntObject``.
-
-The intersection rules are:
-
-* the intersection of two ``VirtualSpec`` of the same ``cls`` is a
- further ``VirtualSpec``, and we proceed with the intersection of
- each field.
-
-* the intersection of two ``VirtualizableSpec`` of the same ``cls`` is
- like the previous case, except that some names may be omitted
- completely from a given ``VirtualizableSpec``; in the case a name is
- present in only one of the ``VirtualizableSpec``, we just keep it
- unmodified in the intersection.
-
-* in other cases, the result is ``FixedClassSpec`` if the two specnodes
- have the same class, or ``NotSpec`` if any is a ``NotSpec`` or if the
- two classes differ.
+For example, ``VirtualSpec(W_IntObject, value = NotSpec))`` describes a
+variable which is a virtual ``W_IntObject``, containing a value that is
+a real integer.
Overall Approach
Modified: pypy/trunk/pypy/jit/metainterp/effectinfo.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/effectinfo.py (original)
+++ pypy/trunk/pypy/jit/metainterp/effectinfo.py Tue Dec 1 11:56:34 2009
@@ -2,21 +2,25 @@
from pypy.rpython.lltypesystem.rclass import OBJECT
from pypy.rpython.lltypesystem import lltype
from pypy.rpython.ootypesystem import ootype
+from pypy.translator.backendopt.graphanalyze import BoolGraphAnalyzer
class EffectInfo(object):
_cache = {}
- def __new__(cls, write_descrs_fields, write_descrs_arrays):
- key = frozenset(write_descrs_fields), frozenset(write_descrs_arrays)
+ def __new__(cls, write_descrs_fields, write_descrs_arrays,
+ promotes_virtualizables=False):
+ key = (frozenset(write_descrs_fields), frozenset(write_descrs_arrays),
+ promotes_virtualizables)
if key in cls._cache:
return cls._cache[key]
result = object.__new__(cls)
result.write_descrs_fields = write_descrs_fields
result.write_descrs_arrays = write_descrs_arrays
+ result.promotes_virtualizables = promotes_virtualizables
cls._cache[key] = result
return result
-def effectinfo_from_writeanalyze(effects, cpu):
+def effectinfo_from_writeanalyze(effects, cpu, promotes_virtualizables=False):
from pypy.translator.backendopt.writeanalyze import top_set
if effects is top_set:
return None
@@ -39,7 +43,8 @@
write_descrs_arrays.append(descr)
else:
assert 0
- return EffectInfo(write_descrs_fields, write_descrs_arrays)
+ return EffectInfo(write_descrs_fields, write_descrs_arrays,
+ promotes_virtualizables)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
@@ -55,7 +60,6 @@
return False
return True
-
def consider_array(ARRAY):
if arrayItem(ARRAY) is lltype.Void:
return False
@@ -64,3 +68,9 @@
if not isinstance(ARRAY, lltype.GcArray): # can be a non-GC-array
return False
return True
+
+# ____________________________________________________________
+
+class VirtualizableAnalyzer(BoolGraphAnalyzer):
+ def analyze_simple_operation(self, op):
+ return op.opname == 'promote_virtualizable'
Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py (original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py Tue Dec 1 11:56:34 2009
@@ -632,6 +632,7 @@
varargs = [jitcode.cfnptr] + varargs
res = self.execute_varargs(rop.CALL, varargs,
descr=jitcode.calldescr, exc=True)
+ self.metainterp.load_fields_from_virtualizable()
else:
# for oosends (ootype only): calldescr is a MethDescr
res = self.execute_varargs(rop.OOSEND, varargs,
@@ -651,7 +652,7 @@
@arguments("descr", "varargs")
def opimpl_residual_call(self, calldescr, varargs):
- return self.execute_varargs(rop.CALL, varargs, descr=calldescr, exc=True)
+ return self.do_residual_call(varargs, descr=calldescr, exc=True)
@arguments("varargs")
def opimpl_recursion_leave_prep(self, varargs):
@@ -675,11 +676,11 @@
greenkey = varargs[1:num_green_args + 1]
if warmrunnerstate.can_inline_callable(greenkey):
return self.perform_call(portal_code, varargs[1:], greenkey)
- return self.execute_varargs(rop.CALL, varargs, descr=calldescr, exc=True)
+ return self.do_residual_call(varargs, descr=calldescr, exc=True)
@arguments("descr", "varargs")
def opimpl_residual_call_noexception(self, calldescr, varargs):
- self.execute_varargs(rop.CALL, varargs, descr=calldescr, exc=False)
+ self.do_residual_call(varargs, descr=calldescr, exc=False)
@arguments("descr", "varargs")
def opimpl_residual_call_pure(self, calldescr, varargs):
@@ -696,8 +697,8 @@
return self.perform_call(jitcode, varargs)
else:
# but we should not follow calls to that graph
- return self.execute_varargs(rop.CALL, [box] + varargs,
- descr=calldescr, exc=True)
+ return self.do_residual_call([box] + varargs,
+ descr=calldescr, exc=True)
@arguments("orgpc", "methdescr", "varargs")
def opimpl_oosend(self, pc, methdescr, varargs):
@@ -924,7 +925,6 @@
if isinstance(box, Const): # no need for a guard
return
metainterp = self.metainterp
- metainterp_sd = metainterp.staticdata
if metainterp.is_blackholing():
return
saved_pc = self.pc
@@ -933,8 +933,14 @@
moreargs = [box] + extraargs
else:
moreargs = list(extraargs)
+ metainterp_sd = metainterp.staticdata
original_greenkey = metainterp.resumekey.original_greenkey
- resumedescr = compile.ResumeGuardDescr(metainterp_sd, original_greenkey)
+ if opnum == rop.GUARD_NOT_FORCED:
+ resumedescr = compile.ResumeGuardForcedDescr(metainterp_sd,
+ original_greenkey)
+ else:
+ resumedescr = compile.ResumeGuardDescr(metainterp_sd,
+ original_greenkey)
guard_op = metainterp.history.record(opnum, moreargs, None,
descr=resumedescr)
virtualizable_boxes = None
@@ -980,6 +986,24 @@
return self.metainterp.handle_exception()
return False
+ def do_residual_call(self, argboxes, descr, exc):
+ effectinfo = descr.get_extra_info()
+ if effectinfo is None or effectinfo.promotes_virtualizables:
+ # residual calls require attention to keep virtualizables in-sync
+ self.metainterp.vable_before_residual_call()
+ # xxx do something about code duplication
+ resbox = self.metainterp.execute_and_record_varargs(
+ rop.CALL_MAY_FORCE, argboxes, descr=descr)
+ self.metainterp.vable_after_residual_call()
+ if resbox is not None:
+ self.make_result_box(resbox)
+ self.generate_guard(self.pc, rop.GUARD_NOT_FORCED, None, [])
+ if exc:
+ return self.metainterp.handle_exception()
+ return False
+ else:
+ return self.execute_varargs(rop.CALL, argboxes, descr, exc)
+
# ____________________________________________________________
class MetaInterpStaticData(object):
@@ -1142,6 +1166,7 @@
self.indirectcall_dict = None
self.addr2name = None
self.loopnumbering = 0
+ self.resume_virtuals = {}
#
state = staticdata.state
if state is not None:
@@ -1168,6 +1193,8 @@
class MetaInterp(object):
in_recursion = 0
+ _already_allocated_resume_virtuals = None
+
def __init__(self, staticdata):
self.staticdata = staticdata
self.cpu = staticdata.cpu
@@ -1298,7 +1325,8 @@
@specialize.arg(1)
def execute_and_record(self, opnum, descr, *argboxes):
history.check_descr(descr)
- assert opnum != rop.CALL and opnum != rop.OOSEND
+ assert (opnum != rop.CALL and opnum != rop.CALL_MAY_FORCE
+ and opnum != rop.OOSEND)
# execute the operation
profiler = self.staticdata.profiler
profiler.count_ops(opnum)
@@ -1315,12 +1343,6 @@
@specialize.arg(1)
def execute_and_record_varargs(self, opnum, argboxes, descr=None):
history.check_descr(descr)
- # residual calls require attention to keep virtualizables in-sync.
- # CALL_PURE doesn't need it because so far 'promote_virtualizable'
- # as an operation is enough to make the called function non-pure.
- require_attention = (opnum == rop.CALL or opnum == rop.OOSEND)
- if require_attention and not self.is_blackholing():
- self.before_residual_call()
# execute the operation
profiler = self.staticdata.profiler
profiler.count_ops(opnum)
@@ -1328,17 +1350,12 @@
if self.is_blackholing():
profiler.count_ops(opnum, BLACKHOLED_OPS)
else:
- if require_attention:
- require_attention = self.after_residual_call()
# check if the operation can be constant-folded away
argboxes = list(argboxes)
if rop._ALWAYS_PURE_FIRST <= opnum <= rop._ALWAYS_PURE_LAST:
resbox = self._record_helper_pure_varargs(opnum, resbox, descr, argboxes)
else:
resbox = self._record_helper_nonpure_varargs(opnum, resbox, descr, argboxes)
- # if we are blackholing require_attention has the initial meaning
- if require_attention:
- self.after_generate_residual_call()
return resbox
def _record_helper_pure(self, opnum, resbox, descr, *argboxes):
@@ -1572,7 +1589,8 @@
self.framestack[-1].follow_jump()
elif opnum == rop.GUARD_FALSE: # a goto_if_not that stops jumping
self.framestack[-1].dont_follow_jump()
- elif opnum == rop.GUARD_NO_EXCEPTION or opnum == rop.GUARD_EXCEPTION:
+ elif (opnum == rop.GUARD_NO_EXCEPTION or opnum == rop.GUARD_EXCEPTION
+ or opnum == rop.GUARD_NOT_FORCED):
self.handle_exception()
elif opnum == rop.GUARD_NO_OVERFLOW: # an overflow now detected
self.raise_overflow_error()
@@ -1722,36 +1740,39 @@
vinfo = self.staticdata.virtualizable_info
virtualizable_box = self.virtualizable_boxes[-1]
virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
- vinfo.clear_vable_rti(virtualizable)
+ vinfo.clear_vable_token(virtualizable)
- def before_residual_call(self):
+ def vable_before_residual_call(self):
+ if self.is_blackholing():
+ return
vinfo = self.staticdata.virtualizable_info
if vinfo is not None:
virtualizable_box = self.virtualizable_boxes[-1]
virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
vinfo.tracing_before_residual_call(virtualizable)
+ #
+ force_token_box = history.BoxInt()
+ self.history.record(rop.FORCE_TOKEN, [], force_token_box)
+ self.history.record(rop.SETFIELD_GC, [virtualizable_box,
+ force_token_box],
+ None, descr=vinfo.vable_token_descr)
- def after_residual_call(self):
- vinfo = self.staticdata.virtualizable_info
- if vinfo is not None:
- virtualizable_box = self.virtualizable_boxes[-1]
- virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
- if vinfo.tracing_after_residual_call(virtualizable):
- # This is after the residual call is done, but before it
- # is actually generated. We first generate a store-
- # everything-back, *without actually performing it now*
- # as it contains the old values (before the call)!
- self.gen_store_back_in_virtualizable_no_perform()
- return True # must call after_generate_residual_call()
- # otherwise, don't call after_generate_residual_call()
- return False
-
- def after_generate_residual_call(self):
- # Called after generating a residual call, and only if
- # after_residual_call() returned True, i.e. if code in the residual
- # call causes the virtualizable to escape. Reload the modified
- # fields of the virtualizable.
- self.gen_load_fields_from_virtualizable()
+ def vable_after_residual_call(self):
+ if self.is_blackholing():
+ vable_escapes = True
+ else:
+ vable_escapes = False
+ vinfo = self.staticdata.virtualizable_info
+ if vinfo is not None:
+ virtualizable_box = self.virtualizable_boxes[-1]
+ virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
+ if vinfo.tracing_after_residual_call(virtualizable):
+ # We just did the residual call, and it shows that the
+ # virtualizable escapes.
+ self.switch_to_blackhole()
+ vable_escapes = True
+ if vable_escapes:
+ self.load_fields_from_virtualizable()
def handle_exception(self):
etype = self.cpu.get_exception()
@@ -1793,7 +1814,7 @@
# is and stays NULL.
virtualizable_box = self.virtualizable_boxes[-1]
virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
- assert not virtualizable.vable_rti
+ assert not virtualizable.vable_token
self.synchronize_virtualizable()
def check_synchronized_virtualizable(self):
@@ -1809,27 +1830,17 @@
virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
vinfo.write_boxes(virtualizable, self.virtualizable_boxes)
- def gen_load_fields_from_virtualizable(self):
+ def load_fields_from_virtualizable(self):
+ # Force a reload of the virtualizable fields into the local
+ # boxes (called only in escaping cases)
+ assert self.is_blackholing()
vinfo = self.staticdata.virtualizable_info
if vinfo is not None:
- vbox = self.virtualizable_boxes[-1]
- for i in range(vinfo.num_static_extra_boxes):
- descr = vinfo.static_field_descrs[i]
- fieldbox = self.execute_and_record(rop.GETFIELD_GC, descr,
- vbox)
- self.virtualizable_boxes[i] = fieldbox
- i = vinfo.num_static_extra_boxes
- virtualizable = vinfo.unwrap_virtualizable_box(vbox)
- for k in range(vinfo.num_arrays):
- descr = vinfo.array_field_descrs[k]
- abox = self.execute_and_record(rop.GETFIELD_GC, descr, vbox)
- descr = vinfo.array_descrs[k]
- for j in range(vinfo.get_array_length(virtualizable, k)):
- itembox = self.execute_and_record(rop.GETARRAYITEM_GC,
- descr, abox, ConstInt(j))
- self.virtualizable_boxes[i] = itembox
- i += 1
- assert i + 1 == len(self.virtualizable_boxes)
+ virtualizable_box = self.virtualizable_boxes[-1]
+ virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
+ self.virtualizable_boxes = vinfo.read_boxes(self.cpu,
+ virtualizable)
+ self.virtualizable_boxes.append(virtualizable_box)
def gen_store_back_in_virtualizable(self):
vinfo = self.staticdata.virtualizable_info
@@ -1853,29 +1864,6 @@
abox, ConstInt(j), itembox)
assert i + 1 == len(self.virtualizable_boxes)
- def gen_store_back_in_virtualizable_no_perform(self):
- vinfo = self.staticdata.virtualizable_info
- # xxx only write back the fields really modified
- vbox = self.virtualizable_boxes[-1]
- for i in range(vinfo.num_static_extra_boxes):
- fieldbox = self.virtualizable_boxes[i]
- self.history.record(rop.SETFIELD_GC, [vbox, fieldbox], None,
- descr=vinfo.static_field_descrs[i])
- i = vinfo.num_static_extra_boxes
- virtualizable = vinfo.unwrap_virtualizable_box(vbox)
- for k in range(vinfo.num_arrays):
- abox = vinfo.BoxArray()
- self.history.record(rop.GETFIELD_GC, [vbox], abox,
- descr=vinfo.array_field_descrs[k])
- for j in range(vinfo.get_array_length(virtualizable, k)):
- itembox = self.virtualizable_boxes[i]
- i += 1
- self.history.record(rop.SETARRAYITEM_GC,
- [abox, ConstInt(j), itembox],
- None,
- descr=vinfo.array_descrs[k])
- assert i + 1 == len(self.virtualizable_boxes)
-
def replace_box(self, oldbox, newbox):
for frame in self.framestack:
boxes = frame.env
Modified: pypy/trunk/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resoperation.py (original)
+++ pypy/trunk/pypy/jit/metainterp/resoperation.py Tue Dec 1 11:56:34 2009
@@ -125,6 +125,7 @@
'GUARD_EXCEPTION',
'GUARD_NO_OVERFLOW',
'GUARD_OVERFLOW',
+ 'GUARD_NOT_FORCED',
'_GUARD_LAST', # ----- end of guard operations -----
'_NOSIDEEFFECT_FIRST', # ----- start of no_side_effect operations -----
@@ -220,9 +221,11 @@
'COND_CALL_GC_MALLOC', # [a, b, if_(a<=b)_result, if_(a>b)_call, args...]
# => result (for mallocs)
'DEBUG_MERGE_POINT/1', # debugging only
+ 'FORCE_TOKEN/0',
'_CANRAISE_FIRST', # ----- start of can_raise operations -----
'CALL',
+ 'CALL_MAY_FORCE',
'OOSEND', # ootype operation
'_CANRAISE_LAST', # ----- end of can_raise operations -----
Modified: pypy/trunk/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resume.py (original)
+++ pypy/trunk/pypy/jit/metainterp/resume.py Tue Dec 1 11:56:34 2009
@@ -455,6 +455,10 @@
metainterp.framestack.reverse()
return virtualizable_boxes
+def force_from_resumedata(metainterp, newboxes, storage):
+ resumereader = ResumeDataReader(storage, newboxes, metainterp)
+ return resumereader.consume_boxes(), resumereader.virtuals
+
class ResumeDataReader(object):
virtuals = None
@@ -468,6 +472,10 @@
def _prepare_virtuals(self, metainterp, virtuals):
if virtuals:
+ v = metainterp._already_allocated_resume_virtuals
+ if v is not None:
+ self.virtuals = v
+ return
self.virtuals = [None] * len(virtuals)
for i in range(len(virtuals)):
vinfo = virtuals[i]
@@ -476,7 +484,8 @@
for i in range(len(virtuals)):
vinfo = virtuals[i]
if vinfo is not None:
- vinfo.setfields(metainterp, self.virtuals[i], self._decode_box)
+ vinfo.setfields(metainterp, self.virtuals[i],
+ self._decode_box)
def consume_boxes(self):
numb = self.cur_numb
Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py Tue Dec 1 11:56:34 2009
@@ -50,8 +50,8 @@
assert get_stats().enter_count <= count
def check_jumps(self, maxcount):
assert get_stats().exec_jumps <= maxcount
- def check_aborted_count(self, maxcount):
- assert get_stats().aborted_count == maxcount
+ def check_aborted_count(self, count):
+ assert get_stats().aborted_count == count
def meta_interp(self, *args, **kwds):
kwds['CPUClass'] = self.CPUClass
@@ -148,6 +148,9 @@
type_system = 'ootype'
CPUClass = runner.OOtypeCPU
+ def setup_class(cls):
+ py.test.skip("ootype tests skipped for now")
+
@staticmethod
def Ptr(T):
return T
Modified: pypy/trunk/pypy/jit/metainterp/test/test_codewriter.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_codewriter.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_codewriter.py Tue Dec 1 11:56:34 2009
@@ -121,8 +121,8 @@
supports_floats = False
def fielddescrof(self, STRUCT, fieldname):
return ('fielddescr', STRUCT, fieldname)
- def calldescrof(self, FUNC, NON_VOID_ARGS, RESULT, stuff=None):
- return ('calldescr', FUNC, NON_VOID_ARGS, RESULT)
+ def calldescrof(self, FUNC, NON_VOID_ARGS, RESULT, effectinfo=None):
+ return ('calldescr', FUNC, NON_VOID_ARGS, RESULT, effectinfo)
def typedescrof(self, CLASS):
return ('typedescr', CLASS)
def methdescrof(self, CLASS, methname):
@@ -273,9 +273,17 @@
cw._start(self.metainterp_sd, None)
jitcode = cw.make_one_bytecode((graphs[0], None), False)
assert len(self.metainterp_sd.indirectcalls) == 1
- names = [jitcode.name for (fnaddress, jitcode)
+ names = [jitcode1.name for (fnaddress, jitcode1)
in self.metainterp_sd.indirectcalls]
assert dict.fromkeys(names) == {'g': None}
+ calldescrs = [calldescr for calldescr in jitcode.constants
+ if isinstance(calldescr, tuple) and
+ calldescr[0] == 'calldescr']
+ assert len(calldescrs) == 1
+ assert calldescrs[0][4] is not None
+ assert not calldescrs[0][4].write_descrs_fields
+ assert not calldescrs[0][4].write_descrs_arrays
+ assert not calldescrs[0][4].promotes_virtualizables
def test_oosend_look_inside_only_one(self):
class A:
@@ -386,6 +394,47 @@
assert cw.list_of_addr2name[0][1].endswith('.A1')
assert cw.list_of_addr2name[1][1] == 'A1.g'
+ def test_promote_virtualizable_effectinfo(self):
+ class Frame(object):
+ _virtualizable2_ = ['x']
+
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ def g1(f):
+ f.x += 1
+
+ def g2(f):
+ return f.x
+
+ def h(f):
+ f.y -= 1
+
+ def f(n):
+ f_inst = Frame(n+1, n+2)
+ g1(f_inst)
+ r = g2(f_inst)
+ h(f_inst)
+ return r
+
+ graphs = self.make_graphs(f, [5])
+ cw = CodeWriter(self.rtyper)
+ cw.candidate_graphs = [graphs[0]]
+ cw._start(self.metainterp_sd, None)
+ jitcode = cw.make_one_bytecode((graphs[0], None), False)
+ calldescrs = [calldescr for calldescr in jitcode.constants
+ if isinstance(calldescr, tuple) and
+ calldescr[0] == 'calldescr']
+ assert len(calldescrs) == 4 # for __init__, g1, g2, h.
+ effectinfo_g1 = calldescrs[1][4]
+ effectinfo_g2 = calldescrs[2][4]
+ effectinfo_h = calldescrs[3][4]
+ assert effectinfo_g1.promotes_virtualizables
+ assert effectinfo_g2.promotes_virtualizables
+ assert not effectinfo_h.promotes_virtualizables
+
+
class ImmutableFieldsTests:
def test_fields(self):
Modified: pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_recursive.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_recursive.py Tue Dec 1 11:56:34 2009
@@ -144,10 +144,11 @@
f = self.get_interpreter(codes)
assert self.meta_interp(f, [0, 0, 0], optimizer=OPTIMIZER_SIMPLE) == 42
- self.check_loops(int_add = 1, call = 1)
+ self.check_loops(int_add = 1, call_may_force = 1, call = 0)
assert self.meta_interp(f, [0, 0, 0], optimizer=OPTIMIZER_SIMPLE,
inline=True) == 42
- self.check_loops(int_add = 2, call = 0, guard_no_exception = 0)
+ self.check_loops(int_add = 2, call_may_force = 0, call = 0,
+ guard_no_exception = 0)
def test_inline_jitdriver_check(self):
code = "021"
@@ -158,7 +159,7 @@
assert self.meta_interp(f, [0, 0, 0], optimizer=OPTIMIZER_SIMPLE,
inline=True) == 42
- self.check_loops(call = 1)
+ self.check_loops(call_may_force = 1, call = 0)
def test_inline_faulty_can_inline(self):
code = "021"
@@ -488,10 +489,10 @@
return loop(100)
res = self.meta_interp(main, [0], optimizer=OPTIMIZER_SIMPLE, trace_limit=TRACE_LIMIT)
- self.check_loops(call=1)
+ self.check_loops(call_may_force=1, call=0)
res = self.meta_interp(main, [1], optimizer=OPTIMIZER_SIMPLE, trace_limit=TRACE_LIMIT)
- self.check_loops(call=0)
+ self.check_loops(call_may_force=0, call=0)
def test_leave_jit_hook(self):
from pypy.rpython.annlowlevel import hlstr
@@ -645,7 +646,7 @@
result += f('-c-----------l-', i+100)
self.meta_interp(g, [10], backendopt=True)
self.check_aborted_count(1)
- self.check_history(call=1)
+ self.check_history(call_may_force=1, call=0)
self.check_tree_loop_count(3)
Modified: pypy/trunk/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_resume.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_resume.py Tue Dec 1 11:56:34 2009
@@ -41,6 +41,8 @@
assert not tagged_list_eq([tag(1, TAGBOX), tag(-2, TAGBOX)], [tag(1, TAGBOX)])
class MyMetaInterp:
+ _already_allocated_resume_virtuals = None
+
def __init__(self, cpu=None):
if cpu is None:
cpu = LLtypeMixin.cpu
@@ -124,6 +126,7 @@
rd_numb = []
rd_consts = []
class FakeMetainterp(object):
+ _already_allocated_resume_virtuals = None
cpu = None
reader = ResumeDataReader(FakeStorage(), [], FakeMetainterp())
assert reader.virtuals == ["allocated", None]
Modified: pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py Tue Dec 1 11:56:34 2009
@@ -7,7 +7,6 @@
from pypy.rlib.jit import OPTIMIZER_SIMPLE, OPTIMIZER_FULL
from pypy.rlib.rarithmetic import intmask
from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
-from pypy.rpython.lltypesystem.rvirtualizable2 import VABLERTIPTR
from pypy.rpython.rclass import FieldListAccessor
from pypy.jit.metainterp.warmspot import get_stats, get_translator
from pypy.jit.metainterp import history, heaptracker
@@ -41,8 +40,7 @@
XY = lltype.GcStruct(
'XY',
('parent', rclass.OBJECT),
- ('vable_base', llmemory.Address),
- ('vable_rti', VABLERTIPTR),
+ ('vable_token', lltype.Signed),
('inst_x', lltype.Signed),
('inst_node', lltype.Ptr(LLtypeMixin.NODE)),
hints = {'virtualizable2_accessor': FieldListAccessor()})
@@ -57,7 +55,7 @@
def setup(self):
xy = lltype.malloc(self.XY)
- xy.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
+ xy.vable_token = 0
xy.parent.typeptr = self.xy_vtable
return xy
@@ -200,79 +198,12 @@
assert res == 134
self.check_loops(getfield_gc=1, setfield_gc=1)
- def test_external_read_while_tracing(self):
- myjitdriver = JitDriver(greens = [], reds = ['n', 'm', 'xy'],
- virtualizables = ['xy'])
- class Outer:
- pass
- outer = Outer()
- def ext():
- xy = outer.xy
- promote_virtualizable(xy, 'inst_x')
- return xy.inst_x + 2
- def f(n):
- xy = self.setup()
- xy.inst_x = 10
- outer.xy = xy
- m = 0
- while n > 0:
- myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
- myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
- promote_virtualizable(xy, 'inst_x')
- xy.inst_x = n + 9998 # virtualized away
- m += ext() # 2x setfield_gc, 2x getfield_gc
- promote_virtualizable(xy, 'inst_x')
- xy.inst_x = 10 # virtualized away
- n -= 1
- return m
- assert f(20) == 10000*20 + (20*21)/2
- res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
- assert res == 10000*20 + (20*21)/2
- # there are no getfields because the optimizer gets rid of them
- self.check_loops(call=1, getfield_gc=0, setfield_gc=2)
- # xxx for now a call that forces the virtualizable during tracing
- # is supposed to always force it later too.
-
- def test_external_write_while_tracing(self):
- myjitdriver = JitDriver(greens = [], reds = ['n', 'm', 'xy'],
- virtualizables = ['xy'])
- class Outer:
- pass
- outer = Outer()
- def ext():
- xy = outer.xy
- promote_virtualizable(xy, 'inst_x')
- xy.inst_x += 2
- def f(n):
- xy = self.setup()
- xy.inst_x = 10
- outer.xy = xy
- m = 0
- while n > 0:
- myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
- myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
- promote_virtualizable(xy, 'inst_x')
- xy.inst_x = n + 9998 # virtualized away
- ext() # 2x setfield_gc, 2x getfield_gc
- promote_virtualizable(xy, 'inst_x')
- m += xy.inst_x # virtualized away
- n -= 1
- return m
- res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
- assert res == f(20)
- # the getfield_gc of inst_node is optimized away, because ext does not
- # write to it
- self.check_loops(call=1, getfield_gc=1, setfield_gc=2)
- # xxx for now a call that forces the virtualizable during tracing
- # is supposed to always force it later too.
-
# ------------------------------
XY2 = lltype.GcStruct(
'XY2',
('parent', rclass.OBJECT),
- ('vable_base', llmemory.Address),
- ('vable_rti', VABLERTIPTR),
+ ('vable_token', lltype.Signed),
('inst_x', lltype.Signed),
('inst_l1', lltype.Ptr(lltype.GcArray(lltype.Signed))),
('inst_l2', lltype.Ptr(lltype.GcArray(lltype.Signed))),
@@ -285,7 +216,7 @@
def setup2(self):
xy2 = lltype.malloc(self.XY2)
- xy2.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
+ xy2.vable_token = 0
xy2.parent.typeptr = self.xy2_vtable
return xy2
@@ -458,7 +389,7 @@
def setup2sub(self):
xy2 = lltype.malloc(self.XY2SUB)
- xy2.parent.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
+ xy2.parent.vable_token = 0
xy2.parent.parent.typeptr = self.xy2_vtable
return xy2
@@ -649,7 +580,8 @@
res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
assert res == f(123)
-
+ self.check_aborted_count(2)
+ self.check_tree_loop_count(0)
def test_external_write(self):
jitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -680,10 +612,10 @@
res = self.meta_interp(f, [240], policy=StopAtXPolicy(g))
assert res == f(240)
+ self.check_aborted_count(3)
+ self.check_tree_loop_count(0)
def test_external_read_sometimes(self):
- py.test.skip("known bug: access the frame in a residual call but"
- " only sometimes, so that it's not seen during tracing")
jitdriver = JitDriver(greens = [], reds = ['frame'],
virtualizables = ['frame'])
@@ -719,6 +651,226 @@
res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
assert res == f(123)
+ def test_external_read_sometimes_with_virtuals(self):
+ jitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y']
+ class Y:
+ pass
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def g():
+ somewhere_else.counter += 1
+ if somewhere_else.counter == 70:
+ y = somewhere_else.top_frame.y # external read
+ debug_print(lltype.Void, '-+-+-+-+- external read')
+ else:
+ y = None
+ return y
+
+ def f(n):
+ frame = Frame()
+ frame.x = n
+ somewhere_else.counter = 0
+ somewhere_else.top_frame = frame
+ while frame.x > 0:
+ jitdriver.can_enter_jit(frame=frame)
+ jitdriver.jit_merge_point(frame=frame)
+ frame.y = y = Y()
+ result = g()
+ if frame.y is not y:
+ return -660
+ if result:
+ if result is not y:
+ return -661
+ frame.y = None
+ frame.x -= 1
+ return frame.x
+
+ res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
+ assert res == f(123)
+
+ def test_external_read_sometimes_changing_virtuals(self):
+ jitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y']
+ class Y:
+ pass
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def g():
+ somewhere_else.counter += 1
+ if somewhere_else.counter == 70:
+ y = somewhere_else.top_frame.y # external read
+ debug_print(lltype.Void, '-+-+-+-+- external virtual write')
+ assert y.num == 123
+ y.num += 2
+ else:
+ y = None
+ return y
+
+ def f(n):
+ frame = Frame()
+ frame.x = n
+ somewhere_else.counter = 0
+ somewhere_else.top_frame = frame
+ while frame.x > 0:
+ jitdriver.can_enter_jit(frame=frame)
+ jitdriver.jit_merge_point(frame=frame)
+ frame.y = y = Y()
+ y.num = 123
+ result = g()
+ if frame.y is not y:
+ return -660
+ if result:
+ if result is not y:
+ return -661
+ if y.num != 125:
+ return -662
+ frame.y = None
+ frame.x -= 1
+ return frame.x
+
+ res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
+ assert res == f(123)
+
+ def test_external_read_sometimes_with_exception(self):
+ jitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y']
+ class FooBarError(Exception):
+ pass
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def g():
+ somewhere_else.counter += 1
+ if somewhere_else.counter == 70:
+ result = somewhere_else.top_frame.y # external read
+ debug_print(lltype.Void, '-+-+-+-+- external read:', result)
+ assert result == 79
+ raise FooBarError
+ else:
+ result = 1
+ return result
+
+ def f(n):
+ frame = Frame()
+ frame.x = n
+ frame.y = 10
+ somewhere_else.counter = 0
+ somewhere_else.top_frame = frame
+ try:
+ while frame.x > 0:
+ jitdriver.can_enter_jit(frame=frame)
+ jitdriver.jit_merge_point(frame=frame)
+ frame.x -= g()
+ frame.y += 1
+ except FooBarError:
+ pass
+ return frame.x
+
+ res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
+ assert res == f(123)
+
+ def test_external_read_sometimes_dont_compile_guard(self):
+ jitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y']
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def g():
+ somewhere_else.counter += 1
+ if somewhere_else.counter == 70:
+ result = somewhere_else.top_frame.y # external read
+ debug_print(lltype.Void, '-+-+-+-+- external read:', result)
+ assert result == 79
+ else:
+ result = 1
+ return result
+
+ def f(n):
+ frame = Frame()
+ frame.x = n
+ frame.y = 10
+ somewhere_else.counter = 0
+ somewhere_else.top_frame = frame
+ while frame.x > 0:
+ jitdriver.can_enter_jit(frame=frame)
+ jitdriver.jit_merge_point(frame=frame)
+ frame.x -= g()
+ frame.y += 1
+ return frame.x
+
+ res = self.meta_interp(f, [123], policy=StopAtXPolicy(g), repeat=7)
+ assert res == f(123)
+
+ def test_external_read_sometimes_recursive(self):
+ jitdriver = JitDriver(greens = [], reds = ['frame', 'rec'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y']
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def g(rec):
+ somewhere_else.counter += 1
+ if somewhere_else.counter == 70:
+ frame = somewhere_else.top_frame
+ result1 = frame.y # external read
+ result2 = frame.back.y # external read
+ debug_print(lltype.Void, '-+-+-+-+- external read:',
+ result1, result2)
+ assert result1 == 13
+ assert result2 == 1023
+ result = 2
+ elif rec:
+ res = f(4, False)
+ assert res == 0 or res == -1
+ result = 1
+ else:
+ result = 1
+ return result
+
+ def f(n, rec):
+ frame = Frame()
+ frame.x = n
+ frame.y = 10 + 1000 * rec
+ frame.back = somewhere_else.top_frame
+ somewhere_else.top_frame = frame
+ while frame.x > 0:
+ jitdriver.can_enter_jit(frame=frame, rec=rec)
+ jitdriver.jit_merge_point(frame=frame, rec=rec)
+ frame.x -= g(rec)
+ frame.y += 1
+ somewhere_else.top_frame = frame.back
+ return frame.x
+
+ def main(n):
+ somewhere_else.counter = 0
+ somewhere_else.top_frame = None
+ return f(n, True)
+
+ res = self.meta_interp(main, [123], policy=StopAtXPolicy(g))
+ assert res == main(123)
+
def test_promote_index_in_virtualizable_list(self):
jitdriver = JitDriver(greens = [], reds = ['frame', 'n'],
virtualizables = ['frame'])
@@ -829,26 +981,26 @@
assert res == 55
self.check_loops(new_with_vtable=0)
- def test_check_for_nonstandardness_only_once(self):
- myjitdriver = JitDriver(greens = [], reds = ['frame'],
- virtualizables = ['frame'])
-
- class Frame(object):
- _virtualizable2_ = ['x', 'y', 'z']
-
- def __init__(self, x, y, z=1):
- self = hint(self, access_directly=True)
- self.x = x
- self.y = y
- self.z = z
-
- class SomewhereElse:
- pass
- somewhere_else = SomewhereElse()
-
- def f(n):
- frame = Frame(n, 0)
- somewhere_else.top_frame = frame # escapes
+ def test_check_for_nonstandardness_only_once(self):
+ myjitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y', 'z']
+
+ def __init__(self, x, y, z=1):
+ self = hint(self, access_directly=True)
+ self.x = x
+ self.y = y
+ self.z = z
+
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
+
+ def f(n):
+ frame = Frame(n, 0)
+ somewhere_else.top_frame = frame # escapes
frame = hint(frame, access_directly=True)
while frame.x > 0:
myjitdriver.can_enter_jit(frame=frame)
Modified: pypy/trunk/pypy/jit/metainterp/typesystem.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/typesystem.py (original)
+++ pypy/trunk/pypy/jit/metainterp/typesystem.py Tue Dec 1 11:56:34 2009
@@ -49,10 +49,6 @@
CONST_NULL = history.ConstPtr(history.ConstPtr.value)
CVAL_NULLREF = None # patched by optimizeopt.py
- def get_VABLERTI(self):
- from pypy.rpython.lltypesystem.rvirtualizable2 import VABLERTIPTR
- return VABLERTIPTR
-
def new_ConstRef(self, x):
ptrval = lltype.cast_opaque_ptr(llmemory.GCREF, x)
return history.ConstPtr(ptrval)
@@ -159,10 +155,6 @@
loops_done_with_this_frame_ref = None # patched by compile.py
CONST_NULL = history.ConstObj(history.ConstObj.value)
CVAL_NULLREF = None # patched by optimizeopt.py
-
- def get_VABLERTI(self):
- from pypy.rpython.ootypesystem.rvirtualizable2 import VABLERTI
- return VABLERTI
def new_ConstRef(self, x):
obj = ootype.cast_to_object(x)
Modified: pypy/trunk/pypy/jit/metainterp/virtualizable.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/virtualizable.py (original)
+++ pypy/trunk/pypy/jit/metainterp/virtualizable.py Tue Dec 1 11:56:34 2009
@@ -4,19 +4,24 @@
from pypy.rpython import rvirtualizable2
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib.nonconst import NonConstant
from pypy.jit.metainterp.typesystem import deref, fieldType, arrayItem
from pypy.jit.metainterp import history
from pypy.jit.metainterp.warmstate import wrap, unwrap
class VirtualizableInfo:
+ token_none = 0
+ token_tracing = -1
+
def __init__(self, warmrunnerdesc):
self.warmrunnerdesc = warmrunnerdesc
jitdriver = warmrunnerdesc.jitdriver
cpu = warmrunnerdesc.cpu
+ if cpu.ts.name == 'ootype':
+ import py
+ py.test.skip("ootype: fix virtualizables")
self.cpu = cpu
- self.VABLERTI = cpu.ts.get_VABLERTI()
- self.null_vable_rti = cpu.ts.nullptr(deref(self.VABLERTI))
self.BoxArray = cpu.ts.BoxRef
#
assert len(jitdriver.virtualizables) == 1 # for now
@@ -29,6 +34,7 @@
self.VTYPEPTR = VTYPEPTR
self.VTYPE = VTYPE = deref(VTYPEPTR)
self.null_vable = cpu.ts.nullptr(VTYPE)
+ self.vable_token_descr = cpu.fielddescrof(VTYPE, 'vable_token')
#
accessor = VTYPE._hints['virtualizable2_accessor']
all_fields = accessor.fields
@@ -148,7 +154,7 @@
def finish(self):
#
def force_if_necessary(virtualizable):
- if virtualizable.vable_rti:
+ if virtualizable.vable_token:
self.force_now(virtualizable)
force_if_necessary._always_inline_ = True
#
@@ -169,72 +175,57 @@
def is_vtypeptr(self, TYPE):
return rvirtualizable2.match_virtualizable_type(TYPE, self.VTYPEPTR)
- def cast_instance_to_base_ptr(self, vable_rti):
- if we_are_translated():
- return self.cpu.ts.cast_instance_to_base_ref(vable_rti)
- else:
- vable_rti._TYPE = self.VABLERTI # hack for non-translated mode
- return vable_rti
+ def reset_vable_token(self, virtualizable):
+ virtualizable.vable_token = self.token_none
- def clear_vable_rti(self, virtualizable):
- if virtualizable.vable_rti:
+ def clear_vable_token(self, virtualizable):
+ if virtualizable.vable_token:
self.force_now(virtualizable)
- assert not virtualizable.vable_rti
+ assert not virtualizable.vable_token
def tracing_before_residual_call(self, virtualizable):
- assert not virtualizable.vable_rti
- ptr = self.cast_instance_to_base_ptr(tracing_vable_rti)
- virtualizable.vable_rti = ptr
+ assert not virtualizable.vable_token
+ virtualizable.vable_token = self.token_tracing
def tracing_after_residual_call(self, virtualizable):
- if virtualizable.vable_rti:
+ if virtualizable.vable_token:
# not modified by the residual call; assert that it is still
# set to 'tracing_vable_rti' and clear it.
- ptr = self.cast_instance_to_base_ptr(tracing_vable_rti)
- assert virtualizable.vable_rti == ptr
- virtualizable.vable_rti = self.null_vable_rti
+ assert virtualizable.vable_token == self.token_tracing
+ virtualizable.vable_token = self.token_none
return False
else:
# marker "modified during residual call" set.
return True
def force_now(self, virtualizable):
- rti = virtualizable.vable_rti
- virtualizable.vable_rti = self.null_vable_rti
- if we_are_translated():
- rti = cast_base_ptr_to_instance(AbstractVableRti, rti)
- rti.force_now(virtualizable)
+ token = virtualizable.vable_token
+ virtualizable.vable_token = self.token_none
+ if token == self.token_tracing:
+ # The values in the virtualizable are always correct during
+ # tracing. We only need to reset vable_token to token_none
+ # as a marker for the tracing, to tell it that this
+ # virtualizable escapes.
+ pass
+ else:
+ from pypy.jit.metainterp.compile import ResumeGuardForcedDescr
+ faildescr = self.cpu.force(token)
+ assert isinstance(faildescr, ResumeGuardForcedDescr)
+ faildescr.force_virtualizable(self, virtualizable, token)
force_now._dont_inline_ = True
# ____________________________________________________________
#
-# The 'vable_rti' field of a virtualizable is either NULL or points
-# to an instance of the following classes. It is:
+# The 'vable_token' field of a virtualizable is either 0, -1, or points
+# into the CPU stack to a particular field in the current frame. It is:
#
-# 1. NULL if not in the JIT at all, except as described below.
+# 1. 0 (token_none) if not in the JIT at all, except as described below.
#
-# 2. always NULL when tracing is in progress.
+# 2. equal to 0 when tracing is in progress; except:
#
-# 3. 'tracing_vable_rti' during tracing when we do a residual call,
+# 3. equal to -1 (token_tracing) during tracing when we do a residual call,
# calling random unknown other parts of the interpreter; it is
-# reset to NULL as soon as something occurs to the virtualizable.
+# reset to 0 as soon as something occurs to the virtualizable.
#
-# 4. NULL for now when running the machine code with a virtualizable;
-# later it will be a RunningVableRti().
-
-
-class AbstractVableRti(object):
-
- def force_now(self, virtualizable):
- raise NotImplementedError
-
-
-class TracingVableRti(AbstractVableRti):
-
- def force_now(self, virtualizable):
- # The values if the virtualizable are always correct during tracing.
- # We only need to set a marker to tell that forcing occurred.
- # As the caller resets vable_rti to NULL, it plays the role of marker.
- pass
-
-tracing_vable_rti = TracingVableRti()
+# 4. when running the machine code with a virtualizable, it is set
+# to the address in the CPU stack by the FORCE_TOKEN operation.
Modified: pypy/trunk/pypy/jit/metainterp/warmstate.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/warmstate.py (original)
+++ pypy/trunk/pypy/jit/metainterp/warmstate.py Tue Dec 1 11:56:34 2009
@@ -213,6 +213,8 @@
virtualizable = vinfo.cast_to_vtype(virtualizable)
assert virtualizable != globaldata.blackhole_virtualizable, (
"reentering same frame via blackhole")
+ else:
+ virtualizable = None
# look for the cell corresponding to the current greenargs
greenargs = args[:num_green_args]
@@ -247,6 +249,8 @@
fail_descr = metainterp_sd.cpu.execute_token(loop_token)
debug_stop("jit-running")
metainterp_sd.profiler.end_running()
+ if vinfo is not None:
+ vinfo.reset_vable_token(virtualizable)
loop_token = fail_descr.handle_fail(metainterp_sd)
maybe_compile_and_run._dont_inline_ = True
Modified: pypy/trunk/pypy/rlib/jit.py
==============================================================================
--- pypy/trunk/pypy/rlib/jit.py (original)
+++ pypy/trunk/pypy/rlib/jit.py Tue Dec 1 11:56:34 2009
@@ -22,6 +22,7 @@
def purefunction_promote(func):
import inspect
purefunction(func)
+ func._pure_function_with_all_promoted_args_ = True
args, varargs, varkw, defaults = inspect.getargspec(func)
args = ["v%s" % (i, ) for i in range(len(args))]
assert varargs is None and varkw is None
Modified: pypy/trunk/pypy/rpython/llinterp.py
==============================================================================
--- pypy/trunk/pypy/rpython/llinterp.py (original)
+++ pypy/trunk/pypy/rpython/llinterp.py Tue Dec 1 11:56:34 2009
@@ -807,9 +807,6 @@
def op_gc__collect(self, *gen):
self.heap.collect(*gen)
- def op_gc_assume_young_pointers(self, addr):
- raise NotImplementedError
-
def op_gc_heap_stats(self):
raise NotImplementedError
Modified: pypy/trunk/pypy/rpython/lltypesystem/ll2ctypes.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/ll2ctypes.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/ll2ctypes.py Tue Dec 1 11:56:34 2009
@@ -448,6 +448,9 @@
self._storage._setitem(index, value, boundscheck=False)
def getitems(self):
+ if self._TYPE.OF != lltype.Char:
+ raise Exception("cannot get all items of an unknown-length "
+ "array of %r" % self._TYPE.OF)
_items = []
i = 0
while 1:
Modified: pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lloperation.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lloperation.py Tue Dec 1 11:56:34 2009
@@ -460,7 +460,7 @@
# allocating non-GC structures only
'gc_thread_run' : LLOp(),
'gc_thread_die' : LLOp(),
- 'gc_assume_young_pointers': LLOp(),
+ 'gc_assume_young_pointers': LLOp(canrun=True),
'gc_heap_stats' : LLOp(canunwindgc=True),
# ------- JIT & GC interaction, only for some GCs ----------
Modified: pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/opimpl.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/opimpl.py Tue Dec 1 11:56:34 2009
@@ -486,6 +486,9 @@
def op_get_member_index(memberoffset):
raise NotImplementedError
+def op_gc_assume_young_pointers(addr):
+ pass
+
# ____________________________________________________________
def get_op_impl(opname):
Modified: pypy/trunk/pypy/rpython/lltypesystem/rffi.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rffi.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rffi.py Tue Dec 1 11:56:34 2009
@@ -57,7 +57,7 @@
def llexternal(name, args, result, _callable=None,
compilation_info=ExternalCompilationInfo(),
sandboxsafe=False, threadsafe='auto',
- canraise=False, _nowrapper=False, calling_conv='c',
+ _nowrapper=False, calling_conv='c',
oo_primitive=None, pure_function=False):
"""Build an external function that will invoke the C function 'name'
with the given 'args' types and 'result' type.
@@ -68,6 +68,10 @@
pointing to a read-only null-terminated character of arrays, as usual
for C.
+ The C function can have callbacks, but they must be specified explicitly
+ as constant RPython functions. We don't support yet C functions that
+ invoke callbacks passed otherwise (e.g. set by a previous C call).
+
threadsafe: whether it's ok to release the GIL around the call.
Default is yes, unless sandboxsafe is set, in which case
we consider that the function is really short-running and
@@ -84,12 +88,22 @@
kwds = {}
if oo_primitive:
kwds['oo_primitive'] = oo_primitive
+
+ has_callback = False
+ for ARG in args:
+ if _isfunctype(ARG):
+ has_callback = True
+ if has_callback:
+ kwds['_callbacks'] = callbackholder = CallbackHolder()
+ else:
+ callbackholder = None
+
funcptr = lltype.functionptr(ext_type, name, external='C',
compilation_info=compilation_info,
_callable=_callable,
_safe_not_sandboxed=sandboxsafe,
_debugexc=True, # on top of llinterp
- canraise=canraise,
+ canraise=False,
**kwds)
if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
_callable.funcptr = funcptr
@@ -170,9 +184,11 @@
# XXX pass additional arguments
if invoke_around_handlers:
arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
+ callbackholder,
aroundstate))
else:
- arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg))
+ arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
+ callbackholder))
else:
SOURCE = lltype.typeOf(arg)
if SOURCE != TARGET:
@@ -202,7 +218,11 @@
return func_with_new_name(wrapper, name)
-def _make_wrapper_for(TP, callable, aroundstate=None):
+class CallbackHolder:
+ def __init__(self):
+ self.callbacks = {}
+
+def _make_wrapper_for(TP, callable, callbackholder, aroundstate=None):
""" Function creating wrappers for callbacks. Note that this is
cheating as we assume constant callbacks and we just memoize wrappers
"""
@@ -213,6 +233,7 @@
else:
errorcode = TP.TO.RESULT._example()
callable_name = getattr(callable, '__name__', '?')
+ callbackholder.callbacks[callable] = True
args = ', '.join(['a%d' % i for i in range(len(TP.TO.ARGS))])
source = py.code.Source(r"""
def wrapper(%s): # no *args - no GIL for mallocing the tuple
Modified: pypy/trunk/pypy/rpython/lltypesystem/rvirtualizable2.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rvirtualizable2.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rvirtualizable2.py Tue Dec 1 11:56:34 2009
@@ -3,23 +3,21 @@
from pypy.rpython.lltypesystem.rclass import InstanceRepr, OBJECTPTR
from pypy.rpython.rvirtualizable2 import AbstractVirtualizable2InstanceRepr
-VABLERTIPTR = OBJECTPTR
class Virtualizable2InstanceRepr(AbstractVirtualizable2InstanceRepr, InstanceRepr):
def _setup_repr_llfields(self):
llfields = []
if self.top_of_virtualizable_hierarchy:
- llfields.append(('vable_base', llmemory.Address))
- llfields.append(('vable_rti', VABLERTIPTR))
+ llfields.append(('vable_token', lltype.Signed))
return llfields
def set_vable(self, llops, vinst, force_cast=False):
if self.top_of_virtualizable_hierarchy:
if force_cast:
vinst = llops.genop('cast_pointer', [vinst], resulttype=self)
- cname = inputconst(lltype.Void, 'vable_rti')
- vvalue = inputconst(VABLERTIPTR, lltype.nullptr(VABLERTIPTR.TO))
- llops.genop('setfield', [vinst, cname, vvalue])
+ cname = inputconst(lltype.Void, 'vable_token')
+ cvalue = inputconst(lltype.Signed, 0)
+ llops.genop('setfield', [vinst, cname, cvalue])
else:
self.rbase.set_vable(llops, vinst, force_cast=True)
Modified: pypy/trunk/pypy/rpython/lltypesystem/test/test_rffi.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/test/test_rffi.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/test/test_rffi.py Tue Dec 1 11:56:34 2009
@@ -387,6 +387,7 @@
fn = self.compile(f, [])
assert fn() == 6
+ assert eating_callback._ptr._obj._callbacks.callbacks == {g: True}
def test_double_callback(self):
eating_callback = self.eating_callback()
@@ -406,6 +407,8 @@
fn = self.compile(f, [int])
assert fn(4) == 4
assert fn(1) == 3
+ assert eating_callback._ptr._obj._callbacks.callbacks == {one: True,
+ two: True}
def test_exception_callback(self):
eating_callback = self.eating_callback()
Modified: pypy/trunk/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/framework.py (original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/framework.py Tue Dec 1 11:56:34 2009
@@ -29,12 +29,15 @@
def analyze_direct_call(self, graph, seen=None):
try:
func = graph.func
+ except AttributeError:
+ pass
+ else:
if func is rstack.stack_check:
return self.translator.config.translation.stackless
- if func._gctransformer_hint_cannot_collect_:
+ if getattr(func, '_gctransformer_hint_cannot_collect_', False):
return False
- except AttributeError:
- pass
+ if getattr(func, '_gctransformer_hint_close_stack_', False):
+ return True
return graphanalyze.GraphAnalyzer.analyze_direct_call(self, graph,
seen)
Modified: pypy/trunk/pypy/rpython/memory/gctransform/test/test_framework.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/test/test_framework.py (original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/test/test_framework.py Tue Dec 1 11:56:34 2009
@@ -6,7 +6,7 @@
from pypy.rpython.memory.gctransform.transform import GcHighLevelOp
from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer, \
CollectAnalyzer, find_initializing_stores, find_clean_setarrayitems
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.rpython.rtyper import LowLevelOpList
from pypy.translator.c.gc import FrameworkGcPolicy
from pypy.translator.translator import TranslationContext, graphof
@@ -87,6 +87,33 @@
can_collect = CollectAnalyzer(t).analyze_direct_call(with_check_graph)
assert can_collect
+def test_cancollect_external():
+ fext1 = rffi.llexternal('fext1', [], lltype.Void, threadsafe=False)
+ def g():
+ fext1()
+ t = rtype(g, [])
+ gg = graphof(t, g)
+ assert not CollectAnalyzer(t).analyze_direct_call(gg)
+
+ fext2 = rffi.llexternal('fext2', [], lltype.Void, threadsafe=True)
+ def g():
+ fext2()
+ t = rtype(g, [])
+ gg = graphof(t, g)
+ assert CollectAnalyzer(t).analyze_direct_call(gg)
+
+ S = lltype.GcStruct('S', ('x', lltype.Signed))
+ FUNC = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Void))
+ fext3 = rffi.llexternal('fext3', [FUNC], lltype.Void, threadsafe=False)
+ def h(x):
+ lltype.malloc(S, zero=True)
+ def g():
+ fext3(h)
+ t = rtype(g, [])
+ gg = graphof(t, g)
+ assert CollectAnalyzer(t).analyze_direct_call(gg)
+
+
class WriteBarrierTransformer(FrameworkGCTransformer):
clean_sets = {}
GC_PARAMS = {}
Modified: pypy/trunk/pypy/rpython/ootypesystem/rvirtualizable2.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rvirtualizable2.py (original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rvirtualizable2.py Tue Dec 1 11:56:34 2009
@@ -11,7 +11,7 @@
def _setup_repr_llfields(self):
llfields = []
if self.top_of_virtualizable_hierarchy:
- llfields.append(('vable_rti', VABLERTI))
+ llfields.append(('vable_token', VABLERTI))
return llfields
def set_vable(self, llops, vinst, force_cast=False):
Modified: pypy/trunk/pypy/rpython/test/test_rvirtualizable2.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rvirtualizable2.py (original)
+++ pypy/trunk/pypy/rpython/test/test_rvirtualizable2.py Tue Dec 1 11:56:34 2009
@@ -361,7 +361,7 @@
assert res.item1 == 42
res = lltype.normalizeptr(res.item0)
assert res.inst_v == 42
- assert not res.vable_rti
+ assert res.vable_token == 0
class TestOOtype(OORtypeMixin, BaseTest):
prefix = 'o'
Modified: pypy/trunk/pypy/testrunner_cfg.py
==============================================================================
--- pypy/trunk/pypy/testrunner_cfg.py (original)
+++ pypy/trunk/pypy/testrunner_cfg.py Tue Dec 1 11:56:34 2009
@@ -7,7 +7,8 @@
reldir.startswith('rlib/test') or
reldir.startswith('rpython/memory/') or
reldir.startswith('jit/backend/x86/') or
- reldir.startswith('jit/backend/cli')):
+ #reldir.startswith('jit/backend/cli') or
+ 0):
testdirs.extend(tests)
else:
testdirs.append(reldir)
Modified: pypy/trunk/pypy/translator/backendopt/canraise.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/canraise.py (original)
+++ pypy/trunk/pypy/translator/backendopt/canraise.py Tue Dec 1 11:56:34 2009
@@ -17,7 +17,7 @@
log.WARNING("Unknown operation: %s" % op.opname)
return True
- def analyze_external_call(self, op):
+ def analyze_external_call(self, op, seen=None):
fnobj = get_funcobj(op.args[0].value)
return getattr(fnobj, 'canraise', True)
Modified: pypy/trunk/pypy/translator/backendopt/graphanalyze.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/graphanalyze.py (original)
+++ pypy/trunk/pypy/translator/backendopt/graphanalyze.py Tue Dec 1 11:56:34 2009
@@ -1,4 +1,4 @@
-from pypy.translator.simplify import get_graph
+from pypy.translator.simplify import get_graph, get_funcobj
from pypy.rpython.lltypesystem.lloperation import llop, LL_OPERATIONS
from pypy.rpython.lltypesystem import lltype
@@ -38,8 +38,17 @@
def analyze_startblock(self, block, seen=None):
return self.bottom_result()
- def analyze_external_call(self, op):
- return self.top_result()
+ def analyze_external_call(self, op, seen=None):
+ funcobj = get_funcobj(op.args[0].value)
+ result = self.bottom_result()
+ if hasattr(funcobj, '_callbacks'):
+ bk = self.translator.annotator.bookkeeper
+ for function in funcobj._callbacks.callbacks:
+ desc = bk.getdesc(function)
+ for graph in desc.getgraphs():
+ result = self.join_two_results(
+ result, self.analyze_direct_call(graph, seen))
+ return result
def analyze_external_method(self, op, TYPE, meth):
return self.top_result()
@@ -59,7 +68,7 @@
if op.opname == "direct_call":
graph = get_graph(op.args[0], self.translator)
if graph is None:
- return self.analyze_external_call(op)
+ return self.analyze_external_call(op, seen)
return self.analyze_direct_call(graph, seen)
elif op.opname == "indirect_call":
if op.args[-1].value is None:
Modified: pypy/trunk/pypy/translator/backendopt/test/test_canraise.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/test/test_canraise.py (original)
+++ pypy/trunk/pypy/translator/backendopt/test/test_canraise.py Tue Dec 1 11:56:34 2009
@@ -189,7 +189,8 @@
result = ra.can_raise(fgraph.startblock.operations[0])
assert not result
- z = llexternal('z', [lltype.Signed], lltype.Signed, canraise=True)
+ z = lltype.functionptr(lltype.FuncType([lltype.Signed], lltype.Signed),
+ 'foobar')
def g(x):
return z(x)
t, ra = self.translate(g, [int])
Modified: pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py (original)
+++ pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py Tue Dec 1 11:56:34 2009
@@ -178,6 +178,31 @@
assert name == "length"
assert S1 is S2
+ def test_llexternal_with_callback(self):
+ from pypy.rpython.lltypesystem.rffi import llexternal
+ from pypy.rpython.lltypesystem import lltype
+
+ class Abc:
+ pass
+ abc = Abc()
+
+ FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
+ z = llexternal('z', [lltype.Ptr(FUNC)], lltype.Signed)
+ def g(n):
+ abc.foobar = n
+ return n + 1
+ def f(x):
+ return z(g)
+ t, wa = self.translate(f, [int])
+ fgraph = graphof(t, f)
+ backend_optimizations(t)
+ assert fgraph.startblock.operations[0].opname == 'direct_call'
+
+ result = wa.analyze(fgraph.startblock.operations[0])
+ assert len(result) == 1
+ (struct, T, name), = result
+ assert struct == "struct"
+ assert name.endswith("foobar")
class TestOOtype(BaseTestCanRaise):
Modified: pypy/trunk/pypy/translator/backendopt/writeanalyze.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/writeanalyze.py (original)
+++ pypy/trunk/pypy/translator/backendopt/writeanalyze.py Tue Dec 1 11:56:34 2009
@@ -1,6 +1,5 @@
from pypy.translator.backendopt import graphanalyze
from pypy.rpython.ootypesystem import ootype
-reload(graphanalyze)
top_set = object()
empty_set = frozenset()
@@ -38,9 +37,6 @@
def _array_result(self, TYPE):
return frozenset([("array", TYPE)])
- def analyze_external_call(self, op):
- return self.bottom_result() # an external call cannot change anything
-
def analyze_external_method(self, op, TYPE, meth):
if isinstance(TYPE, ootype.Array):
methname = op.args[0].value
Modified: pypy/trunk/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/trunk/pypy/translator/stackless/transform.py (original)
+++ pypy/trunk/pypy/translator/stackless/transform.py Tue Dec 1 11:56:34 2009
@@ -260,7 +260,7 @@
return LL_OPERATIONS[op.opname].canunwindgc
return False
- def analyze_external_call(self, op):
+ def analyze_external_call(self, op, seen=None):
# An external call cannot cause a stack unwind
# Note that this is essential to get good performance in framework GCs
# because there is a pseudo-external call to ROUND_UP_FOR_ALLOCATION
More information about the Pypy-commit
mailing list