Author: Richard Plangger <planri...@gmail.com>
Branch: gcstress-hypothesis
Changeset: r82980:2eb2117bac14
Date: 2016-03-11 17:04 +0100
http://bitbucket.org/pypy/pypy/changeset/2eb2117bac14/

Log:    finally a basic block of instructions can be generated, list
        strategy may not append some drawn values (critical here)

diff --git a/rpython/jit/backend/llsupport/tl/code.py 
b/rpython/jit/backend/llsupport/tl/code.py
--- a/rpython/jit/backend/llsupport/tl/code.py
+++ b/rpython/jit/backend/llsupport/tl/code.py
@@ -1,22 +1,34 @@
+import struct
 
-import struct
+from hypothesis.stateful import rule, precondition
 
 class ByteCode(object):
     def encode(self, ctx):
         ctx.append_byte(self.BYTE_CODE)
 
     @classmethod
-    def create_from(self, draw, get_strategy_for):
-        pt = getattr(self.__init__, '_param_types', [])
-        return self(*[draw(get_strategy_for(t)) for t in pt])
-
     def filter_bytecode(self, stack):
         """ filter this byte code if the stack does
             not contain the right values on the stack.
             This should only be used for values hypothesis
             cannot forsee (like list manipulation)
         """
-        return False
+        required_types = self._stack_types
+        if len(required_types) > stack.size():
+            # more needed types than available
+            return False
+        # each type should match the stack entry
+        for i in range(len(required_types)):
+            item = stack.peek(i)
+            j = len(required_types) - i - 1
+            rt = required_types[j]
+            if not item.is_of_type(rt):
+                return False
+        return True
+
+    def __repr__(self):
+        name = self.__class__.__name__
+        return name
 
 _c = 0
 
@@ -152,6 +164,7 @@
     @requires_param(BYTE_TYP)
     def __init__(self, size=8):
         self.size = size
+
     def encode(self, ctx):
         ctx.append_byte(self.BYTE_CODE)
         ctx.append_short(self.size)
@@ -162,13 +175,16 @@
     BYTE_CODE = unique_code()
     def __init__(self):
         pass
+    @classmethod
     def filter_bytecode(self, stack):
+        if not ByteCode.filter_bytecode.im_func(self, stack):
+            return False
         w_idx = stack.peek(1)
         w_list = stack.peek(2)
         if w_idx.value >= len(w_list.items) or \
            w_idx.value < 0:
-            return True
-        return False
+            return False
+        return True
 
 @requires_stack(LIST_TYP, IDX_TYP)
 @leaves_on_stack(LIST_TYP)
@@ -176,13 +192,16 @@
     BYTE_CODE = unique_code()
     def __init__(self):
         pass
+    @classmethod
     def filter_bytecode(self, stack):
+        if not ByteCode.filter_bytecode.im_func(self, stack):
+            return False
         w_idx = stack.peek(0)
         w_list = stack.peek(1)
         if w_idx.value >= len(w_list.items) or \
            w_idx.value < 0:
-            return True
-        return False
+            return False
+        return True
 
 @requires_stack(LIST_TYP, INT_TYP) # TODO VAL_TYP)
 @leaves_on_stack(LIST_TYP)
@@ -192,8 +211,18 @@
         pass
 
 def op_modifies_list(clazz):
+    """ NOT_RPYTHON """
     return clazz in (DelList, InsertList)
 
+BC_CLASSES = []
+BC_NUM_TO_CLASS = {}
+
+for name, clazz in locals().items():
+    if hasattr(clazz, 'BYTE_CODE'):
+        BC_CLASSES.append(clazz)
+        assert clazz.BYTE_CODE not in BC_NUM_TO_CLASS
+        BC_NUM_TO_CLASS[clazz.BYTE_CODE] = clazz
+
 # remove comment one by one!
 
 #@requires_stack()
diff --git a/rpython/jit/backend/llsupport/tl/stack.py 
b/rpython/jit/backend/llsupport/tl/stack.py
--- a/rpython/jit/backend/llsupport/tl/stack.py
+++ b/rpython/jit/backend/llsupport/tl/stack.py
@@ -48,7 +48,9 @@
         if stackpos < 0:
             raise IndexError
         self.stackpos = stackpos     # always store a known-nonneg integer here
