Author: Richard Plangger <[email protected]>
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
+ )
+
[email protected]_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)
+
[email protected]_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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit