aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Cuni <anto.cuni@gmail.com>2021-01-21 14:06:19 +0000
committerAntonio Cuni <anto.cuni@gmail.com>2021-01-21 14:06:19 +0000
commit2c8fd1830d483511ddb3a85a8c60fd9bc4523a09 (patch)
tree498c6495330f13703a7fb1d427912a530a874466 /rpython
parentmerge, document py2.7-winreg which backports winreg changes from py3.7 (diff)
parentimprove the test to check that if we constant-fold the MyClass malloc(), ever... (diff)
downloadpypy-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.py16
-rw-r--r--rpython/rlib/objectmodel.py10
-rw-r--r--rpython/rlib/test/test_objectmodel.py37
-rw-r--r--rpython/rtyper/rclass.py3
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