-        return self.stack[stackpos]
+        elem = self.stack[stackpos]
+        self.stack[stackpos] = None
+        return elem
 
     def pick(self, i):
         n = self.stackpos - i - 1
@@ -91,6 +93,6 @@
 
     def __repr__(self):
         """ NOT_RPYTHON """
-        entry_types = [e.TYPE for e in self.stack]
+        entry_types = [e.TYPE for e in self.stack[:self.stackpos]]
         return "Stack(%s)" % ','.join(entry_types)
 
diff --git a/rpython/jit/backend/llsupport/tl/test/code_strategies.py 
b/rpython/jit/backend/llsupport/tl/test/code_strategies.py
--- a/rpython/jit/backend/llsupport/tl/test/code_strategies.py
+++ b/rpython/jit/backend/llsupport/tl/test/code_strategies.py
@@ -1,27 +1,24 @@
 from hypothesis import strategies as st
 from hypothesis.control import assume
-from hypothesis.strategies import defines_strategy, composite
+from hypothesis.strategies import composite
 from rpython.jit.backend.llsupport.tl import code, interp, stack
-from rpython.jit.backend.llsupport.tl.code import (all_types,
-        INT_TYP, STR_TYP, LIST_TYP, SHORT_TYP, BYTE_TYP,
-        COND_TYP, IDX_TYP)
-from hypothesis.searchstrategy.strategies import OneOfStrategy
-from hypothesis.searchstrategy.collections import TupleStrategy
+from hypothesis.searchstrategy.collections import TupleStrategy, ListStrategy
+import hypothesis.internal.conjecture.utils as cu
 
 def get_strategy_for(typ):
-    if typ == INT_TYP:
+    if typ == code.INT_TYP:
         return st.integers(min_value=-2**31, max_value=2**31-1)
-    elif typ == IDX_TYP:
+    elif typ == code.IDX_TYP:
         return st.integers(min_value=-2**31, max_value=2**31-1)
-    elif typ == SHORT_TYP:
+    elif typ == code.SHORT_TYP:
         return st.integers(min_value=-2**15, max_value=2**15-1)
-    elif typ == BYTE_TYP:
+    elif typ == code.BYTE_TYP:
         return st.integers(min_value=-2**7, max_value=2**7-1)
-    elif typ == COND_TYP:
+    elif typ == code.COND_TYP:
         return st.integers(min_value=0, max_value=4)
-    elif typ == STR_TYP:
+    elif typ == code.STR_TYP:
         return st.text().filter(lambda x: x is not None)
-    elif typ == LIST_TYP:
+    elif typ == code.LIST_TYP:
         # TODO recursive
         result = st.lists(elements=st.one_of(get_strategy_for('i')))
         return result.filter(lambda x: x is not None)
@@ -30,13 +27,10 @@
 
 STD_SPACE = interp.Space()
 
-@defines_strategy
-def stack_entry(types=all_types):
+def stack_entry(types=code.all_types):
     return st.one_of(*[get_strategy_for(t) for t in types])
 
