[pypy-commit] pypy gcstress-hypothesis: added some tests to ensure bytecode generation considers the top elements on the stack

plan_rich pypy.commits at gmail.com
Fri Mar 4 09:59:09 EST 2016


Author: Richard Plangger <planrichi at gmail.com>
Branch: gcstress-hypothesis
Changeset: r82769:13234c967ffc
Date: 2016-03-04 10:40 +0100
http://bitbucket.org/pypy/pypy/changeset/13234c967ffc/

Log:	added some tests to ensure bytecode generation considers the top
	elements on the stack

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
@@ -10,6 +10,14 @@
         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
+
 _c = 0
 
 LIST_TYP = 'l'
@@ -154,6 +162,12 @@
     BYTE_CODE = unique_code()
     def __init__(self):
         pass
+    def filter_bytecode(self, stack):
+        w_idx = stack.peek(1)
+        w_list = stack.peek(2)
+        if w_idx.value >= len(w_list.items):
+            return True
+        return False
 
 @requires_stack(LIST_TYP, IDX_TYP)
 @leaves_on_stack(LIST_TYP)
@@ -161,6 +175,12 @@
     BYTE_CODE = unique_code()
     def __init__(self):
         pass
+    def filter_bytecode(self, stack):
+        w_idx = stack.peek(0)
+        w_list = stack.peek(1)
+        if w_idx.value >= len(w_list.items):
+            return True
+        return False
 
 @requires_stack(LIST_TYP, INT_TYP) # TODO VAL_TYP)
 @leaves_on_stack(LIST_TYP)
@@ -169,6 +189,8 @@
     def __init__(self):
         pass
 
+def op_modifies_list(clazz):
+    return clazz in (DelList, InsertList)
 
 # remove comment one by one!
 
diff --git a/rpython/jit/backend/llsupport/tl/interp.py b/rpython/jit/backend/llsupport/tl/interp.py
--- a/rpython/jit/backend/llsupport/tl/interp.py
+++ b/rpython/jit/backend/llsupport/tl/interp.py
@@ -8,6 +8,8 @@
     pass
 
 class W_ListObject(W_Root):
+    TYPE = code.LIST_TYP
+
     def __init__(self, items):
         self.items = items
 
@@ -17,9 +19,11 @@
 
     def is_of_type(self, type):
         """ NOT_RPYTHON """
-        return type in (LIST_TYP,)
+        return type in (code.LIST_TYP,)
 
 class W_IntObject(W_Root):
+    TYPE = code.INT_TYP
+
     def __init__(self, value):
         self.value = value
 
@@ -37,6 +41,8 @@
                         code.BYTE_TYP)
 
 class W_StrObject(W_Root):
+    TYPE = code.STR_TYP
+
     def __init__(self, value):
         self.value = value
 
@@ -79,8 +85,6 @@
 def entry_point(argv):
     bytecode = _read_bytecode_from_file(argv[1])
     consts = _read_consts_from_file(argv[2])
-    print(bytecode)
-    print(consts)
     pc = 0
     end = len(bytecode)
     stack = Stack(16)
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
@@ -19,6 +19,13 @@
     def size(self):
         return self.stackpos
 
+    def copy(self):
+        """ NOT_RPYTHON """
+        copy = Stack(self.size())
+        for item in self.stack:
+            copy.append(item)
+        return copy
+
     def append(self, elem):
         while len(self.stack) <= self.stackpos:
             self.stack.append(None)
@@ -72,3 +79,9 @@
             n = self.stackpos - 1
             assert n >= 0
             self.stack[n] = elem
+
+    def __repr__(self):
+        """ NOT_RPYTHON """
+        entry_types = [e.TYPE for e in self.stack]
+        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
@@ -28,29 +28,15 @@
 
 STD_SPACE = interp.Space()
 
-#@composite
-#def runtime_stack(draw, clazz):
-#    strats = [get_strategy_for(t) for t in clazz._stack_types]
-#    stack_obj = stack.Stack(len(strats))
-#    for i,strat in enumerate(strats):
-#        if clazz._stack_types[i] == IDX_TYP:
-#            # it is only valid to access a list with a valid index!
-#            w_list = stack_obj.peek(i-1)
-#            l = len(w_list.items)
-#            assume(l > 0)
-#            integrals = st.integers(min_value=0, max_value=l-1)
-#            stack_obj.append(STD_SPACE.wrap(draw(integrals)))
-#            continue
-#        stack_obj.append(STD_SPACE.wrap(draw(strat)))
-#    return stack_obj
-
 @defines_strategy
 def stack_entry(types=all_types):
