Author: Maciej Fijalkowski <fij...@gmail.com> Branch: Changeset: r74882:db19352f7582 Date: 2014-12-10 17:46 +0200 http://bitbucket.org/pypy/pypy/changeset/db19352f7582/
Log: merge diff --git a/pypy/module/_rawffi/alt/interp_funcptr.py b/pypy/module/_rawffi/alt/interp_funcptr.py --- a/pypy/module/_rawffi/alt/interp_funcptr.py +++ b/pypy/module/_rawffi/alt/interp_funcptr.py @@ -14,6 +14,7 @@ from rpython.rlib.objectmodel import we_are_translated from pypy.module._rawffi.alt.type_converter import FromAppLevelConverter, ToAppLevelConverter from pypy.module._rawffi.interp_rawffi import got_libffi_error, wrap_dlopenerror +from pypy.module._rawffi import lasterror import os if os.name == 'nt': @@ -201,11 +202,23 @@ self.func = func self.argchain = argchain + def before(self): + lasterror.restore_last_error(self.space) + + def after(self): + lasterror.save_last_error(self.space) + def get_longlong(self, w_ffitype): - return self.func.call(self.argchain, rffi.LONGLONG) + self.before() + x = self.func.call(self.argchain, rffi.LONGLONG) + self.after() + return x def get_ulonglong(self, w_ffitype): - return self.func.call(self.argchain, rffi.ULONGLONG) + self.before() + x = self.func.call(self.argchain, rffi.ULONGLONG) + self.after() + return x def get_signed(self, w_ffitype): # if the declared return type of the function is smaller than LONG, @@ -216,64 +229,94 @@ # to space.wrap in order to get a nice applevel <int>. # restype = w_ffitype.get_ffitype() + self.before() call = self.func.call if restype is libffi.types.slong: - return call(self.argchain, rffi.LONG) + x = call(self.argchain, rffi.LONG) elif restype is libffi.types.sint: - return rffi.cast(rffi.LONG, call(self.argchain, rffi.INT)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.INT)) elif restype is libffi.types.sshort: - return rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT)) elif restype is libffi.types.schar: - return rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR)) else: - self.error(w_ffitype) + raise self.error(w_ffitype) + self.after() + return x def get_unsigned(self, w_ffitype): - return self.func.call(self.argchain, rffi.ULONG) + self.before() + x = self.func.call(self.argchain, rffi.ULONG) + self.after() + return x def get_unsigned_which_fits_into_a_signed(self, w_ffitype): # the same comment as get_signed apply restype = w_ffitype.get_ffitype() + self.before() call = self.func.call if restype is libffi.types.uint: assert not libffi.IS_32_BIT # on 32bit machines, we should never get here, because it's a case # which has already been handled by get_unsigned above. - return rffi.cast(rffi.LONG, call(self.argchain, rffi.UINT)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.UINT)) elif restype is libffi.types.ushort: - return rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT)) elif restype is libffi.types.uchar: - return rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR)) + x = rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR)) else: - self.error(w_ffitype) + raise self.error(w_ffitype) + self.after() + return x def get_pointer(self, w_ffitype): + self.before() ptrres = self.func.call(self.argchain, rffi.VOIDP) + self.after() return rffi.cast(rffi.ULONG, ptrres) def get_char(self, w_ffitype): - return self.func.call(self.argchain, rffi.UCHAR) + self.before() + x = self.func.call(self.argchain, rffi.UCHAR) + self.after() + return x def get_unichar(self, w_ffitype): - return self.func.call(self.argchain, rffi.WCHAR_T) + self.before() + x = self.func.call(self.argchain, rffi.WCHAR_T) + self.after() + return x def get_float(self, w_ffitype): - return self.func.call(self.argchain, rffi.DOUBLE) + self.before() + x = self.func.call(self.argchain, rffi.DOUBLE) + self.after() + return x def get_singlefloat(self, w_ffitype): - return self.func.call(self.argchain, rffi.FLOAT) + self.before() + x = self.func.call(self.argchain, rffi.FLOAT) + self.after() + return x def get_struct(self, w_ffitype, w_structdescr): + self.before() addr = self.func.call(self.argchain, rffi.LONG, is_struct=True) + self.after() return w_structdescr.fromaddress(self.space, addr) def get_struct_rawffi(self, w_ffitype, w_structdescr): + self.before() uintval = self.func.call(self.argchain, rffi.ULONG, is_struct=True) + self.after() return w_structdescr.fromaddress(self.space, uintval) def get_void(self, w_ffitype): - return self.func.call(self.argchain, lltype.Void) + self.before() + x = self.func.call(self.argchain, lltype.Void) + self.after() + return x def unpack_argtypes(space, w_argtypes, w_restype): diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py --- a/pypy/module/_rawffi/interp_rawffi.py +++ b/pypy/module/_rawffi/interp_rawffi.py @@ -18,6 +18,7 @@ from rpython.rlib.rarithmetic import intmask, r_uint from pypy.module._rawffi.buffer import RawFFIBuffer from pypy.module._rawffi.tracker import tracker +from pypy.module._rawffi import lasterror TYPEMAP = { # XXX A mess with unsigned/signed/normal chars :-/ @@ -495,10 +496,14 @@ try: if self.resshape is not None: result = self.resshape.allocate(space, 1, autofree=True) + lasterror.restore_last_error(space) self.ptr.call(args_ll, result.ll_buffer) + lasterror.save_last_error(space) return space.wrap(result) else: + lasterror.restore_last_error(space) self.ptr.call(args_ll, lltype.nullptr(rffi.VOIDP.TO)) + lasterror.save_last_error(space) return space.w_None except StackCheckError, e: raise OperationError(space.w_ValueError, space.wrap(e.message)) @@ -615,12 +620,10 @@ if sys.platform == 'win32': def get_last_error(space): - from rpython.rlib.rwin32 import GetLastError - return space.wrap(GetLastError()) + return space.wrap(lasterror.fetch_last_error(space)) @unwrap_spec(error=int) def set_last_error(space, error): - from rpython.rlib.rwin32 import SetLastError - SetLastError(error) + lasterror.store_last_error(space, error) else: # always have at least a dummy version of these functions # (https://bugs.pypy.org/issue1242) diff --git a/pypy/module/_rawffi/lasterror.py b/pypy/module/_rawffi/lasterror.py new file mode 100644 --- /dev/null +++ b/pypy/module/_rawffi/lasterror.py @@ -0,0 +1,40 @@ +# For Windows only. +# https://bitbucket.org/pypy/pypy/issue/1944/ctypes-on-windows-getlasterror + +import os + +_MS_WINDOWS = os.name == "nt" + + +if _MS_WINDOWS: + from rpython.rlib import rwin32 + from pypy.interpreter.executioncontext import ExecutionContext + + + ExecutionContext._rawffi_last_error = 0 + + def fetch_last_error(space): + ec = space.getexecutioncontext() + return ec._rawffi_last_error + + def store_last_error(space, last_error): + ec = space.getexecutioncontext() + ec._rawffi_last_error = last_error + + def restore_last_error(space): + ec = space.getexecutioncontext() + lasterror = ec._rawffi_last_error + rwin32.SetLastError(lasterror) + + def save_last_error(space): + lasterror = rwin32.GetLastError() + ec = space.getexecutioncontext() + ec._rawffi_last_error = lasterror + +else: + + def restore_last_error(space): + pass + + def save_last_error(space): + pass diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py --- a/pypy/module/_rawffi/test/test__rawffi.py +++ b/pypy/module/_rawffi/test/test__rawffi.py @@ -16,6 +16,7 @@ #include "src/precommondefs.h" #include <stdlib.h> #include <stdio.h> + #include <errno.h> struct x { @@ -204,6 +205,24 @@ return inp; } + RPY_EXPORTED + int check_errno(int incoming) + { + int old_errno = errno; + errno = incoming; + return old_errno; + } + + #ifdef _WIN32 + #include <Windows.h> + RPY_EXPORTED + int check_last_error(int incoming) + { + int old_errno = GetLastError(); + SetLastError(incoming); + return old_errno; + } + #endif ''')) eci = ExternalCompilationInfo(include_dirs=[cdir]) return str(platform.compile([c_file], eci, 'x', standalone=False)) @@ -1150,6 +1169,37 @@ raises(OverflowError, "arg1[0] = 10**900") arg1.free() + def test_errno(self): + import _rawffi + lib = _rawffi.CDLL(self.lib_name) + A = _rawffi.Array('i') + f = lib.ptr('check_errno', ['i'], 'i') + _rawffi.set_errno(42) + arg = A(1) + arg[0] = 43 + res = f(arg) + assert res[0] == 42 + z = _rawffi.get_errno() + assert z == 43 + arg.free() + + def test_last_error(self): + import sys + if sys.platform != 'win32': + skip("Windows test") + import _rawffi + lib = _rawffi.CDLL(self.lib_name) + A = _rawffi.Array('i') + f = lib.ptr('check_last_error', ['i'], 'i') + _rawffi.set_last_error(42) + arg = A(1) + arg[0] = 43 + res = f(arg) + assert res[0] == 42 + z = _rawffi.get_last_error() + assert z == 43 + arg.free() + class AppTestAutoFree: spaceconfig = dict(usemodules=['_rawffi', 'struct']) diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -17,9 +17,13 @@ self.w_value = w_value +def needs_to_unwrap_cell(space, w_value): + return (space.config.objspace.std.withtypeversion and + isinstance(w_value, TypeCell)) + + def unwrap_cell(space, w_value): - if (space.config.objspace.std.withtypeversion and - isinstance(w_value, TypeCell)): + if needs_to_unwrap_cell(space, w_value): return w_value.w_value return w_value @@ -366,8 +370,11 @@ tup = w_self._lookup_where(name) return tup name = promote_string(name) - w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, version_tag) - return w_class, unwrap_cell(space, w_value) + tup_w = w_self._pure_lookup_where_with_method_cache(name, version_tag) + w_class, w_value = tup_w + if needs_to_unwrap_cell(space, w_value): + return w_class, w_value.value + return tup_w # don't make a new tuple, reuse the old one def _pure_lookup_where_possibly_with_method_cache(w_self, name, version_tag): if w_self.space.config.objspace.std.withmethodcache: diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -129,7 +129,8 @@ default=False, cmdline=None), # misc - BoolOption("verbose", "Print extra information", default=False), + BoolOption("verbose", "Print extra information", default=False, + cmdline="--verbose"), StrOption("cc", "Specify compiler to use for compiling generated C", cmdline="--cc"), StrOption("profopt", "Specify profile based optimization script", cmdline="--profopt"), diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py --- a/rpython/flowspace/operation.py +++ b/rpython/flowspace/operation.py @@ -528,6 +528,10 @@ pyfunc = staticmethod(getattr) def constfold(self): + from rpython.flowspace.flowcontext import FlowingError + if len(self.args) == 3: + raise FlowingError( + "getattr() with three arguments not supported: %s" % (self,)) w_obj, w_name = self.args # handling special things like sys if (w_obj in NOT_REALLY_CONST and @@ -538,7 +542,6 @@ try: result = getattr(obj, name) except Exception as e: - from rpython.flowspace.flowcontext import FlowingError etype = e.__class__ msg = "getattr(%s, %s) always raises %s: %s" % ( obj, name, etype, e) diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -964,9 +964,40 @@ assembler_call = False if warmrunnerstate.inlining: if warmrunnerstate.can_inline_callable(greenboxes): + # We've found a potentially inlinable function; now we need to + # see if it's already on the stack. In other words: are we about + # to enter recursion? If so, we don't want to inline the + # recursion, which would be equivalent to unrolling a while + # loop. portal_code = targetjitdriver_sd.mainjitcode - return self.metainterp.perform_call(portal_code, allboxes, - greenkey=greenboxes) + count = 0 + for f in self.metainterp.framestack: + if f.jitcode is not portal_code: + continue + gk = f.greenkey + if gk is None: + continue + assert len(gk) == len(greenboxes) + i = 0 + for i in range(len(gk)): + if not gk[i].same_constant(greenboxes[i]): + break + else: + count += 1 + memmgr = self.metainterp.staticdata.warmrunnerdesc.memory_manager + if count >= memmgr.max_unroll_recursion: + # This function is recursive and has exceeded the + # maximum number of unrollings we allow. We want to stop + # inlining it further and to make sure that, if it + # hasn't happened already, the function is traced + # separately as soon as possible. + if have_debug_prints(): + loc = targetjitdriver_sd.warmstate.get_location_str(greenboxes) + debug_print("recursive function (not inlined):", loc) + warmrunnerstate.dont_trace_here(greenboxes) + else: + return self.metainterp.perform_call(portal_code, allboxes, + greenkey=greenboxes) assembler_call = True # verify that we have all green args, needed to make sure # that assembler that we call is still correct diff --git a/rpython/jit/metainterp/test/test_list.py b/rpython/jit/metainterp/test/test_list.py --- a/rpython/jit/metainterp/test/test_list.py +++ b/rpython/jit/metainterp/test/test_list.py @@ -97,28 +97,6 @@ assert res == f(10) self.check_resops(setarrayitem_gc=0, call=0, getarrayitem_gc=0) - def test_vlist_alloc_and_set(self): - # the check_loops fails, because [non-null] * n is only supported - # if n < 15 (otherwise it is implemented as a residual call) - jitdriver = JitDriver(greens = [], reds = ['n']) - def f(n): - l = [1] * 20 - while n > 0: - jitdriver.can_enter_jit(n=n) - jitdriver.jit_merge_point(n=n) - l = [1] * 20 - l[3] = 5 - x = l[-17] + l[5] - 1 - if n < 3: - return x - n -= 1 - return l[0] - - res = self.meta_interp(f, [10], listops=True) - assert res == f(10) - py.test.skip("'[non-null] * n' for n >= 15 gives a residual call so far") - self.check_loops(setarrayitem_gc=0, getarrayitem_gc=0, call=0) - def test_arraycopy_simpleoptimize(self): def f(): l = [1, 2, 3, 4] diff --git a/rpython/jit/metainterp/test/test_recursive.py b/rpython/jit/metainterp/test/test_recursive.py --- a/rpython/jit/metainterp/test/test_recursive.py +++ b/rpython/jit/metainterp/test/test_recursive.py @@ -1112,6 +1112,37 @@ assert res == 2095 self.check_resops(call_assembler=12) + def test_inline_recursion_limit(self): + driver = JitDriver(greens = ["threshold", "loop"], reds=["i"]) + @dont_look_inside + def f(): + set_param(driver, "max_unroll_recursion", 10) + def portal(threshold, loop, i): + f() + if i > threshold: + return i + while True: + driver.jit_merge_point(threshold=threshold, loop=loop, i=i) + if loop: + portal(threshold, False, 0) + else: + portal(threshold, False, i + 1) + return i + if i > 10: + return 1 + i += 1 + driver.can_enter_jit(threshold=threshold, loop=loop, i=i) + + res1 = portal(10, True, 0) + res2 = self.meta_interp(portal, [10, True, 0], inline=True) + assert res1 == res2 + self.check_resops(call_assembler=2) + + res1 = portal(9, True, 0) + res2 = self.meta_interp(portal, [9, True, 0], inline=True) + assert res1 == res2 + self.check_resops(call_assembler=0) + def test_handle_jitexception_in_portal(self): # a test for _handle_jitexception_in_portal in blackhole.py driver = JitDriver(greens = ['codeno'], reds = ['i', 'str'], diff --git a/rpython/jit/metainterp/test/test_send.py b/rpython/jit/metainterp/test/test_send.py --- a/rpython/jit/metainterp/test/test_send.py +++ b/rpython/jit/metainterp/test/test_send.py @@ -385,8 +385,7 @@ self.check_target_token_count(4) def test_two_behaviors(self): - py.test.skip("XXX fix me!!!!!!! problem in optimize.py") - myjitdriver = JitDriver(greens = [], reds = ['x', 'y']) + myjitdriver = JitDriver(greens = [], reds = ['y', 'x']) class Int: def __init__(self, value): self.value = value @@ -402,11 +401,6 @@ return x.value res = self.meta_interp(f, [len(cases)]) assert res == 110 - # The generated loops don't contain a new_with_vtable at all. This - # is true if we replace "if cases[y]" above with "if not cases[y]" - # -- so there is no good reason that it fails. - self.check_loops(new_with_vtable=0) - self.check_trace_count(2) def test_behavior_change_after_a_while(self): myjitdriver = JitDriver(greens = [], reds = ['y', 'x']) diff --git a/rpython/jit/metainterp/warmspot.py b/rpython/jit/metainterp/warmspot.py --- a/rpython/jit/metainterp/warmspot.py +++ b/rpython/jit/metainterp/warmspot.py @@ -69,7 +69,8 @@ backendopt=False, trace_limit=sys.maxint, inline=False, loop_longevity=0, retrace_limit=5, function_threshold=4, - enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15, **kwds): + enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15, + max_unroll_recursion=7, **kwds): from rpython.config.config import ConfigError translator = interp.typer.annotator.translator try: @@ -91,6 +92,7 @@ jd.warmstate.set_param_retrace_limit(retrace_limit) jd.warmstate.set_param_max_retrace_guards(max_retrace_guards) jd.warmstate.set_param_enable_opts(enable_opts) + jd.warmstate.set_param_max_unroll_recursion(max_unroll_recursion) warmrunnerdesc.finish() if graph_and_interp_only: return interp, graph diff --git a/rpython/jit/metainterp/warmstate.py b/rpython/jit/metainterp/warmstate.py --- a/rpython/jit/metainterp/warmstate.py +++ b/rpython/jit/metainterp/warmstate.py @@ -291,6 +291,11 @@ if self.warmrunnerdesc.memory_manager: self.warmrunnerdesc.memory_manager.max_unroll_loops = value + def set_param_max_unroll_recursion(self, value): + if self.warmrunnerdesc: + if self.warmrunnerdesc.memory_manager: + self.warmrunnerdesc.memory_manager.max_unroll_recursion = value + def disable_noninlinable_function(self, greenkey): cell = self.JitCell.ensure_jit_cell_at_key(greenkey) cell.flags |= JC_DONT_TRACE_HERE @@ -567,19 +572,26 @@ jd = self.jitdriver_sd cpu = self.cpu - def can_inline_greenargs(*greenargs): + def can_inline_callable(greenkey): + greenargs = unwrap_greenkey(greenkey) if can_never_inline(*greenargs): return False cell = JitCell.get_jitcell(*greenargs) if cell is not None and (cell.flags & JC_DONT_TRACE_HERE) != 0: return False return True - def can_inline_callable(greenkey): - greenargs = unwrap_greenkey(greenkey) - return can_inline_greenargs(*greenargs) - self.can_inline_greenargs = can_inline_greenargs self.can_inline_callable = can_inline_callable + def dont_trace_here(greenkey): + # Set greenkey as somewhere that tracing should not occur into; + # notice that, as per the description of JC_DONT_TRACE_HERE earlier, + # if greenkey hasn't been traced separately, setting + # JC_DONT_TRACE_HERE will force tracing the next time the function + # is encountered. + cell = JitCell.ensure_jit_cell_at_key(greenkey) + cell.flags |= JC_DONT_TRACE_HERE + self.dont_trace_here = dont_trace_here + if jd._should_unroll_one_iteration_ptr is None: def should_unroll_one_iteration(greenkey): return False diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -463,6 +463,7 @@ 'max_unroll_loops': 'number of extra unrollings a loop can cause', 'enable_opts': 'INTERNAL USE ONLY (MAY NOT WORK OR LEAD TO CRASHES): ' 'optimizations to enable, or all = %s' % ENABLE_ALL_OPTS, + 'max_unroll_recursion': 'how many levels deep to unroll a recursive function' } PARAMETERS = {'threshold': 1039, # just above 1024, prime @@ -476,6 +477,7 @@ 'max_retrace_guards': 15, 'max_unroll_loops': 0, 'enable_opts': 'all', + 'max_unroll_recursion': 7, } unroll_parameters = unrolling_iterable(PARAMETERS.items()) diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -182,8 +182,8 @@ def _freeze_(self): return True - def _inline_is_varsize(self, last): - return False + def _note_inlined_into(self, parent, first, last): + """Called when this type is being used inline in a container.""" def _is_atomic(self): return False @@ -201,8 +201,9 @@ class ContainerType(LowLevelType): _adtmeths = {} - def _inline_is_varsize(self, last): - raise TypeError("%r cannot be inlined in structure" % self) + def _note_inlined_into(self, parent, first, last): + raise TypeError("%r cannot be inlined in %r" % ( + self.__class__.__name__, parent.__class__.__name__)) def _install_extras(self, adtmeths={}, hints={}): self._adtmeths = frozendict(adtmeths) @@ -279,11 +280,13 @@ # look if we have an inlined variable-sized array as the last field if fields: + first = True for name, typ in fields[:-1]: - typ._inline_is_varsize(False) + typ._note_inlined_into(self, first=first, last=False) first = False name, typ = fields[-1] - if typ._inline_is_varsize(True): + typ._note_inlined_into(self, first=first, last=True) + if typ._is_varsize(): self._arrayfld = name self._flds = frozendict(flds) self._names = tuple(names) @@ -299,11 +302,14 @@ return first, FIRSTTYPE return None, None - def _inline_is_varsize(self, last): - if self._arrayfld: + def _note_inlined_into(self, parent, first, last): + if self._arrayfld is not None: raise TypeError("cannot inline a var-sized struct " "inside another container") - return False + if self._gckind == 'gc': + if not first or not isinstance(parent, GcStruct): + raise TypeError("a GcStruct can only be inlined as the first " + "field of another GcStruct") def _is_atomic(self): for typ in self._flds.values(): @@ -428,15 +434,18 @@ if isinstance(self.OF, ContainerType) and self.OF._gckind != 'raw': raise TypeError("cannot have a %s container as array item type" % (self.OF._gckind,)) - self.OF._inline_is_varsize(False) + self.OF._note_inlined_into(self, first=False, last=False) self._install_extras(**kwds) - def _inline_is_varsize(self, last): - if not last: + def _note_inlined_into(self, parent, first, last): + if not last or not isinstance(parent, Struct): raise TypeError("cannot inline an array in another container" " unless as the last field of a structure") - return True + if self._gckind == 'gc': + raise TypeError("cannot inline a GC array inside a structure") + if parent._gckind == 'gc' and self._hints.get('nolength', False): + raise TypeError("cannot inline a no-length array inside a GcStruct") def _is_atomic(self): return self.OF._is_atomic() @@ -474,9 +483,6 @@ class GcArray(Array): _gckind = 'gc' - def _inline_is_varsize(self, last): - raise TypeError("cannot inline a GC array inside a structure") - class FixedSizeArray(Struct): # behaves more or less like a Struct with fields item0, item1, ... @@ -508,7 +514,7 @@ if isinstance(self.OF, ContainerType) and self.OF._gckind != 'raw': raise TypeError("cannot have a %s container as array item type" % (self.OF._gckind,)) - self.OF._inline_is_varsize(False) + self.OF._note_inlined_into(self, first=False, last=False) def _str_fields(self): return str(self.OF) @@ -579,8 +585,11 @@ def __str__(self): return "%s (opaque)" % self.tag - def _inline_is_varsize(self, last): - return False # OpaqueType can be inlined + def _note_inlined_into(self, parent, first, last): + # OpaqueType can be inlined, but not GcOpaqueType + if self._gckind == 'gc': + raise TypeError("%r cannot be inlined in %r" % ( + self.__class__.__name__, parent.__class__.__name__)) def _container_example(self): return _opaque(self) @@ -599,10 +608,6 @@ def __str__(self): return "%s (gcopaque)" % self.tag - def _inline_is_varsize(self, last): - raise TypeError("%r cannot be inlined in structure" % self) - - class ForwardReference(ContainerType): _gckind = 'raw' def become(self, realcontainertype): diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py b/rpython/rtyper/lltypesystem/test/test_lltype.py --- a/rpython/rtyper/lltypesystem/test/test_lltype.py +++ b/rpython/rtyper/lltypesystem/test/test_lltype.py @@ -808,6 +808,20 @@ assert F.RESULT == Signed assert F.ARGS == (Signed,) +def test_cannot_inline_random_stuff_in_gcstruct(): + S = GcStruct('S') + GcStruct('X', ('a', S)) # works + py.test.raises(TypeError, GcStruct, 'X', ('a', Signed), ('b', S)) + GcStruct('X', ('a', Array(Signed))) # works + py.test.raises(TypeError, GcStruct, 'X', ('a', Array(Signed)), + ('b', Signed)) + Struct('X', ('a', Array(Signed, hints={'nolength': True}))) # works + py.test.raises(TypeError, GcStruct, 'X', + ('a', Array(Signed, hints={'nolength': True}))) + GcStruct('X', ('a', OpaqueType('foo'))) # works + py.test.raises(TypeError, GcStruct, 'X', ('a', GcOpaqueType('foo'))) + + class TestTrackAllocation: def test_automatic_tracking(self): # calls to start_tracking_allocations/stop_tracking_allocations diff --git a/rpython/translator/backendopt/inline.py b/rpython/translator/backendopt/inline.py --- a/rpython/translator/backendopt/inline.py +++ b/rpython/translator/backendopt/inline.py @@ -478,6 +478,7 @@ 'malloc': 2, 'instrument_count': 0, 'debug_assert': -1, + 'jit_force_virtualizable': 0, } def block_weight(block, weights=OP_WEIGHTS): diff --git a/rpython/translator/backendopt/storesink.py b/rpython/translator/backendopt/storesink.py --- a/rpython/translator/backendopt/storesink.py +++ b/rpython/translator/backendopt/storesink.py @@ -5,7 +5,7 @@ from rpython.translator import simplify def has_side_effects(op): - if op.opname == 'debug_assert': + if op.opname == 'debug_assert' or op.opname == 'jit_force_virtualizable': return False try: return getattr(llop, op.opname).sideeffects _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit