Author: Philip Jenvey <pjen...@underboss.org> Branch: py3k Changeset: r74767:e01964ac6c50 Date: 2014-11-30 11:51 -0800 http://bitbucket.org/pypy/pypy/changeset/e01964ac6c50/
Log: merge default diff too long, truncating to 2000 out of 3374 lines diff --git a/lib-python/2.7/subprocess.py b/lib-python/2.7/subprocess.py --- a/lib-python/2.7/subprocess.py +++ b/lib-python/2.7/subprocess.py @@ -655,6 +655,21 @@ """Create new Popen instance.""" _cleanup() + # --- PyPy hack, see _pypy_install_libs_after_virtualenv() --- + # match arguments passed by different versions of virtualenv + if args[1:] in ( + ['-c', 'import sys; print(sys.prefix)'], # 1.6 10ba3f3c + ['-c', "\nimport sys\nprefix = sys.prefix\n" # 1.7 0e9342ce + "if sys.version_info[0] == 3:\n" + " prefix = prefix.encode('utf8')\n" + "if hasattr(sys.stdout, 'detach'):\n" + " sys.stdout = sys.stdout.detach()\n" + "elif hasattr(sys.stdout, 'buffer'):\n" + " sys.stdout = sys.stdout.buffer\nsys.stdout.write(prefix)\n"], + ['-c', 'import sys;out=sys.stdout;getattr(out, "buffer"' + ', out).write(sys.prefix.encode("utf-8"))']): # 1.7.2 a9454bce + _pypy_install_libs_after_virtualenv(args[0]) + if not isinstance(bufsize, (int, long)): raise TypeError("bufsize must be an integer") @@ -1560,6 +1575,27 @@ self.send_signal(signal.SIGKILL) +def _pypy_install_libs_after_virtualenv(target_executable): + # https://bitbucket.org/pypy/pypy/issue/1922/future-proofing-virtualenv + # + # PyPy 2.4.1 turned --shared on by default. This means the pypy binary + # depends on the 'libpypy-c.so' shared library to be able to run. + # The virtualenv code existing at the time did not account for this + # and would break. Try to detect that we're running under such a + # virtualenv in the "Testing executable with" phase and copy the + # library ourselves. + caller = sys._getframe(2) + if ('virtualenv_version' in caller.f_globals and + 'copyfile' in caller.f_globals): + dest_dir = sys.pypy_resolvedirof(target_executable) + src_dir = sys.pypy_resolvedirof(sys.executable) + for libname in ['libpypy-c.so']: + dest_library = os.path.join(dest_dir, libname) + src_library = os.path.join(src_dir, libname) + if os.path.exists(src_library): + caller.f_globals['copyfile'](src_library, dest_library) + + def _demo_posix(): # # Example 1: Simple redirection: Get process list diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -205,23 +205,28 @@ The above is true both in CPython and in PyPy. Differences can occur about whether a built-in function or method will call an overridden method of *another* object than ``self``. -In PyPy, they are generally always called, whereas not in -CPython. For example, in PyPy, ``dict1.update(dict2)`` -considers that ``dict2`` is just a general mapping object, and -will thus call overridden ``keys()`` and ``__getitem__()`` -methods on it. So the following code prints ``42`` on PyPy -but ``foo`` on CPython:: +In PyPy, they are often called in cases where CPython would not. +Two examples:: - >>>> class D(dict): - .... def __getitem__(self, key): - .... return 42 - .... - >>>> - >>>> d1 = {} - >>>> d2 = D(a='foo') - >>>> d1.update(d2) - >>>> print d1['a'] - 42 + class D(dict): + def __getitem__(self, key): + return "%r from D" % (key,) + + class A(object): + pass + + a = A() + a.__dict__ = D() + a.foo = "a's own foo" + print a.foo + # CPython => a's own foo + # PyPy => 'foo' from D + + glob = D(foo="base item") + loc = {} + exec "print foo" in glob, loc + # CPython => base item + # PyPy => 'foo' from D Mutating classes of objects which are already used as dictionary keys @@ -292,6 +297,9 @@ above types will return a value that is computed from the argument, and can thus be larger than ``sys.maxint`` (i.e. it can be an arbitrary long). +Notably missing from the list above are ``str`` and ``unicode``. If your +code relies on comparing strings with ``is``, then it might break in PyPy. + Miscellaneous ------------- diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py --- a/pypy/interpreter/astcompiler/assemble.py +++ b/pypy/interpreter/astcompiler/assemble.py @@ -1,15 +1,13 @@ -""" -Python control flow graph generation and bytecode assembly. -""" +"""Python control flow graph generation and bytecode assembly.""" -from pypy.interpreter.astcompiler import ast, consts, symtable -from pypy.interpreter import pycode +from rpython.rlib import rfloat +from rpython.rlib.objectmodel import specialize, we_are_translated + +from pypy.interpreter.astcompiler import ast, consts, misc, symtable +from pypy.interpreter.error import OperationError +from pypy.interpreter.pycode import PyCode from pypy.tool import stdlib_opcode as ops -from pypy.interpreter.error import OperationError -from rpython.rlib.objectmodel import specialize, we_are_translated -from rpython.rlib import rfloat - class Instruction(object): """Represents a single opcode.""" @@ -21,14 +19,12 @@ self.has_jump = False def size(self): - """Return the size of bytes of this instruction when it is encoded.""" + """Return the size of bytes of this instruction when it is + encoded. + """ if self.opcode >= ops.HAVE_ARGUMENT: - if self.arg > 0xFFFF: - return 6 - else: - return 3 - else: - return 1 + return (6 if self.arg > 0xFFFF else 3) + return 1 def jump_to(self, target, absolute=False): """Indicate the target this jump instruction. @@ -54,9 +50,9 @@ class Block(object): """A basic control flow block. - It has one entry point and several possible exit points. Its instructions - may be jumps to other blocks, or if control flow reaches the end of the - block, it continues to next_block. + It has one entry point and several possible exit points. Its + instructions may be jumps to other blocks, or if control flow + reaches the end of the block, it continues to next_block. """ def __init__(self): @@ -71,10 +67,10 @@ stack.append(nextblock) def post_order(self): - """Return this block and its children in post order. - This means that the graph of blocks is first cleaned up to - ignore back-edges, thus turning it into a DAG. Then the DAG - is linearized. For example: + """Return this block and its children in post order. This means + that the graph of blocks is first cleaned up to ignore + back-edges, thus turning it into a DAG. Then the DAG is + linearized. For example: A --> B -\ => [A, D, B, C] \-> D ---> C @@ -105,7 +101,9 @@ return resultblocks def code_size(self): - """Return the encoded size of all the instructions in this block.""" + """Return the encoded size of all the instructions in this + block. + """ i = 0 for instr in self.instructions: i += instr.size() @@ -141,6 +139,7 @@ i += 1 return result + @specialize.argtype(0) def _iter_to_dict(iterable, offset=0): result = {} @@ -302,11 +301,11 @@ def _resolve_block_targets(self, blocks): """Compute the arguments of jump instructions.""" last_extended_arg_count = 0 - # The reason for this loop is extended jumps. EXTENDED_ARG extends the - # bytecode size, so it might invalidate the offsets we've already given. - # Thus we have to loop until the number of extended args is stable. Any - # extended jump at all is extremely rare, so performance is not too - # concerning. + # The reason for this loop is extended jumps. EXTENDED_ARG + # extends the bytecode size, so it might invalidate the offsets + # we've already given. Thus we have to loop until the number of + # extended args is stable. Any extended jump at all is + # extremely rare, so performance is not too concerning. while True: extended_arg_count = 0 offset = 0 @@ -332,7 +331,8 @@ instr.opcode = ops.JUMP_ABSOLUTE absolute = True elif target_op == ops.RETURN_VALUE: - # Replace JUMP_* to a RETURN into just a RETURN + # Replace JUMP_* to a RETURN into + # just a RETURN instr.opcode = ops.RETURN_VALUE instr.arg = 0 instr.has_jump = False @@ -347,7 +347,8 @@ instr.arg = jump_arg if jump_arg > 0xFFFF: extended_arg_count += 1 - if extended_arg_count == last_extended_arg_count and not force_redo: + if (extended_arg_count == last_extended_arg_count and + not force_redo): break else: last_extended_arg_count = extended_arg_count @@ -362,12 +363,14 @@ while True: try: w_key = space.next(w_iter) - except OperationError, e: + except OperationError as e: if not e.match(space, space.w_StopIteration): raise break w_index = space.getitem(w_consts, w_key) - consts_w[space.int_w(w_index)] = space.getitem(w_key, first) + w_constant = space.getitem(w_key, first) + w_constant = misc.intern_if_common_string(space, w_constant) + consts_w[space.int_w(w_index)] = w_constant return consts_w def _get_code_flags(self): @@ -435,15 +438,16 @@ continue addr = offset - current_off # Python assumes that lineno always increases with - # increasing bytecode address (lnotab is unsigned char). - # Depending on when SET_LINENO instructions are emitted this - # is not always true. Consider the code: + # increasing bytecode address (lnotab is unsigned + # char). Depending on when SET_LINENO instructions + # are emitted this is not always true. Consider the + # code: # a = (1, # b) - # In the bytecode stream, the assignment to "a" occurs after - # the loading of "b". This works with the C Python compiler - # because it only generates a SET_LINENO instruction for the - # assignment. + # In the bytecode stream, the assignment to "a" + # occurs after the loading of "b". This works with + # the C Python compiler because it only generates a + # SET_LINENO instruction for the assignment. if line or addr: while addr > 255: push(chr(255)) @@ -488,23 +492,23 @@ # (Only) inherit compilerflags in PyCF_MASK flags |= (self.compile_info.flags & consts.PyCF_MASK) bytecode = ''.join([block.get_code() for block in blocks]) - return pycode.PyCode(self.space, - self.argcount, - self.kwonlyargcount, - len(self.var_names), - stack_depth, - flags, - bytecode, - list(consts_w), - names, - var_names, - self.compile_info.filename, - self.name, - self.first_lineno, - lnotab, - free_names, - cell_names, - self.compile_info.hidden_applevel) + return PyCode(self.space, + self.argcount, + self.kwonlyargcount, + len(self.var_names), + stack_depth, + flags, + bytecode, + list(consts_w), + names, + var_names, + self.compile_info.filename, + self.name, + self.first_lineno, + lnotab, + free_names, + cell_names, + self.compile_info.hidden_applevel) def _list_from_dict(d, offset=0): @@ -515,116 +519,116 @@ _static_opcode_stack_effects = { - ops.NOP : 0, - ops.STOP_CODE : 0, + ops.NOP: 0, + ops.STOP_CODE: 0, - ops.POP_TOP : -1, - ops.ROT_TWO : 0, - ops.ROT_THREE : 0, - ops.DUP_TOP : 1, - ops.DUP_TOP_TWO : 2, + ops.POP_TOP: -1, + ops.ROT_TWO: 0, + ops.ROT_THREE: 0, + ops.DUP_TOP: 1, + ops.DUP_TOP_TWO: 2, - ops.UNARY_POSITIVE : 0, - ops.UNARY_NEGATIVE : 0, - ops.UNARY_NOT : 0, - ops.UNARY_INVERT : 0, + ops.UNARY_POSITIVE: 0, + ops.UNARY_NEGATIVE: 0, + ops.UNARY_NOT: 0, + ops.UNARY_INVERT: 0, - ops.LIST_APPEND : -1, - ops.SET_ADD : -1, - ops.MAP_ADD : -2, - ops.STORE_MAP : -2, + ops.LIST_APPEND: -1, + ops.SET_ADD: -1, + ops.MAP_ADD: -2, + ops.STORE_MAP: -2, - ops.BINARY_POWER : -1, - ops.BINARY_MULTIPLY : -1, - ops.BINARY_MODULO : -1, - ops.BINARY_ADD : -1, - ops.BINARY_SUBTRACT : -1, - ops.BINARY_SUBSCR : -1, - ops.BINARY_FLOOR_DIVIDE : -1, - ops.BINARY_TRUE_DIVIDE : -1, - ops.BINARY_LSHIFT : -1, - ops.BINARY_RSHIFT : -1, - ops.BINARY_AND : -1, - ops.BINARY_OR : -1, - ops.BINARY_XOR : -1, + ops.BINARY_POWER: -1, + ops.BINARY_MULTIPLY: -1, + ops.BINARY_MODULO: -1, + ops.BINARY_ADD: -1, + ops.BINARY_SUBTRACT: -1, + ops.BINARY_SUBSCR: -1, + ops.BINARY_FLOOR_DIVIDE: -1, + ops.BINARY_TRUE_DIVIDE: -1, + ops.BINARY_LSHIFT: -1, + ops.BINARY_RSHIFT: -1, + ops.BINARY_AND: -1, + ops.BINARY_OR: -1, + ops.BINARY_XOR: -1, - ops.INPLACE_FLOOR_DIVIDE : -1, - ops.INPLACE_TRUE_DIVIDE : -1, - ops.INPLACE_ADD : -1, - ops.INPLACE_SUBTRACT : -1, - ops.INPLACE_MULTIPLY : -1, - ops.INPLACE_MODULO : -1, - ops.INPLACE_POWER : -1, - ops.INPLACE_LSHIFT : -1, - ops.INPLACE_RSHIFT : -1, - ops.INPLACE_AND : -1, - ops.INPLACE_OR : -1, - ops.INPLACE_XOR : -1, + ops.INPLACE_FLOOR_DIVIDE: -1, + ops.INPLACE_TRUE_DIVIDE: -1, + ops.INPLACE_ADD: -1, + ops.INPLACE_SUBTRACT: -1, + ops.INPLACE_MULTIPLY: -1, + ops.INPLACE_MODULO: -1, + ops.INPLACE_POWER: -1, + ops.INPLACE_LSHIFT: -1, + ops.INPLACE_RSHIFT: -1, + ops.INPLACE_AND: -1, + ops.INPLACE_OR: -1, + ops.INPLACE_XOR: -1, - ops.STORE_SUBSCR : -2, - ops.DELETE_SUBSCR : -2, + ops.STORE_SUBSCR: -2, + ops.DELETE_SUBSCR: -2, - ops.GET_ITER : 0, - ops.FOR_ITER : 1, - ops.BREAK_LOOP : 0, - ops.CONTINUE_LOOP : 0, - ops.SETUP_LOOP : 0, + ops.GET_ITER: 0, + ops.FOR_ITER: 1, + ops.BREAK_LOOP: 0, + ops.CONTINUE_LOOP: 0, + ops.SETUP_LOOP: 0, - ops.PRINT_EXPR : -1, + ops.PRINT_EXPR: -1, - ops.WITH_CLEANUP : -1, - ops.LOAD_BUILD_CLASS : 1, - ops.STORE_LOCALS : -1, - ops.POP_BLOCK : 0, - ops.POP_EXCEPT : 0, - ops.END_FINALLY : -1, - ops.SETUP_WITH : 1, - ops.SETUP_FINALLY : 0, - ops.SETUP_EXCEPT : 4, + ops.WITH_CLEANUP: -1, + ops.LOAD_BUILD_CLASS: 1, + ops.STORE_LOCALS: -1, + ops.POP_BLOCK: 0, + ops.POP_EXCEPT: 0, + ops.END_FINALLY: -1, + ops.SETUP_WITH: 1, + ops.SETUP_FINALLY: 0, + ops.SETUP_EXCEPT: 4, - ops.RETURN_VALUE : -1, - ops.YIELD_VALUE : 0, - ops.BUILD_MAP : 1, - ops.BUILD_SET : 1, - ops.COMPARE_OP : -1, + ops.RETURN_VALUE: -1, + ops.YIELD_VALUE: 0, + ops.BUILD_MAP: 1, + ops.BUILD_SET: 1, + ops.COMPARE_OP: -1, - ops.LOOKUP_METHOD : 1, + ops.LOOKUP_METHOD: 1, - ops.LOAD_NAME : 1, - ops.STORE_NAME : -1, - ops.DELETE_NAME : 0, + ops.LOAD_NAME: 1, + ops.STORE_NAME: -1, + ops.DELETE_NAME: 0, - ops.LOAD_FAST : 1, - ops.STORE_FAST : -1, - ops.DELETE_FAST : 0, + ops.LOAD_FAST: 1, + ops.STORE_FAST: -1, + ops.DELETE_FAST: 0, - ops.LOAD_ATTR : 0, - ops.STORE_ATTR : -2, - ops.DELETE_ATTR : -1, + ops.LOAD_ATTR: 0, + ops.STORE_ATTR: -2, + ops.DELETE_ATTR: -1, - ops.LOAD_GLOBAL : 1, - ops.STORE_GLOBAL : -1, - ops.DELETE_GLOBAL : 0, - ops.DELETE_DEREF : 0, - - ops.LOAD_CLOSURE : 1, - ops.LOAD_DEREF : 1, - ops.STORE_DEREF : -1, + ops.LOAD_GLOBAL: 1, + ops.STORE_GLOBAL: -1, + ops.DELETE_GLOBAL: 0, ops.DELETE_DEREF: 0, - ops.LOAD_CONST : 1, + ops.LOAD_CLOSURE: 1, + ops.LOAD_DEREF: 1, + ops.STORE_DEREF: -1, + ops.DELETE_DEREF: 0, - ops.IMPORT_STAR : -1, - ops.IMPORT_NAME : -1, - ops.IMPORT_FROM : 1, + ops.LOAD_CONST: 1, - ops.JUMP_FORWARD : 0, - ops.JUMP_ABSOLUTE : 0, - ops.JUMP_IF_TRUE_OR_POP : 0, - ops.JUMP_IF_FALSE_OR_POP : 0, - ops.POP_JUMP_IF_TRUE : -1, - ops.POP_JUMP_IF_FALSE : -1, - ops.JUMP_IF_NOT_DEBUG : 0, + ops.IMPORT_STAR: -1, + ops.IMPORT_NAME: -1, + ops.IMPORT_FROM: 1, + + ops.JUMP_FORWARD: 0, + ops.JUMP_ABSOLUTE: 0, + ops.JUMP_IF_TRUE_OR_POP: 0, + ops.JUMP_IF_FALSE_OR_POP: 0, + ops.POP_JUMP_IF_TRUE: -1, + ops.POP_JUMP_IF_FALSE: -1, + ops.JUMP_IF_NOT_DEBUG: 0, ops.BUILD_LIST_FROM_ARG: 1, } diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py --- a/pypy/interpreter/astcompiler/misc.py +++ b/pypy/interpreter/astcompiler/misc.py @@ -108,6 +108,16 @@ return "_%s%s" % (klass[i:], name) +def intern_if_common_string(space, w_const): + # only intern identifier-like strings + if not space.is_w(space.type(w_const), space.w_str): + return w_const + for c in space.str_w(w_const): + if not (c.isalnum() or c == '_'): + return w_const + return space.new_interned_w_str(w_const) + + def new_identifier(space, name): # Check whether there are non-ASCII characters in the identifier; if # so, normalize to NFKC diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -279,6 +279,11 @@ if w_const is None: return tup consts_w[i] = w_const + # intern the string constants packed into the tuple here, + # because assemble.py will see the result as just a tuple constant + for i in range(len(consts_w)): + consts_w[i] = misc.intern_if_common_string( + self.space, consts_w[i]) else: consts_w = [] w_consts = self.space.newtuple(consts_w) diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -15,7 +15,7 @@ UserDelAction) from pypy.interpreter.error import OperationError, new_exception_class, oefmt from pypy.interpreter.argument import Arguments -from pypy.interpreter.miscutils import ThreadLocals +from pypy.interpreter.miscutils import ThreadLocals, make_weak_value_dictionary __all__ = ['ObjSpace', 'OperationError', 'W_Root'] @@ -362,7 +362,7 @@ self.builtin_modules = {} self.reloading_modules = {} - self.interned_strings = {} + self.interned_strings = make_weak_value_dictionary(self, str, W_Root) self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module self.user_del_action = UserDelAction(self) @@ -766,25 +766,30 @@ return self.w_False def new_interned_w_str(self, w_s): + assert isinstance(w_s, W_Root) # and is not None s = self.str_w(w_s) if not we_are_translated(): assert type(s) is str - try: - return self.interned_strings[s] - except KeyError: - pass - self.interned_strings[s] = w_s - return w_s + w_s1 = self.interned_strings.get(s) + if w_s1 is None: + w_s1 = w_s + self.interned_strings.set(s, w_s1) + return w_s1 def new_interned_str(self, s): if not we_are_translated(): assert type(s) is str - try: - return self.interned_strings[s] - except KeyError: - pass - w_s = self.interned_strings[s] = self.wrap(s) - return w_s + w_s1 = self.interned_strings.get(s) + if w_s1 is None: + w_s1 = self.wrap(s) + self.interned_strings.set(s, w_s1) + return w_s1 + + def is_interned_str(self, s): + # interface for marshal_impl + if not we_are_translated(): + assert type(s) is str + return self.interned_strings.get(s) is not None def descr_self_interp_w(self, RequiredClass, w_obj): if not isinstance(w_obj, RequiredClass): diff --git a/pypy/interpreter/miscutils.py b/pypy/interpreter/miscutils.py --- a/pypy/interpreter/miscutils.py +++ b/pypy/interpreter/miscutils.py @@ -31,3 +31,19 @@ def getallvalues(self): return {0: self._value} + + +def make_weak_value_dictionary(space, keytype, valuetype): + "NOT_RPYTHON" + if space.config.translation.rweakref: + from rpython.rlib.rweakref import RWeakValueDictionary + return RWeakValueDictionary(keytype, valuetype) + else: + class FakeWeakValueDict(object): + def __init__(self): + self._dict = {} + def get(self, key): + return self._dict.get(key, None) + def set(self, key, value): + self._dict[key] = value + return FakeWeakValueDict() diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -131,7 +131,6 @@ # class bodies only have CO_NEWLOCALS. # CO_NEWLOCALS: make a locals dict unless optimized is also set # CO_OPTIMIZED: no locals dict needed at all - # NB: this method is overridden in nestedscope.py flags = code.co_flags if not (flags & pycode.CO_OPTIMIZED): if flags & pycode.CO_NEWLOCALS: diff --git a/pypy/interpreter/test/test_compiler.py b/pypy/interpreter/test/test_compiler.py --- a/pypy/interpreter/test/test_compiler.py +++ b/pypy/interpreter/test/test_compiler.py @@ -1094,7 +1094,12 @@ sys.stdout = out output = s.getvalue() assert "CALL_METHOD" in output - + + def test_interned_strings(self): + source = """x = ('foo_bar42', 5); y = 'foo_bar42'; z = x[0]""" + exec source + assert y is z + class AppTestExceptions: def test_indentation_error(self): diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py --- a/pypy/interpreter/test/test_objspace.py +++ b/pypy/interpreter/test/test_objspace.py @@ -378,3 +378,41 @@ assert space.str_w(space.getattr(space.sys, w_executable)) == 'foobar' space.startup() assert space.str_w(space.getattr(space.sys, w_executable)) == 'foobar' + + def test_interned_strings_are_weak(self): + import weakref, gc, random + space = self.space + assert space.config.translation.rweakref + w1 = space.new_interned_str("abcdef") + w2 = space.new_interned_str("abcdef") + assert w2 is w1 + # + # check that 'w1' goes away if we don't hold a reference to it + rw1 = weakref.ref(w1) + del w1, w2 + i = 10 + while rw1() is not None: + i -= 1 + assert i >= 0 + gc.collect() + # + s = "foobar%r" % random.random() + w0 = space.wrap(s) + w1 = space.new_interned_w_str(w0) + assert w1 is w0 + w2 = space.new_interned_w_str(w0) + assert w2 is w0 + w3 = space.wrap(s) + assert w3 is not w0 + w4 = space.new_interned_w_str(w3) + assert w4 is w0 + # + # check that 'w0' goes away if we don't hold a reference to it + # (even if we hold a reference to 'w3') + rw0 = weakref.ref(w0) + del w0, w1, w2, w4 + i = 10 + while rw0() is not None: + i -= 1 + assert i >= 0 + gc.collect() diff --git a/pypy/module/marshal/interp_marshal.py b/pypy/module/marshal/interp_marshal.py --- a/pypy/module/marshal/interp_marshal.py +++ b/pypy/module/marshal/interp_marshal.py @@ -147,7 +147,6 @@ atom_int(tc, int) puts code and int atom_int64(tc, int64) puts code and int64 atom_str(tc, str) puts code, len and string - atom_strlist(tc, strlist) puts code, len and list of strings building blocks for compound types: @@ -201,15 +200,6 @@ self.atom_int(typecode, len(x)) self.put(x) - def atom_strlist(self, typecode, tc2, x): - self.atom_int(typecode, len(x)) - atom_str = self.atom_str - for item in x: - # type(str) seems to be forbidden - #if type(item) is not str: - # self.raise_exc('object with wrong type in strlist') - atom_str(tc2, item) - def start(self, typecode): # type(char) not supported self.put(typecode) @@ -382,16 +372,6 @@ self.start(typecode) return self.get_lng() - def atom_strlist(self, typecode, tc2): - self.start(typecode) - lng = self.get_lng() - res = [None] * lng - idx = 0 - while idx < lng: - res[idx] = self.atom_str(tc2) - idx += 1 - return res - def start(self, typecode): tc = self.get1() if tc != typecode: @@ -439,7 +419,6 @@ def get_w_obj(self, allow_null=False): space = self.space - w_ret = space.w_None # something not None tc = self.get1() w_ret = self._dispatch[ord(tc)](space, self, tc) if w_ret is None and not allow_null: diff --git a/pypy/module/pypyjit/test_pypy_c/model.py b/pypy/module/pypyjit/test_pypy_c/model.py --- a/pypy/module/pypyjit/test_pypy_c/model.py +++ b/pypy/module/pypyjit/test_pypy_c/model.py @@ -275,9 +275,19 @@ @classmethod def parse_op(cls, line): - # strip comment + # strip comment after '#', but not if it appears inside parentheses if '#' in line: - line = line[:line.index('#')] + nested = 0 + for i, c in enumerate(line): + if c == '(': + nested += 1 + elif c == ')': + assert nested > 0, "more ')' than '(' in %r" % (line,) + nested -= 1 + elif c == '#' and nested == 0: + line = line[:i] + break + # if line.strip() == 'guard_not_invalidated?': return 'guard_not_invalidated', None, [], '...', False # find the resvar, if any @@ -314,7 +324,7 @@ # to repeat it every time ticker_check = """ guard_not_invalidated? - ticker0 = getfield_raw(ticker_address, descr=<FieldS pypysig_long_struct.c_value .*>) + ticker0 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value .*>) ticker_cond0 = int_lt(ticker0, 0) guard_false(ticker_cond0, descr=...) """ @@ -323,9 +333,9 @@ # this is the ticker check generated if we have threads thread_ticker_check = """ guard_not_invalidated? - ticker0 = getfield_raw(ticker_address, descr=<FieldS pypysig_long_struct.c_value .*>) - ticker1 = int_sub(ticker0, _) - setfield_raw(ticker_address, ticker1, descr=<FieldS pypysig_long_struct.c_value .*>) + ticker0 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value .*>) + ticker1 = int_sub(ticker0, #) + setfield_raw(#, ticker1, descr=<FieldS pypysig_long_struct.c_value .*>) ticker_cond0 = int_lt(ticker1, 0) guard_false(ticker_cond0, descr=...) """ @@ -333,7 +343,7 @@ # # this is the ticker check generated in PyFrame.handle_operation_error exc_ticker_check = """ - ticker2 = getfield_raw(ticker_address, descr=<FieldS pypysig_long_struct.c_value .*>) + ticker2 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value .*>) ticker_cond1 = int_lt(ticker2, 0) guard_false(ticker_cond1, descr=...) """ @@ -351,18 +361,31 @@ @staticmethod def as_numeric_const(v1): + # returns one of: ('int', value) ('float', value) None try: - return int(v1) - except (ValueError, TypeError): - return None + return ('int', int(v1)) + except ValueError: + pass + if '.' in v1: + try: + return ('float', float(v1)) + except ValueError: + pass + return None def match_var(self, v1, exp_v2): assert v1 != '_' - if exp_v2 == '_': + if exp_v2 == '_': # accept anything return True + if exp_v2 is None: + return v1 is None + assert exp_v2 != '...' # bogus use of '...' in the expected code n1 = self.as_numeric_const(v1) + if exp_v2 == '#': # accept any (integer or float) number + return n1 is not None n2 = self.as_numeric_const(exp_v2) - if n1 is not None and n2 is not None: + if n1 is not None or n2 is not None: + # at least one is a number; check that both are, and are equal return n1 == n2 if self.is_const(v1) or self.is_const(exp_v2): return v1[:-1].startswith(exp_v2[:-1]) @@ -382,10 +405,13 @@ def match_op(self, op, (exp_opname, exp_res, exp_args, exp_descr, _)): self._assert(op.name == exp_opname, "operation mismatch") self.match_var(op.res, exp_res) - if exp_args != ['...']: + if exp_args[-1:] == ['...']: # exp_args ends with '...' + exp_args = exp_args[:-1] + self._assert(len(op.args) >= len(exp_args), "not enough arguments") + else: self._assert(len(op.args) == len(exp_args), "wrong number of arguments") - for arg, exp_arg in zip(op.args, exp_args): - self._assert(self.match_var(arg, exp_arg), "variable mismatch: %r instead of %r" % (arg, exp_arg)) + for arg, exp_arg in zip(op.args, exp_args): + self._assert(self.match_var(arg, exp_arg), "variable mismatch: %r instead of %r" % (arg, exp_arg)) self.match_descr(op.descr, exp_descr) diff --git a/pypy/module/pypyjit/test_pypy_c/test_00_model.py b/pypy/module/pypyjit/test_pypy_c/test_00_model.py --- a/pypy/module/pypyjit/test_pypy_c/test_00_model.py +++ b/pypy/module/pypyjit/test_pypy_c/test_00_model.py @@ -158,6 +158,24 @@ assert match_var('v0', 'V0') assert match_var('ConstPtr(ptr0)', '_') py.test.raises(AssertionError, "match_var('_', 'v0')") + # + # numerics + assert match_var('1234', '1234') + assert not match_var('1234', '1235') + assert not match_var('v0', '1234') + assert not match_var('1234', 'v0') + assert match_var('1234', '#') # the '#' char matches any number + assert not match_var('v0', '#') + assert match_var('1234', '_') # the '_' char matches anything + # + # float numerics + assert match_var('0.000000', '0.0') + assert not match_var('0.000000', '0') + assert not match_var('0', '0.0') + assert not match_var('v0', '0.0') + assert not match_var('0.0', 'v0') + assert match_var('0.0', '#') + assert match_var('0.0', '_') def test_parse_op(self): res = OpMatcher.parse_op(" a = int_add( b, 3 ) # foo") @@ -210,6 +228,19 @@ """ assert not self.match(loop, expected) + def test_dotdotdot_in_operation(self): + loop = """ + [i0, i1] + jit_debug(i0, 1, ConstClass(myclass), i1) + """ + assert self.match(loop, "jit_debug(...)") + assert self.match(loop, "jit_debug(i0, ...)") + assert self.match(loop, "jit_debug(i0, 1, ...)") + assert self.match(loop, "jit_debug(i0, 1, _, ...)") + assert self.match(loop, "jit_debug(i0, 1, _, i1, ...)") + py.test.raises(AssertionError, self.match, + loop, "jit_debug(i0, 1, ..., i1)") + def test_match_descr(self): loop = """ [p0] @@ -232,7 +263,7 @@ jump(i4) """ expected = """ - i1 = int_add(0, 1) + i1 = int_add(i0, 1) ... i4 = int_mul(i1, 1000) jump(i4, descr=...) @@ -249,7 +280,7 @@ jump(i4, descr=...) """ expected = """ - i1 = int_add(0, 1) + i1 = int_add(i0, 1) ... _ = int_mul(_, 1000) jump(i4, descr=...) @@ -268,7 +299,7 @@ jump(i4) """ expected = """ - i1 = int_add(0, 1) + i1 = int_add(i0, 1) ... """ assert self.match(loop, expected) diff --git a/pypy/module/pypyjit/test_pypy_c/test_buffers.py b/pypy/module/pypyjit/test_pypy_c/test_buffers.py --- a/pypy/module/pypyjit/test_pypy_c/test_buffers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_buffers.py @@ -21,7 +21,7 @@ i65 = getfield_gc(p18, descr=...) i67 = int_gt(0, i65) guard_false(i67, descr=...) - i69 = int_gt(., i65) + i69 = int_gt(#, i65) guard_true(i69, descr=...) --TICK-- """) @@ -56,7 +56,7 @@ guard_false(i99, descr=...) i100 = int_lshift(i98, 24) i101 = int_or(i97, i100) - i102 = getfield_raw(\d+, descr=<FieldS pypysig_long_struct.c_value 0>) + i102 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value 0>) i103 = int_lt(i102, 0) guard_false(i103, descr=...) """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py --- a/pypy/module/pypyjit/test_pypy_c/test_call.py +++ b/pypy/module/pypyjit/test_pypy_c/test_call.py @@ -395,7 +395,7 @@ setarrayitem_gc(p24, 0, p26, descr=<ArrayP .>) setfield_gc(p22, p24, descr=<FieldP .*Arguments.inst_arguments_w .*>) }}} - p32 = call_may_force(..., p18, p22, descr=<Callr . rr EF=6>) + p32 = call_may_force(_, p18, p22, descr=<Callr . rr EF=6>) ... """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py --- a/pypy/module/pypyjit/test_pypy_c/test_containers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py @@ -82,7 +82,7 @@ guard_no_exception(descr=...) i23 = call(ConstClass(ll_dict_lookup_trampoline), p13, p10, i12, descr=<Calli . rri EF=4 OS=4>) guard_no_exception(descr=...) - i26 = int_and(i23, .*) + i26 = int_and(i23, #) i27 = int_is_true(i26) guard_false(i27, descr=...) p28 = getfield_gc(p13, descr=<FieldP dicttable.entries .*>) diff --git a/pypy/module/pypyjit/test_pypy_c/test_math.py b/pypy/module/pypyjit/test_pypy_c/test_math.py --- a/pypy/module/pypyjit/test_pypy_c/test_math.py +++ b/pypy/module/pypyjit/test_pypy_c/test_math.py @@ -21,7 +21,7 @@ guard_true(i2, descr=...) guard_not_invalidated(descr=...) f1 = cast_int_to_float(i0) - i3 = float_le(f1, 0) + i3 = float_le(f1, 0.0) guard_false(i3, descr=...) f2 = call(ConstClass(log), f1, descr=<Callf . f EF=2>) f3 = call(ConstClass(log10), f1, descr=<Callf . f EF=2>) @@ -56,7 +56,7 @@ f3 = call(ConstClass(cos), f1, descr=<Callf . f EF=0>) f4 = float_sub(f2, f3) f5 = float_add(f0, f4) - i7 = int_add(i0, f1) + i7 = int_add(i0, 1) --TICK-- jump(..., descr=) """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -104,7 +104,7 @@ setarrayitem_gc(p150, 1, 0, descr=<ArrayS .+>) setarrayitem_gc(p150, 0, 0, descr=<ArrayS .+>) guard_not_invalidated(descr=...) - i154 = getfield_raw(ticker_address, descr=<FieldS pypysig_long_struct.c_value 0>) + i154 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value 0>) i155 = int_lt(i154, 0) guard_false(i155, descr=...) p156 = new_with_vtable(...) @@ -142,7 +142,7 @@ raw_store(i103, i132, 42.000000, descr=<ArrayF 8>) p152 = getfield_gc_pure(p126, descr=<FieldP pypy.module.micronumpy.iterators.IterState.inst_indices .+>) i153 = int_add(i120, 1) - i154 = getfield_raw(ticker_address, descr=<FieldS pypysig_long_struct.c_value 0>) + i154 = getfield_raw(#, descr=<FieldS pypysig_long_struct.c_value 0>) setarrayitem_gc(p152, 1, 0, descr=<ArrayS .+>) setarrayitem_gc(p152, 0, 0, descr=<ArrayS .+>) i157 = int_lt(i154, 0) diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -67,15 +67,15 @@ i11 = int_lt(i6, i7) guard_true(i11, descr=...) guard_not_invalidated(descr=...) - i13 = int_eq(i6, %d) + i13 = int_eq(i6, %d) # value provided below guard_false(i13, descr=...) - i15 = int_mod(i6, i8) - i17 = int_rshift(i15, %d) - i18 = int_and(i8, i17) + i15 = int_mod(i6, 10) + i17 = int_rshift(i15, %d) # value provided below + i18 = int_and(10, i17) i19 = int_add(i15, i18) i21 = int_lt(i19, 0) guard_false(i21, descr=...) - i22 = int_ge(i19, i8) + i22 = int_ge(i19, 10) guard_false(i22, descr=...) i23 = strgetitem(p10, i19) p25 = newstr(1) @@ -83,7 +83,7 @@ p93 = call(ConstClass(fromstr), p25, 16, descr=<Callr . ri EF=3>) guard_no_exception(descr=...) i95 = getfield_gc_pure(p93, descr=<FieldS rpython.rlib.rbigint.rbigint.inst_size .*>) - i96 = int_gt(i95, .*) + i96 = int_gt(i95, #) guard_false(i96, descr=...) i94 = call(ConstClass(rbigint._toint_helper), p93, descr=<Calli . r EF=3>) guard_no_exception(descr=...) diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py b/pypy/module/pypyjit/test_pypy_c/test_thread.py --- a/pypy/module/pypyjit/test_pypy_c/test_thread.py +++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py @@ -64,7 +64,7 @@ guard_true(i56, descr=...) p57 = force_token() setfield_gc(p0, p57, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>) - i58 = call_release_gil(..., i37, 1, descr=<Calli 4 ii EF=6>) + i58 = call_release_gil(_, i37, 1, descr=<Calli 4 ii EF=6>) guard_not_forced(descr=...) guard_no_exception(descr=...) i59 = int_is_true(i58) @@ -72,14 +72,14 @@ i60 = int_sub(i44, 1) p62 = force_token() setfield_gc(p0, p62, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>) - i63 = call_release_gil(..., i37, 0, descr=<Calli 4 ii EF=6>) + i63 = call_release_gil(_, i37, 0, descr=<Calli 4 ii EF=6>) guard_not_forced(descr=...) guard_no_exception(descr=...) i64 = int_is_true(i63) guard_false(i64, descr=...) p65 = force_token() setfield_gc(p0, p65, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>) - call_release_gil(..., i37, descr=<Callv 0 i EF=6>) + call_release_gil(_, i37, descr=<Callv 0 i EF=6>) guard_not_forced(descr=...) guard_no_exception(descr=...) guard_not_invalidated(descr=...) diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py --- a/pypy/objspace/std/marshal_impl.py +++ b/pypy/objspace/std/marshal_impl.py @@ -322,8 +322,8 @@ m.put_int(x.co_stacksize) m.put_int(x.co_flags) m.atom_str(TYPE_STRING, x.co_code) - m.put_tuple_w(TYPE_TUPLE, x.co_consts_w[:]) - m.atom_strlist(TYPE_TUPLE, TYPE_STRING, [space.str_w(w_name) for w_name in x.co_names_w]) + m.put_tuple_w(TYPE_TUPLE, x.co_consts_w) + m.put_tuple_w(TYPE_TUPLE, x.co_names_w) m.atom_strlist(TYPE_TUPLE, TYPE_STRING, x.co_varnames) m.atom_strlist(TYPE_TUPLE, TYPE_STRING, x.co_freevars) m.atom_strlist(TYPE_TUPLE, TYPE_STRING, x.co_cellvars) @@ -332,9 +332,8 @@ m.put_int(x.co_firstlineno) m.atom_str(TYPE_STRING, x.co_lnotab) -# helper for unmarshalling string lists of code objects. -# unfortunately they now can be interned or referenced, -# so we no longer can handle it in interp_marshal.atom_strlist +# helper for unmarshalling "tuple of string" objects +# into rpython-level lists of strings. Only for code objects. def unmarshal_str(u): w_obj = u.get_w_obj() diff --git a/rpython/flowspace/bytecode.py b/rpython/flowspace/bytecode.py --- a/rpython/flowspace/bytecode.py +++ b/rpython/flowspace/bytecode.py @@ -81,37 +81,37 @@ and **varkwarg, if they exist.""" return self.signature.scope_length() - def read(self, pos): + def read(self, offset): """ - Decode the instruction starting at position ``next_instr``. + Decode the instruction starting at position ``offset``. - Returns (next_instr, opname, oparg). + Returns (next_offset, opname, oparg). """ co_code = self.co_code - opnum = ord(co_code[pos]) - next_instr = pos + 1 + opnum = ord(co_code[offset]) + next_offset = offset + 1 if opnum >= HAVE_ARGUMENT: - lo = ord(co_code[next_instr]) - hi = ord(co_code[next_instr+1]) - next_instr += 2 + lo = ord(co_code[next_offset]) + hi = ord(co_code[next_offset + 1]) + next_offset += 2 oparg = (hi * 256) | lo else: oparg = 0 while opnum == EXTENDED_ARG: - opnum = ord(co_code[next_instr]) + opnum = ord(co_code[next_offset]) if opnum < HAVE_ARGUMENT: raise BytecodeCorruption - lo = ord(co_code[next_instr+1]) - hi = ord(co_code[next_instr+2]) - next_instr += 3 + lo = ord(co_code[next_offset + 1]) + hi = ord(co_code[next_offset + 2]) + next_offset += 3 oparg = (oparg * 65536) | (hi * 256) | lo if opnum in opcode.hasjrel: - oparg += next_instr + oparg += next_offset opname = self.opnames[opnum] - return next_instr, opname, oparg + return next_offset, opname, oparg @property def is_generator(self): diff --git a/rpython/flowspace/flowcontext.py b/rpython/flowspace/flowcontext.py --- a/rpython/flowspace/flowcontext.py +++ b/rpython/flowspace/flowcontext.py @@ -29,7 +29,7 @@ msg = ["\n"] msg += map(str, self.args) msg += [""] - msg += source_lines(self.ctx.graph, None, offset=self.ctx.last_instr) + msg += source_lines(self.ctx.graph, None, offset=self.ctx.last_offset) return "\n".join(msg) @@ -288,7 +288,7 @@ self.init_closure(func.func_closure) self.f_lineno = code.co_firstlineno - self.last_instr = 0 + self.last_offset = 0 self.init_locals_stack(code) @@ -359,7 +359,7 @@ self.locals_stack_w[:len(items_w)] = items_w self.dropvaluesuntil(len(items_w)) - def getstate(self, next_pos): + def getstate(self, next_offset): # getfastscope() can return real None, for undefined locals data = self.save_locals_stack() if self.last_exception is None: @@ -369,7 +369,7 @@ data.append(self.last_exception.w_type) data.append(self.last_exception.w_value) recursively_flatten(data) - return FrameState(data, self.blockstack[:], next_pos) + return FrameState(data, self.blockstack[:], next_offset) def setstate(self, state): """ Reset the context to the given frame state. """ @@ -393,7 +393,7 @@ if getattr(recorder, 'final_state', None) is not None: self.mergeblock(recorder.crnt_block, recorder.final_state) raise StopFlowing - spaceop.offset = self.last_instr + spaceop.offset = self.last_offset recorder.append(spaceop) def do_op(self, op): @@ -424,12 +424,12 @@ def record_block(self, block): self.setstate(block.framestate) - next_pos = block.framestate.next_instr + next_offset = block.framestate.next_offset self.recorder = block.make_recorder() try: while True: - next_pos = self.handle_bytecode(next_pos) - self.recorder.final_state = self.getstate(next_pos) + next_offset = self.handle_bytecode(next_offset) + self.recorder.final_state = self.getstate(next_offset) except RaiseImplicit as e: w_exc = e.w_exc @@ -467,10 +467,10 @@ self.recorder = None def mergeblock(self, currentblock, currentstate): - next_instr = currentstate.next_instr + next_offset = currentstate.next_offset # can 'currentstate' be merged with one of the blocks that # already exist for this bytecode position? - candidates = self.joinpoints.setdefault(next_instr, []) + candidates = self.joinpoints.setdefault(next_offset, []) for block in candidates: newstate = block.framestate.union(currentstate) if newstate is not None: @@ -526,12 +526,12 @@ stack_items_w[i] = w_new break - def handle_bytecode(self, next_instr): - self.last_instr = next_instr - next_instr, methodname, oparg = self.pycode.read(next_instr) + def handle_bytecode(self, next_offset): + self.last_offset = next_offset + next_offset, methodname, oparg = self.pycode.read(next_offset) try: - res = getattr(self, methodname)(oparg) - return res if res is not None else next_instr + offset = getattr(self, methodname)(oparg) + return offset if offset is not None else next_offset except FlowSignal as signal: return self.unroll(signal) @@ -856,14 +856,9 @@ def WITH_CLEANUP(self, oparg): # Note: RPython context managers receive None in lieu of tracebacks # and cannot suppress the exception. - # This opcode changed a lot between CPython versions - if sys.version_info >= (2, 6): - unroller = self.popvalue() - w_exitfunc = self.popvalue() - self.pushvalue(unroller) - else: - w_exitfunc = self.popvalue() - unroller = self.peekvalue(0) + unroller = self.popvalue() + w_exitfunc = self.popvalue() + self.pushvalue(unroller) if isinstance(unroller, Raise): w_exc = unroller.w_exc diff --git a/rpython/flowspace/framestate.py b/rpython/flowspace/framestate.py --- a/rpython/flowspace/framestate.py +++ b/rpython/flowspace/framestate.py @@ -3,10 +3,10 @@ class FrameState(object): - def __init__(self, mergeable, blocklist, next_instr): + def __init__(self, mergeable, blocklist, next_offset): self.mergeable = mergeable self.blocklist = blocklist - self.next_instr = next_instr + self.next_offset = next_offset def copy(self): "Make a copy of this state in which all Variables are fresh." @@ -15,7 +15,7 @@ if isinstance(w, Variable): w = Variable(w) newstate.append(w) - return FrameState(newstate, self.blocklist, self.next_instr) + return FrameState(newstate, self.blocklist, self.next_offset) def getvariables(self): return [w for w in self.mergeable if isinstance(w, Variable)] @@ -26,7 +26,7 @@ # safety check, don't try to compare states with different # nonmergeable states assert self.blocklist == other.blocklist - assert self.next_instr == other.next_instr + assert self.next_offset == other.next_offset for w1, w2 in zip(self.mergeable, other.mergeable): if not (w1 == w2 or (isinstance(w1, Variable) and isinstance(w2, Variable))): @@ -44,7 +44,7 @@ newstate.append(union(w1, w2)) except UnionError: return None - return FrameState(newstate, self.blocklist, self.next_instr) + return FrameState(newstate, self.blocklist, self.next_offset) def getoutputargs(self, targetstate): "Return the output arguments needed to link self to targetstate." diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py --- a/rpython/jit/backend/x86/assembler.py +++ b/rpython/jit/backend/x86/assembler.py @@ -1832,15 +1832,17 @@ offset = self.cpu.get_ofs_of_frame_field('jf_guard_exc') mc.MOV_br(offset, ebx.value) - # now we return from the complete frame, which starts from - # _call_header_with_stack_check(). The LEA in _call_footer below - # throws away most of the frame, including all the PUSHes that we - # did just above. + # fill in the jf_descr and jf_gcmap fields of the frame according + # to which failure we are resuming from. These are constants + # pushed on the stack just before we jump to the current helper, + # in generate_quick_failure(). ofs = self.cpu.get_ofs_of_frame_field('jf_descr') ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap') mc.POP_b(ofs2) mc.POP_b(ofs) + # now we return from the complete frame, which starts from + # _call_header_with_stack_check(). The _call_footer below does it. self._call_footer() rawstart = mc.materialize(self.cpu.asmmemmgr, []) self.failure_recovery_code[exc + 2 * withfloats] = rawstart diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -216,8 +216,8 @@ # fall-back number. "nursery_size": 896*1024, - # The system page size. Like obmalloc.c, we assume that it is 4K - # for 32-bit systems; unlike obmalloc.c, we assume that it is 8K + # The system page size. Like malloc, we assume that it is 4K + # for 32-bit systems; unlike malloc, we assume that it is 8K # for 64-bit systems, for consistent results. "page_size": 1024*WORD, diff --git a/rpython/memory/gc/minimark.py b/rpython/memory/gc/minimark.py --- a/rpython/memory/gc/minimark.py +++ b/rpython/memory/gc/minimark.py @@ -174,8 +174,8 @@ # fall-back number. "nursery_size": 896*1024, - # The system page size. Like obmalloc.c, we assume that it is 4K - # for 32-bit systems; unlike obmalloc.c, we assume that it is 8K + # The system page size. Like malloc, we assume that it is 4K + # for 32-bit systems; unlike malloc, we assume that it is 8K # for 64-bit systems, for consistent results. "page_size": 1024*WORD, diff --git a/rpython/memory/gc/minimarktest.py b/rpython/memory/gc/minimarktest.py --- a/rpython/memory/gc/minimarktest.py +++ b/rpython/memory/gc/minimarktest.py @@ -5,7 +5,7 @@ from rpython.rlib.rarithmetic import LONG_BIT # For testing, a simple implementation of ArenaCollection. -# This version could be used together with obmalloc.c, but +# This version could be used together with malloc, but # it requires an extra word per object in the 'all_objects' # list. diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -461,17 +461,18 @@ annmodel.SomeInteger(nonneg=True)], annmodel.s_None) - self.pin_ptr = getfn(GCClass.pin, - [s_gc, SomeAddress()], - annmodel.SomeBool()) + if GCClass.can_usually_pin_objects: + self.pin_ptr = getfn(GCClass.pin, + [s_gc, SomeAddress()], + annmodel.SomeBool()) - self.unpin_ptr = getfn(GCClass.unpin, - [s_gc, SomeAddress()], - annmodel.s_None) + self.unpin_ptr = getfn(GCClass.unpin, + [s_gc, SomeAddress()], + annmodel.s_None) - self._is_pinned_ptr = getfn(GCClass._is_pinned, - [s_gc, SomeAddress()], - annmodel.SomeBool()) + self._is_pinned_ptr = getfn(GCClass._is_pinned, + [s_gc, SomeAddress()], + annmodel.SomeBool()) self.write_barrier_ptr = None self.write_barrier_from_array_ptr = None @@ -1042,6 +1043,10 @@ v_size]) def gct_gc_pin(self, hop): + if not hasattr(self, 'pin_ptr'): + c_false = rmodel.inputconst(lltype.Bool, False) + hop.genop("same_as", [c_false], resultvar=hop.spaceop.result) + return op = hop.spaceop v_addr = hop.genop('cast_ptr_to_adr', [op.args[0]], resulttype=llmemory.Address) @@ -1049,6 +1054,8 @@ resultvar=op.result) def gct_gc_unpin(self, hop): + if not hasattr(self, 'unpin_ptr'): + return op = hop.spaceop v_addr = hop.genop('cast_ptr_to_adr', [op.args[0]], resulttype=llmemory.Address) @@ -1056,6 +1063,10 @@ resultvar=op.result) def gct_gc__is_pinned(self, hop): + if not hasattr(self, '_is_pinned_ptr'): + c_false = rmodel.inputconst(lltype.Bool, False) + hop.genop("same_as", [c_false], resultvar=hop.spaceop.result) + return op = hop.spaceop v_addr = hop.genop('cast_ptr_to_adr', [op.args[0]], resulttype=llmemory.Address) diff --git a/rpython/rlib/rweakref.py b/rpython/rlib/rweakref.py --- a/rpython/rlib/rweakref.py +++ b/rpython/rlib/rweakref.py @@ -105,7 +105,7 @@ rtyper.getrepr(self.s_key)) def rtyper_makekey(self): - return self.__class__, + return self.__class__, self.s_key.rtyper_makekey(), self.valueclassdef def method_get(self, s_key): return annmodel.SomeInstance(self.valueclassdef, can_be_None=True) @@ -165,7 +165,7 @@ return _rweakkeydict.WeakKeyDictRepr(rtyper) def rtyper_makekey(self): - return self.__class__, + return self.__class__, self.keyclassdef, self.valueclassdef def method_get(self, s_key): assert isinstance(s_key, annmodel.SomeInstance) diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py --- a/rpython/rlib/test/test_rsocket.py +++ b/rpython/rlib/test/test_rsocket.py @@ -311,7 +311,7 @@ assert isinstance(lst, list) found = False for family, socktype, protocol, canonname, addr in lst: - if addr.get_host() == '140.211.10.69': + if addr.get_host() == '104.130.43.121': found = True result[i] += found diff --git a/rpython/rlib/test/test_rweakvaldict.py b/rpython/rlib/test/test_rweakvaldict.py --- a/rpython/rlib/test/test_rweakvaldict.py +++ b/rpython/rlib/test/test_rweakvaldict.py @@ -144,3 +144,13 @@ d = RWeakValueDictionary(str, Y) d.set("x", X()) py.test.raises(Exception, interpret, g, [1]) + + +def test_bogus_makekey(): + class X: pass + class Y: pass + def g(): + X(); Y() + RWeakValueDictionary(str, X).get("foobar") + RWeakValueDictionary(int, Y).get(42) + interpret(g, []) diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py --- a/rpython/rtyper/lltypesystem/test/test_rffi.py +++ b/rpython/rtyper/lltypesystem/test/test_rffi.py @@ -100,7 +100,6 @@ def test_string_reverse(self): c_source = py.code.Source(""" #include <string.h> - #include <src/allocator.h> #include <src/mem.h> char *f(char* arg) diff --git a/rpython/rtyper/normalizecalls.py b/rpython/rtyper/normalizecalls.py --- a/rpython/rtyper/normalizecalls.py +++ b/rpython/rtyper/normalizecalls.py @@ -298,12 +298,16 @@ # ____________________________________________________________ +class TooLateForNewSubclass(Exception): + pass + class TotalOrderSymbolic(ComputedIntSymbolic): def __init__(self, orderwitness, peers): self.orderwitness = orderwitness self.peers = peers self.value = None + self._with_subclasses = None # unknown peers.append(self) def __cmp__(self, other): @@ -320,12 +324,34 @@ def __rsub__(self, other): return other - self.compute_fn() + def check_any_subclass_in_peer_list(self, i): + # check if the next peer, in order, is or not the end + # marker for this start marker + assert self.peers[i] is self + return self.peers[i + 1].orderwitness != self.orderwitness + [MAX] + + def number_with_subclasses(self): + # Return True or False depending on whether this is the + # subclassrange_min corresponding to a class which has subclasses + # or not. If this is called and returns False, then adding later + # new subclasses will crash in compute_fn(). + if self._with_subclasses is None: # unknown so far + self.peers.sort() + i = self.peers.index(self) + self._with_subclasses = self.check_any_subclass_in_peer_list(i) + return self._with_subclasses + def compute_fn(self): if self.value is None: self.peers.sort() for i, peer in enumerate(self.peers): assert peer.value is None or peer.value == i peer.value = i + # + if peer._with_subclasses is False: + if peer.check_any_subclass_in_peer_list(i): + raise TooLateForNewSubclass + # assert self.value is not None return self.value diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py --- a/rpython/rtyper/rclass.py +++ b/rpython/rtyper/rclass.py @@ -1007,14 +1007,11 @@ v_obj, v_cls = hop.inputargs(instance_repr, class_repr) if isinstance(v_cls, Constant): cls = v_cls.value - # XXX re-implement the following optimization - #if cls.subclassrange_max == cls.subclassrange_min: - # # a class with no subclass - # return hop.gendirectcall(rclass.ll_isinstance_exact, v_obj, v_cls) - #else: - minid = hop.inputconst(Signed, cls.subclassrange_min) - maxid = hop.inputconst(Signed, cls.subclassrange_max) - return hop.gendirectcall(ll_isinstance_const, v_obj, minid, maxid) + llf, llf_nonnull = make_ll_isinstance(self.rtyper, cls) + if hop.args_s[0].can_be_None: + return hop.gendirectcall(llf, v_obj) + else: + return hop.gendirectcall(llf_nonnull, v_obj) else: return hop.gendirectcall(ll_isinstance, v_obj, v_cls) @@ -1128,16 +1125,26 @@ obj_cls = obj.typeptr return ll_issubclass(obj_cls, cls) -def ll_isinstance_const(obj, minid, maxid): - if not obj: - return False - return ll_issubclass_const(obj.typeptr, minid, maxid) - -def ll_isinstance_exact(obj, cls): - if not obj: - return False - obj_cls = obj.typeptr - return obj_cls == cls +def make_ll_isinstance(rtyper, cls): + try: + return rtyper.isinstance_helpers[cls._obj] + except KeyError: + minid = cls.subclassrange_min + maxid = cls.subclassrange_max + if minid.number_with_subclasses(): + def ll_isinstance_const_nonnull(obj): + objid = obj.typeptr.subclassrange_min + return llop.int_between(Bool, minid, objid, maxid) + else: + def ll_isinstance_const_nonnull(obj): + return obj.typeptr == cls + def ll_isinstance_const(obj): + if not obj: + return False + return ll_isinstance_const_nonnull(obj) + result = (ll_isinstance_const, ll_isinstance_const_nonnull) + rtyper.isinstance_helpers[cls._obj] = result + return result def ll_runtime_type_info(obj): return obj.typeptr.rtti diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py --- a/rpython/rtyper/rtyper.py +++ b/rpython/rtyper/rtyper.py @@ -59,6 +59,7 @@ self.typererror_count = 0 # make the primitive_to_repr constant mapping self.primitive_to_repr = {} + self.isinstance_helpers = {} self.exceptiondata = ExceptionData(self) self.custom_trace_funcs = [] diff --git a/rpython/rtyper/test/test_normalizecalls.py b/rpython/rtyper/test/test_normalizecalls.py --- a/rpython/rtyper/test/test_normalizecalls.py +++ b/rpython/rtyper/test/test_normalizecalls.py @@ -6,6 +6,7 @@ from rpython.rtyper.test.test_llinterp import interpret from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.normalizecalls import TotalOrderSymbolic, MAX +from rpython.rtyper.normalizecalls import TooLateForNewSubclass def test_TotalOrderSymbolic(): @@ -21,6 +22,49 @@ assert t1 <= 5 assert t1.value == 0 +def test_TotalOrderSymbolic_with_subclasses(): + lst = [] + t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst) + t1 = TotalOrderSymbolic([3, 4], lst) + t2 = TotalOrderSymbolic([3, 4, 2], lst) + t4 = TotalOrderSymbolic([3, 4, MAX], lst) + assert t1.number_with_subclasses() + assert not t2.number_with_subclasses() + assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4) + # + lst = [] + t1 = TotalOrderSymbolic([3, 4], lst) + t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst) + t4 = TotalOrderSymbolic([3, 4, MAX], lst) + t2 = TotalOrderSymbolic([3, 4, 2], lst) + assert not t2.number_with_subclasses() + assert t1.number_with_subclasses() + assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4) + # + lst = [] + t1 = TotalOrderSymbolic([3, 4], lst) + t4 = TotalOrderSymbolic([3, 4, MAX], lst) + assert not t1.number_with_subclasses() + t2 = TotalOrderSymbolic([3, 4, 2], lst) + t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst) + py.test.raises(TooLateForNewSubclass, t2.compute_fn) + # + lst = [] + t1 = TotalOrderSymbolic([3, 4], lst) + t4 = TotalOrderSymbolic([3, 4, MAX], lst) + assert not t1.number_with_subclasses() + t2 = TotalOrderSymbolic([1], lst) + t3 = TotalOrderSymbolic([1, MAX], lst) + assert [t.compute_fn() for t in [t2, t3, t1, t4]] == range(4) + # + lst = [] + t1 = TotalOrderSymbolic([3, 4], lst) + t4 = TotalOrderSymbolic([3, 4, MAX], lst) + assert not t1.number_with_subclasses() + t2 = TotalOrderSymbolic([6], lst) + t3 = TotalOrderSymbolic([6, MAX], lst) + assert [t.compute_fn() for t in [t1, t4, t2, t3]] == range(4) + # ____________________________________________________________ class TestNormalize(object): diff --git a/rpython/tool/jitlogparser/parser.py b/rpython/tool/jitlogparser/parser.py --- a/rpython/tool/jitlogparser/parser.py +++ b/rpython/tool/jitlogparser/parser.py @@ -216,7 +216,7 @@ line_starts_here = property(getline_starts_here) def __repr__(self): - return "[%s]" % ", ".join([repr(op) for op in self.operations]) + return "[%s\n]" % "\n ".join([repr(op) for op in self.operations]) def pretty_print(self, out): pass diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py --- a/rpython/translator/c/genc.py +++ b/rpython/translator/c/genc.py @@ -405,8 +405,6 @@ ('debug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT" debug_target'), ('debug_exc', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DDO_LOG_EXC" debug_target'), ('debug_mem', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPYPY_USE_TRIVIAL_MALLOC" debug_target'), - ('no_obmalloc', '', '$(MAKE) CFLAGS="-g -O2 -DRPY_ASSERT -DPYPY_NO_OBMALLOC" $(TARGET)'), - ('linuxmemchk', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPYPY_USE_LINUXMEMCHK" debug_target'), ('llsafer', '', '$(MAKE) CFLAGS="-O2 -DRPY_LL_ASSERT" $(TARGET)'), ('lldebug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'), ('lldebug0','', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -O0 -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'), @@ -762,7 +760,6 @@ srcdir = py.path.local(__file__).join('..', 'src') files = [ srcdir / 'entrypoint.c', # ifdef PYPY_STANDALONE - srcdir / 'allocator.c', # ifdef PYPY_STANDALONE srcdir / 'mem.c', srcdir / 'exception.c', srcdir / 'rtyper.c', # ifdef HAVE_RTYPER diff --git a/rpython/translator/c/src/allocator.c b/rpython/translator/c/src/allocator.c deleted file mode 100644 --- a/rpython/translator/c/src/allocator.c +++ /dev/null @@ -1,33 +0,0 @@ -/* allocation functions */ -#include "common_header.h" -#ifdef PYPY_STANDALONE -#include <stdlib.h> - -#if defined(PYPY_USE_TRIVIAL_MALLOC) - void *PyObject_Malloc(size_t n) { return malloc(n); } - void *PyObject_Realloc(void *p, size_t n) { return realloc(p, n); } - void PyObject_Free(void *p) { if (p) { *((int*)p) = 0xDDDDDDDD; } free(p); } - -#elif defined(PYPY_USE_LINUXMEMCHK) -# include "linuxmemchk.c" - -#elif defined(PYPY_NO_OBMALLOC) - void *PyObject_Malloc(size_t n) { return malloc(n); } - void *PyObject_Realloc(void *p, size_t n) { return realloc(p, n); } - void PyObject_Free(void *p) { free(p); } - -#else -# ifndef WITH_PYMALLOC -# define WITH_PYMALLOC -# endif -/* The same obmalloc as CPython */ -# include "src/obmalloc.c" - -#endif -#elif defined _MSC_VER -/* link will fail without some kind of definition for the functions */ - void *PyObject_Malloc(size_t n) { return NULL; } - void *PyObject_Realloc(void *p, size_t n) { return NULL; } - void PyObject_Free(void *p) { } - -#endif /* PYPY_STANDALONE */ diff --git a/rpython/translator/c/src/allocator.h b/rpython/translator/c/src/allocator.h deleted file mode 100644 --- a/rpython/translator/c/src/allocator.h +++ /dev/null @@ -1,4 +0,0 @@ -/* allocation functions prototypes */ -RPY_EXTERN void *PyObject_Malloc(size_t n); -RPY_EXTERN void *PyObject_Realloc(void *p, size_t n); -RPY_EXTERN void PyObject_Free(void *p); diff --git a/rpython/translator/c/src/g_include.h b/rpython/translator/c/src/g_include.h --- a/rpython/translator/c/src/g_include.h +++ b/rpython/translator/c/src/g_include.h @@ -33,7 +33,6 @@ # include "src/debug_traceback.h" #endif -# include "src/allocator.h" #ifdef PYPY_STANDALONE # include "src/entrypoint.h" #endif diff --git a/rpython/translator/c/src/linuxmemchk.c b/rpython/translator/c/src/linuxmemchk.c deleted file mode 100644 --- a/rpython/translator/c/src/linuxmemchk.c +++ /dev/null @@ -1,101 +0,0 @@ -/* custom checking allocators a la Electric Fence */ -#include <stdlib.h> -#include <stdio.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#define PAGESIZE 4096 -#ifndef MALLOC_BIGBUFFER -# define MALLOC_BIGBUFFER (PAGESIZE*32768) /* 128MB */ -#endif - - -struct _alloc_s { - void* ptr; - int npages; -}; -static void* _na_start = NULL; -static char* _na_cur; - -static void _na_assert(int x, char* msg) -{ - if (!x) - { - fprintf(stderr, "linuxmemchk: failed assertion: %s\n", msg); - abort(); - } -} - -static struct _alloc_s* _na_find(void* data) -{ - int err; - long data1; - struct _alloc_s* s; - _na_assert(_na_start+PAGESIZE <= data && - data < _na_start+MALLOC_BIGBUFFER-PAGESIZE, - "corrupted na_start"); - data1 = (long) data; - data1 &= ~(PAGESIZE-1); - data1 -= PAGESIZE; - err = mprotect((void*) data1, PAGESIZE, PROT_READ|PROT_WRITE); - _na_assert(!err, "mprotect[1] failed"); - s = (struct _alloc_s*) data1; - _na_assert(s->npages > 0, "corrupted s->npages"); - return s; -} - -void* PyObject_Malloc(size_t size) -{ - int err, npages = (size + PAGESIZE-1) / PAGESIZE + 1; - struct _alloc_s* s; - char* data; - if (_na_start == NULL) - { - _na_start = mmap(NULL, MALLOC_BIGBUFFER, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - _na_assert(_na_start != MAP_FAILED, "initial mmap failed"); - _na_cur = (char*) _na_start; - } - s = (struct _alloc_s*) _na_cur; - _na_cur += npages * PAGESIZE; - if (_na_cur >= ((char*) _na_start) + MALLOC_BIGBUFFER) - { - fprintf(stderr, "linuxmemchk.c: Nothing wrong so far, but we are running out\nlinuxmemchk.c: of mmap'ed memory. Increase MALLOC_BIGBUFFER.\n"); - abort(); - } - err = mprotect(s, npages * PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC); - _na_assert(!err, "mprotect[2] failed"); - s->ptr = data = _na_cur - /*((size+3)&~3)*/ size; - s->npages = npages; - err = mprotect(s, PAGESIZE, PROT_NONE); - _na_assert(!err, "mprotect[3] failed"); - return data; -} - -void PyObject_Free(void* data) -{ - int err, npages; - struct _alloc_s* s; - if (data == NULL) - return; - s = _na_find(data); - _na_assert(s->ptr == data, "free got a pointer not returned by malloc"); - npages = s->npages; - s->npages = 0; - err = mprotect(s, npages * PAGESIZE, PROT_NONE); - _na_assert(!err, "mprotect[4] failed"); -} - -void* PyObject_Realloc(void* data, size_t nsize) -{ - size_t size; - struct _alloc_s* s = _na_find(data); - void* ndata = PyObject_Malloc(nsize); - - _na_assert(s->ptr == data, "realloc got a pointer not returned by malloc"); - size = ((char*)s) + s->npages * PAGESIZE - (char*)data; - memcpy(ndata, data, size<nsize ? size : nsize); - PyObject_Free(data); - return ndata; -} diff --git a/rpython/translator/c/src/mem.h b/rpython/translator/c/src/mem.h --- a/rpython/translator/c/src/mem.h +++ b/rpython/translator/c/src/mem.h @@ -9,13 +9,13 @@ #define OP_RAW_MALLOC(size, r, restype) { \ - r = (restype) PyObject_Malloc(size); \ + r = (restype) malloc(size); \ if (r != NULL) { \ COUNT_MALLOC; \ } \ } -#define OP_RAW_FREE(p, r) PyObject_Free(p); COUNT_FREE; +#define OP_RAW_FREE(p, r) free(p); COUNT_FREE; #define OP_RAW_MEMCLEAR(p, size, r) memset((void*)p, 0, size) #define OP_RAW_MEMSET(p, byte, size, r) memset((void*)p, byte, size) diff --git a/rpython/translator/c/src/obmalloc.c b/rpython/translator/c/src/obmalloc.c deleted file mode 100644 --- a/rpython/translator/c/src/obmalloc.c +++ /dev/null @@ -1,1418 +0,0 @@ - -#ifdef WITH_PYMALLOC - -#include <string.h> -#include <assert.h> - -/* An object allocator for Python. - - Here is an introduction to the layers of the Python memory architecture, - showing where the object allocator is actually used (layer +2), It is - called for every object allocation and deallocation (PyObject_New/Del), - unless the object-specific allocators implement a proprietary allocation - scheme (ex.: ints use a simple free list). This is also the place where - the cyclic garbage collector operates selectively on container objects. - - - Object-specific allocators - _____ ______ ______ ________ - [ int ] [ dict ] [ list ] ... [ string ] Python core | -+3 | <----- Object-specific memory -----> | <-- Non-object memory --> | - _______________________________ | | - [ Python's object allocator ] | | -+2 | ####### Object memory ####### | <------ Internal buffers ------> | - ______________________________________________________________ | - [ Python's raw memory allocator (PyMem_ API) ] | -+1 | <----- Python memory (under PyMem manager's control) ------> | | - __________________________________________________________________ - [ Underlying general-purpose allocator (ex: C library malloc) ] - 0 | <------ Virtual memory allocated for the python process -------> | - - ========================================================================= - _______________________________________________________________________ - [ OS-specific Virtual Memory Manager (VMM) ] --1 | <--- Kernel dynamic storage allocation & management (page-based) ---> | - __________________________________ __________________________________ - [ ] [ ] --2 | <-- Physical memory: ROM/RAM --> | | <-- Secondary storage (swap) --> | - -*/ -/*==========================================================================*/ - -/* A fast, special-purpose memory allocator for small blocks, to be used - on top of a general-purpose malloc -- heavily based on previous art. */ - -/* Vladimir Marangozov -- August 2000 */ - -/* - * "Memory management is where the rubber meets the road -- if we do the wrong - * thing at any level, the results will not be good. And if we don't make the - * levels work well together, we are in serious trouble." (1) - * - * (1) Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles, - * "Dynamic Storage Allocation: A Survey and Critical Review", - * in Proc. 1995 Int'l. Workshop on Memory Management, September 1995. - */ - _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit