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