Author: mattip <matti.pi...@gmail.com> Branch: cpyext-ext Changeset: r83439:b707f5d98670 Date: 2016-03-30 20:07 +0300 http://bitbucket.org/pypy/pypy/changeset/b707f5d98670/
Log: merge default into branch diff too long, truncating to 2000 out of 9177 lines diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -46,7 +46,6 @@ except detect_cpu.ProcessorAutodetectError: pass - translation_modules = default_modules.copy() translation_modules.update([ "fcntl", "time", "select", "signal", "_rawffi", "zlib", "struct", "_md5", diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -27,3 +27,8 @@ .. branch: fix_transpose_for_list_v3 Allow arguments to transpose to be sequences + +.. branch: jit-leaner-frontend + +Improve the tracing speed in the frontend as well as heapcache by using a more compact representation +of traces \ No newline at end of file diff --git a/pypy/module/_multibytecodec/src/cjkcodecs/cjkcodecs.h b/pypy/module/_multibytecodec/src/cjkcodecs/cjkcodecs.h --- a/pypy/module/_multibytecodec/src/cjkcodecs/cjkcodecs.h +++ b/pypy/module/_multibytecodec/src/cjkcodecs/cjkcodecs.h @@ -10,6 +10,7 @@ #define _CJKCODECS_H_ #include "src/cjkcodecs/multibytecodec.h" +#include "src/cjkcodecs/fixnames.h" /* a unicode "undefined" codepoint */ diff --git a/pypy/module/_multibytecodec/src/cjkcodecs/fixnames.h b/pypy/module/_multibytecodec/src/cjkcodecs/fixnames.h new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/src/cjkcodecs/fixnames.h @@ -0,0 +1,9 @@ + +/* this is only included from the .c files in this directory: rename + these pypymbc-prefixed names to locally define the CPython names */ +typedef pypymbc_ssize_t Py_ssize_t; +#define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t) -1) >> 1)) +#define Py_UNICODE_SIZE pypymbc_UNICODE_SIZE +typedef pypymbc_wchar_t Py_UNICODE; +typedef pypymbc_ucs4_t ucs4_t; +typedef pypymbc_ucs2_t ucs2_t, DBCHAR; diff --git a/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.c b/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.c --- a/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.c +++ b/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <string.h> #include "src/cjkcodecs/multibytecodec.h" +#include "src/cjkcodecs/fixnames.h" struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec) diff --git a/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.h b/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.h --- a/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.h +++ b/pypy/module/_multibytecodec/src/cjkcodecs/multibytecodec.h @@ -9,31 +9,28 @@ #include <assert.h> #ifdef _WIN64 -typedef __int64 ssize_t +typedef __int64 pypymbc_ssize_t #elif defined(_WIN32) -typedef int ssize_t; +typedef int pypymbc_ssize_t; #else #include <unistd.h> -#endif - -#ifndef Py_UNICODE_SIZE -#ifdef _WIN32 -#define Py_UNICODE_SIZE 2 -#else -#define Py_UNICODE_SIZE 4 -#endif -typedef wchar_t Py_UNICODE; -typedef ssize_t Py_ssize_t; -#define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t) -1) >> 1)) +typedef ssize_t pypymbc_ssize_t; #endif #ifdef _WIN32 -typedef unsigned int ucs4_t; -typedef unsigned short ucs2_t, DBCHAR; +#define pypymbc_UNICODE_SIZE 2 +#else +#define pypymbc_UNICODE_SIZE 4 +#endif +typedef wchar_t pypymbc_wchar_t; + +#ifdef _WIN32 +typedef unsigned int pypymbc_ucs4_t; +typedef unsigned short pypymbc_ucs2_t; #else #include <stdint.h> -typedef uint32_t ucs4_t; -typedef uint16_t ucs2_t, DBCHAR; +typedef uint32_t pypymbc_ucs4_t; +typedef uint16_t pypymbc_ucs2_t; #endif @@ -42,28 +39,28 @@ void *p; int i; unsigned char c[8]; - ucs2_t u2[4]; - ucs4_t u4[2]; + pypymbc_ucs2_t u2[4]; + pypymbc_ucs4_t u4[2]; } MultibyteCodec_State; typedef int (*mbcodec_init)(const void *config); -typedef Py_ssize_t (*mbencode_func)(MultibyteCodec_State *state, +typedef pypymbc_ssize_t (*mbencode_func)(MultibyteCodec_State *state, const void *config, - const Py_UNICODE **inbuf, Py_ssize_t inleft, - unsigned char **outbuf, Py_ssize_t outleft, + const pypymbc_wchar_t **inbuf, pypymbc_ssize_t inleft, + unsigned char **outbuf, pypymbc_ssize_t outleft, int flags); typedef int (*mbencodeinit_func)(MultibyteCodec_State *state, const void *config); -typedef Py_ssize_t (*mbencodereset_func)(MultibyteCodec_State *state, +typedef pypymbc_ssize_t (*mbencodereset_func)(MultibyteCodec_State *state, const void *config, - unsigned char **outbuf, Py_ssize_t outleft); -typedef Py_ssize_t (*mbdecode_func)(MultibyteCodec_State *state, + unsigned char **outbuf, pypymbc_ssize_t outleft); +typedef pypymbc_ssize_t (*mbdecode_func)(MultibyteCodec_State *state, const void *config, - const unsigned char **inbuf, Py_ssize_t inleft, - Py_UNICODE **outbuf, Py_ssize_t outleft); + const unsigned char **inbuf, pypymbc_ssize_t inleft, + pypymbc_wchar_t **outbuf, pypymbc_ssize_t outleft); typedef int (*mbdecodeinit_func)(MultibyteCodec_State *state, const void *config); -typedef Py_ssize_t (*mbdecodereset_func)(MultibyteCodec_State *state, +typedef pypymbc_ssize_t (*mbdecodereset_func)(MultibyteCodec_State *state, const void *config); typedef struct MultibyteCodec_s { @@ -94,59 +91,59 @@ const MultibyteCodec *codec; MultibyteCodec_State state; const unsigned char *inbuf_start, *inbuf, *inbuf_end; - Py_UNICODE *outbuf_start, *outbuf, *outbuf_end; + pypymbc_wchar_t *outbuf_start, *outbuf, *outbuf_end; }; RPY_EXTERN struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, - char *inbuf, Py_ssize_t inlen); +pypymbc_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, + char *inbuf, pypymbc_ssize_t inlen); RPY_EXTERN void pypy_cjk_dec_free(struct pypy_cjk_dec_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_chunk(struct pypy_cjk_dec_s *); +pypymbc_ssize_t pypy_cjk_dec_chunk(struct pypy_cjk_dec_s *); RPY_EXTERN -Py_UNICODE *pypy_cjk_dec_outbuf(struct pypy_cjk_dec_s *); +pypymbc_wchar_t *pypy_cjk_dec_outbuf(struct pypy_cjk_dec_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_outlen(struct pypy_cjk_dec_s *); +pypymbc_ssize_t pypy_cjk_dec_outlen(struct pypy_cjk_dec_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_inbuf_remaining(struct pypy_cjk_dec_s *d); +pypymbc_ssize_t pypy_cjk_dec_inbuf_remaining(struct pypy_cjk_dec_s *d); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_inbuf_consumed(struct pypy_cjk_dec_s* d); +pypymbc_ssize_t pypy_cjk_dec_inbuf_consumed(struct pypy_cjk_dec_s* d); RPY_EXTERN -Py_ssize_t pypy_cjk_dec_replace_on_error(struct pypy_cjk_dec_s* d, - Py_UNICODE *, Py_ssize_t, Py_ssize_t); +pypymbc_ssize_t pypy_cjk_dec_replace_on_error(struct pypy_cjk_dec_s* d, + pypymbc_wchar_t *, pypymbc_ssize_t, pypymbc_ssize_t); struct pypy_cjk_enc_s { const MultibyteCodec *codec; MultibyteCodec_State state; - const Py_UNICODE *inbuf_start, *inbuf, *inbuf_end; + const pypymbc_wchar_t *inbuf_start, *inbuf, *inbuf_end; unsigned char *outbuf_start, *outbuf, *outbuf_end; }; RPY_EXTERN struct pypy_cjk_enc_s *pypy_cjk_enc_new(const MultibyteCodec *codec); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, - Py_UNICODE *inbuf, Py_ssize_t inlen); +pypymbc_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, + pypymbc_wchar_t *inbuf, pypymbc_ssize_t inlen); RPY_EXTERN void pypy_cjk_enc_free(struct pypy_cjk_enc_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_chunk(struct pypy_cjk_enc_s *, Py_ssize_t); +pypymbc_ssize_t pypy_cjk_enc_chunk(struct pypy_cjk_enc_s *, pypymbc_ssize_t); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_reset(struct pypy_cjk_enc_s *); +pypymbc_ssize_t pypy_cjk_enc_reset(struct pypy_cjk_enc_s *); RPY_EXTERN char *pypy_cjk_enc_outbuf(struct pypy_cjk_enc_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_outlen(struct pypy_cjk_enc_s *); +pypymbc_ssize_t pypy_cjk_enc_outlen(struct pypy_cjk_enc_s *); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_inbuf_remaining(struct pypy_cjk_enc_s *d); +pypymbc_ssize_t pypy_cjk_enc_inbuf_remaining(struct pypy_cjk_enc_s *d); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_inbuf_consumed(struct pypy_cjk_enc_s* d); +pypymbc_ssize_t pypy_cjk_enc_inbuf_consumed(struct pypy_cjk_enc_s* d); RPY_EXTERN -Py_ssize_t pypy_cjk_enc_replace_on_error(struct pypy_cjk_enc_s* d, - char *, Py_ssize_t, Py_ssize_t); +pypymbc_ssize_t pypy_cjk_enc_replace_on_error(struct pypy_cjk_enc_s* d, + char *, pypymbc_ssize_t, pypymbc_ssize_t); RPY_EXTERN const MultibyteCodec *pypy_cjk_enc_getcodec(struct pypy_cjk_enc_s *); @@ -191,5 +188,7 @@ DEFINE_CODEC(big5) DEFINE_CODEC(cp950) +#undef DEFINE_CODEC + #endif diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py --- a/pypy/module/cpyext/test/test_sequence.py +++ b/pypy/module/cpyext/test/test_sequence.py @@ -101,8 +101,10 @@ self.raises(space, api, IndexError, api.PySequence_SetItem, l, 3, w_value) + t = api.PyTuple_New(1) + api.PyTuple_SetItem(t, 0, l) self.raises(space, api, TypeError, api.PySequence_SetItem, - api.PyTuple_New(1), 0, w_value) + t, 0, w_value) self.raises(space, api, TypeError, api.PySequence_SetItem, space.newdict(), 0, w_value) diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py --- a/pypy/module/cpyext/test/test_tupleobject.py +++ b/pypy/module/cpyext/test/test_tupleobject.py @@ -5,6 +5,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.debug import FatalError class TestTupleObject(BaseApiTest): @@ -18,29 +19,44 @@ #assert api.PyTuple_GET_SIZE(atuple) == 3 --- now a C macro raises(TypeError, api.PyTuple_Size(space.newlist([]))) api.PyErr_Clear() - + + def test_tuple_realize_refuses_nulls(self, space, api): + py_tuple = api.PyTuple_New(1) + py.test.raises(FatalError, from_ref, space, py_tuple) + def test_tuple_resize(self, space, api): w_42 = space.wrap(42) + w_43 = space.wrap(43) + w_44 = space.wrap(44) ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw') py_tuple = api.PyTuple_New(3) # inside py_tuple is an array of "PyObject *" items which each hold # a reference rffi.cast(PyTupleObject, py_tuple).c_ob_item[0] = make_ref(space, w_42) + rffi.cast(PyTupleObject, py_tuple).c_ob_item[1] = make_ref(space, w_43) ar[0] = py_tuple api._PyTuple_Resize(ar, 2) w_tuple = from_ref(space, ar[0]) assert space.int_w(space.len(w_tuple)) == 2 assert space.int_w(space.getitem(w_tuple, space.wrap(0))) == 42 + assert space.int_w(space.getitem(w_tuple, space.wrap(1))) == 43 api.Py_DecRef(ar[0]) py_tuple = api.PyTuple_New(3) rffi.cast(PyTupleObject, py_tuple).c_ob_item[0] = make_ref(space, w_42) + rffi.cast(PyTupleObject, py_tuple).c_ob_item[1] = make_ref(space, w_43) + rffi.cast(PyTupleObject, py_tuple).c_ob_item[2] = make_ref(space, w_44) ar[0] = py_tuple api._PyTuple_Resize(ar, 10) + assert api.PyTuple_Size(ar[0]) == 10 + for i in range(3, 10): + rffi.cast(PyTupleObject, py_tuple).c_ob_item[i] = make_ref( + space, space.wrap(42 + i)) w_tuple = from_ref(space, ar[0]) assert space.int_w(space.len(w_tuple)) == 10 - assert space.int_w(space.getitem(w_tuple, space.wrap(0))) == 42 + for i in range(10): + assert space.int_w(space.getitem(w_tuple, space.wrap(i))) == 42 + i api.Py_DecRef(ar[0]) lltype.free(ar, flavor='raw') diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -1,5 +1,6 @@ from pypy.interpreter.error import OperationError from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.debug import fatalerror_notb from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL, build_type_checkers, PyObjectFields, cpython_struct, bootstrap_function) @@ -91,14 +92,22 @@ def tuple_realize(space, py_obj): """ Creates the tuple in the interpreter. The PyTupleObject must not - be modified after this call. + be modified after this call. We check that it does not contain + any NULLs at this point (which would correspond to half-broken + W_TupleObjects). """ py_tup = rffi.cast(PyTupleObject, py_obj) l = py_tup.c_ob_size p = py_tup.c_ob_item items_w = [None] * l for i in range(l): - items_w[i] = from_ref(space, p[i]) + w_item = from_ref(space, p[i]) + if w_item is None: + fatalerror_notb( + "Fatal error in cpyext, CPython compatibility layer: " + "converting a PyTupleObject into a W_TupleObject, " + "but found NULLs as items") + items_w[i] = w_item w_obj = space.newtuple(items_w) track_reference(space, py_obj, w_obj) return w_obj diff --git a/pypy/objspace/std/objectobject.py b/pypy/objspace/std/objectobject.py --- a/pypy/objspace/std/objectobject.py +++ b/pypy/objspace/std/objectobject.py @@ -110,7 +110,7 @@ def descr__init__(space, w_obj, __args__): # don't allow arguments unless __new__ is overridden w_type = space.type(w_obj) - w_parent_new, _ = w_type.lookup_where('__new__') + w_parent_new, _ = space.lookup_in_type_where(w_type, '__new__') if w_parent_new is space.w_object: try: __args__.fixedunpack(0) diff --git a/pypy/tool/gdb_pypy.py b/pypy/tool/gdb_pypy.py --- a/pypy/tool/gdb_pypy.py +++ b/pypy/tool/gdb_pypy.py @@ -288,9 +288,11 @@ RPyListPrinter.recursive = True try: itemlist = [] - for i in range(length): + for i in range(min(length, MAX_DISPLAY_LENGTH)): item = items[i] itemlist.append(str(item)) # may recurse here + if length > MAX_DISPLAY_LENGTH: + itemlist.append("...") str_items = ', '.join(itemlist) finally: RPyListPrinter.recursive = False diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -126,6 +126,9 @@ ChoiceOption("jit_profiler", "integrate profiler support into the JIT", ["off", "oprofile"], default="off"), + ChoiceOption("jit_opencoder_model", "the model limits the maximal length" + " of traces. Use big if you want to go bigger than " + "the default", ["big", "normal"], default="normal"), BoolOption("check_str_without_nul", "Forbid NUL chars in strings in some external function calls", default=False, cmdline=None), diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py --- a/rpython/jit/backend/arm/assembler.py +++ b/rpython/jit/backend/arm/assembler.py @@ -939,9 +939,9 @@ op = operations[i] self.mc.mark_op(op) opnum = op.getopnum() - if op.has_no_side_effect() and op not in regalloc.longevity: + if rop.has_no_side_effect(opnum) and op not in regalloc.longevity: regalloc.possibly_free_vars_for_op(op) - elif not we_are_translated() and op.getopnum() == -127: + elif not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: regalloc.prepare_force_spill(op, fcond) else: arglocs = regalloc_operations[opnum](regalloc, op, fcond) @@ -949,7 +949,7 @@ fcond = asm_operations[opnum](self, op, arglocs, regalloc, fcond) assert fcond is not None - if op.is_guard(): + if rop.is_guard(opnum): regalloc.possibly_free_vars(op.getfailargs()) if op.type != 'v': regalloc.possibly_free_var(op) diff --git a/rpython/jit/backend/arm/detect.py b/rpython/jit/backend/arm/detect.py --- a/rpython/jit/backend/arm/detect.py +++ b/rpython/jit/backend/arm/detect.py @@ -63,3 +63,44 @@ "falling back to", "ARMv%d" % n) debug_stop("jit-backend-arch") return n + + +# Once we can rely on the availability of glibc >= 2.16, replace this with: +# from rpython.rtyper.lltypesystem import lltype, rffi +# getauxval = rffi.llexternal("getauxval", [lltype.Unsigned], lltype.Unsigned) +def getauxval(type_, filename='/proc/self/auxv'): + fd = os.open(filename, os.O_RDONLY, 0644) + + buf_size = 2048 + struct_size = 8 # 2x uint32 + try: + buf = os.read(fd, buf_size) + finally: + os.close(fd) + + # decode chunks of 8 bytes (a_type, a_val), and + # return the a_val whose a_type corresponds to type_, + # or zero if not found. + i = 0 + while i <= buf_size - struct_size: + # We only support little-endian ARM + a_type = (ord(buf[i]) | + (ord(buf[i+1]) << 8) | + (ord(buf[i+2]) << 16) | + (ord(buf[i+3]) << 24)) + a_val = (ord(buf[i+4]) | + (ord(buf[i+5]) << 8) | + (ord(buf[i+6]) << 16) | + (ord(buf[i+7]) << 24)) + i += struct_size + if a_type == type_: + return a_val + + return 0 + + +def detect_neon(): + AT_HWCAP = 16 + HWCAP_NEON = 1 << 12 + hwcap = getauxval(AT_HWCAP) + return bool(hwcap & HWCAP_NEON) diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -1092,8 +1092,8 @@ self.mc.VCVT_int_to_float(res.value, r.svfp_ip.value) return fcond - # the following five instructions are only ARMv7; - # regalloc.py won't call them at all on ARMv6 + # the following five instructions are only ARMv7 with NEON; + # regalloc.py won't call them at all, in other cases emit_opx_llong_add = gen_emit_float_op('llong_add', 'VADD_i64') emit_opx_llong_sub = gen_emit_float_op('llong_sub', 'VSUB_i64') emit_opx_llong_and = gen_emit_float_op('llong_and', 'VAND_i64') diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -530,7 +530,7 @@ EffectInfo.OS_LLONG_AND, EffectInfo.OS_LLONG_OR, EffectInfo.OS_LLONG_XOR): - if self.cpu.cpuinfo.arch_version >= 7: + if self.cpu.cpuinfo.neon: args = self._prepare_llong_binop_xx(op, fcond) self.perform_extra(op, args, fcond) return diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py --- a/rpython/jit/backend/arm/runner.py +++ b/rpython/jit/backend/arm/runner.py @@ -7,13 +7,14 @@ from rpython.rlib.jit_hooks import LOOP_RUN_CONTAINER from rpython.rtyper.lltypesystem import lltype, llmemory from rpython.jit.backend.arm.detect import detect_hardfloat -from rpython.jit.backend.arm.detect import detect_arch_version +from rpython.jit.backend.arm.detect import detect_arch_version, detect_neon jitframe.STATICSIZE = JITFRAME_FIXED_SIZE class CPUInfo(object): hf_abi = False arch_version = 6 + neon = False class AbstractARMCPU(AbstractLLCPU): @@ -48,6 +49,7 @@ def setup_once(self): self.cpuinfo.arch_version = detect_arch_version() self.cpuinfo.hf_abi = detect_hardfloat() + self.cpuinfo.neon = detect_neon() #self.codemap.setup() self.assembler.setup_once() diff --git a/rpython/jit/backend/arm/test/test_detect.py b/rpython/jit/backend/arm/test/test_detect.py --- a/rpython/jit/backend/arm/test/test_detect.py +++ b/rpython/jit/backend/arm/test/test_detect.py @@ -1,6 +1,6 @@ import py from rpython.tool.udir import udir -from rpython.jit.backend.arm.detect import detect_arch_version +from rpython.jit.backend.arm.detect import detect_arch_version, getauxval cpuinfo = "Processor : ARMv%d-compatible processor rev 7 (v6l)""" cpuinfo2 = """processor : 0 @@ -29,6 +29,19 @@ address sizes : 36 bits physical, 48 bits virtual power management: """ +# From a Marvell Armada 370/XP +auxv = ( + '\x10\x00\x00\x00\xd7\xa8\x1e\x00\x06\x00\x00\x00\x00\x10\x00\x00\x11\x00' + '\x00\x00d\x00\x00\x00\x03\x00\x00\x004\x00\x01\x00\x04\x00\x00\x00 \x00' + '\x00\x00\x05\x00\x00\x00\t\x00\x00\x00\x07\x00\x00\x00\x00\xe0\xf3\xb6' + '\x08\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00t\xcf\x04\x00\x0b\x00\x00' + '\x000\x0c\x00\x00\x0c\x00\x00\x000\x0c\x00\x00\r\x00\x00\x000\x0c\x00\x00' + '\x0e\x00\x00\x000\x0c\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00' + '\x00\x8a\xf3\x87\xbe\x1a\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\xec' + '\xff\x87\xbe\x0f\x00\x00\x00\x9a\xf3\x87\xbe\x00\x00\x00\x00\x00\x00\x00' + '\x00' +) + def write_cpuinfo(info): filepath = udir.join('get_arch_version') @@ -46,3 +59,10 @@ py.test.raises(ValueError, 'detect_arch_version(write_cpuinfo(cpuinfo % 5))') assert detect_arch_version(write_cpuinfo(cpuinfo2)) == 6 + + +def test_getauxval_no_neon(): + path = udir.join('auxv') + path.write(auxv, 'wb') + AT_HWCAP = 16 + assert getauxval(AT_HWCAP, filename=str(path)) == 2009303 diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py --- a/rpython/jit/backend/llgraph/runner.py +++ b/rpython/jit/backend/llgraph/runner.py @@ -455,7 +455,7 @@ if box is not frame.current_op: value = frame.env[box] else: - value = box.getvalue() # 0 or 0.0 or NULL + value = 0 # box.getvalue() # 0 or 0.0 or NULL else: value = None values.append(value) @@ -472,6 +472,13 @@ # ------------------------------------------------------------ + def setup_descrs(self): + all_descrs = [] + for k, v in self.descrs.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + return all_descrs + def calldescrof(self, FUNC, ARGS, RESULT, effect_info): key = ('call', getkind(RESULT), tuple([getkind(A) for A in ARGS]), diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py --- a/rpython/jit/backend/llsupport/assembler.py +++ b/rpython/jit/backend/llsupport/assembler.py @@ -331,7 +331,7 @@ counter = self._register_counter(tp, number, token) c_adr = ConstInt(rffi.cast(lltype.Signed, counter)) operations.append( - ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr], None)) + ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr])) def _register_counter(self, tp, number, token): # YYY very minor leak -- we need the counters to stay alive diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py --- a/rpython/jit/backend/llsupport/descr.py +++ b/rpython/jit/backend/llsupport/descr.py @@ -21,6 +21,30 @@ self._cache_call = {} self._cache_interiorfield = {} + def setup_descrs(self): + all_descrs = [] + for k, v in self._cache_size.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_field.iteritems(): + for k1, v1 in v.iteritems(): + v1.descr_index = len(all_descrs) + all_descrs.append(v1) + for k, v in self._cache_array.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_arraylen.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_call.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_interiorfield.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + assert len(all_descrs) < 2**15 + return all_descrs + def init_size_descr(self, STRUCT, sizedescr): pass diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -316,6 +316,9 @@ return ll_frame return execute_token + def setup_descrs(self): + return self.gc_ll_descr.setup_descrs() + # ------------------- helpers and descriptions -------------------- @staticmethod diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -683,7 +683,7 @@ for i in range(len(operations)-1, -1, -1): op = operations[i] if op.type != 'v': - if op not in last_used and op.has_no_side_effect(): + if op not in last_used and rop.has_no_side_effect(op.opnum): continue opnum = op.getopnum() for j in range(op.numargs()): @@ -695,7 +695,7 @@ if opnum != rop.JUMP and opnum != rop.LABEL: if arg not in last_real_usage: last_real_usage[arg] = i - if op.is_guard(): + if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue @@ -732,14 +732,7 @@ return longevity, last_real_usage def is_comparison_or_ovf_op(opnum): - from rpython.jit.metainterp.resoperation import opclasses - cls = opclasses[opnum] - # hack hack: in theory they are instance method, but they don't use - # any instance field, we can use a fake object - class Fake(cls): - pass - op = Fake() - return op.is_comparison() or op.is_ovf() + return rop.is_comparison(opnum) or rop.is_ovf(opnum) def valid_addressing_size(size): return size == 1 or size == 2 or size == 4 or size == 8 diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py --- a/rpython/jit/backend/llsupport/rewrite.py +++ b/rpython/jit/backend/llsupport/rewrite.py @@ -103,7 +103,7 @@ orig_op.set_forwarded(op) replaced = True op.setarg(i, arg) - if op.is_guard(): + if rop.is_guard(op.opnum): if not replaced: op = op.copy_and_change(op.getopnum()) orig_op.set_forwarded(op) @@ -212,7 +212,7 @@ # self._emit_mul_if_factor_offset_not_supported(v_length, scale, 0) # op.setarg(1, ConstInt(scale)) # op.setarg(2, v_length) - if op.is_getarrayitem() or \ + if rop.is_getarrayitem(opnum) or \ opnum in (rop.GETARRAYITEM_RAW_I, rop.GETARRAYITEM_RAW_F): self.handle_getarrayitem(op) @@ -324,13 +324,13 @@ if self.transform_to_gc_load(op): continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- - if op.is_malloc(): + if rop.is_malloc(op.opnum): self.handle_malloc_operation(op) continue - if (op.is_guard() or + if (rop.is_guard(op.opnum) or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() - elif op.can_malloc(): + elif rop.can_malloc(op.opnum): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.LABEL: self.emitting_an_operation_that_can_collect() @@ -370,8 +370,8 @@ # return True in cases where the operation and the following guard # should likely remain together. Simplified version of # can_merge_with_next_guard() in llsupport/regalloc.py. - if not op.is_comparison(): - return op.is_ovf() # int_xxx_ovf() / guard_no_overflow() + if not rop.is_comparison(op.opnum): + return rop.is_ovf(op.opnum) # int_xxx_ovf() / guard_no_overflow() if i + 1 >= len(operations): return False next_op = operations[i + 1] @@ -400,7 +400,6 @@ # it's hard to test all cases). Rewrite it away. value = int(opnum == rop.GUARD_FALSE) op1 = ResOperation(rop.SAME_AS_I, [ConstInt(value)]) - op1.setint(value) self.emit_op(op1) lst = op.getfailargs()[:] lst[i] = op1 @@ -633,8 +632,7 @@ args = [frame, arglist[jd.index_of_virtualizable]] else: args = [frame] - call_asm = ResOperation(op.getopnum(), args, - op.getdescr()) + call_asm = ResOperation(op.getopnum(), args, descr=op.getdescr()) self.replace_op_with(self.get_box_replacement(op), call_asm) self.emit_op(call_asm) @@ -708,7 +706,7 @@ def _gen_call_malloc_gc(self, args, v_result, descr): """Generate a CALL_MALLOC_GC with the given args.""" self.emitting_an_operation_that_can_collect() - op = ResOperation(rop.CALL_MALLOC_GC, args, descr) + op = ResOperation(rop.CALL_MALLOC_GC, args, descr=descr) self.replace_op_with(v_result, op) self.emit_op(op) # In general, don't add v_result to write_barrier_applied: diff --git a/rpython/jit/backend/ppc/regalloc.py b/rpython/jit/backend/ppc/regalloc.py --- a/rpython/jit/backend/ppc/regalloc.py +++ b/rpython/jit/backend/ppc/regalloc.py @@ -286,7 +286,8 @@ self.assembler.mc.mark_op(op) self.rm.position = i self.fprm.position = i - if op.has_no_side_effect() and op not in self.longevity: + opnum = op.opnum + if rop.has_no_side_effect(opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue @@ -298,8 +299,7 @@ else: self.fprm.temp_boxes.append(box) # - opnum = op.getopnum() - if not we_are_translated() and opnum == -127: + if not we_are_translated() and opnum == rop.FORCE_SPILL: self._consider_force_spill(op) else: arglocs = oplist[opnum](self, op) diff --git a/rpython/jit/backend/test/test_ll_random.py b/rpython/jit/backend/test/test_ll_random.py --- a/rpython/jit/backend/test/test_ll_random.py +++ b/rpython/jit/backend/test/test_ll_random.py @@ -2,6 +2,7 @@ from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr from rpython.rtyper import rclass from rpython.jit.backend.test import test_random +from rpython.jit.backend.test.test_random import getint, getref_base, getref from rpython.jit.metainterp.resoperation import ResOperation, rop, optypes from rpython.jit.metainterp.history import ConstInt, ConstPtr, getkind from rpython.jit.codewriter import heaptracker @@ -169,7 +170,7 @@ if length == 0: raise test_random.CannotProduceOperation v_index = r.choice(self.intvars) - if not (0 <= v_index.getint() < length): + if not (0 <= getint(v_index) < length): v_index = ConstInt(r.random_integer() % length) return v_index @@ -311,7 +312,7 @@ def field_descr(self, builder, r): v, A = builder.get_structptr_var(r, type=lltype.Array, array_of_structs=True) - array = v.getref(lltype.Ptr(A)) + array = getref(lltype.Ptr(A), v) v_index = builder.get_index(len(array), r) choice = [] for name in A.OF._names: @@ -344,7 +345,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value: break builder.do(self.opnum, [v, w], descr) @@ -357,7 +358,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value: break builder.do(self.opnum, [v, v_index, w], descr) @@ -389,7 +390,7 @@ class GetArrayItemOperation(ArrayOperation): def field_descr(self, builder, r): v, A = builder.get_arrayptr_var(r) - array = v.getref(lltype.Ptr(A)) + array = getref(lltype.Ptr(A), v) v_index = builder.get_index(len(array), r) descr = self.array_descr(builder, A) return v, A, v_index, descr @@ -411,7 +412,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(A.OF, value)) == value: break builder.do(self.opnum, [v, v_index, w], descr) @@ -455,7 +456,7 @@ v_ptr = builder.do(self.opnum, [v_length]) getattr(builder, self.builder_cache).append(v_ptr) # Initialize the string. Is there a better way to do this? - for i in range(v_length.getint()): + for i in range(getint(v_length)): v_index = ConstInt(i) v_char = ConstInt(r.random_integer() % self.max) builder.do(self.set_char, [v_ptr, v_index, v_char]) @@ -471,9 +472,9 @@ current = getattr(builder, self.builder_cache) if current and r.random() < .8: v_string = r.choice(current) - string = v_string.getref(self.ptr) + string = getref(self.ptr, v_string) else: - string = self.alloc(builder.get_index(500, r).getint()) + string = self.alloc(getint(builder.get_index(500, r))) v_string = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, string)) current.append(v_string) for i in range(len(string.chars)): @@ -484,7 +485,7 @@ class AbstractGetItemOperation(AbstractStringOperation): def produce_into(self, builder, r): v_string = self.get_string(builder, r) - v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r) + v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r) builder.do(self.opnum, [v_string, v_index]) class AbstractSetItemOperation(AbstractStringOperation): @@ -492,7 +493,7 @@ v_string = self.get_string(builder, r) if isinstance(v_string, ConstPtr): raise test_random.CannotProduceOperation # setitem(Const, ...) - v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r) + v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r) v_target = ConstInt(r.random_integer() % self.max) builder.do(self.opnum, [v_string, v_index, v_target]) @@ -505,15 +506,15 @@ def produce_into(self, builder, r): v_srcstring = self.get_string(builder, r) v_dststring = self.get_string(builder, r) - src = v_srcstring.getref(self.ptr) - dst = v_dststring.getref(self.ptr) + src = getref(self.ptr, v_srcstring) + dst = getref(self.ptr, v_dststring) if src == dst: # because it's not a raise test_random.CannotProduceOperation # memmove(), but memcpy() srclen = len(src.chars) dstlen = len(dst.chars) v_length = builder.get_index(min(srclen, dstlen), r) - v_srcstart = builder.get_index(srclen - v_length.getint() + 1, r) - v_dststart = builder.get_index(dstlen - v_length.getint() + 1, r) + v_srcstart = builder.get_index(srclen - getint(v_length) + 1, r) + v_dststart = builder.get_index(dstlen - getint(v_length) + 1, r) builder.do(self.opnum, [v_srcstring, v_dststring, v_srcstart, v_dststart, v_length]) @@ -585,7 +586,7 @@ """ % funcargs).compile() vtableptr = v._hints['vtable']._as_ptr() d = { - 'ptr': S.getref_base(), + 'ptr': getref_base(S), 'vtable' : vtableptr, 'LLException' : LLException, } diff --git a/rpython/jit/backend/test/test_random.py b/rpython/jit/backend/test/test_random.py --- a/rpython/jit/backend/test/test_random.py +++ b/rpython/jit/backend/test/test_random.py @@ -11,11 +11,9 @@ from rpython.jit.metainterp.executor import _execute_arglist, wrap_constant from rpython.jit.metainterp.resoperation import opname from rpython.jit.codewriter import longlong -from rpython.rtyper.lltypesystem import lltype, rstr +from rpython.rtyper.lltypesystem import lltype, llmemory, rstr from rpython.rtyper import rclass -class PleaseRewriteMe(Exception): - pass class DummyLoop(object): def __init__(self, subops): @@ -27,6 +25,41 @@ def execute_raised(self, exc, constant=False): self._got_exc = exc + +def getint(v): + if isinstance(v, (ConstInt, InputArgInt)): + return v.getint() + else: + return v._example_int + +def getfloatstorage(v): + if isinstance(v, (ConstFloat, InputArgFloat)): + return v.getfloatstorage() + else: + return v._example_float + +def getfloat(v): + return longlong.getrealfloat(getfloatstorage(v)) + +def getref_base(v): + if isinstance(v, (ConstPtr, InputArgRef)): + return v.getref_base() + else: + return v._example_ref + +def getref(PTR, v): + return lltype.cast_opaque_ptr(PTR, getref_base(v)) + +def constbox(v): + if v.type == INT: + return ConstInt(getint(v)) + if v.type == FLOAT: + return ConstFloat(getfloatstorage(v)) + if v.type == REF: + return ConstPtr(getref_base(v)) + assert 0, v.type + + class OperationBuilder(object): def __init__(self, cpu, loop, vars): self.cpu = cpu @@ -57,11 +90,21 @@ def do(self, opnum, argboxes, descr=None): self.fakemetainterp._got_exc = None op = ResOperation(opnum, argboxes, descr) + argboxes = map(constbox, argboxes) result = _execute_arglist(self.cpu, self.fakemetainterp, opnum, argboxes, descr) if result is not None: - c_result = wrap_constant(result) - op.copy_value_from(c_result) + if lltype.typeOf(result) == lltype.Signed: + op._example_int = result + elif isinstance(result, bool): + op._example_int = int(result) + elif lltype.typeOf(result) == longlong.FLOATSTORAGE: + op._example_float = result + elif isinstance(result, float): + op._example_float = longlong.getfloatstorage(result) + else: + assert lltype.typeOf(result) == llmemory.GCREF + op._example_ref = result self.loop.operations.append(op) return op @@ -101,7 +144,7 @@ if v in names: args.append(names[v]) elif isinstance(v, ConstPtr): - assert not v.getref_base() # otherwise should be in the names + assert not getref_base(v) # otherwise should be in the names args.append('ConstPtr(lltype.nullptr(llmemory.GCREF.TO))') elif isinstance(v, ConstFloat): args.append('ConstFloat(longlong.getfloatstorage(%r))' @@ -198,10 +241,10 @@ # def writevar(v, nameprefix, init=''): if nameprefix == 'const_ptr': - if not v.getref_base(): + if not getref_base(v): return 'lltype.nullptr(llmemory.GCREF.TO)' - TYPE = v.getref_base()._obj.ORIGTYPE - cont = lltype.cast_opaque_ptr(TYPE, v.getref_base()) + TYPE = getref_base(v)._obj.ORIGTYPE + cont = lltype.cast_opaque_ptr(TYPE, getref_base(v)) if TYPE.TO._is_varsize(): if isinstance(TYPE.TO, lltype.GcStruct): lgt = len(cont.chars) @@ -252,9 +295,9 @@ for i, v in enumerate(self.loop.inputargs): assert not isinstance(v, Const) if v.type == FLOAT: - vals.append("longlong.getfloatstorage(%r)" % v.getfloat()) + vals.append("longlong.getfloatstorage(%r)" % getfloat(v)) else: - vals.append("%r" % v.getint()) + vals.append("%r" % getint(v)) print >>s, ' loop_args = [%s]' % ", ".join(vals) print >>s, ' frame = cpu.execute_token(looptoken, *loop_args)' if self.should_fail_by is None: @@ -264,10 +307,10 @@ for i, v in enumerate(fail_args): if v.type == FLOAT: print >>s, (' assert longlong.getrealfloat(' - 'cpu.get_float_value(frame, %d)) == %r' % (i, v.getfloatstorage())) + 'cpu.get_float_value(frame, %d)) == %r' % (i, getfloatstorage(v))) else: print >>s, (' assert cpu.get_int_value(frame, %d) == %d' - % (i, v.getint())) + % (i, getint(v))) self.names = names s.flush() @@ -295,7 +338,7 @@ builder.intvars.append(v_result) boolres = self.boolres if boolres == 'sometimes': - boolres = v_result.getint() in [0, 1] + boolres = getint(v_result) in [0, 1] if boolres: builder.boolvars.append(v_result) elif v_result.type == FLOAT: @@ -346,10 +389,10 @@ v_second = ConstInt((value & self.and_mask) | self.or_mask) else: v = r.choice(builder.intvars) - v_value = v.getint() + v_value = getint(v) if (v_value & self.and_mask) != v_value: v = builder.do(rop.INT_AND, [v, ConstInt(self.and_mask)]) - v_value = v.getint() + v_value = getint(v) if (v_value | self.or_mask) != v_value: v = builder.do(rop.INT_OR, [v, ConstInt(self.or_mask)]) v_second = v @@ -395,9 +438,9 @@ v_second = ConstFloat(r.random_float_storage()) else: v_second = r.choice(builder.floatvars) - if abs(v_first.getfloat()) > 1E100 or abs(v_second.getfloat()) > 1E100: + if abs(getfloat(v_first)) > 1E100 or abs(getfloat(v_second)) > 1E100: raise CannotProduceOperation # avoid infinities - if abs(v_second.getfloat()) < 1E-100: + if abs(getfloat(v_second)) < 1E-100: raise CannotProduceOperation # e.g. division by zero error self.put(builder, [v_first, v_second]) @@ -432,7 +475,7 @@ if not builder.floatvars: raise CannotProduceOperation box = r.choice(builder.floatvars) - if not (-sys.maxint-1 <= box.getfloat() <= sys.maxint): + if not (-sys.maxint-1 <= getfloat(box) <= sys.maxint): raise CannotProduceOperation # would give an overflow self.put(builder, [box]) @@ -440,8 +483,8 @@ def gen_guard(self, builder, r): v = builder.get_bool_var(r) op = ResOperation(self.opnum, [v]) - passing = ((self.opnum == rop.GUARD_TRUE and v.getint()) or - (self.opnum == rop.GUARD_FALSE and not v.getint())) + passing = ((self.opnum == rop.GUARD_TRUE and getint(v)) or + (self.opnum == rop.GUARD_FALSE and not getint(v))) return op, passing def produce_into(self, builder, r): @@ -459,8 +502,8 @@ raise CannotProduceOperation box = r.choice(builder.ptrvars)[0] op = ResOperation(self.opnum, [box]) - passing = ((self.opnum == rop.GUARD_NONNULL and box.getref_base()) or - (self.opnum == rop.GUARD_ISNULL and not box.getref_base())) + passing = ((self.opnum == rop.GUARD_NONNULL and getref_base(box)) or + (self.opnum == rop.GUARD_ISNULL and not getref_base(box))) return op, passing class GuardValueOperation(GuardOperation): @@ -470,14 +513,14 @@ other = r.choice(builder.intvars) else: if r.random() < 0.75: - value = v.getint() + value = getint(v) elif r.random() < 0.5: - value = v.getint() ^ 1 + value = getint(v) ^ 1 else: value = r.random_integer() other = ConstInt(value) op = ResOperation(self.opnum, [v, other]) - return op, (v.getint() == other.getint()) + return op, (getint(v) == getint(other)) # ____________________________________________________________ @@ -675,7 +718,7 @@ assert not hasattr(loop, '_targettoken') for i in range(position): op = loop.operations[i] - if (not op.has_no_side_effect() + if (not rop.has_no_side_effect(op.opnum) or op.type not in (INT, FLOAT)): position = i break # cannot move the LABEL later @@ -728,9 +771,9 @@ self.expected = {} for v in endvars: if v.type == INT: - self.expected[v] = v.getint() + self.expected[v] = getint(v) elif v.type == FLOAT: - self.expected[v] = v.getfloatstorage() + self.expected[v] = getfloatstorage(v) else: assert 0, v.type @@ -742,7 +785,7 @@ args = [] for box in self.startvars: if box not in self.loop.inputargs: - box = box.constbox() + box = constbox(box) args.append(box) self.cpu.compile_loop(self.loop.inputargs, [ResOperation(rop.JUMP, args, @@ -760,7 +803,7 @@ def clear_state(self): for v, S, fields in self.prebuilt_ptr_consts: - container = v.getref_base()._obj.container + container = getref_base(v)._obj.container for name, value in fields.items(): if isinstance(name, str): setattr(container, name, value) @@ -781,9 +824,9 @@ arguments = [] for box in self.loop.inputargs: if box.type == INT: - arguments.append(box.getint()) + arguments.append(getint(box)) elif box.type == FLOAT: - arguments.append(box.getfloatstorage()) + arguments.append(getfloatstorage(box)) else: assert 0, box.type deadframe = cpu.execute_token(self.runjitcelltoken(), *arguments) @@ -795,7 +838,7 @@ if v not in self.expected: assert v.getopnum() == rop.SAME_AS_I # special case assert isinstance(v.getarg(0), ConstInt) - self.expected[v] = v.getarg(0).getint() + self.expected[v] = getint(v.getarg(0)) if v.type == FLOAT: value = cpu.get_float_value(deadframe, i) else: @@ -807,7 +850,7 @@ ) exc = cpu.grab_exc_value(deadframe) if (self.guard_op is not None and - self.guard_op.is_guard_exception()): + rop.is_guard_exception(self.guard_op.getopnum())): if self.guard_op.getopnum() == rop.GUARD_NO_EXCEPTION: do_assert(exc, "grab_exc_value() should not be %r" % (exc,)) @@ -840,7 +883,7 @@ # generate the branch: a sequence of operations that ends in a FINISH subloop = DummyLoop([]) self.subloops.append(subloop) # keep around for debugging - if guard_op.is_guard_exception(): + if rop.is_guard_exception(guard_op.getopnum()): subloop.operations.append(exc_handling(guard_op)) bridge_builder = self.builder.fork(self.builder.cpu, subloop, op.getfailargs()[:]) @@ -876,9 +919,9 @@ args = [] for x in subset: if x.type == INT: - args.append(InputArgInt(x.getint())) + args.append(InputArgInt(getint(x))) elif x.type == FLOAT: - args.append(InputArgFloat(x.getfloatstorage())) + args.append(InputArgFloat(getfloatstorage(x))) else: assert 0, x.type rl = RandomLoop(self.builder.cpu, self.builder.fork, diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py --- a/rpython/jit/backend/x86/regalloc.py +++ b/rpython/jit/backend/x86/regalloc.py @@ -358,11 +358,11 @@ assert self.assembler.mc._frame_size == DEFAULT_FRAME_BYTES self.rm.position = i self.xrm.position = i - if op.has_no_side_effect() and op not in self.longevity: + if rop.has_no_side_effect(op.opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue - if not we_are_translated() and op.getopnum() == -127: + if not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: self._consider_force_spill(op) else: oplist[op.getopnum()](self, op) diff --git a/rpython/jit/backend/zarch/regalloc.py b/rpython/jit/backend/zarch/regalloc.py --- a/rpython/jit/backend/zarch/regalloc.py +++ b/rpython/jit/backend/zarch/regalloc.py @@ -476,7 +476,8 @@ self.assembler.mc.mark_op(op) self.rm.position = i self.fprm.position = i - if op.has_no_side_effect() and op not in self.longevity: + opnum = op.getopnum() + if rop.has_no_side_effect(opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue @@ -488,8 +489,7 @@ else: self.fprm.temp_boxes.append(box) # - opnum = op.getopnum() - if not we_are_translated() and opnum == -127: + if not we_are_translated() and opnum == rop.FORCE_SPILL: self._consider_force_spill(op) else: arglocs = prepare_oplist[opnum](self, op) diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -1585,7 +1585,6 @@ def _done_with_this_frame(self): # rare case: we only get there if the blackhole interps all returned # normally (in general we get a ContinueRunningNormally exception). - sd = self.builder.metainterp_sd kind = self._return_type if kind == 'v': raise jitexc.DoneWithThisFrameVoid() diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py --- a/rpython/jit/metainterp/compile.py +++ b/rpython/jit/metainterp/compile.py @@ -27,12 +27,11 @@ class CompileData(object): memo = None + log_noopt = True def forget_optimization_info(self): - for arg in self.start_label.getarglist(): + for arg in self.trace.inputargs: arg.set_forwarded(None) - for op in self.operations: - op.set_forwarded(None) class LoopCompileData(CompileData): """ An object that accumulates all of the necessary info for @@ -40,15 +39,13 @@ This is the case of label() ops label() """ - def __init__(self, start_label, end_label, operations, - call_pure_results=None, enable_opts=None): - self.start_label = start_label - self.end_label = end_label + def __init__(self, trace, runtime_boxes, call_pure_results=None, + enable_opts=None): self.enable_opts = enable_opts - assert start_label.getopnum() == rop.LABEL - assert end_label.getopnum() == rop.LABEL - self.operations = operations + self.trace = trace self.call_pure_results = call_pure_results + assert runtime_boxes is not None + self.runtime_boxes = runtime_boxes def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll): from rpython.jit.metainterp.optimizeopt.unroll import (UnrollOptimizer, @@ -56,23 +53,21 @@ if unroll: opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_preamble(self.start_label, self.end_label, - self.operations, + return opt.optimize_preamble(self.trace, + self.runtime_boxes, self.call_pure_results, self.box_names_memo) else: opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.propagate_all_forward(self.start_label.getarglist(), - self.operations, self.call_pure_results) + return opt.propagate_all_forward(self.trace, self.call_pure_results) class SimpleCompileData(CompileData): """ This represents label() ops jump with no extra info associated with the label """ - def __init__(self, start_label, operations, call_pure_results=None, + def __init__(self, trace, call_pure_results=None, enable_opts=None): - self.start_label = start_label - self.operations = operations + self.trace = trace self.call_pure_results = call_pure_results self.enable_opts = enable_opts @@ -81,17 +76,17 @@ #assert not unroll opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.propagate_all_forward(self.start_label.getarglist(), - self.operations, self.call_pure_results) + return opt.propagate_all_forward(self.trace.get_iter(), + self.call_pure_results) class BridgeCompileData(CompileData): """ This represents ops() with a jump at the end that goes to some loop, we need to deal with virtual state and inlining of short preamble """ - def __init__(self, start_label, operations, call_pure_results=None, + def __init__(self, trace, runtime_boxes, call_pure_results=None, enable_opts=None, inline_short_preamble=False): - self.start_label = start_label - self.operations = operations + self.trace = trace + self.runtime_boxes = runtime_boxes self.call_pure_results = call_pure_results self.enable_opts = enable_opts self.inline_short_preamble = inline_short_preamble @@ -100,7 +95,7 @@ from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_bridge(self.start_label, self.operations, + return opt.optimize_bridge(self.trace, self.runtime_boxes, self.call_pure_results, self.inline_short_preamble, self.box_names_memo) @@ -109,12 +104,13 @@ """ This represents label() ops jump with extra info that's from the run of LoopCompileData. Jump goes to the same label """ - def __init__(self, start_label, end_jump, operations, state, + log_noopt = False + + def __init__(self, trace, celltoken, state, call_pure_results=None, enable_opts=None, inline_short_preamble=True): - self.start_label = start_label - self.end_jump = end_jump - self.operations = operations + self.trace = trace + self.celltoken = celltoken self.enable_opts = enable_opts self.state = state self.call_pure_results = call_pure_results @@ -125,9 +121,8 @@ assert unroll # we should not be here if it's disabled opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_peeled_loop(self.start_label, self.end_jump, - self.operations, self.state, self.call_pure_results, - self.inline_short_preamble) + return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state, + self.call_pure_results, self.inline_short_preamble) def show_procedures(metainterp_sd, procedure=None, error=None): # debugging @@ -208,23 +203,21 @@ # ____________________________________________________________ -def compile_simple_loop(metainterp, greenkey, start, inputargs, ops, jumpargs, - enable_opts): +def compile_simple_loop(metainterp, greenkey, trace, runtime_args, enable_opts, + cut_at): from rpython.jit.metainterp.optimizeopt import optimize_trace jitdriver_sd = metainterp.jitdriver_sd metainterp_sd = metainterp.staticdata jitcell_token = make_jitcell_token(jitdriver_sd) - label = ResOperation(rop.LABEL, inputargs[:], descr=jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=jitcell_token) call_pure_results = metainterp.call_pure_results - data = SimpleCompileData(label, ops + [jump_op], - call_pure_results=call_pure_results, - enable_opts=enable_opts) + data = SimpleCompileData(trace, call_pure_results=call_pure_results, + enable_opts=enable_opts) try: loop_info, ops = optimize_trace(metainterp_sd, jitdriver_sd, data, metainterp.box_names_memo) except InvalidLoop: + trace.cut_at(cut_at) return None loop = create_empty_loop(metainterp) loop.original_jitcell_token = jitcell_token @@ -241,7 +234,7 @@ loop.check_consistency() jitcell_token.target_tokens = [target_token] send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop", - inputargs, metainterp.box_names_memo) + runtime_args, metainterp.box_names_memo) record_loop_or_bridge(metainterp_sd, loop) return target_token @@ -255,6 +248,7 @@ metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd history = metainterp.history + trace = history.trace warmstate = jitdriver_sd.warmstate enable_opts = jitdriver_sd.warmstate.enable_opts @@ -264,16 +258,16 @@ enable_opts = enable_opts.copy() del enable_opts['unroll'] - ops = history.operations[start:] + jitcell_token = make_jitcell_token(jitdriver_sd) + cut_at = history.get_trace_position() + history.record(rop.JUMP, jumpargs, None, descr=jitcell_token) + if start != (0, 0, 0): + trace = trace.cut_trace_from(start, inputargs) if 'unroll' not in enable_opts or not metainterp.cpu.supports_guard_gc_type: - return compile_simple_loop(metainterp, greenkey, start, inputargs, ops, - jumpargs, enable_opts) - jitcell_token = make_jitcell_token(jitdriver_sd) - label = ResOperation(rop.LABEL, inputargs, - descr=TargetToken(jitcell_token)) - end_label = ResOperation(rop.LABEL, jumpargs, descr=jitcell_token) + return compile_simple_loop(metainterp, greenkey, trace, jumpargs, + enable_opts, cut_at) call_pure_results = metainterp.call_pure_results - preamble_data = LoopCompileData(label, end_label, ops, + preamble_data = LoopCompileData(trace, jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -281,17 +275,15 @@ preamble_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut_at) return None metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd - end_label = ResOperation(rop.LABEL, inputargs, - descr=jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs, descr=jitcell_token) start_descr = TargetToken(jitcell_token, original_jitcell_token=jitcell_token) jitcell_token.target_tokens = [start_descr] - loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state, + loop_data = UnrolledLoopData(trace, jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -299,11 +291,12 @@ loop_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut_at) return None if ((warmstate.vec and jitdriver_sd.vec) or warmstate.vec_all): from rpython.jit.metainterp.optimizeopt.vector import optimize_vector - loop_info, loop_ops = optimize_vector(metainterp_sd, + loop_info, loop_ops = optimize_vector(trace, metainterp_sd, jitdriver_sd, warmstate, loop_info, loop_ops, jitcell_token) @@ -342,22 +335,20 @@ to the first operation. """ from rpython.jit.metainterp.optimizeopt import optimize_trace - from rpython.jit.metainterp.optimizeopt.optimizer import BasicLoopInfo - history = metainterp.history + trace = metainterp.history.trace.cut_trace_from(start, inputargs) metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd + history = metainterp.history loop_jitcell_token = metainterp.get_procedure_token(greenkey) assert loop_jitcell_token - end_label = ResOperation(rop.LABEL, inputargs[:], - descr=loop_jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=loop_jitcell_token) + cut = history.get_trace_position() + history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) enable_opts = jitdriver_sd.warmstate.enable_opts - ops = history.operations[start:] call_pure_results = metainterp.call_pure_results - loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state, + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -366,8 +357,9 @@ metainterp.box_names_memo) except InvalidLoop: # Fall back on jumping directly to preamble - jump_op = ResOperation(rop.JUMP, inputargs[:], descr=loop_jitcell_token) - loop_data = UnrolledLoopData(end_label, jump_op, [jump_op], start_state, + history.cut(cut) + history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=False) @@ -376,9 +368,13 @@ loop_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut) return None - label_token = loop_info.label_op.getdescr() + label_op = loop_info.label_op + if label_op is None: + assert False, "unreachable code" # hint for some strange tests + label_token = label_op.getdescr() assert isinstance(label_token, TargetToken) if label_token.short_preamble: metainterp_sd.logger_ops.log_short_preamble([], @@ -445,13 +441,13 @@ box = inputargs[i] opnum = OpHelpers.getfield_for_descr(descr) emit_op(extra_ops, - ResOperation(opnum, [vable_box], descr)) + ResOperation(opnum, [vable_box], descr=descr)) box.set_forwarded(extra_ops[-1]) i += 1 arrayindex = 0 for descr in vinfo.array_field_descrs: arraylen = vinfo.get_array_length(vable, arrayindex) - arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr) + arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr=descr) emit_op(extra_ops, arrayop) arraydescr = vinfo.array_descrs[arrayindex] assert i + arraylen <= len(inputargs) @@ -1017,9 +1013,9 @@ metainterp_sd.stats.add_jitcell_token(jitcell_token) -def compile_trace(metainterp, resumekey): +def compile_trace(metainterp, resumekey, runtime_boxes): """Try to compile a new bridge leading from the beginning of the history - to some existing place. + to some existging place. """ from rpython.jit.metainterp.optimizeopt import optimize_trace @@ -1037,20 +1033,19 @@ else: inline_short_preamble = True inputargs = metainterp.history.inputargs[:] - operations = metainterp.history.operations - label = ResOperation(rop.LABEL, inputargs) + trace = metainterp.history.trace jitdriver_sd = metainterp.jitdriver_sd enable_opts = jitdriver_sd.warmstate.enable_opts call_pure_results = metainterp.call_pure_results - if operations[-1].getopnum() == rop.JUMP: - data = BridgeCompileData(label, operations[:], + if metainterp.history.ends_with_jump: + data = BridgeCompileData(trace, runtime_boxes, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=inline_short_preamble) else: - data = SimpleCompileData(label, operations[:], + data = SimpleCompileData(trace, call_pure_results=call_pure_results, enable_opts=enable_opts) try: diff --git a/rpython/jit/metainterp/executor.py b/rpython/jit/metainterp/executor.py --- a/rpython/jit/metainterp/executor.py +++ b/rpython/jit/metainterp/executor.py @@ -9,7 +9,7 @@ from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID, AbstractDescr from rpython.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr from rpython.jit.metainterp import resoperation -from rpython.jit.metainterp.resoperation import rop +from rpython.jit.metainterp.resoperation import rop, opname from rpython.jit.metainterp.blackhole import BlackholeInterpreter, NULL from rpython.jit.codewriter import longlong @@ -314,7 +314,8 @@ def _make_execute_list(): execute_by_num_args = {} - for key, value in rop.__dict__.items(): + for key in opname.values(): + value = getattr(rop, key) if not key.startswith('_'): if (rop._FINAL_FIRST <= value <= rop._FINAL_LAST or rop._GUARD_FIRST <= value <= rop._GUARD_LAST): @@ -384,6 +385,11 @@ rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, rop.NURSERY_PTR_INCREMENT, rop.LABEL, + rop.ESCAPE_I, + rop.ESCAPE_N, + rop.ESCAPE_R, + rop.ESCAPE_F, + rop.FORCE_SPILL, rop.SAVE_EXC_CLASS, rop.SAVE_EXCEPTION, rop.RESTORE_EXCEPTION, diff --git a/rpython/jit/metainterp/graphpage.py b/rpython/jit/metainterp/graphpage.py --- a/rpython/jit/metainterp/graphpage.py +++ b/rpython/jit/metainterp/graphpage.py @@ -170,7 +170,8 @@ while True: op = operations[opindex] op_repr = op.repr(self.memo, graytext=True) - if op.getopnum() == rop.DEBUG_MERGE_POINT: + if (op.getopnum() == rop.DEBUG_MERGE_POINT and + self.metainterp_sd is not None): jd_sd = self.metainterp_sd.jitdrivers_sd[op.getarg(0).getint()] if jd_sd._get_printable_location_ptr: s = jd_sd.warmstate.get_location_str(op.getarglist()[3:]) diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py --- a/rpython/jit/metainterp/heapcache.py +++ b/rpython/jit/metainterp/heapcache.py @@ -1,33 +1,59 @@ -from rpython.jit.metainterp.history import ConstInt +from rpython.jit.metainterp.history import Const, ConstInt +from rpython.jit.metainterp.history import FrontendOp, RefFrontendOp from rpython.jit.metainterp.resoperation import rop, OpHelpers +from rpython.jit.metainterp.executor import constant_from_op +from rpython.rlib.rarithmetic import r_uint32, r_uint +from rpython.rlib.objectmodel import always_inline -class HeapCacheValue(object): - def __init__(self, box): - self.box = box - self.likely_virtual = False - self.reset_keep_likely_virtual() +""" A big note: we don't do heap caches on Consts, because it used +to be done with the identity of the Const instance. This gives very wonky +results at best, so we decided to not do it at all. Can be fixed with +interning of Consts (already done on trace anyway) +""" - def reset_keep_likely_virtual(self): - self.known_class = False - self.known_nullity = False - # did we see the allocation during tracing? - self.seen_allocation = False - self.is_unescaped = False - self.nonstandard_virtualizable = False - self.length = None - self.dependencies = None +# RefFrontendOp._heapc_flags: +HF_LIKELY_VIRTUAL = 0x01 +HF_KNOWN_CLASS = 0x02 +HF_KNOWN_NULLITY = 0x04 +HF_SEEN_ALLOCATION = 0x08 # did we see the allocation during tracing? +HF_IS_UNESCAPED = 0x10 +HF_NONSTD_VABLE = 0x20 - def __repr__(self): - return 'HeapCacheValue(%s)' % (self.box, ) +_HF_VERSION_INC = 0x40 # must be last +_HF_VERSION_MAX = r_uint(2 ** 32 - _HF_VERSION_INC) + +@always_inline +def add_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + f |= r_uint(flags) + ref_frontend_op._set_heapc_flags(f) + +@always_inline +def remove_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + f &= r_uint(~flags) + ref_frontend_op._set_heapc_flags(f) + +@always_inline +def test_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + return bool(f & r_uint(flags)) + +def maybe_replace_with_const(box): + if not isinstance(box, Const) and box.is_replaced_with_const(): + return constant_from_op(box) + else: + return box class CacheEntry(object): - def __init__(self): - # both are {from_value: to_value} dicts + def __init__(self, heapcache): + # both are {from_ref_box: to_field_box} dicts # the first is for boxes where we did not see the allocation, the # second for anything else. the reason that distinction makes sense is # because if we saw the allocation, we know it cannot alias with # anything else where we saw the allocation. + self.heapcache = heapcache self.cache_anything = {} self.cache_seen_allocation = {} @@ -36,112 +62,137 @@ self.cache_seen_allocation.clear() self.cache_anything.clear() - def _getdict(self, value): - if value.seen_allocation: + def _seen_alloc(self, ref_box): + if not isinstance(ref_box, RefFrontendOp): + return False + return self.heapcache._check_flag(ref_box, HF_SEEN_ALLOCATION) + + def _getdict(self, seen_alloc): + if seen_alloc: return self.cache_seen_allocation else: return self.cache_anything - def do_write_with_aliasing(self, value, fieldvalue): - self._clear_cache_on_write(value.seen_allocation) - self._getdict(value)[value] = fieldvalue + def do_write_with_aliasing(self, ref_box, fieldbox): + seen_alloc = self._seen_alloc(ref_box) + self._clear_cache_on_write(seen_alloc) + self._getdict(seen_alloc)[ref_box] = fieldbox - def read(self, value): - return self._getdict(value).get(value, None) + def read(self, ref_box): + dict = self._getdict(self._seen_alloc(ref_box)) + try: + res_box = dict[ref_box] + except KeyError: + return None + return maybe_replace_with_const(res_box) - def read_now_known(self, value, fieldvalue): - self._getdict(value)[value] = fieldvalue + def read_now_known(self, ref_box, fieldbox): + self._getdict(self._seen_alloc(ref_box))[ref_box] = fieldbox def invalidate_unescaped(self): self._invalidate_unescaped(self.cache_anything) self._invalidate_unescaped(self.cache_seen_allocation) def _invalidate_unescaped(self, d): - for value in d.keys(): - if not value.is_unescaped: - del d[value] + for ref_box in d.keys(): + if not self.heapcache.is_unescaped(ref_box): + del d[ref_box] class FieldUpdater(object): - def __init__(self, heapcache, value, cache, fieldvalue): - self.heapcache = heapcache - self.value = value + def __init__(self, ref_box, cache, fieldbox): + self.ref_box = ref_box self.cache = cache - if fieldvalue is not None: - self.currfieldbox = fieldvalue.box - else: - self.currfieldbox = None + self.currfieldbox = fieldbox # <= read directly from pyjitpl.py def getfield_now_known(self, fieldbox): - fieldvalue = self.heapcache.getvalue(fieldbox) - self.cache.read_now_known(self.value, fieldvalue) + self.cache.read_now_known(self.ref_box, fieldbox) def setfield(self, fieldbox): - fieldvalue = self.heapcache.getvalue(fieldbox) - self.cache.do_write_with_aliasing(self.value, fieldvalue) + self.cache.do_write_with_aliasing(self.ref_box, fieldbox) + +class DummyFieldUpdater(FieldUpdater): + def __init__(self): + self.currfieldbox = None + + def getfield_now_known(self, fieldbox): + pass + + def setfield(self, fieldbox): + pass + +dummy_field_updater = DummyFieldUpdater() class HeapCache(object): def __init__(self): + # Works with flags stored on RefFrontendOp._heapc_flags. + # There are two ways to do a global resetting of these flags: + # reset() and reset_keep_likely_virtual(). The basic idea is + # to use a version number in each RefFrontendOp, and in order + # to reset the flags globally, we increment the global version + # number in this class. Then when we read '_heapc_flags' we + # also check if the associated version number is up-to-date + # or not. More precisely, we have two global version numbers + # here: 'head_version' and 'likely_virtual_version'. Normally + # we use 'head_version'. For is_likely_virtual() though, we + # use the other, older version number. + self.head_version = r_uint(0) + self.likely_virtual_version = r_uint(0) self.reset() def reset(self): - # maps boxes to values - self.values = {} - # store the boxes that contain newly allocated objects, this maps the - # boxes to a bool, the bool indicates whether or not the object has - # escaped the trace or not (True means the box never escaped, False - # means it did escape), its presences in the mapping shows that it was - # allocated inside the trace - #if trace_branch: - #self.new_boxes = {} - # pass - #else: - #for box in self.new_boxes: - # self.new_boxes[box] = False - # pass - #if reset_virtuals: - # self.likely_virtuals = {} # only for jit.isvirtual() - # Tracks which boxes should be marked as escaped when the key box - # escapes. - #self.dependencies = {} - + # Global reset of all flags. Update both version numbers so + # that any access to '_heapc_flags' will be marked as outdated. + assert self.head_version < _HF_VERSION_MAX + self.head_version += _HF_VERSION_INC + self.likely_virtual_version = self.head_version + # # heap cache # maps descrs to CacheEntry self.heap_cache = {} # heap array cache - # maps descrs to {index: {from_value: to_value}} dicts + # maps descrs to {index: CacheEntry} dicts self.heap_array_cache = {} def reset_keep_likely_virtuals(self): - for value in self.values.itervalues(): - value.reset_keep_likely_virtual() + # Update only 'head_version', but 'likely_virtual_version' remains + # at its older value. + assert self.head_version < _HF_VERSION_MAX + self.head_version += _HF_VERSION_INC self.heap_cache = {} self.heap_array_cache = {} - def getvalue(self, box, create=True): - value = self.values.get(box, None) - if not value and create: - value = self.values[box] = HeapCacheValue(box) - return value + @always_inline + def test_head_version(self, ref_frontend_op): + return ref_frontend_op._get_heapc_flags() >= self.head_version - def getvalues(self, boxes): - return [self.getvalue(box) for box in boxes] + @always_inline + def test_likely_virtual_version(self, ref_frontend_op): + return ref_frontend_op._get_heapc_flags() >= self.likely_virtual_version + + def update_version(self, ref_frontend_op): + """Ensure the version of 'ref_frontend_op' is current. If not, + it will update 'ref_frontend_op' (removing most flags currently set). + """ + if not self.test_head_version(ref_frontend_op): + f = self.head_version + if (self.test_likely_virtual_version(ref_frontend_op) and + test_flags(ref_frontend_op, HF_LIKELY_VIRTUAL)): + f |= HF_LIKELY_VIRTUAL + ref_frontend_op._set_heapc_flags(f) + ref_frontend_op._heapc_deps = None def invalidate_caches(self, opnum, descr, argboxes): self.mark_escaped(opnum, descr, argboxes) self.clear_caches(opnum, descr, argboxes) def _escape_from_write(self, box, fieldbox): - value = self.getvalue(box, create=False) - fieldvalue = self.getvalue(fieldbox, create=False) - if (value is not None and value.is_unescaped and - fieldvalue is not None and fieldvalue.is_unescaped): - if value.dependencies is None: - value.dependencies = [] - value.dependencies.append(fieldvalue) - elif fieldvalue is not None: - self._escape(fieldvalue) + if self.is_unescaped(box) and self.is_unescaped(fieldbox): + deps = self._get_deps(box) + deps.append(fieldbox) + elif fieldbox is not None: + self._escape_box(fieldbox) def mark_escaped(self, opnum, descr, argboxes): if opnum == rop.SETFIELD_GC: @@ -176,19 +227,20 @@ self._escape_box(box) def _escape_box(self, box): - value = self.getvalue(box, create=False) - if not value: - return - self._escape(value) - - def _escape(self, value): - value.is_unescaped = False - value.likely_virtual = False - deps = value.dependencies - value.dependencies = None - if deps is not None: - for dep in deps: - self._escape(dep) + if isinstance(box, RefFrontendOp): + remove_flags(box, HF_LIKELY_VIRTUAL | HF_IS_UNESCAPED) + deps = box._heapc_deps + if deps is not None: + if not self.test_head_version(box): + box._heapc_deps = None + else: + # 'deps[0]' is abused to store the array length, keep it + if deps[0] is None: + box._heapc_deps = None + else: + box._heapc_deps = [deps[0]] + for i in range(1, len(deps)): + self._escape_box(deps[i]) def clear_caches(self, opnum, descr, argboxes): if (opnum == rop.SETFIELD_GC or @@ -241,7 +293,8 @@ self.reset_keep_likely_virtuals() def _clear_caches_arraycopy(self, opnum, desrc, argboxes, effectinfo): - seen_allocation_of_target = self.getvalue(argboxes[2]).seen_allocation + seen_allocation_of_target = self._check_flag( + argboxes[2], HF_SEEN_ALLOCATION) if ( isinstance(argboxes[3], ConstInt) and isinstance(argboxes[4], ConstInt) and @@ -285,74 +338,82 @@ return self.reset_keep_likely_virtuals() + def _get_deps(self, box): + if not isinstance(box, RefFrontendOp): + return None + self.update_version(box) + if box._heapc_deps is None: + box._heapc_deps = [None] + return box._heapc_deps + + def _check_flag(self, box, flag): + return (isinstance(box, RefFrontendOp) and + self.test_head_version(box) and + test_flags(box, flag)) + + def _set_flag(self, box, flag): + assert isinstance(box, RefFrontendOp) + self.update_version(box) + add_flags(box, flag) + def is_class_known(self, box): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit