Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r88711:c8b4b78af01a Date: 2016-11-28 18:33 +0100 http://bitbucket.org/pypy/pypy/changeset/c8b4b78af01a/
Log: hg merge default diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py b/lib-python/2.7/distutils/sysconfig_pypy.py --- a/lib-python/2.7/distutils/sysconfig_pypy.py +++ b/lib-python/2.7/distutils/sysconfig_pypy.py @@ -74,6 +74,19 @@ g['LIBDIR'] = os.path.join(sys.prefix, 'lib') g['VERSION'] = get_python_version() + if sys.platform[:6] == "darwin": + import platform + if platform.machine() == 'i386': + if platform.architecture()[0] == '32bit': + arch = 'i386' + else: + arch = 'x86_64' + else: + # just a guess + arch = platform.machine() + g['LDSHARED'] += ' -undefined dynamic_lookup' + g['CC'] += ' -arch %s' % (arch,) + global _config_vars _config_vars = g @@ -109,6 +122,12 @@ _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_config_vars) + if args: vals = [] for name in args: 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 @@ -34,3 +34,7 @@ Add jit.conditional_call_elidable(), a way to tell the JIT "conditonally call this function" returning a result. + +.. branch: desc-specialize + +Refactor FunctionDesc.specialize() and related code (RPython annotator). diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -302,6 +302,12 @@ elif config.objspace.usemodules.pypyjit: config.translation.jit = True + if config.objspace.usemodules.cpyext: + if config.translation.gc != 'incminimark': + raise Exception("The 'cpyext' module requires the 'incminimark'" + " GC. You need either 'targetpypystandalone.py" + " --withoutmod-cpyext' or '--gc=incminimark'") + config.translating = True import translate diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -559,6 +559,7 @@ space.wrap(msg)) return OperationError(exc, w_error) +@specialize.arg(3) def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError', w_exception_class=None): assert isinstance(e, OSError) @@ -586,8 +587,8 @@ w_error = space.call_function(exc, space.wrap(errno), space.wrap(msg)) return OperationError(exc, w_error) -wrap_oserror2._annspecialcase_ = 'specialize:arg(3)' +@specialize.arg(3) def wrap_oserror(space, e, filename=None, exception_name='w_OSError', w_exception_class=None): if filename is not None: @@ -598,7 +599,6 @@ return wrap_oserror2(space, e, None, exception_name=exception_name, w_exception_class=w_exception_class) -wrap_oserror._annspecialcase_ = 'specialize:arg(3)' def exception_from_saved_errno(space, w_type): from rpython.rlib.rposix import get_saved_errno diff --git a/pypy/module/_collections/interp_deque.py b/pypy/module/_collections/interp_deque.py --- a/pypy/module/_collections/interp_deque.py +++ b/pypy/module/_collections/interp_deque.py @@ -1,4 +1,5 @@ import sys +from rpython.rlib.objectmodel import specialize from pypy.interpreter import gateway from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.typedef import TypeDef, make_weakref_descr @@ -7,7 +8,6 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.objspace.std.sliceobject import unwrap_start_stop from rpython.rlib.debug import check_nonneg -from rpython.rlib.objectmodel import specialize # A `dequeobject` is composed of a doubly-linked list of `block` nodes. diff --git a/pypy/module/cppyy/test/test_zjit.py b/pypy/module/cppyy/test/test_zjit.py --- a/pypy/module/cppyy/test/test_zjit.py +++ b/pypy/module/cppyy/test/test_zjit.py @@ -124,13 +124,13 @@ assert isinstance(w_obj, FakeFloat) return w_obj.val + @specialize.arg(1) def interp_w(self, RequiredClass, w_obj, can_be_None=False): if can_be_None and w_obj is None: return None if not isinstance(w_obj, RequiredClass): raise TypeError return w_obj - interp_w._annspecialcase_ = 'specialize:arg(1)' def getarg_w(self, code, w_obj): # for retrieving buffers return FakeBuffer(w_obj) diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -35,8 +35,6 @@ from rpython.rlib.objectmodel import specialize from pypy.module import exceptions from pypy.module.exceptions import interp_exceptions -# CPython 2.4 compatibility -from py.builtin import BaseException from rpython.tool.sourcetools import func_with_new_name from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib import rawrefcount @@ -1622,9 +1620,8 @@ miniglobals = {'__name__': __name__, # for module name propagation } exec source.compile() in miniglobals - call_external_function = miniglobals['cpy_call_external'] + call_external_function = specialize.ll()(miniglobals['cpy_call_external']) call_external_function._dont_inline_ = True - call_external_function._annspecialcase_ = 'specialize:ll' call_external_function._gctransformer_hint_close_stack_ = True # don't inline, as a hack to guarantee that no GC pointer is alive # anywhere in call_external_function diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -15,6 +15,7 @@ from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper from rpython.rlib import rawrefcount +from rpython.rlib.debug import fatalerror #________________________________________________________ @@ -192,6 +193,8 @@ rawrefcount.create_link_pypy(w_obj, py_obj) +w_marker_deallocating = W_Root() + def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -202,7 +205,23 @@ return None w_obj = rawrefcount.to_obj(W_Root, ref) if w_obj is not None: - return w_obj + if w_obj is not w_marker_deallocating: + return w_obj + fatalerror( + "*** Invalid usage of a dying CPython object ***\n" + "\n" + "cpyext, the emulation layer, detected that while it is calling\n" + "an object's tp_dealloc, the C code calls back a function that\n" + "tries to recreate the PyPy version of the object. Usually it\n" + "means that tp_dealloc calls some general PyXxx() API. It is\n" + "a dangerous and potentially buggy thing to do: even in CPython\n" + "the PyXxx() function could, in theory, cause a reference to the\n" + "object to be taken and stored somewhere, for an amount of time\n" + "exceeding tp_dealloc itself. Afterwards, the object will be\n" + "freed, making that reference point to garbage.\n" + ">>> PyPy could contain some workaround to still work if\n" + "you are lucky, but it is not done so far; better fix the bug in\n" + "the CPython extension.") # This reference is not yet a real interpreter object. # Realize it. @@ -233,7 +252,8 @@ INTERPLEVEL_API['as_pyobj'] = as_pyobj def pyobj_has_w_obj(pyobj): - return rawrefcount.to_obj(W_Root, pyobj) is not None + w_obj = rawrefcount.to_obj(W_Root, pyobj) + return w_obj is not None and w_obj is not w_marker_deallocating INTERPLEVEL_API['pyobj_has_w_obj'] = staticmethod(pyobj_has_w_obj) @@ -335,6 +355,7 @@ pto = obj.c_ob_type #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \ # "'s type which is", rffi.charp2str(pto.c_tp_name) + rawrefcount.mark_deallocating(w_marker_deallocating, obj) generic_cpy_call(space, pto.c_tp_dealloc, obj) @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL) diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py --- a/pypy/module/pyexpat/interp_pyexpat.py +++ b/pypy/module/pyexpat/interp_pyexpat.py @@ -4,11 +4,11 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.unicodehelper import encode_utf8 from rpython.rlib import rgc, jit +from rpython.rlib.objectmodel import specialize from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rtyper.tool import rffi_platform from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.platform import platform -from rpython.rlib.objectmodel import specialize import sys import weakref diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -208,6 +208,7 @@ def newunicode(self, x): return w_some_obj() + @specialize.argtype(1) def wrap(self, x): if not we_are_translated(): if isinstance(x, gateway.interp2app): @@ -221,7 +222,6 @@ return w_some_obj() self._wrap_not_rpython(x) return w_some_obj() - wrap._annspecialcase_ = "specialize:argtype(1)" def _wrap_not_rpython(self, x): "NOT_RPYTHON" @@ -314,10 +314,10 @@ is_root(w_complex) return 1.1, 2.2 + @specialize.arg(1) def allocate_instance(self, cls, w_subtype): is_root(w_subtype) return instantiate(cls) - allocate_instance._annspecialcase_ = "specialize:arg(1)" def decode_index(self, w_index_or_slice, seqlength): is_root(w_index_or_slice) diff --git a/pypy/objspace/fake/test/test_checkmodule.py b/pypy/objspace/fake/test/test_checkmodule.py --- a/pypy/objspace/fake/test/test_checkmodule.py +++ b/pypy/objspace/fake/test/test_checkmodule.py @@ -9,9 +9,9 @@ def make_checker(): check = [] + @specialize.memo() def see(): check.append(True) - see._annspecialcase_ = 'specialize:memo' return see, check def test_wrap_interp2app(): diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py --- a/pypy/objspace/std/formatting.py +++ b/pypy/objspace/std/formatting.py @@ -2,12 +2,12 @@ import sys from rpython.rlib import jit +from rpython.rlib.objectmodel import specialize from rpython.rlib.rarithmetic import INT_MAX from rpython.rlib.rfloat import DTSF_ALT, formatd, isnan, isinf from rpython.rlib.rstring import StringBuilder, UnicodeBuilder from rpython.rlib.unroll import unrolling_iterable from rpython.tool.sourcetools import func_with_new_name -from rpython.rlib.objectmodel import specialize from pypy.interpreter.error import OperationError, oefmt diff --git a/pypy/tool/cpyext/extbuild.py b/pypy/tool/cpyext/extbuild.py --- a/pypy/tool/cpyext/extbuild.py +++ b/pypy/tool/cpyext/extbuild.py @@ -206,6 +206,10 @@ pass from distutils.ccompiler import new_compiler from distutils import sysconfig + + # XXX for Darwin running old versions of CPython 2.7.x + sysconfig.get_config_vars() + compiler = new_compiler(force=1) sysconfig.customize_compiler(compiler) # XXX objects = [] diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -2,6 +2,7 @@ import types from collections import defaultdict +from contextlib import contextmanager from rpython.tool.ansi_print import AnsiLogger from rpython.tool.pairtype import pair @@ -83,22 +84,17 @@ annmodel.TLS.check_str_without_nul = ( self.translator.config.translation.check_str_without_nul) - flowgraph, inputs_s = self.get_call_parameters(function, args_s, policy) + with self.using_policy(policy): + flowgraph, inputs_s = self.get_call_parameters(function, args_s) if main_entry_point: self.translator.entry_point_graph = flowgraph return self.build_graph_types(flowgraph, inputs_s, complete_now=complete_now) - def get_call_parameters(self, function, args_s, policy): - desc = self.bookkeeper.getdesc(function) - prevpolicy = self.policy - self.policy = policy - self.bookkeeper.enter(None) - try: + def get_call_parameters(self, function, args_s): + with self.bookkeeper.at_position(None): + desc = self.bookkeeper.getdesc(function) return desc.get_call_parameters(args_s) - finally: - self.bookkeeper.leave() - self.policy = prevpolicy def annotate_helper(self, function, args_s, policy=None): if policy is None: @@ -107,21 +103,29 @@ # XXX hack annmodel.TLS.check_str_without_nul = ( self.translator.config.translation.check_str_without_nul) - graph, inputcells = self.get_call_parameters(function, args_s, policy) - self.build_graph_types(graph, inputcells, complete_now=False) - self.complete_helpers(policy) + with self.using_policy(policy): + graph, inputcells = self.get_call_parameters(function, args_s) + self.build_graph_types(graph, inputcells, complete_now=False) + self.complete_helpers() return graph - def complete_helpers(self, policy): - saved = self.policy, self.added_blocks - self.policy = policy + def complete_helpers(self): + saved = self.added_blocks + self.added_blocks = {} try: - self.added_blocks = {} self.complete() # invoke annotation simplifications for the new blocks self.simplify(block_subset=self.added_blocks) finally: - self.policy, self.added_blocks = saved + self.added_blocks = saved + + @contextmanager + def using_policy(self, policy): + """A context manager that temporarily replaces the annotator policy""" + old_policy = self.policy + self.policy = policy + yield + self.policy = old_policy def build_graph_types(self, flowgraph, inputcells, complete_now=True): checkgraph(flowgraph) diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py --- a/rpython/annotator/bookkeeper.py +++ b/rpython/annotator/bookkeeper.py @@ -9,6 +9,7 @@ from collections import OrderedDict from rpython.flowspace.model import Constant +from rpython.flowspace.bytecode import cpython_code_signature from rpython.annotator.model import ( SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance, SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint, @@ -21,6 +22,7 @@ from rpython.annotator import description from rpython.annotator.signature import annotationoftype from rpython.annotator.argument import simple_args +from rpython.annotator.specialize import memo from rpython.rlib.objectmodel import r_dict, r_ordereddict, Symbolic from rpython.tool.algo.unionfind import UnionFind from rpython.rtyper import extregistry @@ -358,7 +360,7 @@ return self.descs[obj_key] except KeyError: if isinstance(pyobj, types.FunctionType): - result = description.FunctionDesc(self, pyobj) + result = self.newfuncdesc(pyobj) elif isinstance(pyobj, (type, types.ClassType)): if pyobj is object: raise Exception("ClassDesc for object not supported") @@ -403,6 +405,23 @@ self.descs[obj_key] = result return result + def newfuncdesc(self, pyfunc): + name = pyfunc.__name__ + if hasattr(pyfunc, '_generator_next_method_of_'): + from rpython.flowspace.argument import Signature + signature = Signature(['entry']) # haaaaaack + defaults = () + else: + signature = cpython_code_signature(pyfunc.func_code) + defaults = pyfunc.func_defaults + # get the specializer based on the tag of the 'pyobj' + # (if any), according to the current policy + tag = getattr(pyfunc, '_annspecialcase_', None) + specializer = self.annotator.policy.get_specializer(tag) + if specializer is memo: + return description.MemoDesc(self, pyfunc, name, signature, defaults, specializer) + return description.FunctionDesc(self, pyfunc, name, signature, defaults, specializer) + def getfrozen(self, pyobj): return description.FrozenDesc(self, pyobj) diff --git a/rpython/annotator/classdesc.py b/rpython/annotator/classdesc.py --- a/rpython/annotator/classdesc.py +++ b/rpython/annotator/classdesc.py @@ -608,7 +608,7 @@ if mixin: # make a new copy of the FunctionDesc for this class, # but don't specialize further for all subclasses - funcdesc = FunctionDesc(self.bookkeeper, value) + funcdesc = self.bookkeeper.newfuncdesc(value) self.classdict[name] = funcdesc return # NB. if value is, say, AssertionError.__init__, then we diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py --- a/rpython/annotator/description.py +++ b/rpython/annotator/description.py @@ -3,11 +3,10 @@ from rpython.annotator.signature import ( enforce_signature_args, enforce_signature_return, finish_type) from rpython.flowspace.model import FunctionGraph -from rpython.flowspace.bytecode import cpython_code_signature from rpython.annotator.argument import rawshape, ArgErr, simple_args from rpython.tool.sourcetools import valid_identifier from rpython.tool.pairtype import extendabletype -from rpython.annotator.model import AnnotatorError, s_ImpossibleValue +from rpython.annotator.model import AnnotatorError, s_ImpossibleValue, unionof class CallFamily(object): """A family of Desc objects that could be called from common call sites. @@ -117,7 +116,6 @@ self.s_value = s_ImpossibleValue # union of possible values def update(self, other): - from rpython.annotator.model import unionof self.descs.update(other.descs) self.read_locations.update(other.read_locations) self.s_value = unionof(self.s_value, other.s_value) @@ -192,24 +190,12 @@ class FunctionDesc(Desc): knowntype = types.FunctionType - def __init__(self, bookkeeper, pyobj=None, - name=None, signature=None, defaults=None, + def __init__(self, bookkeeper, pyobj, name, signature, defaults, specializer=None): super(FunctionDesc, self).__init__(bookkeeper, pyobj) - if name is None: - name = pyobj.func_name - if signature is None: - if hasattr(pyobj, '_generator_next_method_of_'): - from rpython.flowspace.argument import Signature - signature = Signature(['entry']) # haaaaaack - defaults = () - else: - signature = cpython_code_signature(pyobj.func_code) - if defaults is None: - defaults = pyobj.func_defaults self.name = name self.signature = signature - self.defaults = defaults or () + self.defaults = defaults if defaults is not None else () # 'specializer' is a function with the following signature: # specializer(funcdesc, args_s) => graph # or => s_result (overridden/memo cases) @@ -288,12 +274,43 @@ getattr(self.bookkeeper, "position_key", None) is not None): _, block, i = self.bookkeeper.position_key op = block.operations[i] - if self.specializer is None: - # get the specializer based on the tag of the 'pyobj' - # (if any), according to the current policy - tag = getattr(self.pyobj, '_annspecialcase_', None) - policy = self.bookkeeper.annotator.policy - self.specializer = policy.get_specializer(tag) + self.normalize_args(inputcells) + if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"): + return self.specializer(self, inputcells, op) + else: + return self.specializer(self, inputcells) + + def pycall(self, whence, args, s_previous_result, op=None): + inputcells = self.parse_arguments(args) + graph = self.specialize(inputcells, op) + assert isinstance(graph, FunctionGraph) + # if that graph has a different signature, we need to re-parse + # the arguments. + # recreate the args object because inputcells may have been changed + new_args = args.unmatch_signature(self.signature, inputcells) + inputcells = self.parse_arguments(new_args, graph) + annotator = self.bookkeeper.annotator + result = annotator.recursivecall(graph, whence, inputcells) + signature = getattr(self.pyobj, '_signature_', None) + if signature: + sigresult = enforce_signature_return(self, signature[1], result) + if sigresult is not None: + annotator.addpendingblock( + graph, graph.returnblock, [sigresult]) + result = sigresult + # Some specializations may break the invariant of returning + # annotations that are always more general than the previous time. + # We restore it here: + result = unionof(result, s_previous_result) + return result + + def normalize_args(self, inputs_s): + """ + Canonicalize argument annotations into the exact parameter + annotations of a specific specialized graph. + + Note: this method has no return value but mutates its argument instead. + """ enforceargs = getattr(self.pyobj, '_annenforceargs_', None) signature = getattr(self.pyobj, '_signature_', None) if enforceargs and signature: @@ -304,39 +321,9 @@ from rpython.annotator.signature import Sig enforceargs = Sig(*enforceargs) self.pyobj._annenforceargs_ = enforceargs - enforceargs(self, inputcells) # can modify inputcells in-place + enforceargs(self, inputs_s) # can modify inputs_s in-place if signature: - enforce_signature_args(self, signature[0], inputcells) # mutates inputcells - if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"): - return self.specializer(self, inputcells, op) - else: - return self.specializer(self, inputcells) - - def pycall(self, whence, args, s_previous_result, op=None): - inputcells = self.parse_arguments(args) - result = self.specialize(inputcells, op) - if isinstance(result, FunctionGraph): - graph = result # common case - annotator = self.bookkeeper.annotator - # if that graph has a different signature, we need to re-parse - # the arguments. - # recreate the args object because inputcells may have been changed - new_args = args.unmatch_signature(self.signature, inputcells) - inputcells = self.parse_arguments(new_args, graph) - result = annotator.recursivecall(graph, whence, inputcells) - signature = getattr(self.pyobj, '_signature_', None) - if signature: - sigresult = enforce_signature_return(self, signature[1], result) - if sigresult is not None: - annotator.addpendingblock( - graph, graph.returnblock, [sigresult]) - result = sigresult - # Some specializations may break the invariant of returning - # annotations that are always more general than the previous time. - # We restore it here: - from rpython.annotator.model import unionof - result = unionof(result, s_previous_result) - return result + enforce_signature_args(self, signature[0], inputs_s) # mutates inputs_s def get_graph(self, args, op): inputs_s = self.parse_arguments(args) @@ -405,6 +392,16 @@ return s_sigs +class MemoDesc(FunctionDesc): + def pycall(self, whence, args, s_previous_result, op=None): + inputcells = self.parse_arguments(args) + s_result = self.specialize(inputcells, op) + if isinstance(s_result, FunctionGraph): + s_result = s_result.getreturnvar().annotation + s_result = unionof(s_result, s_previous_result) + return s_result + + class MethodDesc(Desc): knowntype = types.MethodType diff --git a/rpython/annotator/specialize.py b/rpython/annotator/specialize.py --- a/rpython/annotator/specialize.py +++ b/rpython/annotator/specialize.py @@ -3,11 +3,13 @@ from rpython.tool.sourcetools import func_with_new_name from rpython.tool.algo.unionfind import UnionFind -from rpython.flowspace.model import Block, Link, Variable, SpaceOperation +from rpython.flowspace.model import Block, Link, Variable from rpython.flowspace.model import checkgraph from rpython.flowspace.operation import op from rpython.annotator import model as annmodel from rpython.flowspace.argument import Signature +from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool +from rpython.annotator.model import unionof def flatten_star_args(funcdesc, args_s): argnames, vararg, kwarg = funcdesc.signature @@ -127,7 +129,6 @@ def finish(self): if self.do_not_process: return - from rpython.annotator.model import unionof assert self.graph is None, "MemoTable already finished" # list of which argument positions can take more than one value example_args, example_value = self.table.iteritems().next() @@ -246,34 +247,36 @@ args_s.append(unionof(*values_s)) annotator.addpendinggraph(self.graph, args_s) +def all_values(s): + """Return the exhaustive list of possible values matching annotation `s`. -def memo(funcdesc, arglist_s): - from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool - from rpython.annotator.model import unionof + Raises `AnnotatorError` if no such (reasonably small) finite list exists. + """ + if s.is_constant(): + return [s.const] + elif isinstance(s, SomePBC): + values = [] + assert not s.can_be_None, "memo call: cannot mix None and PBCs" + for desc in s.descriptions: + if desc.pyobj is None: + raise annmodel.AnnotatorError( + "memo call with a class or PBC that has no " + "corresponding Python object (%r)" % (desc,)) + values.append(desc.pyobj) + return values + elif isinstance(s, SomeImpossibleValue): + return [] + elif isinstance(s, SomeBool): + return [False, True] + else: + raise annmodel.AnnotatorError("memo call: argument must be a class " + "or a frozen PBC, got %r" % (s,)) + +def memo(funcdesc, args_s): # call the function now, and collect possible results - argvalues = [] - for s in arglist_s: - if s.is_constant(): - values = [s.const] - elif isinstance(s, SomePBC): - values = [] - assert not s.can_be_None, "memo call: cannot mix None and PBCs" - for desc in s.descriptions: - if desc.pyobj is None: - raise annmodel.AnnotatorError( - "memo call with a class or PBC that has no " - "corresponding Python object (%r)" % (desc,)) - values.append(desc.pyobj) - elif isinstance(s, SomeImpossibleValue): - return s # we will probably get more possible args later - elif isinstance(s, SomeBool): - values = [False, True] - else: - raise annmodel.AnnotatorError("memo call: argument must be a class " - "or a frozen PBC, got %r" % (s,)) - argvalues.append(values) + # the list of all possible tuples of arguments to give to the memo function - possiblevalues = cartesian_product(argvalues) + possiblevalues = cartesian_product([all_values(s_arg) for s_arg in args_s]) # a MemoTable factory -- one MemoTable per family of arguments that can # be called together, merged via a UnionFind. 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 @@ -2936,6 +2936,12 @@ self._pyobj(pyobject).ob_pypy_link = objint # there is no rrc_o_dict + def rawrefcount_mark_deallocating(self, gcobj, pyobject): + ll_assert(self.rrc_enabled, "rawrefcount.init not called") + obj = llmemory.cast_ptr_to_adr(gcobj) # should be a prebuilt obj + objint = llmemory.cast_adr_to_int(obj, "symbolic") + self._pyobj(pyobject).ob_pypy_link = objint + def rawrefcount_from_obj(self, gcobj): obj = llmemory.cast_ptr_to_adr(gcobj) if self.is_in_nursery(obj): 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 @@ -476,6 +476,10 @@ GCClass.rawrefcount_create_link_pyobj, [s_gc, s_gcref, SomeAddress()], annmodel.s_None) + self.rawrefcount_mark_deallocating = getfn( + GCClass.rawrefcount_mark_deallocating, + [s_gc, s_gcref, SomeAddress()], + annmodel.s_None) self.rawrefcount_from_obj_ptr = getfn( GCClass.rawrefcount_from_obj, [s_gc, s_gcref], SomeAddress(), inline = True) @@ -1281,6 +1285,14 @@ [self.rawrefcount_create_link_pyobj_ptr, self.c_const_gc, v_gcobj, v_pyobject]) + def gct_gc_rawrefcount_mark_deallocating(self, hop): + [v_gcobj, v_pyobject] = hop.spaceop.args + assert v_gcobj.concretetype == llmemory.GCREF + assert v_pyobject.concretetype == llmemory.Address + hop.genop("direct_call", + [self.rawrefcount_mark_deallocating, self.c_const_gc, + v_gcobj, v_pyobject]) + def gct_gc_rawrefcount_from_obj(self, hop): [v_gcobj] = hop.spaceop.args assert v_gcobj.concretetype == llmemory.GCREF diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -3,7 +3,7 @@ import py from rpython.rlib.nonconst import NonConstant -from rpython.rlib.objectmodel import CDefinedIntSymbolic, keepalive_until_here, specialize, not_rpython +from rpython.rlib.objectmodel import CDefinedIntSymbolic, keepalive_until_here, specialize, not_rpython, we_are_translated from rpython.rlib.unroll import unrolling_iterable from rpython.rtyper.extregistry import ExtRegistryEntry from rpython.tool.sourcetools import rpython_wrapper @@ -1221,7 +1221,8 @@ x = jit.conditional_call_elidable(self.cache, _compute_and_cache, ...) """ - if we_are_jitted(): + if we_are_translated() and we_are_jitted(): + #^^^ the occasional test patches we_are_jitted() to True return _jit_conditional_call_value(value, function, *args) else: if isinstance(value, int): diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -25,6 +25,10 @@ lltype.Unsigned, widen it to lltype.Signed. Useful because the translator doesn't support arithmetic on the smaller types. +ovfcheck_int32_add/sub/mul(x, y) + perform an add/sub/mul between two regular integers, + but assumes that they fit inside signed 32-bit ints + and raises OverflowError if the result no longer does These are meant to be erased by translation, r_uint in the process should mark unsigned values, ovfcheck should @@ -796,6 +800,47 @@ return longlong2float(rffi.cast(rffi.LONGLONG, res)) return rffi.cast(T, res) +if sys.maxint == 2147483647: + def ovfcheck_int32_add(x, y): + return ovfcheck(x + y) + def ovfcheck_int32_sub(x, y): + return ovfcheck(x - y) + def ovfcheck_int32_mul(x, y): + return ovfcheck(x * y) +else: + def ovfcheck_int32_add(x, y): + """x and y are assumed to fit inside the 32-bit rffi.INT; + raises OverflowError if the result doesn't fit rffi.INT""" + from rpython.rtyper.lltypesystem import lltype, rffi + x = rffi.cast(lltype.Signed, x) + y = rffi.cast(lltype.Signed, y) + z = x + y + if z != rffi.cast(lltype.Signed, rffi.cast(rffi.INT, z)): + raise OverflowError + return z + + def ovfcheck_int32_sub(x, y): + """x and y are assumed to fit inside the 32-bit rffi.INT; + raises OverflowError if the result doesn't fit rffi.INT""" + from rpython.rtyper.lltypesystem import lltype, rffi + x = rffi.cast(lltype.Signed, x) + y = rffi.cast(lltype.Signed, y) + z = x - y + if z != rffi.cast(lltype.Signed, rffi.cast(rffi.INT, z)): + raise OverflowError + return z + + def ovfcheck_int32_mul(x, y): + """x and y are assumed to fit inside the 32-bit rffi.INT; + raises OverflowError if the result doesn't fit rffi.INT""" + from rpython.rtyper.lltypesystem import lltype, rffi + x = rffi.cast(lltype.Signed, x) + y = rffi.cast(lltype.Signed, y) + z = x * y + if z != rffi.cast(lltype.Signed, rffi.cast(rffi.INT, z)): + raise OverflowError + return z + # String parsing support # --------------------------- diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -36,6 +36,7 @@ _pypy2ob = {} _pypy2ob_rev = {} _d_list = [] + _d_marker = None _dealloc_trigger_callback = dealloc_trigger_callback @not_rpython @@ -62,6 +63,14 @@ _o_list.append(ob) @not_rpython +def mark_deallocating(marker, ob): + """mark the PyObject as deallocating, by storing 'marker' + inside its ob_pypy_link field""" + assert ob._obj not in _pypy2ob_rev + assert not ob.c_ob_pypy_link + ob.c_ob_pypy_link = _build_pypy_link(marker) + +@not_rpython def from_obj(OB_PTR_TYPE, p): ob = _pypy2ob.get(p) if ob is None: @@ -221,7 +230,7 @@ class Entry(ExtRegistryEntry): - _about_ = (create_link_pypy, create_link_pyobj) + _about_ = (create_link_pypy, create_link_pyobj, mark_deallocating) def compute_result_annotation(self, s_p, s_ob): pass @@ -231,6 +240,8 @@ name = 'gc_rawrefcount_create_link_pypy' elif self.instance is create_link_pyobj: name = 'gc_rawrefcount_create_link_pyobj' + elif self.instance is mark_deallocating: + name = 'gc_rawrefcount_mark_deallocating' v_p, v_ob = hop.inputargs(*hop.args_r) hop.exception_cannot_occur() hop.genop(name, [_unspec_p(hop, v_p), _unspec_ob(hop, v_ob)]) diff --git a/rpython/rlib/rjitlog/rjitlog.py b/rpython/rlib/rjitlog/rjitlog.py --- a/rpython/rlib/rjitlog/rjitlog.py +++ b/rpython/rlib/rjitlog/rjitlog.py @@ -13,6 +13,7 @@ from rpython.jit.metainterp.resoperation import rop from rpython.jit.metainterp.history import ConstInt, ConstFloat from rpython.rlib.objectmodel import we_are_translated +from rpython.rlib.rarithmetic import r_longlong from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from rpython.rlib.objectmodel import compute_unique_id, always_inline from rpython.rlib.objectmodel import we_are_translated, specialize @@ -113,6 +114,7 @@ @always_inline def encode_le_64bit(val): + val = r_longlong(val) # force 64-bit, even on 32-bit return ''.join([chr((val >> 0) & 0xff), chr((val >> 8) & 0xff), chr((val >> 16) & 0xff), diff --git a/rpython/rlib/rurandom.py b/rpython/rlib/rurandom.py --- a/rpython/rlib/rurandom.py +++ b/rpython/rlib/rurandom.py @@ -75,16 +75,6 @@ return rffi.charpsize2str(rffi.cast(rffi.CCHARP, buf), n) -elif 0: # __VMS - from rpython.rlib.ropenssl import libssl_RAND_pseudo_bytes - def init_urandom(): - pass - - def urandom(context, n): - with rffi.scoped_alloc_buffer(n) as buf: - if libssl_RAND_pseudo_bytes(self.raw, n) < 0: - raise ValueError("RAND_pseudo_bytes") - return buf.str(n) else: # Posix implementation @not_rpython def init_urandom(): diff --git a/rpython/rlib/rvmprof/src/vmprof_getpc.h b/rpython/rlib/rvmprof/src/vmprof_getpc.h --- a/rpython/rlib/rvmprof/src/vmprof_getpc.h +++ b/rpython/rlib/rvmprof/src/vmprof_getpc.h @@ -126,13 +126,14 @@ // how we'd get the PC (using StackWalk64?) // http://msdn2.microsoft.com/en-us/library/ms680650.aspx -#include "base/logging.h" // for RAW_LOG -#ifndef HAVE_CYGWIN_SIGNAL_H -typedef int ucontext_t; -#endif +// #include "base/logging.h" // for RAW_LOG +// #ifndef HAVE_CYGWIN_SIGNAL_H +// typedef int ucontext_t; +// #endif intptr_t GetPC(ucontext_t *signal_ucontext) { - RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n"); + // RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n"); + fprintf(stderr, "GetPC is not yet implemented on Windows\n"); return NULL; } diff --git a/rpython/rlib/test/test_rarithmetic.py b/rpython/rlib/test/test_rarithmetic.py --- a/rpython/rlib/test/test_rarithmetic.py +++ b/rpython/rlib/test/test_rarithmetic.py @@ -675,3 +675,11 @@ assert a ^ r_uint32(42) == "a^42" assert r_uint32(42) ** a == "42**a" assert a ** r_uint32(42) == "a**42" + +def test_ovfcheck_int32(): + assert ovfcheck_int32_add(-2**30, -2**30) == -2**31 + py.test.raises(OverflowError, ovfcheck_int32_add, 2**30, 2**30) + assert ovfcheck_int32_sub(-2**30, 2**30) == -2**31 + py.test.raises(OverflowError, ovfcheck_int32_sub, 2**30, -2**30) + assert ovfcheck_int32_mul(-2**16, 2**15) == -2**31 + py.test.raises(OverflowError, ovfcheck_int32_mul, -2**16, -2**15) diff --git a/rpython/rlib/test/test_rawrefcount.py b/rpython/rlib/test/test_rawrefcount.py --- a/rpython/rlib/test/test_rawrefcount.py +++ b/rpython/rlib/test/test_rawrefcount.py @@ -212,6 +212,15 @@ assert rawrefcount.to_obj(W_Root, ob) == p lltype.free(ob, flavor='raw') + def test_mark_deallocating(self): + ob = lltype.malloc(PyObjectS, flavor='raw', zero=True) + w_marker = W_Root(42) + rawrefcount.mark_deallocating(w_marker, ob) + assert rawrefcount.to_obj(W_Root, ob) is w_marker + rawrefcount._collect() + assert rawrefcount.to_obj(W_Root, ob) is w_marker + lltype.free(ob, flavor='raw') + class TestTranslated(StandaloneTests): @@ -222,6 +231,7 @@ state.seen = [] def dealloc_trigger(): state.seen.append(1) + w_marker = W_Root(-1) def make_p(): p = W_Root(42) @@ -257,6 +267,13 @@ if rawrefcount.next_dead(PyObject) != lltype.nullptr(PyObjectS): print "NEXT_DEAD second time != NULL" return 1 + if rawrefcount.to_obj(W_Root, ob) is not None: + print "to_obj(dead) is not None?" + return 1 + rawrefcount.mark_deallocating(w_marker, ob) + if rawrefcount.to_obj(W_Root, ob) is not w_marker: + print "to_obj(marked-dead) is not w_marker" + return 1 print "OK!" lltype.free(ob, flavor='raw') return 0 diff --git a/rpython/rtyper/annlowlevel.py b/rpython/rtyper/annlowlevel.py --- a/rpython/rtyper/annlowlevel.py +++ b/rpython/rtyper/annlowlevel.py @@ -138,11 +138,12 @@ # get the graph of the mix-level helper ll_function and prepare it for # being annotated. Annotation and RTyping should be done in a single shot # at the end with finish(). - graph, args_s = self.rtyper.annotator.get_call_parameters( - ll_function, args_s, policy = self.policy) + ann = self.rtyper.annotator + with ann.using_policy(self.policy): + graph, args_s = ann.get_call_parameters(ll_function, args_s) for v_arg, s_arg in zip(graph.getargs(), args_s): - self.rtyper.annotator.setbinding(v_arg, s_arg) - self.rtyper.annotator.setbinding(graph.getreturnvar(), s_result) + ann.setbinding(v_arg, s_arg) + ann.setbinding(graph.getreturnvar(), s_result) #self.rtyper.annotator.annotated[graph.returnblock] = graph self.pending.append((ll_function, graph, args_s, s_result)) return graph @@ -224,16 +225,17 @@ bk = ann.bookkeeper translator = ann.translator original_graph_count = len(translator.graphs) - for ll_function, graph, args_s, s_result in self.pending: - # mark the return block as already annotated, because the return var - # annotation was forced in getgraph() above. This prevents temporary - # less general values reaching the return block from crashing the - # annotator (on the assert-that-new-binding-is-not-less-general). - ann.annotated[graph.returnblock] = graph - s_function = bk.immutablevalue(ll_function) - bk.emulate_pbc_call(graph, s_function, args_s) - self.newgraphs.add(graph) - ann.complete_helpers(self.policy) + with ann.using_policy(self.policy): + for ll_function, graph, args_s, s_result in self.pending: + # mark the return block as already annotated, because the return var + # annotation was forced in getgraph() above. This prevents temporary + # less general values reaching the return block from crashing the + # annotator (on the assert-that-new-binding-is-not-less-general). + ann.annotated[graph.returnblock] = graph + s_function = bk.immutablevalue(ll_function) + bk.emulate_pbc_call(graph, s_function, args_s) + self.newgraphs.add(graph) + ann.complete_helpers() for ll_function, graph, args_s, s_result in self.pending: s_real_result = ann.binding(graph.getreturnvar()) if s_real_result != s_result: diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -959,6 +959,9 @@ def op_gc_rawrefcount_create_link_pypy(self, *args): raise NotImplementedError("gc_rawrefcount_create_link_pypy") + def op_gc_rawrefcount_mark_deallocating(self, *args): + raise NotImplementedError("gc_rawrefcount_mark_deallocating") + def op_do_malloc_fixedsize(self): raise NotImplementedError("do_malloc_fixedsize") def op_do_malloc_fixedsize_clear(self): diff --git a/rpython/rtyper/lltypesystem/llmemory.py b/rpython/rtyper/lltypesystem/llmemory.py --- a/rpython/rtyper/lltypesystem/llmemory.py +++ b/rpython/rtyper/lltypesystem/llmemory.py @@ -384,7 +384,6 @@ def _sizeof_none(TYPE): assert not TYPE._is_varsize() return ItemOffset(TYPE) -_sizeof_none._annspecialcase_ = 'specialize:memo' @specialize.memo() def _internal_array_field(TYPE): diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -490,6 +490,7 @@ 'gc_rawrefcount_init': LLOp(), 'gc_rawrefcount_create_link_pypy': LLOp(), 'gc_rawrefcount_create_link_pyobj': LLOp(), + 'gc_rawrefcount_mark_deallocating': LLOp(), 'gc_rawrefcount_from_obj': LLOp(sideeffects=False), 'gc_rawrefcount_to_obj': LLOp(sideeffects=False), diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -7,7 +7,7 @@ from rpython.rtyper.lltypesystem.llmemory import itemoffsetof from rpython.rtyper.llannotation import lltype_to_annotation from rpython.tool.sourcetools import func_with_new_name -from rpython.rlib.objectmodel import Symbolic +from rpython.rlib.objectmodel import Symbolic, specialize from rpython.rlib.objectmodel import keepalive_until_here, enforceargs from rpython.rlib import rarithmetic, rgc from rpython.rtyper.extregistry import ExtRegistryEntry @@ -74,7 +74,7 @@ def llexternal(name, args, result, _callable=None, compilation_info=ExternalCompilationInfo(), sandboxsafe=False, releasegil='auto', - _nowrapper=False, calling_conv='c', + _nowrapper=False, calling_conv=None, elidable_function=False, macro=None, random_effects_on_gcobjs='auto', save_err=RFFI_ERR_NONE): @@ -96,7 +96,17 @@ we consider that the function is really short-running and don't bother releasing the GIL. An explicit True or False overrides this logic. + + calling_conv: if 'unknown' or 'win', the C function is not directly seen + by the JIT. If 'c', it can be seen (depending on + releasegil=False). For tests only, or if _nowrapper, + it defaults to 'c'. """ + if calling_conv is None: + if sys.platform == 'win32' and not _nowrapper: + calling_conv = 'unknown' + else: + calling_conv = 'c' if _callable is not None: assert callable(_callable) ext_type = lltype.FuncType(args, result) @@ -107,7 +117,8 @@ _callable = generate_macro_wrapper( name, macro, ext_type, compilation_info) else: - _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv) + _callable = ll2ctypes.LL2CtypesCallable(ext_type, + 'c' if calling_conv == 'unknown' else calling_conv) else: assert macro is None, "'macro' is useless if you specify '_callable'" if elidable_function: @@ -205,11 +216,17 @@ else: # if we don't have to invoke the GIL handling, we can just call # the low-level function pointer carelessly - if macro is None and save_err == RFFI_ERR_NONE: + # ...well, unless it's a macro, in which case we still have + # to hide it from the JIT... + need_wrapper = (macro is not None or save_err != RFFI_ERR_NONE) + # ...and unless we're on Windows and the calling convention is + # 'win' or 'unknown' + if calling_conv != 'c': + need_wrapper = True + # + if not need_wrapper: call_external_function = funcptr else: - # ...well, unless it's a macro, in which case we still have - # to hide it from the JIT... argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" def call_external_function(%(argnames)s): @@ -308,10 +325,6 @@ # for debugging, stick ll func ptr to that wrapper._ptr = funcptr wrapper = func_with_new_name(wrapper, name) - - if calling_conv != "c": - wrapper = jit.dont_look_inside(wrapper) - return wrapper @@ -1288,10 +1301,12 @@ c_memcpy = llexternal("memcpy", [VOIDP, VOIDP, SIZE_T], lltype.Void, - releasegil=False + releasegil=False, + calling_conv='c', ) c_memset = llexternal("memset", [VOIDP, lltype.Signed, SIZE_T], lltype.Void, - releasegil=False + releasegil=False, + calling_conv='c', ) diff --git a/rpython/rtyper/rpbc.py b/rpython/rtyper/rpbc.py --- a/rpython/rtyper/rpbc.py +++ b/rpython/rtyper/rpbc.py @@ -362,9 +362,9 @@ def get_concrete_llfn(self, s_pbc, args_s, op): bk = self.rtyper.annotator.bookkeeper funcdesc, = s_pbc.descriptions - args = simple_args(args_s) with bk.at_position(None): - graph = funcdesc.get_graph(args, op) + argspec = simple_args(args_s) + graph = funcdesc.get_graph(argspec, op) llfn = self.rtyper.getcallable(graph) return inputconst(typeOf(llfn), llfn) diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py --- a/rpython/rtyper/rtyper.py +++ b/rpython/rtyper/rtyper.py @@ -846,28 +846,29 @@ rtyper = self.rtyper args_s = [] newargs_v = [] - for v in args_v: - if v.concretetype is Void: - s_value = rtyper.annotation(v) - if s_value is None: - s_value = annmodel.s_None - if not s_value.is_constant(): - raise TyperError("non-constant variable of type Void") - if not isinstance(s_value, (annmodel.SomePBC, annmodel.SomeNone)): - raise TyperError("non-PBC Void argument: %r", (s_value,)) - args_s.append(s_value) - else: - args_s.append(lltype_to_annotation(v.concretetype)) - newargs_v.append(v) + with rtyper.annotator.using_policy(rtyper.lowlevel_ann_policy): + for v in args_v: + if v.concretetype is Void: + s_value = rtyper.annotation(v) + if s_value is None: + s_value = annmodel.s_None + if not s_value.is_constant(): + raise TyperError("non-constant variable of type Void") + if not isinstance(s_value, (annmodel.SomePBC, annmodel.SomeNone)): + raise TyperError("non-PBC Void argument: %r", (s_value,)) + args_s.append(s_value) + else: + args_s.append(lltype_to_annotation(v.concretetype)) + newargs_v.append(v) - self.rtyper.call_all_setups() # compute ForwardReferences now + self.rtyper.call_all_setups() # compute ForwardReferences now - # hack for bound methods - if hasattr(ll_function, 'im_func'): - bk = rtyper.annotator.bookkeeper - args_s.insert(0, bk.immutablevalue(ll_function.im_self)) - newargs_v.insert(0, inputconst(Void, ll_function.im_self)) - ll_function = ll_function.im_func + # hack for bound methods + if hasattr(ll_function, 'im_func'): + bk = rtyper.annotator.bookkeeper + args_s.insert(0, bk.immutablevalue(ll_function.im_self)) + newargs_v.insert(0, inputconst(Void, ll_function.im_self)) + ll_function = ll_function.im_func graph = annotate_lowlevel_helper(rtyper.annotator, ll_function, args_s, rtyper.lowlevel_ann_policy) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit