diff options
author | Antonio Cuni <anto.cuni@gmail.com> | 2021-01-21 14:06:19 +0000 |
---|---|---|
committer | Antonio Cuni <anto.cuni@gmail.com> | 2021-01-21 14:06:19 +0000 |
commit | 2c8fd1830d483511ddb3a85a8c60fd9bc4523a09 (patch) | |
tree | 498c6495330f13703a7fb1d427912a530a874466 /rpython | |
parent | merge, document py2.7-winreg which backports winreg changes from py3.7 (diff) | |
parent | improve the test to check that if we constant-fold the MyClass malloc(), ever... (diff) | |
download | pypy-2c8fd1830d483511ddb3a85a8c60fd9bc4523a09.tar.gz pypy-2c8fd1830d483511ddb3a85a8c60fd9bc4523a09.tar.bz2 pypy-2c8fd1830d483511ddb3a85a8c60fd9bc4523a09.zip |
Merge branch 'branch/rpython-never-allocate' into 'branch/default'
introduce a @never_allocate class decorator
See merge request pypy/pypy!764
Diffstat (limited to 'rpython')
-rw-r--r-- | rpython/memory/gctransform/transform.py | 16 | ||||
-rw-r--r-- | rpython/rlib/objectmodel.py | 10 | ||||
-rw-r--r-- | rpython/rlib/test/test_objectmodel.py | 37 | ||||
-rw-r--r-- | rpython/rtyper/rclass.py | 3 |
4 files changed, 63 insertions, 3 deletions
diff --git a/rpython/memory/gctransform/transform.py b/rpython/memory/gctransform/transform.py index d793f91ef2..d3dd64702f 100644 --- a/rpython/memory/gctransform/transform.py +++ b/rpython/memory/gctransform/transform.py @@ -19,6 +19,8 @@ from rpython.rtyper.lltypesystem.lloperation import llop from rpython.translator.simplify import cleanup_graph from rpython.memory.gctransform.log import log +class GCTransformError(Exception): + pass class GcHighLevelOp(object): def __init__(self, gct, op, index, llops): @@ -236,8 +238,12 @@ class BaseGCTransformer(object): inserted_empty_startblock = True is_borrowed = self.compute_borrowed_vars(graph) - for block in graph.iterblocks(): - self.transform_block(block, is_borrowed) + try: + for block in graph.iterblocks(): + self.transform_block(block, is_borrowed) + except GCTransformError as e: + e.args = ('[function %s]: %s' % (graph.name, e.message),) + raise for link, livecounts in self.links_to_split.iteritems(): llops = LowLevelOpList() @@ -519,6 +525,12 @@ class GCTransformer(BaseGCTransformer): def gct_malloc(self, hop, add_flags=None): TYPE = hop.spaceop.result.concretetype.TO + if TYPE._hints.get('never_allocate'): + raise GCTransformError( + "struct %s was marked as @never_allocate but a call to malloc() " + "was found. This probably means that the corresponding class is " + "supposed to be constant-folded away, but for some reason it was not." + % TYPE._name) assert not TYPE._is_varsize() flags = hop.spaceop.args[1].value flavor = flags['flavor'] diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py index 5faa6850fd..37ccdf9cb1 100644 --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -1067,3 +1067,13 @@ def import_from_mixin(M, special_methods=['__init__', '__del__']): target[key] = value if immutable_fields: target['_immutable_fields_'] = target.get('_immutable_fields_', []) + immutable_fields + +def never_allocate(cls): + """ + Class decorator to ensure that a class is NEVER instantiated at runtime. + + Useful e.g for context manager which are expected to be constant-folded + away. + """ + cls._rpython_never_allocate_ = True + return cls diff --git a/rpython/rlib/test/test_objectmodel.py b/rpython/rlib/test/test_objectmodel.py index 6bcb9d60c6..5f14528556 100644 --- a/rpython/rlib/test/test_objectmodel.py +++ b/rpython/rlib/test/test_objectmodel.py @@ -7,7 +7,8 @@ from rpython.rlib.objectmodel import ( resizelist_hint, is_annotation_constant, always_inline, NOT_CONSTANT, iterkeys_with_hash, iteritems_with_hash, contains_with_hash, setitem_with_hash, getitem_with_hash, delitem_with_hash, import_from_mixin, - fetch_translated_config, try_inline, delitem_if_value_is, move_to_end) + fetch_translated_config, try_inline, delitem_if_value_is, move_to_end, + never_allocate, dont_inline) from rpython.translator.translator import TranslationContext, graphof from rpython.rtyper.test.tool import BaseRtypingTest from rpython.rtyper.test.test_llinterp import interpret @@ -851,3 +852,37 @@ def test_import_from_mixin_immutable_fields(): import_from_mixin(C) assert BA._immutable_fields_ == ['c', 'a'] + + +def test_never_allocate(): + from rpython.translator.c.test.test_genc import compile as c_compile + from rpython.memory.gctransform.transform import GCTransformError + + @never_allocate + class MyClass(object): + def __init__(self, x): + self.x = x + 1 + + @dont_inline + def allocate_MyClass(x): + return MyClass(x) + + def f(x): + # this fails because the allocation of MyClass can't be + # constant-folded (because it's inside a @dont_inline function) + return allocate_MyClass(x).x + + def g(x): + # this works because MyClass is constant folded, so the GC transformer + # never sees a malloc(MyClass) + return MyClass(x).x + + # test what happens if MyClass escapes + with py.test.raises(GCTransformError) as exc: + c_compile(f, [int]) + assert '[function allocate_MyClass]' in str(exc) + assert 'was marked as @never_allocate' in str(exc) + + # test that it works in the "normal" case + compiled_g = c_compile(g, [int]) + assert compiled_g(41) == 42 diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py index 0d09123617..a98a04649b 100644 --- a/rpython/rtyper/rclass.py +++ b/rpython/rtyper/rclass.py @@ -527,6 +527,9 @@ class InstanceRepr(Repr): if hints is None: hints = {} hints = self._check_for_immutable_hints(hints) + if self.classdef.classdesc.get_param('_rpython_never_allocate_'): + hints['never_allocate'] = True + kwds = {} if self.gcflavor == 'gc': kwds['rtti'] = True |