-@defines_strategy
-def runtime_stack(min_size=0, average_size=5, max_size=4096,
-          types=all_types):
+def runtime_stack(min_size=0, average_size=5, max_size=4096, 
types=code.all_types):
     if max_size == 0:
         return st.just(stack.Stack(0))
     stack_entries = st.lists(stack_entry(all_types), min_size=min_size,
@@ -45,62 +39,86 @@
     return stack_entries.map(lambda elems: \
                                 stack.Stack.from_items(STD_SPACE, elems))
 
-def byte_code_classes():
-    for name, clazz in code.__dict__.items():
-        if hasattr(clazz, 'BYTE_CODE'):
-            yield clazz
-
 def get_byte_code_class(num):
-    for clazz in byte_code_classes():
-        if clazz.BYTE_CODE == num:
-            return clazz
-    return None
+    return code.BC_NUM_TO_CLASS[num]
 
 def find_next(stack, type, off=0):
     i = off
     while i < stack.size():
-        if stack.peek(i).is_of_type(LIST_TYP):
+        if stack.peek(i).is_of_type(type):
             break
         i += 1
     else:
         return None
     return stack.peek(i)
 
-@defines_strategy
+class BasicBlockStrategy(ListStrategy):
+    """ Generates a list of values, but does not throw away elements.
+        See XXX """
+
+    def do_draw(self, data):
+        if self.max_size == self.min_size:
+            return [
+                data.draw(self.element_strategy)
+                for _ in range(self.min_size)
+            ]
+
+        stopping_value = 1 - 1.0 / (1 + self.average_length)
+        result = []
+        while True:
+            data.start_example()
+            more = cu.biased_coin(data, stopping_value)
+            value = data.draw(self.element_strategy)
+            data.stop_example()
+            if not more:
+                if len(result) < self.min_size:
+                    # XXX if not appended the resulting list will have
+                    # a bigger stack but a missing op code
+                    result.append(value)
+                    continue
+                else:
+                    break
+            result.append(value)
+        if self.max_size < float('inf'):
+            result = result[:self.max_size]
+        return result
+
+    def __repr__(self):
+        return (
+            'BasicBlockStrategy(%r, min_size=%r, average_size=%r, max_size=%r)'
+        ) % (
+            self.element_strategy, self.min_size, self.average_length,
+            self.max_size
+        )
+
+@st.defines_strategy
+def basic_block(strategy, min_size=1, average_size=8, max_size=128):
+    return BasicBlockStrategy([strategy], min_size=min_size,
+                              average_length=average_size,
+                              max_size=max_size)
+
+@st.defines_strategy
 def bytecode_class(stack):
-    def filter_using_stack(bytecode_class):
-        required_types = bytecode_class._stack_types
-        if len(required_types) > stack.size():
-            return False
-        for i in range(len(required_types)):
-            item = stack.peek(i)
-            j = len(required_types) - i - 1
-            rt = required_types[j]
-            if not item.is_of_type(rt):
-                return False
-        if code.op_modifies_list(bytecode_class):
-            w_list = find_next(stack, LIST_TYP)
-            if w_list is None or len(w_list.items) == 0:
-                # on an empty list we cannot insert or delete
-                return False
-        return True
-    clazzes = filter(filter_using_stack, byte_code_classes())
-    return st.sampled_from(clazzes)
+    # get a byte code class, only allow what is valid for the run_stack
+    return st.sampled_from(code.BC_CLASSES).filter(lambda clazz: 
clazz.filter_bytecode(stack))
+
 
 @composite
 def bytecode(draw, max_stack_size=4096):
     # get a stack that is the same for one test run
-    stack_strat = runtime_stack(max_size=max_stack_size)
-    run_stack = draw(st.shared(stack_strat, 'stack'))
+    run_stack = draw(st.shared(st.just(stack.Stack(0)), 'stack2'))
+
+    # get a byte code class, only allow what is valid for the run_stack
+    clazz = draw(st.sampled_from(code.BC_CLASSES).filter(lambda clazz: 
clazz.filter_bytecode(run_stack)))
+
+    # create an instance of the chosen class
+    pt = getattr(clazz.__init__, '_param_types', [])
+    args = [draw(get_strategy_for(t)) for t in pt]
+    inst = clazz(*args)
+
     # propagate the changes to the stack
-    orig_stack = run_stack.copy(values=True)
-    assert orig_stack is not run_stack
+    bytecode, consts = code.Context().transform([inst])
+    interp.dispatch_once(STD_SPACE, 0, bytecode, consts, run_stack)
 
-    # get a byte code class
-    clazz = draw(bytecode_class(run_stack))
-    inst = clazz.create_from(draw, get_strategy_for)
-    assume(not inst.filter_bytecode(run_stack))
-    bytecode, consts = code.Context().transform([inst])
+    return inst
 
-    interp.dispatch_once(STD_SPACE, 0, bytecode, consts, run_stack)
-    return inst, orig_stack
diff --git a/rpython/jit/backend/llsupport/tl/test/test_tl_interp.py 
b/rpython/jit/backend/llsupport/tl/test/test_tl_interp.py
--- a/rpython/jit/backend/llsupport/tl/test/test_tl_interp.py
+++ b/rpython/jit/backend/llsupport/tl/test/test_tl_interp.py
@@ -1,5 +1,5 @@
 import py
-from hypothesis import given
+from hypothesis import given, settings, Verbosity
 from hypothesis.strategies import lists, data
 from rpython.jit.backend.llsupport.tl import code, interp
 from rpython.jit.backend.llsupport.tl.stack import Stack
@@ -23,7 +23,6 @@
         assert c.get_short(3) == 1
 
 class TestCodeStrategies(object):
-
     DEFAULT_ACTION_CLASSES = (code.CreateList, code.PutInt,
          code.LoadStr)
 
@@ -84,29 +83,25 @@
             assert(clazz in self.DEFAULT_ACTION_CLASSES + \
                             (code.InsertList, code.AppendList))
 
+    @given(data())
+    def test_empty_stack_no_list_op(self, data):
+        space = interp.Space()
+        stack = Stack(0)
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert not (clazz in (code.DelList, code.InsertList,
+                                    code.AppendList, code.AddList,
+                                    code.AddStr))
 
 class TestInterp(object):
-    @given(st.bytecode())
-    def test_consume_stack(self, args):
-        bc_obj, stack = args
-        bytecode, consts = code.Context().transform([bc_obj])
-        space = interp.Space()
-        i = interp.dispatch_once(space, 0, bytecode, consts, stack)
-        assert i == len(bytecode)
-        clazz = st.get_byte_code_class(ord(bytecode[0]))
-        assert stack.size() >= len(clazz._return_on_stack_types)
-        for i,type in enumerate(clazz._return_on_stack_types):
-            j = len(clazz._return_on_stack_types) - i - 1
-            assert stack.peek(j).is_of_type(type)
 
-    @given(lists(st.bytecode(max_stack_size=0), min_size=1))
-    def test_execute_bytecode_block(self, codes):
-        bc_obj_list = [bc for bc,stack in codes]
-        _, stack = codes[0]
+    @given(st.basic_block(st.bytecode(), min_size=1))
+    def test_execute_bytecode_block(self, bc_obj_list):
         bytecode, consts = code.Context().transform(bc_obj_list)
         space = interp.Space()
         pc = 0
         end = len(bytecode)
+        stack = Stack(0)
         while pc < end:
             pc = interp.dispatch_once(space, pc, bytecode, consts, stack)
         assert pc == len(bytecode)
diff --git a/rpython/jit/backend/llsupport/tl/test/zrpy_gc_hypo_test.py 
b/rpython/jit/backend/llsupport/tl/test/zrpy_gc_hypo_test.py
--- a/rpython/jit/backend/llsupport/tl/test/zrpy_gc_hypo_test.py
+++ b/rpython/jit/backend/llsupport/tl/test/zrpy_gc_hypo_test.py
@@ -52,10 +52,8 @@
         return res.returncode, res.out, res.err
 
     # cannot have a non empty stack, cannot pass stack to executable!
-    @given(st.bytecode(max_stack_size=0))
-    def test_execute_single_bytecode(self, program):
-        bc_obj, stack = program
-        assert stack.size() == 0
+    @given(st.bytecode())
+    def test_execute_single_bytecode(self, bc_obj):
         bytecode, consts = code.Context().transform([bc_obj])
         result, out, err = self.execute(bytecode, consts)
         if result != 0:
@@ -63,11 +61,8 @@
                             " stderr:\n%s\nstdout:\n%s\n") % (result, err, 
out))
 
     # cannot have a non empty stack, cannot pass stack to executable!
-    @given(lists(st.bytecode(max_stack_size=0), min_size=1, average_size=24))
-    def test_execute_bytecodes(self, args):
-        _, stack = args[0]
-        assert stack.size() == 0
-        bc_objs = [bc for bc, _ in args]
+    @given(st.basic_block(st.bytecode(), min_size=1, average_size=24))
+    def test_execute_basic_block(self, bc_objs):
         bytecode, consts = code.Context().transform(bc_objs)
         result, out, err = self.execute(bytecode, consts)
         if result != 0:
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to