-    return st.sampled_from([get_strategy_for(t) for t in 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):
+    if max_size < average_size:
+        average_size = max_size // 2
     stack_entries = st.lists(stack_entry(all_types), min_size,
                              average_size, max_size)
     return stack_entries.map(lambda elems: \
@@ -67,30 +53,50 @@
             return clazz
     return None
 
+def find_next(stack, type, off=0):
+    i = off
+    while i < stack.size():
+        if stack.peek(i).is_of_type(LIST_TYP):
+            break
+        i += 1
+    else:
+        return None
+    return stack.peek(i)
 
 @defines_strategy
 def bytecode_class(stack):
     def filter_using_stack(bytecode_class):
-        required_types = bytecode_class.requires_stack
-        if len(required_types) < stack.size():
+        required_types = bytecode_class._stack_types
+        if len(required_types) > stack.size():
             return False
-        j = len(required_types)-1
-        for i in range(stack.size()):
+        for i in range(len(required_types)):
             item = stack.peek(i)
-            if not item.is_of_type(required_types[j]):
+            j = len(required_types) - i - 1
+            rt = required_types[j]
+            if not item.is_of_type(rt):
                 return False
-            j -= 1
-            if j < 0:
-                break
+        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
-    return st.sampled_from(byte_code_classes()).filter(filter_using_stack)
+    clazzes = filter(filter_using_stack, byte_code_classes())
+    return st.sampled_from(clazzes)
 
 @composite
 def bytecode(draw, max_stack_size=4096):
     # get a stack that is the same for one test run
-    rs = runtime_stack(max_size=max_stack_size)
-    stack = draw(st.shared(rs, 'stack'))
-    clazz = draw(bytecode_class(stack))
+    stack_strat = runtime_stack(max_size=max_stack_size)
+    run_stack = draw(st.shared(stack_strat, '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 bytecode, consts, stack
+
+    # propagate the changes to the stack
+    orig_stack = run_stack.copy()
+    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,6 +1,6 @@
 import py
 from hypothesis import given
-from hypothesis.strategies import lists
+from hypothesis.strategies import lists, data
 from rpython.jit.backend.llsupport.tl import code, interp
 from rpython.jit.backend.llsupport.tl.stack import Stack
 from rpython.jit.backend.llsupport.tl.test import code_strategies as st
@@ -22,19 +22,88 @@
         assert c.get_byte(4) == code.AddStr.BYTE_CODE
         assert c.get_short(3) == 1
 
+class TestCodeStrategies(object):
+
+    DEFAULT_ACTION_CLASSES = (code.CreateList, code.PutInt,
+         code.LoadStr)
+
+    @given(data())
+    def test_bytecode_class_generation(self, data):
+        space = interp.Space()
+        stack = Stack(0)
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES)
+
+    @given(data())
+    def test_bytecode_class_generation_int(self, data):
+        space = interp.Space()
+        stack = Stack(0)
+        stack.append(space.wrap(0))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES)
+        stack.append(space.wrap(0))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES + \
+                            (code.CompareInt,))
+
+    @given(data())
+    def test_bytecode_class_generation_str(self, data):
+        space = interp.Space()
+        stack = Stack(0)
+        stack.append(space.wrap("hello"))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES)
+        stack.append(space.wrap("world"))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES + \
+                            (code.AddStr,))
+
+    @given(data())
+    def test_bytecode_class_generation_list(self, data):
+        space = interp.Space()
+        stack = Stack(0)
+        stack.append(space.wrap([]))
+        stack.append(space.wrap(0))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz not in (code.InsertList, code.DelList))
+        stack.append(space.wrap([space.wrap(1)]))
+        stack.append(space.wrap(0))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES + \
+                            (code.DelList, code.AppendList))
+        stack.append(space.wrap("haskell"))
+        for i in range(10):
+            clazz = data.draw(st.bytecode_class(stack))
+            assert(clazz in self.DEFAULT_ACTION_CLASSES + \
+                            (code.InsertList, code.AppendList))
+
+
 class TestInterp(object):
     @given(st.bytecode())
     def test_consume_stack(self, args):
-        bytecode, consts, stack = 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)
+        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)))
-    def test_execute_bytecode_block(self, args):
-        bytecode, consts, _ = args
+    @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]
+        bytecode, consts = code.Context().transform(bc_obj_list)
         space = interp.Space()
         stack = Stack(16)
         pc = 0


More information about the pypy-commit mailing list