[pypy-commit] pypy stacklet: In-progress: kill the stackless transformer and the llops depending on it.
arigo
noreply at buildbot.pypy.org
Sat Aug 6 12:31:12 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46307:7fb6f7a2a34a
Date: 2011-08-06 09:34 +0200
http://bitbucket.org/pypy/pypy/changeset/7fb6f7a2a34a/
Log: In-progress: kill the stackless transformer and the llops depending
on it.
diff --git a/pypy/rlib/rstack.py b/pypy/rlib/rstack.py
--- a/pypy/rlib/rstack.py
+++ b/pypy/rlib/rstack.py
@@ -14,25 +14,6 @@
from pypy.rpython.controllerentry import Controller, SomeControlledInstance
from pypy.translator.tool.cbuild import ExternalCompilationInfo
-def stack_unwind():
- if we_are_translated():
- return llop.stack_unwind(lltype.Void)
- raise RuntimeError("cannot unwind stack in non-translated versions")
-
-
-def stack_capture():
- if we_are_translated():
- ptr = llop.stack_capture(OPAQUE_STATE_HEADER_PTR)
- return frame_stack_top_controller.box(ptr)
- raise RuntimeError("cannot unwind stack in non-translated versions")
-
-
-def stack_frames_depth():
- if we_are_translated():
- return llop.stack_frames_depth(lltype.Signed)
- else:
- return len(inspect.stack())
-
# ____________________________________________________________
compilation_info = ExternalCompilationInfo(includes=['src/stack.h'])
@@ -88,78 +69,6 @@
@rgc.no_collect
def stack_check_slowpath(current):
if ord(_stack_too_big_slowpath(current)):
- # Now we are sure that the stack is really too big. Note that the
- # stack_unwind implementation is different depending on if stackless
- # is enabled. If it is it unwinds the stack, otherwise it simply
- # raises a RuntimeError.
- stack_unwind()
+ from pypy.rlib.rstackovf import _StackOverflow
+ raise _StackOverflow
stack_check_slowpath._dont_inline_ = True
-
-# ____________________________________________________________
-
-def yield_current_frame_to_caller():
- raise NotImplementedError("only works in translated versions")
-
-
-class frame_stack_top(object):
- def switch(self):
- raise NotImplementedError("only works in translated versions")
-
-
-class BoundSwitchOfFrameStackTop(object): pass
-class BoundSwitchOfFrameStackTopController(Controller):
- knowntype = BoundSwitchOfFrameStackTop
- def call(self, real_object):
- from pypy.rpython.lltypesystem.lloperation import llop
- ptr = llop.stack_switch(OPAQUE_STATE_HEADER_PTR, real_object)
- return frame_stack_top_controller.box(ptr)
-
-
-class FrameStackTopController(Controller):
- knowntype = frame_stack_top
- can_be_None = True
-
- def is_true(self, real_object):
- return bool(real_object)
-
- def get_switch(self, real_object):
- return bound_switch_of_frame_stack_top_controller.box(real_object)
-
- def convert(self, obj):
- assert obj is None
- return lltype.nullptr(OPAQUE_STATE_HEADER_PTR.TO)
-
-frame_stack_top_controller = FrameStackTopController()
-bound_switch_of_frame_stack_top_controller = BoundSwitchOfFrameStackTopController()
-OPAQUE_STATE_HEADER = lltype.GcOpaqueType("OPAQUE_STATE_HEADER", hints={"render_structure": True})
-OPAQUE_STATE_HEADER_PTR = lltype.Ptr(OPAQUE_STATE_HEADER)
-
-
-
-class FrameStackTopReturningFnEntry(ExtRegistryEntry):
- def compute_result_annotation(self):
- from pypy.annotation import model as annmodel
- return SomeControlledInstance(annmodel.lltype_to_annotation(OPAQUE_STATE_HEADER_PTR), frame_stack_top_controller)
-
-
-class YieldCurrentFrameToCallerFnEntry(FrameStackTopReturningFnEntry):
- _about_ = yield_current_frame_to_caller
-
- def specialize_call(self, hop):
- var = hop.genop("yield_current_frame_to_caller", [], hop.r_result.lowleveltype)
- return var
-
-
-# ____________________________________________________________
-
-def get_stack_depth_limit():
- if we_are_translated():
- from pypy.rpython.lltypesystem.lloperation import llop
- return llop.get_stack_depth_limit(lltype.Signed)
- raise RuntimeError("no stack depth limit in non-translated versions")
-
-def set_stack_depth_limit(limit):
- if we_are_translated():
- from pypy.rpython.lltypesystem.lloperation import llop
- return llop.set_stack_depth_limit(lltype.Void, limit)
- raise RuntimeError("no stack depth limit in non-translated versions")
diff --git a/pypy/rpython/llinterp.py b/pypy/rpython/llinterp.py
--- a/pypy/rpython/llinterp.py
+++ b/pypy/rpython/llinterp.py
@@ -675,21 +675,6 @@
#log.warn("op_indirect_call with graphs=None:", f)
return self.op_direct_call(f, *args)
- def op_adr_call(self, TGT, f, *inargs):
- checkadr(f)
- obj = self.llinterpreter.typer.type_system.deref(f.ref())
- assert hasattr(obj, 'graph') # don't want to think about that
- graph = obj.graph
- args = []
- for inarg, arg in zip(inargs, obj.graph.startblock.inputargs):
- args.append(lltype._cast_whatever(arg.concretetype, inarg))
- frame = self.newsubframe(graph, args)
- result = frame.eval()
- from pypy.translator.stackless.frame import storage_type
- assert storage_type(lltype.typeOf(result)) == TGT
- return lltype._cast_whatever(TGT, result)
- op_adr_call.need_result_type = True
-
def op_malloc(self, obj, flags):
flavor = flags['flavor']
zero = flags.get('zero', False)
@@ -930,27 +915,6 @@
def op_get_write_barrier_from_array_failing_case(self):
raise NotImplementedError("get_write_barrier_from_array_failing_case")
- def op_yield_current_frame_to_caller(self):
- raise NotImplementedError("yield_current_frame_to_caller")
-
- def op_stack_frames_depth(self):
- return len(self.llinterpreter.frame_stack)
-
- def op_stack_switch(self, frametop):
- raise NotImplementedError("stack_switch")
-
- def op_stack_unwind(self):
- raise NotImplementedError("stack_unwind")
-
- def op_stack_capture(self):
- raise NotImplementedError("stack_capture")
-
- def op_get_stack_depth_limit(self):
- raise NotImplementedError("get_stack_depth_limit")
-
- def op_set_stack_depth_limit(self):
- raise NotImplementedError("set_stack_depth_limit")
-
def op_stack_current(self):
return 0
@@ -1131,16 +1095,6 @@
assert isinstance(x, (int, Symbolic))
return bool(x)
- # read frame var support
-
- def op_get_frame_base(self):
- self._obj0 = self # hack
- return llmemory.fakeaddress(self)
-
- def op_frame_info(self, *vars):
- pass
- op_frame_info.specialform = True
-
# hack for jit.codegen.llgraph
def op_check_and_clear_exc(self):
diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -9,7 +9,7 @@
class LLOp(object):
def __init__(self, sideeffects=True, canfold=False, canraise=(),
- pyobj=False, canunwindgc=False, canrun=False, oo=False,
+ pyobj=False, canmallocgc=False, canrun=False, oo=False,
tryfold=False):
# self.opname = ... (set afterwards)
@@ -36,12 +36,12 @@
# The operation manipulates PyObjects
self.pyobj = pyobj
- # The operation can unwind the stack in a stackless gc build
- self.canunwindgc = canunwindgc
- if canunwindgc:
- if (StackException not in self.canraise and
+ # The operation can go a GC malloc
+ self.canmallocgc = canmallocgc
+ if canmallocgc:
+ if (MemoryError not in self.canraise and
Exception not in self.canraise):
- self.canraise += (StackException,)
+ self.canraise += (MemoryError,)
# The operation can be run directly with __call__
self.canrun = canrun or canfold
@@ -175,10 +175,6 @@
return hop.genop(op.opname, args_v, resulttype=hop.r_result.lowleveltype)
-class StackException(Exception):
- """Base for internal exceptions possibly used by the stackless
- implementation."""
-
# ____________________________________________________________
#
# This list corresponds to the operations implemented by the LLInterpreter.
@@ -356,10 +352,10 @@
# __________ pointer operations __________
- 'malloc': LLOp(canraise=(MemoryError,), canunwindgc=True),
- 'malloc_varsize': LLOp(canraise=(MemoryError,), canunwindgc=True),
- 'malloc_nonmovable': LLOp(canraise=(MemoryError,), canunwindgc=True),
- 'malloc_nonmovable_varsize':LLOp(canraise=(MemoryError,),canunwindgc=True),
+ 'malloc': LLOp(canmallocgc=True),
+ 'malloc_varsize': LLOp(canmallocgc=True),
+ 'malloc_nonmovable': LLOp(canmallocgc=True),
+ 'malloc_nonmovable_varsize':LLOp(canmallocgc=True),
'shrink_array': LLOp(canrun=True),
'zero_gc_pointers_inside': LLOp(),
'free': LLOp(),
@@ -414,7 +410,6 @@
'adr_ne': LLOp(canfold=True),
'adr_gt': LLOp(canfold=True),
'adr_ge': LLOp(canfold=True),
- 'adr_call': LLOp(canraise=(Exception,)),
'cast_ptr_to_adr': LLOp(sideeffects=False),
'cast_adr_to_ptr': LLOp(canfold=True),
'cast_adr_to_int': LLOp(sideeffects=False),
@@ -436,8 +431,8 @@
'jit_force_quasi_immutable': LLOp(canrun=True),
'get_exception_addr': LLOp(),
'get_exc_value_addr': LLOp(),
- 'do_malloc_fixedsize_clear':LLOp(canraise=(MemoryError,),canunwindgc=True),
- 'do_malloc_varsize_clear': LLOp(canraise=(MemoryError,),canunwindgc=True),
+ 'do_malloc_fixedsize_clear':LLOp(canmallocgc=True),
+ 'do_malloc_varsize_clear': LLOp(canmallocgc=True),
'get_write_barrier_failing_case': LLOp(sideeffects=False),
'get_write_barrier_from_array_failing_case': LLOp(sideeffects=False),
'gc_get_type_info_group': LLOp(sideeffects=False),
@@ -445,7 +440,7 @@
# __________ GC operations __________
- 'gc__collect': LLOp(canunwindgc=True),
+ 'gc__collect': LLOp(canmallocgc=True),
'gc_free': LLOp(),
'gc_fetch_exception': LLOp(),
'gc_restore_exception': LLOp(),
@@ -455,17 +450,12 @@
'gc_pop_alive_pyobj': LLOp(),
'gc_reload_possibly_moved': LLOp(),
# see rlib/objectmodel for gc_identityhash and gc_id
- 'gc_identityhash': LLOp(canraise=(MemoryError,), sideeffects=False,
- canunwindgc=True),
- 'gc_id': LLOp(canraise=(MemoryError,), sideeffects=False),
- # ^^^ but canunwindgc=False, as it is
- # allocating non-GC structures only
+ 'gc_identityhash': LLOp(sideeffects=False, canmallocgc=True),
+ 'gc_id': LLOp(sideeffects=False, canmallocgc=True),
'gc_obtain_free_space': LLOp(),
'gc_set_max_heap_size': LLOp(),
'gc_can_move' : LLOp(sideeffects=False),
- 'gc_thread_prepare' : LLOp(canraise=(MemoryError,)),
- # ^^^ but canunwindgc=False, as it is
- # allocating non-GC structures only
+ 'gc_thread_prepare' : LLOp(canmallocgc=True),
'gc_thread_run' : LLOp(),
'gc_thread_start' : LLOp(),
'gc_thread_die' : LLOp(),
@@ -473,7 +463,7 @@
'gc_thread_after_fork': LLOp(), # arguments: (result_of_fork, opaqueaddr)
'gc_assume_young_pointers': LLOp(canrun=True),
'gc_writebarrier_before_copy': LLOp(canrun=True),
- 'gc_heap_stats' : LLOp(canunwindgc=True),
+ 'gc_heap_stats' : LLOp(canmallocgc=True),
'gc_get_rpy_roots' : LLOp(),
'gc_get_rpy_referents': LLOp(),
@@ -494,9 +484,8 @@
# experimental operations in support of thread cloning, only
# implemented by the Mark&Sweep GC
- 'gc_x_swap_pool': LLOp(canraise=(MemoryError,), canunwindgc=True),
- 'gc_x_clone': LLOp(canraise=(MemoryError, RuntimeError),
- canunwindgc=True),
+ 'gc_x_swap_pool': LLOp(canmallocgc=True),
+ 'gc_x_clone': LLOp(canraise=(RuntimeError,), canmallocgc=True),
'gc_x_size_header': LLOp(),
# for asmgcroot support to get the address of various static structures
@@ -504,35 +493,19 @@
'gc_asmgcroot_static': LLOp(sideeffects=False),
'gc_stack_bottom': LLOp(canrun=True),
- # NOTE NOTE NOTE! don't forget *** canunwindgc=True *** for anything that
- # can go through a stack unwind, in particular anything that mallocs!
+ # NOTE NOTE NOTE! don't forget *** canmallocgc=True *** for anything that
+ # can malloc a GC object.
# __________ weakrefs __________
- 'weakref_create': LLOp(canraise=(MemoryError,), sideeffects=False,
- canunwindgc=True),
+ 'weakref_create': LLOp(sideeffects=False, canmallocgc=True),
'weakref_deref': LLOp(sideeffects=False),
'cast_ptr_to_weakrefptr': LLOp(sideeffects=False), # no-op type hiding
'cast_weakrefptr_to_ptr': LLOp(sideeffects=False), # no-op type revealing
- # __________ stackless operation(s) __________
-
- 'yield_current_frame_to_caller': LLOp(canraise=(StackException,
- RuntimeError)),
- # can always unwind, not just if stackless gc
-
- 'stack_frames_depth': LLOp(sideeffects=False, canraise=(StackException,
- RuntimeError)),
- 'stack_switch': LLOp(canraise=(StackException, RuntimeError)),
- 'stack_unwind': LLOp(canraise=(StackException, RuntimeError)),
- 'stack_capture': LLOp(canraise=(StackException, RuntimeError)),
- 'get_stack_depth_limit':LLOp(sideeffects=False),
- 'set_stack_depth_limit':LLOp(),
+ # __________ misc operations __________
'stack_current': LLOp(sideeffects=False),
-
- # __________ misc operations __________
-
'keepalive': LLOp(),
'same_as': LLOp(canfold=True),
'hint': LLOp(),
@@ -590,10 +563,6 @@
'ooparse_int': LLOp(oo=True, canraise=(ValueError,)),
'ooparse_float': LLOp(oo=True, canraise=(ValueError,)),
'oounicode': LLOp(oo=True, canraise=(UnicodeDecodeError,)),
-
- # _____ read frame var support ___
- 'get_frame_base': LLOp(sideeffects=False),
- 'frame_info': LLOp(sideeffects=False),
}
# ***** Run test_lloperation after changes. *****
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -53,7 +53,7 @@
return flags['flavor'] == 'gc' and not flags.get('nocollect', False)
else:
return (op.opname in LL_OPERATIONS and
- LL_OPERATIONS[op.opname].canunwindgc)
+ LL_OPERATIONS[op.opname].canmallocgc)
def find_initializing_stores(collect_analyzer, graph):
from pypy.objspace.flow.model import mkentrymap
diff --git a/pypy/translator/backendopt/inline.py b/pypy/translator/backendopt/inline.py
--- a/pypy/translator/backendopt/inline.py
+++ b/pypy/translator/backendopt/inline.py
@@ -540,7 +540,6 @@
OP_WEIGHTS = {'same_as': 0,
'cast_pointer': 0,
'malloc': 2,
- 'yield_current_frame_to_caller': sys.maxint, # XXX bit extreme
'instrument_count': 0,
'debug_assert': -1,
}
diff --git a/pypy/translator/c/src/stack.h b/pypy/translator/c/src/stack.h
--- a/pypy/translator/c/src/stack.h
+++ b/pypy/translator/c/src/stack.h
@@ -15,7 +15,6 @@
extern long _LLstacktoobig_stack_length;
extern char _LLstacktoobig_report_error;
-void LL_stack_unwind(void);
char LL_stack_too_big_slowpath(long); /* returns 0 (ok) or 1 (too big) */
void LL_stack_set_length_fraction(double);
diff --git a/pypy/translator/c/test/test_stackless.py b/pypy/translator/c/test/test_stackless.py
deleted file mode 100644
--- a/pypy/translator/c/test/test_stackless.py
+++ /dev/null
@@ -1,280 +0,0 @@
-from pypy.translator.translator import TranslationContext
-from pypy.translator.backendopt.all import backend_optimizations
-from pypy.translator.c.genc import CStandaloneBuilder
-from pypy.translator.c import gc
-from pypy.annotation.listdef import s_list_of_strings
-from pypy.rlib.rstack import stack_unwind, stack_frames_depth
-from pypy.rlib.rstack import yield_current_frame_to_caller, set_stack_depth_limit
-from pypy.config.config import Config
-import os
-
-
-class StacklessTest(object):
- backendopt = False
- gcpolicy = "boehm"
- stacklessgc = False
-
- def setup_class(cls):
- import py
- if cls.gcpolicy in (None, "ref"):
- import py
- py.test.skip("stackless + refcounting doesn't work any more for now")
- elif cls.gcpolicy == "boehm":
- from pypy.rpython.tool.rffi_platform import configure_boehm
- from pypy.translator.platform import CompilationError
- try:
- configure_boehm()
- except CompilationError:
- py.test.skip("Boehm GC not present")
-
- def wrap_stackless_function(self, fn):
- def entry_point(argv):
- os.write(1, str(fn())+"\n")
- return 0
-
- from pypy.config.pypyoption import get_pypy_config
- config = get_pypy_config(translating=True)
- config.translation.gc = self.gcpolicy
- config.translation.stackless = True
- if self.stacklessgc:
- config.translation.gcrootfinder = "stackless"
- t = TranslationContext(config=config)
- self.t = t
- t.buildannotator().build_types(entry_point, [s_list_of_strings])
- t.buildrtyper().specialize()
- if self.backendopt:
- backend_optimizations(t)
-
- from pypy.translator.transform import insert_ll_stackcheck
- insert_ll_stackcheck(t)
-
- cbuilder = CStandaloneBuilder(t, entry_point, config=config)
- cbuilder.stackless = True
- cbuilder.generate_source()
- cbuilder.compile()
- res = cbuilder.cmdexec('')
- return int(res.strip())
-
-# ____________________________________________________________
-
-
-class TestStackless(StacklessTest):
-
- def test_stack_depth(self):
- def g1():
- "just to check Void special cases around the code"
- def g2(ignored):
- g1()
- def f(n):
- g1()
- if n > 0:
- res = f(n-1) + 0 # make sure it is not a tail call
- else:
- res = stack_frames_depth()
- g2(g1)
- return res
-
- def fn():
- count0 = f(0)
- count10 = f(10)
- return count10 - count0
-
- res = self.wrap_stackless_function(fn)
- assert res == 10
-
- def test_stack_withptr(self):
- def f(n):
- if n > 0:
- res, dummy = f(n-1)
- else:
- res, dummy = stack_frames_depth(), 1
- return res, dummy
-
- def fn():
- count0, _ = f(0)
- count10, _ = f(10)
- return count10 - count0
-
- res = self.wrap_stackless_function(fn)
- assert res == 10
-
- def test_stackless_manytimes(self):
- def f(n):
- if n > 0:
- stack_frames_depth()
- res, dummy = f(n-1)
- else:
- res, dummy = stack_frames_depth(), 1
- return res, dummy
-
- def fn():
- count0, _ = f(0)
- count10, _ = f(100)
- return count10 - count0
-
- res = self.wrap_stackless_function(fn)
- assert res == 100
-
- def test_stackless_arguments(self):
- def f(n, d, t):
- if n > 0:
- a, b, c = f(n-1, d, t)
- else:
- a, b, c = stack_frames_depth(), d, t
- return a, b, c
-
- def fn():
- count0, d, t = f(0, 5.5, (1, 2))
- count10, d, t = f(10, 5.5, (1, 2))
- result = (count10 - count0) * 1000000
- result += t[0] * 10000
- result += t[1] * 100
- result += int(d*10)
- return result
-
- res = self.wrap_stackless_function(fn)
- assert res == 10010255
-
-
- def test_stack_unwind(self):
- def f():
- stack_unwind()
- return 42
-
- res = self.wrap_stackless_function(f)
- assert res == 42
-
- def test_auto_stack_unwind(self):
- import sys
- def f(n):
- if n == 1:
- return 1
- return (n+f(n-1)) % 1291
-
- def fn():
- set_stack_depth_limit(sys.maxint)
- return f(10**6)
- res = self.wrap_stackless_function(fn)
- assert res == 704
-
- def test_yield_frame(self):
-
- def g(lst):
- lst.append(2)
- frametop_before_5 = yield_current_frame_to_caller()
- lst.append(4)
- frametop_before_7 = frametop_before_5.switch()
- lst.append(6)
- return frametop_before_7
-
- def f():
- lst = [1]
- frametop_before_4 = g(lst)
- lst.append(3)
- frametop_before_6 = frametop_before_4.switch()
- lst.append(5)
- frametop_after_return = frametop_before_6.switch()
- lst.append(7)
- assert frametop_after_return is None
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- res = self.wrap_stackless_function(f)
- assert res == 1234567
-
- def test_foo(self):
- def f():
- c = g()
- c.switch()
- return 1
- def g():
- d = yield_current_frame_to_caller()
- return d
- res = self.wrap_stackless_function(f)
- assert res == 1
-
-
- def test_yield_noswitch_frame(self):
- # this time we make sure that function 'g' does not
- # need to switch and even does not need to be stackless
-
- def g(lst):
- lst.append(2)
- frametop_before_5 = yield_current_frame_to_caller()
- lst.append(4)
- return frametop_before_5
-
- def f():
- lst = [1]
- frametop_before_4 = g(lst)
- lst.append(3)
- frametop_after_return = frametop_before_4.switch()
- lst.append(5)
- assert frametop_after_return is None
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- res = self.wrap_stackless_function(f)
- assert res == 12345
-
- # tested with refcounting too for sanity checking
- def test_yield_frame_mem_pressure(self):
-
- class A:
- def __init__(self, value):
- self.lst = [0] * 10000
- self.lst[5000] = value
-
- def inc(self, delta):
- self.lst[5000] += delta
- return self.lst[5000]
-
- def g(lst):
- a = A(1)
- lst.append(a.inc(1))
- frametop_before_5 = yield_current_frame_to_caller()
- malloc_a_lot()
- lst.append(a.inc(2))
- frametop_before_7 = frametop_before_5.switch()
- malloc_a_lot()
- lst.append(a.inc(2))
- return frametop_before_7
-
- def f():
- lst = [1]
- frametop_before_4 = g(lst)
- lst.append(3)
- malloc_a_lot()
- frametop_before_6 = frametop_before_4.switch()
- lst.append(5)
- malloc_a_lot()
- frametop_after_return = frametop_before_6.switch()
- lst.append(7)
- assert frametop_after_return is None
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- res = self.wrap_stackless_function(f)
- assert res == 1234567
-
-
-# ____________________________________________________________
-
-def malloc_a_lot():
- i = 0
- while i < 10:
- i += 1
- a = [1] * 10
- j = 0
- while j < 20:
- j += 1
- a.append(j)
- from pypy.rpython.lltypesystem.lloperation import llop
- from pypy.rpython.lltypesystem import lltype
- llop.gc__collect(lltype.Void)
diff --git a/pypy/translator/c/test/test_tasklets.py b/pypy/translator/c/test/test_tasklets.py
deleted file mode 100644
--- a/pypy/translator/c/test/test_tasklets.py
+++ /dev/null
@@ -1,497 +0,0 @@
-import py
-import os
-
-from pypy.rpython.lltypesystem.llmemory import NULL
-from pypy.rlib.rstack import yield_current_frame_to_caller
-
-# ____________________________________________________________
-# For testing
-
-from pypy.translator.tool import cbuild
-from pypy.translator.c import gc
-from pypy.translator.c.test import test_stackless
-
-# count of loops in tests (set lower to speed up)
-loops = 1
-
-def debug(s):
- os.write(2, "%s\n" % s)
-
-class Globals:
- def __init__(self):
- pass
-
-globals = Globals()
-globals.count = 0
-
-# ____________________________________________________________
-
-class ThreadLocals(object):
- pass
-threadlocals = ThreadLocals()
-
-class Resumable(object):
- def __init__(self):
- self.alive = False
-
- def start(self):
- self.resumable = self._start()
-
- def _start(self):
- threadlocals.cc = yield_current_frame_to_caller()
- self.fn()
- return threadlocals.cc
-
- def suspend(self):
- # we suspend ourself
- threadlocals.cc = threadlocals.cc.switch()
-
- def resume(self):
- # the caller resumes me
- self.resumable = self.resumable.switch()
- self.alive = self.resumable is not None
-
- def fn(self):
- pass
-
-class Tasklet(Resumable):
-
- def __init__(self):
- Resumable.__init__(self)
- self.blocked = 0
-
- # propogates round suspend-resume to tell scheduler in run()
- # XXX this should probably be done by setting a value in the
- # scheduler??
- self.remove = False
-
- def start(self):
- Resumable.start(self)
- scheduler.add_tasklet(self)
-
- def run(self):
- Resumable.start(self)
- scheduler.run_immediately(self)
-
- def suspend_and_remove(self, remove):
- self.remove = remove
- self.suspend()
-
- def resume(self):
- assert not self.remove
- Resumable.resume(self)
- return self.alive and not self.remove
-
-class Channel:
- def __init__(self):
- self.queue = []
- self.balance = 0
-
- def send(self, value):
- self.balance += 1
- if self.balance <= 0:
- t = self.queue.pop(0)
- t.data = value
- t.blocked = 0
- t.remove = False
- scheduler.run_immediately(t)
- scheduler.schedule()
-
- # resuming
- t = getcurrent()
- assert t.blocked == 0
-
- else:
- t = getcurrent()
- assert isinstance(t, Tasklet)
- t.data = value
- # let it wait for a receiver to come along
- self.queue.append(t)
- t.blocked = 1
- schedule_remove()
-
- # resuming
- assert t == getcurrent()
- assert t.blocked == 0
-
- def receive(self):
- self.balance -= 1
- # good to go
- if self.balance >= 0:
- t = self.queue.pop(0)
- t.blocked = 0
- t.remove = False
- data = t.data
- scheduler.add_tasklet(t)
- return data
- else:
- # queue ourself
- t = getcurrent()
- assert isinstance(t, Tasklet)
- self.queue.append(t)
-
- # block until send has reenabled me
- t.blocked = -1
- schedule_remove()
-
- # resuming
- assert t == getcurrent()
- assert t.blocked == 0
-
- return t.data
-
-class Scheduler(object):
- def __init__(self):
- self.runnables = []
- self.current_tasklet = None
- self.immediately_schedule = None
-
- def add_tasklet(self, tasklet):
- self.runnables.append(tasklet)
-
- def run_immediately(self, tasklet):
- self.immediately_schedule = tasklet
-
- def run(self):
- while self.runnables:
- runnables = self.runnables
- self.runnables = []
- count = 0
- for t in runnables:
- assert self.current_tasklet is None
-
- self.current_tasklet = t
- if t.resume():
- self.runnables.append(self.current_tasklet)
- self.current_tasklet = None
- count += 1
-
- if self.immediately_schedule:
- self.runnables = [self.immediately_schedule] \
- + runnables[count:] + self.runnables
- self.immediately_schedule = None
- break
-
- def schedule(self, remove=False):
- assert self.current_tasklet is not None
- self.current_tasklet.suspend_and_remove(remove)
-
-# ____________________________________________________________
-
-scheduler = Scheduler()
-
-def schedule():
- scheduler.schedule()
-
-def schedule_remove():
- scheduler.schedule(remove=True)
-
-def run():
- scheduler.run()
-
-def getcurrent():
- return scheduler.current_tasklet
-
-# ____________________________________________________________
-
-class TestTasklets(test_stackless.StacklessTest):
- backendopt = True
-
- def test_simplex(self):
-
- class Tasklet1(Tasklet):
- def fn(self):
- for ii in range(5):
- globals.count += 1
- schedule()
-
- def f():
- for ii in range(loops):
- Tasklet1().start()
- run()
- return globals.count == loops * 5
-
- res = self.wrap_stackless_function(f)
- print res
- assert res == 1
-
- def test_multiple_simple(self):
-
- class Tasklet1(Tasklet):
- def fn(self):
- for ii in range(5):
- globals.count += 1
- schedule()
-
- class Tasklet2(Tasklet):
- def fn(self):
- for ii in range(5):
- globals.count += 1
- schedule()
- globals.count += 1
-
- class Tasklet3(Tasklet):
- def fn(self):
- schedule()
- for ii in range(10):
- globals.count += 1
- if ii % 2:
- schedule()
- schedule()
-
- def f():
- for ii in range(loops):
- Tasklet1().start()
- Tasklet2().start()
- Tasklet3().start()
- run()
- return globals.count == loops * 25
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_schedule_remove(self):
-
- class Tasklet1(Tasklet):
- def fn(self):
- for ii in range(20):
- if ii < 10:
- schedule()
- else:
- schedule_remove()
- globals.count += 1
-
- def f():
- for ii in range(loops):
- Tasklet1().start()
- run()
- for ii in range(loops):
- Tasklet1().start()
- run()
- return globals.count == loops * 10 * 2
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_run_immediately(self):
- globals.intermediate = 0
- class Tasklet1(Tasklet):
- def fn(self):
- for ii in range(20):
- globals.count += 1
- schedule()
-
- class RunImmediate(Tasklet):
- def fn(self):
- globals.intermediate = globals.count
- schedule()
-
- class Tasklet2(Tasklet):
- def fn(self):
- for ii in range(20):
- globals.count += 1
- if ii == 10:
- RunImmediate().run()
- schedule()
-
- def f():
- Tasklet2().start()
- for ii in range(loops):
- Tasklet1().start()
- run()
- total_expected = (loops + 1) * 20
- return (globals.intermediate == total_expected / 2 + 1 and
- globals.count == total_expected)
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_channel1(self):
- ch = Channel()
-
- class Tasklet1(Tasklet):
- def fn(self):
- for ii in range(5):
- ch.send(ii)
-
- class Tasklet2(Tasklet):
- def fn(self):
- #while True: XXX Doesnt annotate
- for ii in range(6):
- globals.count += ch.receive()
-
- def f():
- Tasklet2().start()
- Tasklet1().start()
- run()
- return (globals.count == 10)
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_channel2(self):
-
- class Tasklet1(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- def fn(self):
- for ii in range(5):
- self.ch.send(ii)
-
- class Tasklet2(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- def fn(self):
- #while True:XXX Doesnt annotate
- for ii in range(6):
- res = self.ch.receive()
- globals.count += res
-
- def f():
- ch = Channel()
- Tasklet1(ch).start()
- Tasklet2(ch).start()
- run()
- return globals.count == 10
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_channel3(self):
-
- class Tasklet1(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- def fn(self):
- for ii in range(5):
- self.ch.send(ii)
-
- class Tasklet2(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- def fn(self):
- #while True: XXX Doesnt annotate
- for ii in range(16):
- res = self.ch.receive()
- globals.count += res
-
- def f():
- ch = Channel()
- Tasklet1(ch).start()
- Tasklet1(ch).start()
- Tasklet1(ch).start()
- Tasklet2(ch).start()
- run()
- return globals.count == 30
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_flexible_channels(self):
- """ test with something other than int """
-
- class A(object):
- def __init__(self, num):
- self.num = num
- def getvalue(self):
- res = self.num
- self.num *= 2
- return res
-
- class Data(object):
- pass
-
- class IntData(Data):
- def __init__(self, i):
- self.int = i
-
- class StringData(Data):
- def __init__(self, s):
- self.str = s
-
- class InstanceData(Data):
- def __init__(self, i):
- self.instance = i
-
-
- class Tasklet1(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- def fn(self):
- for ii in range(5):
- self.ch.send(IntData(ii))
-
- class Tasklet2(Tasklet):
- def __init__(self, ch, strdata):
- self.ch = ch
- self.strdata = strdata
- def fn(self):
- for ii in range(5):
- self.ch.send(StringData(self.strdata))
-
- class Tasklet3(Tasklet):
- def __init__(self, ch, instance):
- self.ch = ch
- self.instance = instance
- def fn(self):
- for ii in range(5):
- self.ch.send(InstanceData(self.instance))
-
- class Server(Tasklet):
- def __init__(self, ch):
- self.ch = ch
- self.loop = True
-
- def stop(self):
- self.loop = False
-
- def fn(self):
- while self.loop:
- data = self.ch.receive()
- if isinstance(data, IntData):
- globals.count += data.int
- elif isinstance(data, StringData):
- globals.count += len(data.str)
- elif isinstance(data, InstanceData):
- globals.count += data.instance.getvalue()
-
- ch = Channel()
- server = Server(ch)
-
- def f():
- Tasklet1(ch).start()
- Tasklet2(ch, "abcd").start()
- Tasklet2(ch, "xxx").start()
- Tasklet3(ch, A(1)).start()
- server.start()
- run()
- return globals.count == (0+1+2+3+4) + (5*4) + (5*3) + (1+2+4+8+16)
-
- res = self.wrap_stackless_function(f)
- assert res == 1
-
- def test_original_api(self):
-
- class TaskletAsFunction(Tasklet):
- def __init__(self, fn):
- self.redirect_fn = fn
- def fn(self):
- self.redirect_fn()
-
- def tasklet(fn):
- return TaskletAsFunction(fn)
-
- def simple():
- for ii in range(5):
- globals.count += 1
- schedule()
-
- def f():
- for ii in range(loops):
- tasklet(simple).start()
- run()
- run()
- return globals.count == loops * 5
-
- res = self.wrap_stackless_function(f)
- assert res == 1
diff --git a/pypy/translator/exceptiontransform.py b/pypy/translator/exceptiontransform.py
--- a/pypy/translator/exceptiontransform.py
+++ b/pypy/translator/exceptiontransform.py
@@ -13,7 +13,6 @@
from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
from pypy.rlib.rarithmetic import r_singlefloat
from pypy.rlib.debug import ll_assert
-from pypy.rlib.rstackovf import _StackOverflow
from pypy.annotation import model as annmodel
from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
from pypy.tool.sourcetools import func_with_new_name
@@ -61,8 +60,6 @@
exc_data, null_type, null_value = self.setup_excdata()
rclass = translator.rtyper.type_system.rclass
- (stackovf_ll_exc_type,
- stackovf_ll_exc) = self.get_builtin_exception(_StackOverflow)
(assertion_error_ll_exc_type,
assertion_error_ll_exc) = self.get_builtin_exception(AssertionError)
(n_i_error_ll_exc_type,
@@ -115,9 +112,6 @@
exc_data.exc_type = rclass.ll_inst_type(evalue)
exc_data.exc_value = evalue
- def rpyexc_raise_stack_overflow():
- rpyexc_raise(stackovf_ll_exc_type, stackovf_ll_exc)
-
self.rpyexc_occured_ptr = self.build_func(
"RPyExceptionOccurred",
rpyexc_occured,
@@ -152,11 +146,6 @@
lltype.Void,
jitcallkind='rpyexc_raise') # for the JIT
- self.rpyexc_raise_stack_overflow_ptr = self.build_func(
- "RPyRaiseStackOverflow",
- self.noinline(rpyexc_raise_stack_overflow),
- [], lltype.Void)
-
self.rpyexc_fetch_exception_ptr = self.build_func(
"RPyFetchException",
rpyexc_fetch_exception,
@@ -223,7 +212,6 @@
self.transform_jump_to_except_block(graph, entrymap, link)
#
for block in list(graph.iterblocks()):
- self.replace_stack_unwind(block)
self.replace_fetch_restore_operations(block)
need_exc_matching, gen_exc_checks = self.transform_block(graph, block)
n_need_exc_matching_blocks += need_exc_matching
@@ -231,16 +219,6 @@
cleanup_graph(graph)
return n_need_exc_matching_blocks, n_gen_exc_checks
- def replace_stack_unwind(self, block):
- for i in range(len(block.operations)):
- if block.operations[i].opname == 'stack_unwind':
- # if there are stack_unwind ops left,
- # the graph was not stackless-transformed
- # so we need to raise a StackOverflow in any
- # case
- block.operations[i].opname = "direct_call"
- block.operations[i].args = [self.rpyexc_raise_stack_overflow_ptr]
-
def replace_fetch_restore_operations(self, block):
# the gctransformer will create these operations. It looks as if the
# order of transformations is important - but the gctransformer will
diff --git a/pypy/translator/stackless/__init__.py b/pypy/translator/stackless/__init__.py
deleted file mode 100644
--- a/pypy/translator/stackless/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-#
diff --git a/pypy/translator/stackless/code.py b/pypy/translator/stackless/code.py
deleted file mode 100644
--- a/pypy/translator/stackless/code.py
+++ /dev/null
@@ -1,480 +0,0 @@
-import sys
-from pypy.rpython.lltypesystem import lltype, llmemory, lloperation
-from pypy.tool.sourcetools import func_with_new_name
-from pypy.rlib import rarithmetic, objectmodel, rstackovf
-from pypy.translator.stackless import frame
-from pypy.translator.stackless.frame import STATE_HEADER, SAVED_REFERENCE, STORAGE_TYPES_AND_FIELDS
-
-EMPTY_STATE = frame.make_state_header_type('empty_state')
-
-def check_can_raise_unwind():
- if objectmodel.is_in_callback():
- raise RuntimeError
-
-# ____________________________________________________________
-
-SWITCH_STATE = frame.make_state_header_type('switch_state',
- ('c', SAVED_REFERENCE))
-
-def ll_frame_switch(targetstate):
- if global_state.restart_substate == -1:
- # normal entry point for a call to state.switch()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(SWITCH_STATE)
- s.header.f_restart = INDEX_SWITCH
- s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, targetstate)
- add_frame_state(u, s.header)
- raise u
- elif global_state.restart_substate == 0:
- # STATE 0: we didn't do anything so far, but the stack is unwound
- global_state.restart_substate = -1
- # grab the frame corresponding to ourself
- # the 'targetstate' local is garbage here, it must be read back from
- # 's.c' where we saved it by the normal entry point above
- mystate = global_state.top
- s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
- targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
- # prepare a new saved state for the future switch() back,
- # which will go to STATE 1 below
- sourcestate = lltype.malloc(EMPTY_STATE).header
- sourcestate.f_back = mystate.f_back
- sourcestate.f_restart = INDEX_SWITCH + 1
- sourcestate.f_depth = mystate.f_depth
- global_state.top = targetstate
- global_state.retval_ref = lltype.cast_opaque_ptr(SAVED_REFERENCE,
- sourcestate)
- raise SwitchException() # this jumps to targetstate
- else:
- # STATE 1: switching back into a tasklet suspended by
- # a call to switch()
- global_state.top = frame.null_state
- global_state.restart_substate = -1
- origin_state = lltype.cast_opaque_ptr(frame.OPAQUE_STATE_HEADER_PTR,
- fetch_retval_ref())
- return origin_state # a normal return into the current tasklet,
- # with the source state as return value
-ll_frame_switch.stackless_explicit = True
-
-INDEX_SWITCH = frame.RestartInfo.add_prebuilt(ll_frame_switch,
- [SWITCH_STATE, EMPTY_STATE])
-
-# ____________________________________________________________
-
-def yield_current_frame_to_caller():
- if global_state.restart_substate == -1:
- # normal entry point for yield_current_frame_to_caller()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(EMPTY_STATE).header
- s.f_restart = INDEX_YCFTC
- add_frame_state(u, s)
- raise u # this goes to 'STATE 0' below
-
- elif global_state.restart_substate == 0:
- # STATE 0: we didn't do anything so far, but the stack is unwound
- global_state.restart_substate = -1
- ycftc_state = global_state.top
- our_caller_state = ycftc_state.f_back
- caller_state = our_caller_state.f_back
- caller_state.f_depth = ycftc_state.f_depth - 2
- # when our immediate caller finishes (which is later, when the
- # tasklet finishes), then we will jump to 'STATE 1' below
- endstate = lltype.malloc(EMPTY_STATE).header
- endstate.f_restart = INDEX_YCFTC + 1
- our_caller_state.f_back = endstate
- our_caller_state.f_depth = 1
- global_state.top = caller_state
- global_state.retval_ref = lltype.cast_opaque_ptr(SAVED_REFERENCE,
- our_caller_state)
- raise SwitchException() # this goes to the caller's caller
-
- elif global_state.restart_substate == 1:
- # STATE 1: this is a slight abuse of yield_current_frame_to_caller(),
- # as we return here when our immediate caller returns (and thus the
- # new tasklet finishes).
- global_state.restart_substate = -1
- next_state = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER),
- fetch_retval_ref())
- # return a NULL state pointer to the target of the implicit switch
- global_state.top = next_state
- global_state.retval_ref = frame.null_saved_ref
- raise SwitchException() # this goes to the switch target given by
- # the 'return' at the end of our caller
-
- else:
- # this is never reached! But the annotator doesn't know it,
- # so it makes the whole function be annotated as returning a random
- # non-constant STATE_HEADER pointer.
- return lltype.cast_opaque_ptr(frame.OPAQUE_STATE_HEADER_PTR,
- global_state.top)
-
-yield_current_frame_to_caller.stackless_explicit = True
-
-INDEX_YCFTC = frame.RestartInfo.add_prebuilt(yield_current_frame_to_caller,
- [EMPTY_STATE, EMPTY_STATE])
-
-# ____________________________________________________________
-
-def stack_frames_depth():
- if global_state.restart_substate == -1:
- # normal entry point for stack_frames_depth()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(EMPTY_STATE).header
- s.f_restart = INDEX_DEPTH
- add_frame_state(u, s)
- raise u # goes to STATE 0 below
- else:
- # STATE 0: now the stack is unwound, and we can count the frames
- # in the heap
- cur = global_state.top
- global_state.top = frame.null_state
- global_state.restart_substate = -1
- return cur.f_depth
-stack_frames_depth.stackless_explicit = True
-
-INDEX_DEPTH = frame.RestartInfo.add_prebuilt(stack_frames_depth,
- [EMPTY_STATE])
-
-# ____________________________________________________________
-
-def ll_stack_unwind():
- if global_state.restart_substate == -1:
- # normal entry point for stack_frames_depth()
- # first unwind the stack in the usual way
- u = UnwindException()
- s = lltype.malloc(EMPTY_STATE).header
- s.f_restart = INDEX_UNWIND
- add_frame_state(u, s)
- raise u # goes to STATE 0 below
- else:
- # STATE 0: now the stack is unwound. That was the goal.
- # Return to caller.
- global_state.top = frame.null_state
- global_state.restart_substate = -1
-
-ll_stack_unwind.stackless_explicit = True
-
-INDEX_UNWIND = frame.RestartInfo.add_prebuilt(ll_stack_unwind,
- [EMPTY_STATE])
-
-# ____________________________________________________________
-
-def ll_stack_capture():
- if global_state.restart_substate == -1:
- # normal entry point for ll_stack_capture()
- # first unwind the stack in the usual way
- u = UnwindException()
- s = lltype.malloc(EMPTY_STATE).header
- s.f_restart = INDEX_CAPTURE
- add_frame_state(u, s)
- raise u # goes to STATE 0 below
- else:
- # STATE 0: now the stack is unwound. That was the goal.
- # Return to caller.
- cur = global_state.top
- global_state.top = frame.null_state
- global_state.restart_substate = -1
- # Pass the caller's own saved state back to it.
- # The StacklessFrameworkGCTransformer uses this for introspection.
- return lltype.cast_opaque_ptr(frame.OPAQUE_STATE_HEADER_PTR,
- cur.f_back)
-ll_stack_capture.stackless_explicit = True
-
-INDEX_CAPTURE = frame.RestartInfo.add_prebuilt(ll_stack_capture,
- [EMPTY_STATE])
-
-def resume_after_void(state, retvalue):
- if global_state.restart_substate == -1:
- # normal entry point for a call to state.switch()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(SWITCH_STATE)
- s.header.f_restart = INDEX_RESUME_AFTER_VOID
- s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
- add_frame_state(u, s.header)
- raise u
- elif global_state.restart_substate == 0:
- # STATE 0: we didn't do anything so far, but the stack is unwound
- global_state.restart_substate = -1
- # grab the frame corresponding to ourself
- # the 'targetstate' local is garbage here, it must be read back from
- # 's.c' where we saved it by the normal entry point above
- mystate = global_state.top
- s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
- targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
- resume_bottom = targetstate
- while resume_bottom.f_back:
- resume_bottom = resume_bottom.f_back
- resume_bottom.f_back = mystate.f_back
- global_state.top = targetstate
- raise SwitchException()
-
-resume_after_void.stackless_explicit = True
-INDEX_RESUME_AFTER_VOID = frame.RestartInfo.add_prebuilt(resume_after_void,
- [SWITCH_STATE,
- EMPTY_STATE])
-
-
-def resume_after_raising(state, exception):
- if global_state.restart_substate == -1:
- # normal entry point for a call to state.switch()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(SWITCH_STATE)
- s.header.f_restart = INDEX_RESUME_AFTER_RAISING
- s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
- add_frame_state(u, s.header)
- global_state.exception = exception
- raise u
- elif global_state.restart_substate == 0:
- # STATE 0: we didn't do anything so far, but the stack is unwound
- global_state.restart_substate = -1
- # grab the frame corresponding to ourself
- # the 'targetstate' local is garbage here, it must be read back from
- # 's.c' where we saved it by the normal entry point above
- mystate = global_state.top
- s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
- targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
- resume_bottom = targetstate
- while resume_bottom.f_back:
- resume_bottom = resume_bottom.f_back
- resume_bottom.f_back = mystate.f_back
- global_state.top = targetstate
- raise SwitchException()
-
-resume_after_raising.stackless_explicit = True
-INDEX_RESUME_AFTER_RAISING = frame.RestartInfo.add_prebuilt(resume_after_raising,
- [SWITCH_STATE,
- EMPTY_STATE])
-
-template = """\
-def resume_after_%(typename)s(state, retvalue):
- if global_state.restart_substate == -1:
- # normal entry point for a call to state.switch()
- # first unwind the stack
- u = UnwindException()
- s = lltype.malloc(SWITCH_STATE)
- s.header.f_restart = INDEX_RESUME_AFTER_%(TYPENAME)s
- s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, state)
- global_state.retval_%(typename)s = retvalue
- add_frame_state(u, s.header)
- raise u
- elif global_state.restart_substate == 0:
- # STATE 0: we didn't do anything so far, but the stack is unwound
- global_state.restart_substate = -1
- # grab the frame corresponding to ourself
- # the 'targetstate' local is garbage here, it must be read back from
- # 's.c' where we saved it by the normal entry point above
- mystate = global_state.top
- s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), mystate)
- targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
- resume_bottom = targetstate
- while resume_bottom.f_back:
- resume_bottom = resume_bottom.f_back
- resume_bottom.f_back = mystate.f_back
- global_state.top = targetstate
- raise SwitchException()
-
-
-resume_after_%(typename)s.stackless_explicit = True
-INDEX_RESUME_AFTER_%(TYPENAME)s = frame.RestartInfo.add_prebuilt(resume_after_%(typename)s,
- [SWITCH_STATE,
- EMPTY_STATE])
-"""
-
-for _lltype, typename in STORAGE_TYPES_AND_FIELDS:
- if typename == 'void': continue
- exec template%dict(typename=typename, TYPENAME=typename.upper())
-
-# ____________________________________________________________
-
-def ll_get_stack_depth_limit():
- return global_state.stack_depth_limit
-
-def ll_set_stack_depth_limit(limit):
- global_state.stack_depth_limit = limit
-
-# ____________________________________________________________
-
-class StacklessData:
- def __init__(self):
- self.top = frame.null_state
- self.restart_substate = -1
- self.retval_long = 0
- self.retval_longlong = rarithmetic.r_longlong(0)
- self.retval_float = 0.0
- self.retval_addr = llmemory.NULL
- self.retval_ref = frame.null_saved_ref
- self.exception = None
- self.masterarray = lltype.malloc(frame.FRAME_INFO_ARRAY, 0,
- immortal=True)
- self.stack_depth_limit = 100000 # default limit
-
-global_state = StacklessData()
-
-# the following functions are patched by transform.py in finish()
-# so that they don't really do what they appear to - we discovered
-# that it was not safe at all to produce this kind of C code
-def define_call_function_retval(TYPE, typename):
- FUNCTYPE = lltype.Ptr(lltype.FuncType([], TYPE))
- def call_function_retval_xyz(fnaddr, signature_index):
- fn = llmemory.cast_adr_to_ptr(fnaddr, FUNCTYPE)
- return fn()
- call_function_retval_xyz.stackless_explicit = True
- call_function_retval_xyz._dont_inline_ = True
- fnname = 'call_function_retval_' + typename
- fn = func_with_new_name(call_function_retval_xyz, fnname)
- globals()[fnname] = fn
-for _lltype, _typename in STORAGE_TYPES_AND_FIELDS:
- define_call_function_retval(_lltype, _typename)
-
-
-def call_function(fn, signature_index):
- retval_code = signature_index & frame.storage_type_bitmask
- if retval_code == frame.RETVAL_VOID:
- call_function_retval_void(fn, signature_index)
- elif retval_code == frame.RETVAL_REF:
- global_state.retval_ref = (
- call_function_retval_ref(fn, signature_index))
- elif retval_code == frame.RETVAL_ADDR:
- global_state.retval_addr = (
- call_function_retval_addr(fn, signature_index))
- elif retval_code == frame.RETVAL_LONG:
- global_state.retval_long = (
- call_function_retval_long(fn, signature_index))
- elif retval_code == frame.RETVAL_FLOAT:
- global_state.retval_float = (
- call_function_retval_float(fn, signature_index))
- elif retval_code == frame.RETVAL_LONGLONG:
- global_state.retval_longlong = (
- call_function_retval_longlong(fn, signature_index))
- else:
- assert False
-call_function.stackless_explicit = True
-
-class UnwindException(lloperation.StackException):
- def __init__(self):
- # during unwind, global_state.top points to frame that first caught
- # the UnwindException, whilst frame_bottom points to the frame
- # that most recently caught the UnwindException. In a normal
- # situation, frame_bottom is global_state.top.f_back.f_back.etc...
- # To switch manually to a different frame, code issues a regular
- # UnwindException first, to empty the C stack, and then issues a
- # (XXX complete this comment)
- check_can_raise_unwind()
- self.frame_bottom = frame.null_state
- self.depth = 0
- __init__.stackless_explicit = True
-
-class SwitchException(lloperation.StackException):
- pass
-
-def slp_main_loop(depth):
- """
- slp_main_loop() keeps resuming...
- """
- while True:
- pending = global_state.top
- pending.f_depth = depth # this starts after the first Unwind
- if pending.f_depth > global_state.stack_depth_limit:
- # uncommon case: exceed the limit
- pending = pending.f_back
- pending.f_depth = depth - 1
- e = rstackovf._StackOverflow()
- if not pending:
- raise e
- global_state.exception = e
- global_state.top = pending
-
- while True:
- prevdepth = pending.f_depth - 1
- back = pending.f_back
- decoded = frame.decodestate(pending.f_restart)
- (fn, global_state.restart_substate, signature_index) = decoded
- try:
- call_function(fn, signature_index)
- except UnwindException, u: #XXX annotation support needed
- u.frame_bottom.f_back = back
- depth = prevdepth + u.depth
- break
- except SwitchException:
- pending = global_state.top
- continue
- except Exception, e:
- if not back:
- raise
- global_state.exception = e
- else:
- if not back:
- return
- global_state.top = pending = back
- pending.f_depth = prevdepth
-
-slp_main_loop.stackless_explicit = True
-
-def add_frame_state(u, frame_state):
- if not u.frame_bottom:
- global_state.top = u.frame_bottom = frame_state
- else:
- u.frame_bottom.f_back = frame_state
- u.frame_bottom = frame_state
- u.depth += 1
-add_frame_state.stackless_explicit = True
-
-def fetch_retval_void():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
-fetch_retval_void.stackless_explicit = True
-
-def fetch_retval_long():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
- else:
- return global_state.retval_long
-fetch_retval_long.stackless_explicit = True
-
-def fetch_retval_longlong():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
- else:
- return global_state.retval_longlong
-fetch_retval_longlong.stackless_explicit = True
-
-def fetch_retval_float():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
- else:
- return global_state.retval_float
-fetch_retval_float.stackless_explicit = True
-
-def fetch_retval_addr():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
- else:
- res = global_state.retval_addr
- global_state.retval_addr = llmemory.NULL
- return res
-fetch_retval_addr.stackless_explicit = True
-
-def fetch_retval_ref():
- e = global_state.exception
- if e:
- global_state.exception = None
- raise e
- else:
- res = global_state.retval_ref
- global_state.retval_ref = frame.null_saved_ref
- return res
-fetch_retval_ref.stackless_explicit = True
diff --git a/pypy/translator/stackless/frame.py b/pypy/translator/stackless/frame.py
deleted file mode 100644
--- a/pypy/translator/stackless/frame.py
+++ /dev/null
@@ -1,156 +0,0 @@
-from pypy.rpython.lltypesystem import lltype, llmemory
-from pypy.rpython.typesystem import getfunctionptr
-from pypy.rpython.annlowlevel import annotate_lowlevel_helper
-from pypy.objspace.flow.model import FunctionGraph
-from pypy.tool.sourcetools import compile2
-from pypy.annotation import model as annmodel
-from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
-from pypy.rlib import rstack
-
-# ____________________________________________________________
-# generic data types
-
-SAVED_REFERENCE = llmemory.GCREF
-null_saved_ref = lltype.nullptr(SAVED_REFERENCE.TO)
-
-STORAGE_TYPES_AND_FIELDS = [
- (lltype.Void, 'void'),
- (SAVED_REFERENCE, 'ref'),
- (llmemory.Address, 'addr'),
- (lltype.SignedLongLong, 'longlong'),
- (lltype.Signed, 'long'),
- (lltype.Float, 'float'),
- ]
-
-STORAGE_TYPES = []
-for _TYPE, _FIELD in STORAGE_TYPES_AND_FIELDS:
- # we do not want to add the longlong type twice on 64 bits
- # machines on which longlong is the same as signed
- if _TYPE not in STORAGE_TYPES:
- STORAGE_TYPES.append(_TYPE)
-
-storage_type_bitmask = 0x07 # a power of two - 1
-assert storage_type_bitmask >= len(STORAGE_TYPES)
-
-STORAGE_FIELDS = dict(STORAGE_TYPES_AND_FIELDS)
-del STORAGE_FIELDS[lltype.Void]
-
-for (_key, _value) in STORAGE_TYPES_AND_FIELDS:
- globals()['RETVAL_' + _value.upper()] = STORAGE_TYPES.index(_key)
-
-def storage_type(T):
- """Return the 'erased' storage type corresponding to T.
- """
- if T is lltype.Void:
- return lltype.Void
- elif isinstance(T, lltype.Ptr):
- if T._needsgc():
- return SAVED_REFERENCE
- else:
- return llmemory.Address
- elif T is lltype.Float:
- return lltype.Float
- elif T in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
- return lltype.SignedLongLong
- elif T is llmemory.Address:
- return llmemory.Address
- elif isinstance(T, lltype.Primitive):
- return lltype.Signed
- else:
- raise Exception("don't know about %r" % (T,))
-
-# ____________________________________________________________
-# structures for saved frame states
-
-STATE_HEADER = lltype.GcStruct('state_header',
- ('f_back', lltype.Ptr(lltype.GcForwardReference())),
- ('f_restart', lltype.Signed),
- ('f_depth', lltype.Signed))
-STATE_HEADER.f_back.TO.become(STATE_HEADER)
-
-null_state = lltype.nullptr(STATE_HEADER)
-
-OPAQUE_STATE_HEADER_PTR = rstack.OPAQUE_STATE_HEADER_PTR
-
-
-def make_state_header_type(name, *fields):
- return lltype.GcStruct(name,
- ('header', STATE_HEADER),
- *fields)
-
-# ____________________________________________________________
-# master array giving information about the restart points
-# (STATE_HEADER.frameinfo is an index into this array)
-
-FRAME_INFO = lltype.Struct('frame_info',
- ('fnaddr', llmemory.Address),
- ('info', lltype.Signed))
-FRAME_INFO_ARRAY = lltype.Array(FRAME_INFO)
-
-def decodestate(index):
- from pypy.translator.stackless.code import global_state
- masterarray = global_state.masterarray
- finfo = masterarray[index]
- if finfo.fnaddr:
- restartstate = 0
- else:
- restartstate = finfo.info
- finfo = masterarray[index - restartstate]
- return (finfo.fnaddr, # function ptr
- restartstate, # restart state within function
- finfo.info) # signature_index
-decodestate.stackless_explicit = True
-
-
-class RestartInfo(object):
-
- """
- A RestartInfo is created for each function that needs
- to do explicit stackless manipulations
- (e.g. code.yield_current_frame_to_caller)."""
-
- def __init__(self, func_or_graph, resume_point_count):
- self.func_or_graph = func_or_graph
- self.resume_point_count = resume_point_count
- self.frame_types = ()
-
- def compress(self, signaturecodes, rtyper):
- """This returns sufficient information to be able to build the
- entries that will go in the global array of restart
- information."""
- if self.resume_point_count > 0:
- bk = rtyper.annotator.bookkeeper
- graph = self.func_or_graph
- if not isinstance(graph, FunctionGraph):
- graph = bk.getdesc(graph).getuniquegraph()
- funcptr = getfunctionptr(graph)
- FUNC = lltype.typeOf(funcptr).TO
- rettype_index = STORAGE_TYPES.index(storage_type(FUNC.RESULT))
- cache = signaturecodes[rettype_index]
- key = tuple([storage_type(ARG) for ARG in FUNC.ARGS])
- try:
- signature_index = cache[key]
- except KeyError:
- signature_index = len(cache) * (storage_type_bitmask+1)
- signature_index |= rettype_index
- cache[key] = signature_index
- assert (signature_index & storage_type_bitmask) == rettype_index
- result = [(llmemory.cast_ptr_to_adr(funcptr), signature_index)]
- for i in range(1, self.resume_point_count):
- result.append((llmemory.NULL, i))
- else:
- result = []
- return result
-
- prebuilt = []
- prebuiltindex = 0
-
- def add_prebuilt(cls, func, frame_types):
- assert func.stackless_explicit # did you forget this flag?
- restart = cls(func, len(frame_types))
- restart.frame_types = frame_types
- n = cls.prebuiltindex
- cls.prebuilt.append(restart)
- cls.prebuiltindex += len(frame_types)
- return n
- add_prebuilt = classmethod(add_prebuilt)
diff --git a/pypy/translator/stackless/test/__init__.py b/pypy/translator/stackless/test/__init__.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-#ra
diff --git a/pypy/translator/stackless/test/test_callback.py b/pypy/translator/stackless/test/test_callback.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_callback.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import py
-from pypy.rlib import rstack
-from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.translator.stackless.test.test_transform import \
- run_stackless_function
-
-eci = ExternalCompilationInfo(
- separate_module_sources = ["""
- int f1(int (*callback)(int))
- {
- return callback(25) + 1;
- }
- """])
-
-CALLBACK = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed))
-f1 = rffi.llexternal("f1", [CALLBACK], lltype.Signed, compilation_info=eci)
-
-def my_callback(n):
- try:
- rstack.stack_unwind()
- except RuntimeError:
- return -20
- return n * 10
-
-def test_my_callback():
- def fn():
- return f1(my_callback)
- res = run_stackless_function(fn)
- assert res == -19
diff --git a/pypy/translator/stackless/test/test_clone.py b/pypy/translator/stackless/test/test_clone.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_clone.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from pypy.translator.stackless.test.test_transform import \
- llinterp_stackless_function, run_stackless_function
-from pypy.rlib import rstack
-
-
-def test_simple():
- import py; py.test.skip("to be rewritten with gc_x_clone")
- def g(lst):
- lst.append(1)
- parent = rstack.yield_current_frame_to_caller()
- # compute a bit
- lst.append(3)
- # switch back for the fork
- parent = parent.switch()
- lst.append(6) # we are here twice!
- return parent
-
- def f():
- lst = []
- c = g(lst)
- lst.append(2)
- c1 = c.switch()
- lst.append(4)
- c2 = c1.clone() # clone() here
- lst.append(5)
- end1 = c1.switch()
- lst.append(7)
- end2 = c2.switch()
- lst.append(8)
- assert not end1
- assert not end2
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- data = llinterp_stackless_function(f)
- assert data == 123456768
-
- res = run_stackless_function(f)
- assert res == 123456768
diff --git a/pypy/translator/stackless/test/test_depth.py b/pypy/translator/stackless/test/test_depth.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_depth.py
+++ /dev/null
@@ -1,211 +0,0 @@
-from pypy.translator.stackless.test.test_transform import \
- llinterp_stackless_function, run_stackless_function
-from pypy.rlib import rstack
-import py
-import os, sys
-
-
-def test_simple():
- def g1():
- "just to check Void special cases around the code"
- def g2(ignored):
- pass
- g1()
- def f(n):
- g1()
- if n > 0:
- res = f(n-1) + 0
- else:
- res = rstack.stack_frames_depth()
- g2(g1)
- return res
-
- def fn():
- count0 = f(0)
- count10 = f(10)
- return count10 - count0
-
- res = llinterp_stackless_function(fn)
- assert res == 10
-
- res = run_stackless_function(fn)
- assert res == 10
-
-def test_with_ptr():
- def f(n):
- if n > 0:
- res, dummy = f(n-1)
- return (res, dummy)
- else:
- res = rstack.stack_frames_depth(), 1
- return res
-
- def fn():
- count0, _ = f(0)
- count10, _ = f(10)
- return count10 - count0
-
- res = llinterp_stackless_function(fn)
- assert res == 10
-
- res = run_stackless_function(fn)
- assert res == 10
-
-def test_manytimes():
- def f(n):
- if n > 0:
- res, dummy = f(n-1)
- return (res, dummy)
- else:
- res = rstack.stack_frames_depth(), 1
- return res
-
- def fn():
- count0, _ = f(0)
- count10, _ = f(100)
- return count10 - count0
-
- res = llinterp_stackless_function(fn)
- assert res == 100
-
- res = run_stackless_function(fn)
- assert res == 100
-
-def test_arguments():
- def f(n, d, t):
- if n > 0:
- res, y, z = f(n-1, d, t)
- return res, y, z
- else:
- res = rstack.stack_frames_depth(), d, t
- return res
-
- def fn():
- count0, d, t = f(0, 5.5, (1, 2))
- count10, d, t = f(10, 5.5, (1, 2))
- return count10 - count0 + int(d)
-
- res = llinterp_stackless_function(fn)
- assert res == 15
-
- res = run_stackless_function(fn)
- assert res == 15
-
-def test_stack_capture():
- def fn():
- frame = rstack.stack_capture()
- return int(bool(frame))
-
- res = llinterp_stackless_function(fn)
- assert res == 1
-
-def test_eliminate_tail_calls():
- # make sure that when unwinding the stack there are no frames saved
- # for tail calls
- def f(n):
- if n > 0:
- res = f(n-1)
- else:
- res = rstack.stack_frames_depth()
- return res
- def fn():
- count0 = f(0)
- count10 = f(10)
- return count10 - count0
- res = llinterp_stackless_function(fn)
- assert res == 0
-
-
-def test_depth_bug():
- def g(base):
- print rstack.stack_frames_depth()
- return rstack.stack_frames_depth() - base
- def fn():
- base = rstack.stack_frames_depth()
- print base
- base = rstack.stack_frames_depth()
- print base
- return g(base) + 100
- res = llinterp_stackless_function(fn)
- assert res == 101
-
-def test_depth_along_yield_frame():
-
- def h():
- x = rstack.stack_frames_depth()
- x += 1 # don't remove! otherwise it becomes a tail call
- x -= 1
- return x
-
- def g(base, lst):
- lst.append(rstack.stack_frames_depth() - base)
- #print lst
- frametop_before_5 = rstack.yield_current_frame_to_caller()
- lst.append(h())
- frametop_before_7 = frametop_before_5.switch()
- lst.append(rstack.stack_frames_depth())
- return frametop_before_7
-
- def f(base):
- lst = [rstack.stack_frames_depth() - base]
- #print lst
- frametop_before_4 = g(base, lst)
- lst.append(rstack.stack_frames_depth() - base)
- #print lst
- frametop_before_6 = frametop_before_4.switch()
- lst.append(h() - base)
- frametop_after_return = frametop_before_6.switch()
- lst.append(rstack.stack_frames_depth() - base)
- assert not frametop_after_return
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- def loop(base, n):
- if n > 0:
- return loop(base, n-1) + 1
- else:
- return f(base) + 1
-
- def entrypoint():
- base = rstack.stack_frames_depth()
- return loop(base, 5) - 6
-
- data = llinterp_stackless_function(entrypoint)
- assert data == 7874837
-
- res = run_stackless_function(entrypoint)
- assert res == 7874837
-
-def test_get_set_stack_depth_limit():
- def f():
- rstack.set_stack_depth_limit(12321)
- return rstack.get_stack_depth_limit()
- data = llinterp_stackless_function(f, assert_unwind=False)
- assert data == 12321
-
-def test_stack_limit():
- def g():
- return rstack.stack_frames_depth()
- def f():
- rstack.set_stack_depth_limit(1)
- try:
- return g()
- except RuntimeError:
- return -123
- data = llinterp_stackless_function(f)
- assert data == -123
-
-def test_stack_limit_2():
- def g():
- return rstack.stack_frames_depth()
- def f():
- rstack.stack_frames_depth()
- rstack.set_stack_depth_limit(1)
- try:
- return g()
- except RuntimeError:
- return -123
- data = llinterp_stackless_function(f)
- assert data == -123
diff --git a/pypy/translator/stackless/test/test_transform.py b/pypy/translator/stackless/test/test_transform.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_transform.py
+++ /dev/null
@@ -1,302 +0,0 @@
-import py
-import os
-from pypy.translator.stackless.transform import StacklessTransformer, FrameTyper
-from pypy.translator.c.genc import CStandaloneBuilder
-from pypy.translator.c import gc
-from pypy.translator.unsimplify import varoftype
-from pypy.rpython.lltypesystem import lltype, llmemory
-from pypy.rpython import llinterp
-from pypy.rlib import rstack
-from pypy.translator.translator import TranslationContext, graphof
-from pypy.objspace.flow.model import checkgraph
-from pypy.annotation import model as annmodel
-from pypy.annotation.listdef import s_list_of_strings
-from pypy import conftest
-
-def test_frame_typer():
- class TestFrameTyper(FrameTyper):
- def saving_function_for_type(self, frame_type):
- return None
- ft = TestFrameTyper()
- ft4vars = lambda types:ft.frame_type_for_vars(types)[0]
-
- signed = varoftype(lltype.Signed)
- ptr = varoftype(lltype.Ptr(lltype.GcStruct("S")))
- addr = varoftype(llmemory.Address)
- float = varoftype(lltype.Float)
- longlong = varoftype(lltype.SignedLongLong)
-
-
- s1_1 = ft4vars([signed])
- assert 'header' in s1_1._flds
- assert len(s1_1._flds) == 2
- s1_2 = ft4vars([signed])
- assert s1_1 is s1_2
-
- s2_1 = ft4vars([signed, ptr])
- s2_2 = ft4vars([ptr, signed])
-
- assert s2_1 is s2_2
-
-def factorial(n):
- if n > 1:
- return factorial(n-1) * n
- else:
- return 1
-
-def one(): # but the annotator doesn't know it's one
- return factorial(5) / 120
-
-
-def test_nothing():
- def fn():
- return 21
- res = llinterp_stackless_function(fn, assert_unwind=False)
- assert res == 21
- info = py.test.raises(
- llinterp.LLException,
- "llinterp_stackless_function(fn, assert_unwind=True)")
- assert ''.join(info.value.args[0].name).strip('\x00') == "AssertionError"
-
-def test_simple_transform_llinterp():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- return x + 1
- def example():
- return g(one()) + 1
- res = llinterp_stackless_function(example)
- assert res == 3
-
-def test_simple_transform_llinterp_float():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- return x + 0.125
- def example():
- return int((g(one()) + 1)*1000.0)
- res = llinterp_stackless_function(example)
- assert res == 2125
-
-def test_simple_transform_compiled():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- return x + 1
- def example():
- return g(one()) + 1
- res = run_stackless_function(example)
- assert res == 3
-
-def test_protected_call():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- return x + 1
- def example():
- try:
- y = g(one())
- except Exception:
- y = -1
- return y + 1
- res = llinterp_stackless_function(example)
- assert res == 3
- res = run_stackless_function(example)
- assert res == 3
-
-def test_resume_with_exception():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- if x:
- raise KeyError
- else:
- return x + 1
- def h(x):
- return g(x)
- def example():
- y = h(one())
- return y + 1
- info = py.test.raises(
- llinterp.LLException,
- "llinterp_stackless_function(example)")
- assert llinterp.type_name(info.value.args[0]) == 'KeyError'
-
-def test_resume_with_exception_handling():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- if x:
- raise KeyError
- else:
- return x + 1
- def h(x):
- return g(x)
- def example():
- try:
- y = h(one())
- except KeyError:
- y = -one()
- return y + 1
- res = llinterp_stackless_function(example)
- assert res == 0
-
-def test_resume_with_exception_handling_with_vals():
- def check(x):
- if x:
- rstack.stack_unwind()
- def g(x):
- check(x)
- if x:
- raise KeyError
- else:
- return x + 1
- def h(x):
- return g(x)
- def example():
- y = one()
- try:
- y = h(one())
- except KeyError:
- y = y - 2
- return y + 1
- res = llinterp_stackless_function(example)
- assert res == 0
-
-def test_listcomp():
- def check(x):
- if x:
- rstack.stack_unwind()
- def f():
- l = one()
- check(l)
- return len([x for x in range(l)])
- res = llinterp_stackless_function(f)
- assert res == 1
-
-def test_constant_on_link():
- class A(object):
- pass
- def stuff(m):
- if m > 100:
- raise KeyError
- a = A()
- rstack.stack_unwind()
- a.m = m + 5
- return a
- def g(n, m):
- a = A()
- if m > 0:
- try:
- a = stuff(m)
- except KeyError:
- return -1
- n = 100
- return a.m + n
- def f():
- return g(one(), one())
- res = llinterp_stackless_function(f)
- assert res == 106
-
-def test_dont_transform_too_much():
- def check(x):
- if x:
- rstack.stack_unwind()
- def f(x):
- return x + 2
- def g(x):
- check(x)
- return f(x) + x + 1
- def example():
- return g(one()) + 1
- res, t = llinterp_stackless_function(example, returntranslator=True)
- assert res == 6
-
- ggraph = graphof(t, g)
- for block, op in ggraph.iterblockops():
- if op.opname == 'direct_call':
- if op.args[0].value._obj._callable is f:
- assert op != block.operations[-1]
-
-def test_void_around():
- def f():
- return 6
- def getf():
- return f
- def g():
- f1 = getf()
- for i in range(5):
- rstack.stack_unwind()
- return f1
- def example():
- return g()()
- res = llinterp_stackless_function(example)
- assert res == 6
-
-def rtype_stackless_function(fn):
- t = TranslationContext()
- t.config.translation.stackless = True
- annotator = t.buildannotator()
- annotator.policy.allow_someobjects = False
-
- s_returnvar = annotator.build_types(fn, [s_list_of_strings])
- if not isinstance(s_returnvar, annmodel.SomeInteger):
- raise Exception, "this probably isn't going to work"
- t.buildrtyper().specialize()
-
- from pypy.translator.transform import insert_ll_stackcheck
- insert_ll_stackcheck(t)
-
-# if conftest.option.view:
-# t.view()
- return t
-
-def run_stackless_function(fn):
- def entry_point(argv):
- r = fn()
- os.write(1, str(r)+'\n')
- return 0
-
- t = rtype_stackless_function(entry_point)
-
- cbuilder = CStandaloneBuilder(t, entry_point, config=t.config,
- gcpolicy=gc.BoehmGcPolicy)
- cbuilder.generate_source()
- if conftest.option.view:
- t.view()
- cbuilder.compile()
- res = cbuilder.cmdexec('')
- return int(res.strip())
-
-def llinterp_stackless_function(fn, returntranslator=False,
- assert_unwind=True):
- def wrapper(argv):
- return fn()
- t = rtype_stackless_function(wrapper)
- st = StacklessTransformer(t, wrapper, assert_unwind=assert_unwind)
- st.transform_all()
- if conftest.option.view:
- t.view()
-
- graph = graphof(t, st.slp_entry_point)
- r_list_of_strings = t.rtyper.getrepr(
- t.annotator.binding(graph.startblock.inputargs[0]))
- ll_list = r_list_of_strings.convert_const([''])
- interp = llinterp.LLInterpreter(t.rtyper)
- res = interp.eval_graph(graph, [ll_list])
- if returntranslator:
- return res, t
- else:
- return res
diff --git a/pypy/translator/stackless/test/test_yield_current_frame_to_caller.py b/pypy/translator/stackless/test/test_yield_current_frame_to_caller.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_yield_current_frame_to_caller.py
+++ /dev/null
@@ -1,88 +0,0 @@
-from pypy.translator.stackless.test.test_transform import \
- llinterp_stackless_function, run_stackless_function
-from pypy.rlib import rstack
-import py
-import os
-
-
-def test_simple():
- def f():
- c = g()
- assert c
- return 1
- def g():
- d = rstack.yield_current_frame_to_caller()
- return d
-
- data = llinterp_stackless_function(f)
- assert data == 1
-
- res = run_stackless_function(f)
- assert res == 1
-
-def test_switch():
- def f():
- c = g()
- c.switch()
- return 1
- def g():
- d = rstack.yield_current_frame_to_caller()
- return d
-
- data = llinterp_stackless_function(f)
- assert data == 1
-
- res = run_stackless_function(f)
- assert res == 1
-
-def test_yield_frame():
-
- def g(lst):
- lst.append(2)
- frametop_before_5 = rstack.yield_current_frame_to_caller()
- lst.append(4)
- frametop_before_7 = frametop_before_5.switch()
- lst.append(6)
- return frametop_before_7
-
- def f():
- lst = [1]
- frametop_before_4 = g(lst)
- lst.append(3)
- frametop_before_6 = frametop_before_4.switch()
- lst.append(5)
- frametop_after_return = frametop_before_6.switch()
- lst.append(7)
- assert not frametop_after_return
- n = 0
- for i in lst:
- n = n*10 + i
- return n
-
- data = llinterp_stackless_function(f)
- assert data == 1234567
-
- res = run_stackless_function(f)
- assert res == 1234567
-
-
-def test_frame_none_mix():
- def h(flag):
- if flag:
- c = g()
- else:
- c = None
- return c
- def f():
- return bool(h(False)) * 2 + bool(h(True))
- def g():
- d = rstack.yield_current_frame_to_caller()
- return d
-
- data = llinterp_stackless_function(f)
- assert data == 1
-
- res = run_stackless_function(f)
- assert res == 1
-
-
diff --git a/pypy/translator/stackless/transform.py b/pypy/translator/stackless/transform.py
deleted file mode 100644
--- a/pypy/translator/stackless/transform.py
+++ /dev/null
@@ -1,992 +0,0 @@
-from pypy.rpython.lltypesystem import lltype, llmemory
-from pypy.rpython.lltypesystem.lloperation import LL_OPERATIONS
-from pypy.rlib import rarithmetic
-from pypy.rpython import rclass, rmodel
-from pypy.translator.unsimplify import split_block
-from pypy.objspace.flow import model
-from pypy.translator import unsimplify, simplify
-from pypy.translator.unsimplify import varoftype
-from pypy.annotation import model as annmodel
-from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
-from pypy.translator.stackless import code, frame
-from pypy.rpython.rclass import getinstancerepr
-from pypy.rpython.rbuiltin import gen_cast
-from pypy.rpython.rtyper import LowLevelOpList
-from pypy.rlib.objectmodel import ComputedIntSymbolic
-from pypy.translator.backendopt import graphanalyze
-
-from pypy.translator.stackless.frame import SAVED_REFERENCE, STORAGE_TYPES
-from pypy.translator.stackless.frame import STORAGE_FIELDS
-from pypy.translator.stackless.frame import STATE_HEADER, null_state
-from pypy.translator.stackless.frame import storage_type
-
-SAVE_STATISTICS = False
-
-# we do this _a lot_:
-def copyvar(var):
- if isinstance(var, model.Variable):
- return unsimplify.copyvar(None, var)
- else:
- return varoftype(var.concretetype)
-
-def copy_link_with_varmap(link, varmap):
- varmap = varmap.copy()
- def rename(arg):
- if isinstance(arg, model.Variable):
- if arg in varmap:
- return varmap[arg]
- else:
- assert arg in [link.last_exception, link.last_exc_value]
- r = copyvar(arg)
- varmap[arg] = r
- return r
- else:
- return arg
- return link.copy(rename)
-
-if SAVE_STATISTICS:
- import cStringIO
-
- class StacklessStats:
- def __init__(self):
- self.rp_count = 0
- self.rp_type_counts = {}
- self.rp_per_graph = {}
- self.rp_per_graph_type_counts = {}
- self.saveops = self.resumeops = 0
- self.pot_exact_saves = {}
- self.total_pot_exact_saves = 0
- self.pot_erased_saves = {}
- self.total_pot_erased_saves = 0
- self.saved_retrieval_ops = 0
- self.saved_cast_ops = 0
- self.saved_return_ops = 0
- def __repr__(self):
- s = cStringIO.StringIO()
- print >> s, self.__class__.__name__
- for k in sorted(self.__dict__.keys()):
- r = repr(self.__dict__[k])
- if len(r) > 60:
- r = r[:50] + '...'
- print >>s, ' '+k, r
- return s.getvalue()
-
- def inc(d, key):
- d[key] = d.get(key, 0) + 1
-
- def gkey(graph):
- return (graph.name, id(graph))
-
-# a simple example of what the stackless transform does
-#
-# def func(x):
-# return g() + x + 1
-#
-# STATE_func_0 = lltype.Struct('STATE_func_0',
-# ('header', STATE_HEADER),
-# ('saved_long_0', Signed))
-#
-# def func(x):
-# state = global_state.restart_substate
-# if state == -1: # normal case
-# try:
-# retval = g(x)
-# except code.UnwindException, u:
-# state = lltype.malloc(STATE_func_0, flavor='gc_nocollect')
-# state.header.f_restart = <index in array of frame.FRAMEINFO>
-# state.saved_long_0 = x
-# code.add_frame_state(u, state.header)
-# raise
-# elif state == 0:
-# global_state.restart_substate = -1
-# state = lltype.cast_pointer(lltype.Ptr(STATE_func_0),
-# global_state.top)
-# global_state.top = null_state
-# x = state.saved_long_0
-# retval = code.fetch_retval_long() # can raise an exception
-# elif state == 1:
-# ...
-# elif state == 2:
-# ...
-# else:
-# abort()
-# return retval + x + 1
-
-
-# the strategy for sharing parts of the resume code:
-#
-# when a function is being resumed, there are three things that need to
-# be done: the values (as erased types) need to be read out of the
-# frame state structure, the return value needs to be read out of
-# the global_state (this is also when the check is made if we are
-# resuming with an exception) and the return value might need to be
-# cast to an exact type. our strategy is to do each of these things
-# in separate blocks, reusing blocks where we can. the retrieval and
-# cast blocks will (conceptually at least) have a switch on the
-# restate subcase at the end of it.
-#
-# note that we order types by the index of the erased type in
-# STORAGE_TYPES, to increase the chance that we can reuse the types.
-#
-# in simple cases this approach creates graphs that are more
-# complicated than needed, so we run the graph through a few
-# simplifications to join blocks and remove unused variables.
-
-
-class FrameTyper:
- # this class only exists independently to ease testing
- def __init__(self, stackless_gc=False, transformer=None):
- self.frametypes = {}
- self.stackless_gc = stackless_gc
- self.transformer = transformer
-
- def _key_for_types(self, TYPES):
- counts = {}
- for EXACT_TYPE in TYPES:
- if EXACT_TYPE is lltype.Void:
- continue
- ERASED_TYPE = storage_type(EXACT_TYPE)
- counts[ERASED_TYPE] = counts.get(ERASED_TYPE, 0) + 1
- key = lltype.frozendict(counts)
- return key
-
- def saving_function_for_type(self, FRAME_TYPE):
- v_exception = varoftype(self.transformer.unwind_exception_type)
- v_restart = varoftype(lltype.Signed)
-
- save_block = model.Block([v_exception, v_restart])
-
- llops = LowLevelOpList()
- flags = {'flavor': 'gc'}
- if self.stackless_gc:
- flags['nocollect'] = True
- v_state = llops.genop('malloc',
- [model.Constant(FRAME_TYPE, lltype.Void),
- model.Constant(flags, lltype.Void)],
- resulttype=lltype.Ptr(FRAME_TYPE))
-
- for fieldname in FRAME_TYPE._names[1:]: # skip the 'header' field
- v_arg = varoftype(FRAME_TYPE._flds[fieldname])
- save_block.inputargs.append(v_arg)
- llops.genop('setfield',
- [v_state, model.Constant(fieldname, lltype.Void), v_arg],
- resulttype=lltype.Void)
-
- v_header = gen_cast(llops, lltype.Ptr(STATE_HEADER), v_state)
- llops.genop('direct_call',
- [self.transformer.add_frame_state_ptr, v_exception, v_header],
- resulttype=lltype.Void)
- llops.genop("setfield",
- [v_header, self.transformer.c_f_restart_name, v_restart],
- resulttype=lltype.Void)
-
- save_state_graph = model.FunctionGraph('save_' + FRAME_TYPE._name, save_block,
- varoftype(lltype.Void))
- save_block.operations = llops
- save_block.closeblock(model.Link([v_header], save_state_graph.returnblock))
-
- FUNC_TYPE = lltype.FuncType([v.concretetype for v in save_block.inputargs],
- lltype.Void)
- return lltype.functionptr(FUNC_TYPE, save_state_graph.name,
- graph=save_state_graph)
-
-
- def frame_type_for_vars(self, vars):
- key = self._key_for_types([v.concretetype for v in vars])
- if key not in self.frametypes:
- fields = []
- tcounts = []
- for t in STORAGE_TYPES:
- tcount = key.get(t, 0)
- tcounts.append(str(tcount))
- for j in range(tcount):
- fname = 'state_%s_%d' % (STORAGE_FIELDS[t], j)
- fields.append((fname, t))
-
- FRAME_TYPE = frame.make_state_header_type(
- "FrameState_"+'_'.join(tcounts), *fields)
- self.frametypes[key] = (FRAME_TYPE, self.saving_function_for_type(FRAME_TYPE))
-
- T, save_state_funcptr = self.frametypes[key]
- varsforcall = list(vars)
- def key(v):
- return STORAGE_TYPES.index(storage_type(v.concretetype))
- def mycmp(x, y):
- return cmp(key(x), key(y))
- varsforcall.sort(mycmp)
- return T, varsforcall, save_state_funcptr
-
- def ensure_frame_type_for_types(self, FRAME_TYPE):
- assert len(FRAME_TYPE._names[1:]) <= 1, "too lazy"
- if len(FRAME_TYPE._names[1:]) == 1:
- fname, = FRAME_TYPE._names[1:]
- T = FRAME_TYPE._flds[fname]
- key = self._key_for_types([T])
- else:
- key = self._key_for_types([])
- if key in self.frametypes:
- assert self.frametypes[key][0] is FRAME_TYPE
- self.frametypes[key] = (FRAME_TYPE, self.saving_function_for_type(FRAME_TYPE))
-
-
-class StacklessAnalyzer(graphanalyze.BoolGraphAnalyzer):
- def __init__(self, translator, stackless_gc):
- graphanalyze.GraphAnalyzer.__init__(self, translator)
- self.stackless_gc = stackless_gc
-
- def analyze_simple_operation(self, op, graphinfo):
- if op.opname in ('yield_current_frame_to_caller', 'stack_frames_depth',
- 'stack_switch', 'stack_unwind', 'stack_capture',
- 'get_stack_depth_limit', 'set_stack_depth_limit'):
- return True
- if self.stackless_gc:
- if op.opname in ('malloc', 'malloc_varsize'):
- flags = op.args[1].value
- return flags['flavor'] == 'gc' and not flags.get('nocollect', False)
- return LL_OPERATIONS[op.opname].canunwindgc
- return False
-
- def analyze_external_call(self, op, seen=None):
- # An external call cannot cause a stack unwind
- # Note that this is essential to get good performance in framework GCs
- # because there is a pseudo-external call to ROUND_UP_FOR_ALLOCATION
- # in critical paths there.
- return False
-
-
-def vars_to_save(block):
- lastresult = block.operations[-1].result
- args = []
- for l in block.exits:
- for arg in l.args:
- if isinstance(arg, model.Variable) \
- and arg is not lastresult \
- and arg not in args \
- and arg not in [l.last_exception, l.last_exc_value]:
- args.append(arg)
- return args
-
-class StacklessTransformer(object):
-
- def __init__(self, translator, entrypoint,
- stackless_gc=False, assert_unwind=False):
- self.translator = translator
- self.stackless_gc = stackless_gc
-
- self.frametyper = FrameTyper(stackless_gc, self)
- self.masterarray1 = []
- self.curr_graph = None
-
- self.signaturecodes = [{} for RETTYPE in frame.STORAGE_TYPES]
- # self.signaturecodes[n] is a dict {ARGTYPES: signature_index}
- # where n is the return type as an index in STORAGE_TYPES.
- # the signature_index is an arbitrary number but it encodes
- # the type of the result, i.e.
- # n == (signature_index & storage_type_bitmask)
-
- bk = translator.annotator.bookkeeper
-
- self.unwind_exception_type = getinstancerepr(
- self.translator.rtyper,
- bk.getuniqueclassdef(code.UnwindException)).lowleveltype
- self.analyzer = StacklessAnalyzer(translator, stackless_gc)
-
- # the point of this little dance is to not annotate
- # code.global_state.masterarray as a constant.
- data_classdef = bk.getuniqueclassdef(code.StacklessData)
- data_classdef.generalize_attr(
- 'masterarray',
- annmodel.SomePtr(lltype.Ptr(frame.FRAME_INFO_ARRAY)))
-
- mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
- l2a = annmodel.lltype_to_annotation
-
- if assert_unwind:
- def slp_entry_point(argv):
- try:
- r = entrypoint(argv)
- except code.UnwindException, u:
- code.slp_main_loop(u.depth)
- return code.global_state.retval_long
- else:
- assert False, "entrypoint never unwound the stack"
- return r
- slp_entry_point.stackless_explicit = True
- else:
- def slp_entry_point(argv):
- try:
- r = entrypoint(argv)
- except code.UnwindException, u:
- code.slp_main_loop(u.depth)
- return code.global_state.retval_long
- return r
- slp_entry_point.stackless_explicit = True
-
- self.slp_entry_point = slp_entry_point
- oldgraph = bk.getdesc(entrypoint).getuniquegraph()
- s_argv = translator.annotator.binding(oldgraph.getargs()[0])
- self.slp_entry_point_ptr = mixlevelannotator.constfunc(
- slp_entry_point, [s_argv], annmodel.SomeInteger())
-
- unwinddef = bk.getuniqueclassdef(code.UnwindException)
- self.add_frame_state_ptr = mixlevelannotator.constfunc(
- code.add_frame_state,
- [annmodel.SomeInstance(unwinddef),
- annmodel.SomePtr(lltype.Ptr(STATE_HEADER))],
- l2a(lltype.Void))
-
- # order really matters on 64 bits machines on which
- # longlong==signed; so lltype.Signed must appear *after*
- # longlong in this dict
- self.fetch_retvals = {
- lltype.Void: mixlevelannotator.constfunc(
- code.fetch_retval_void, [], annmodel.s_None),
- lltype.SignedLongLong: mixlevelannotator.constfunc(
- code.fetch_retval_longlong, [], annmodel.SomeInteger(knowntype=rarithmetic.r_longlong)),
- lltype.Signed: mixlevelannotator.constfunc(
- code.fetch_retval_long, [], annmodel.SomeInteger()),
- lltype.Float: mixlevelannotator.constfunc(
- code.fetch_retval_float, [], annmodel.SomeFloat()),
- llmemory.Address: mixlevelannotator.constfunc(
- code.fetch_retval_addr, [], annmodel.SomeAddress()),
- SAVED_REFERENCE: mixlevelannotator.constfunc(
- code.fetch_retval_ref, [], annmodel.SomePtr(SAVED_REFERENCE)),
- }
-
- s_StatePtr = annmodel.SomePtr(frame.OPAQUE_STATE_HEADER_PTR)
-
- self.operation_replacement = {
- 'yield_current_frame_to_caller': mixlevelannotator.constfunc(
- code.yield_current_frame_to_caller, [], s_StatePtr),
- 'stack_frames_depth': mixlevelannotator.constfunc(
- code.stack_frames_depth, [], annmodel.SomeInteger()),
- 'stack_switch': mixlevelannotator.constfunc(
- code.ll_frame_switch, [s_StatePtr], s_StatePtr),
- 'stack_unwind': mixlevelannotator.constfunc(
- code.ll_stack_unwind, [], annmodel.s_None),
- 'stack_capture': mixlevelannotator.constfunc(
- code.ll_stack_capture, [], s_StatePtr),
- 'get_stack_depth_limit': mixlevelannotator.constfunc(
- code.ll_get_stack_depth_limit, [], annmodel.SomeInteger()),
- 'set_stack_depth_limit': mixlevelannotator.constfunc(
- code.ll_set_stack_depth_limit, [annmodel.SomeInteger()],
- annmodel.s_None),
- }
-
-
- s_hdrptr = annmodel.SomePtr(lltype.Ptr(STATE_HEADER))
- # order really matters on 64 bits machines on which
- # longlong==signed; so lltype.Signed must appear *after*
- # longlong in this dict
- self.resume_afters = {
- lltype.Void: mixlevelannotator.constfunc(
- code.resume_after_void,
- [s_StatePtr, annmodel.s_None],
- annmodel.s_None),
- lltype.SignedLongLong: mixlevelannotator.constfunc(
- code.resume_after_longlong,
- [s_StatePtr, annmodel.SomeInteger(knowntype=rarithmetic.r_longlong)],
- annmodel.s_None),
- lltype.Signed: mixlevelannotator.constfunc(
- code.resume_after_long,
- [s_StatePtr, annmodel.SomeInteger()],
- annmodel.s_None),
- lltype.Float: mixlevelannotator.constfunc(
- code.resume_after_float,
- [s_StatePtr, annmodel.SomeFloat()],
- annmodel.s_None),
- llmemory.Address: mixlevelannotator.constfunc(
- code.resume_after_addr,
- [s_StatePtr, annmodel.SomeAddress()],
- annmodel.s_None),
- SAVED_REFERENCE: mixlevelannotator.constfunc(
- code.resume_after_ref,
- [s_StatePtr, annmodel.SomePtr(SAVED_REFERENCE)],
- annmodel.s_None),
- }
- exception_def = bk.getuniqueclassdef(Exception)
- self.resume_after_raising_ptr = mixlevelannotator.constfunc(
- code.resume_after_raising,
- [s_StatePtr, annmodel.SomeInstance(exception_def)],
- annmodel.s_None)
- self.exception_type = getinstancerepr(
- self.translator.rtyper, exception_def).lowleveltype
-
- def set_back_pointer(frame, back):
- frame.f_back = back
- if back:
- frame.f_depth = back.f_depth + 1
- else:
- frame.f_depth = 0
- self.set_back_pointer_ptr = mixlevelannotator.constfunc(
- set_back_pointer,
- [s_hdrptr, s_hdrptr],
- annmodel.s_None)
-
- mixlevelannotator.finish()
-
- s_global_state = bk.immutablevalue(code.global_state)
- r_global_state = translator.rtyper.getrepr(s_global_state)
- self.ll_global_state = model.Constant(
- r_global_state.convert_const(code.global_state),
- r_global_state.lowleveltype)
- self.seen_blocks = set()
-
- # some prebuilt constants to save memory
- self.c_restart_substate_name = model.Constant("inst_restart_substate",
- lltype.Void)
- self.c_inst_top_name = model.Constant("inst_top", lltype.Void)
- self.c_f_restart_name = model.Constant("f_restart", lltype.Void)
- self.c_minus_one = model.Constant(-1, lltype.Signed)
- self.c_null_state = model.Constant(null_state,
- lltype.typeOf(null_state))
- self.c_gc_nocollect = model.Constant({'flavor': 'gc', 'nocollect': True}, lltype.Void)
-
- self.is_finished = False
-
- # the mauling of frame_typer internals should be a method on FrameTyper.
- for restartinfo in frame.RestartInfo.prebuilt:
- name = restartinfo.func_or_graph.__name__
- for i in range(len(restartinfo.frame_types)):
- frame_type = restartinfo.frame_types[i]
- self.frametyper.ensure_frame_type_for_types(frame_type)
- self.register_restart_info(restartinfo)
-
- if SAVE_STATISTICS:
- translator.stackless_stats = self.stats = StacklessStats()
-
- def transform_all(self):
- for graph in self.translator.graphs:
- self.transform_graph(graph)
- self.finish()
-
- def transform_graph(self, graph):
- self.resume_blocks = []
-
- if hasattr(graph, 'func'):
- if getattr(graph.func, 'stackless_explicit', False):
- if self.stackless_gc:
- self.transform_gc_nocollect(graph)
- return
-
- if not self.analyzer.analyze_direct_call(graph):
- return
-
- assert self.curr_graph is None
- self.curr_graph = graph
- self.curr_graph_save_blocks = {}
- self.curr_graph_resume_retrieval_blocks = {}
- self.curr_graph_resume_return_blocks = {}
- self.curr_graph_resume_cast_blocks = {}
-
- if SAVE_STATISTICS:
- self.stats.cur_rp_exact_types = {}
- self.stats.cur_rp_erased_types = {}
-
- for block in list(graph.iterblocks()):
- assert block not in self.seen_blocks
- self.transform_block(block)
- self.seen_blocks.add(block)
-
- if self.resume_blocks:
- self.insert_resume_handling(graph)
- self.generate_restart_infos(graph)
-
- if SAVE_STATISTICS:
- pot_exact_save_count = 0
- for t, count in self.stats.cur_rp_exact_types.items():
- pot_exact_save_count += count - 1
- del self.stats.cur_rp_exact_types
- self.stats.pot_exact_saves[gkey(self.curr_graph)] = pot_exact_save_count
- self.stats.total_pot_exact_saves += pot_exact_save_count
-
- pot_erased_save_count = 0
- for t, count in self.stats.cur_rp_erased_types.items():
- pot_erased_save_count += count - 1
- del self.stats.cur_rp_erased_types
- self.stats.pot_erased_saves[gkey(self.curr_graph)] = pot_erased_save_count
- self.stats.total_pot_erased_saves += pot_erased_save_count
-
- self.curr_graph = None
- self.curr_graph_save_blocks = None
- self.curr_graph_resume_retrieval_blocks = None
- self.curr_graph_resume_return_blocks = None
- self.curr_graph_resume_cast_blocks = None
-
- def insert_resume_handling(self, graph):
- old_start_block = graph.startblock
- newinputargs = [unsimplify.copyvar(self.translator.annotator, v)
- for v in old_start_block.inputargs]
- new_start_block = model.Block(newinputargs)
- v_resume_substate = varoftype(lltype.Signed)
- new_start_block.operations.append(
- model.SpaceOperation("getfield",
- [self.ll_global_state,
- self.c_restart_substate_name],
- v_resume_substate))
- not_resuming_link = model.Link(newinputargs, old_start_block, -1)
- not_resuming_link.llexitcase = -1
- resuming_links = []
- for resume_index, resume_block in enumerate(self.resume_blocks):
- resuming_links.append(
- model.Link([v_resume_substate], resume_block, resume_index))
- resuming_links[-1].llexitcase = resume_index
-
- new_start_block.exitswitch = v_resume_substate
- new_start_block.closeblock(not_resuming_link, *resuming_links)
-
- old_start_block.isstartblock = False
- new_start_block.isstartblock = True
- graph.startblock = new_start_block
-
- for block in graph.iterblocks():
- if len(block.exits) == 1 and block.exitswitch is not None:
- block.exitswitch = None
- block.exits[0].exitcase = block.exits[0].llexitcase = None
- simplify.simplify_graph(graph, [simplify.eliminate_empty_blocks,
- simplify.join_blocks,
- simplify.transform_dead_op_vars])
-
- def insert_return_conversion(self, link, targettype, retvar):
- llops = LowLevelOpList()
- newvar = gen_cast(llops, targettype, retvar)
- convertblock = unsimplify.insert_empty_block(None, link, llops)
- # begin ouch!
- for index, linkvar in enumerate(convertblock.exits[0].args):
- # does this var come from retval ?
- try:
- index1 = convertblock.inputargs.index(linkvar)
- except ValueError: # e.g. linkvar is a Constant
- continue
- if link.args[index1] is retvar:
- # yes
- convertblock.exits[0].args[index] = newvar
- # end ouch!
-
- def insert_unwind_handling(self, block, i):
- # for the case where we are resuming to an except:
- # block we need to store here a list of links that
- # might be resumed to, and in insert_resume_handling
- # we need to basically copy each link onto the
- # resuming block.
- #
- # it probably also makes sense to compute the list of
- # args to save once, here, and save that too.
- #
- # finally, it is important that the fetch_retval
- # function be called right at the end of the resuming
- # block, and that it is called even if the return
- # value is not again used.
-
- edata = self.translator.rtyper.getexceptiondata()
- etype = edata.lltype_of_exception_type
- evalue = edata.lltype_of_exception_value
-
- if i == len(block.operations) - 1 \
- and block.exitswitch == model.c_last_exception:
- link = block.exits[0]
- exitcases = dict.fromkeys([l.exitcase for l in block.exits])
- nextblock = None
- else:
- link = split_block(None, block, i+1)
- nextblock = link.target
- block.exitswitch = model.c_last_exception
- link.llexitcase = None
- # add a general Exception link, because all calls can
- # raise anything
- v_exctype = varoftype(etype)
- v_excvalue = varoftype(evalue)
- newlink = model.Link([v_exctype, v_excvalue],
- self.curr_graph.exceptblock,
- Exception)
- newlink.last_exception = v_exctype
- newlink.last_exc_value = v_excvalue
- newexits = list(block.exits)
- newexits.append(newlink)
- block.recloseblock(*newexits)
- self.translator.rtyper._convert_link(block, newlink)
-
- v_unwind_exception = varoftype(evalue)
-
- op = block.operations[i]
-
- args = vars_to_save(block)
-
- save_block, varsforcall = self.generate_save_and_resume_blocks(
- args, v_unwind_exception, op.result, block.exits)
-
- newlink = model.Link(varsforcall + [v_unwind_exception],
- save_block, code.UnwindException)
- newlink.last_exception = model.Constant(code.UnwindException,
- etype)
- newlink.last_exc_value = v_unwind_exception
- newexits = list(block.exits)
- newexits.insert(1, newlink)
- block.recloseblock(*newexits)
- self.translator.rtyper._convert_link(block, newlink)
-
- return nextblock
-
- def transform_block(self, block):
- i = 0
-
- def replace_with_call(fnptr):
- args = [fnptr] + op.args
- newop = model.SpaceOperation('direct_call', args, op.result)
- block.operations[i] = newop
- return newop
-
- while i < len(block.operations):
- stackless_op = False
- op = block.operations[i]
-
- if op.opname in self.operation_replacement:
- op = replace_with_call(self.operation_replacement[op.opname])
- stackless_op = True
-
- if (op.opname in ('direct_call', 'indirect_call')
- or self.analyzer.analyze(op)):
-
- if not stackless_op and not self.analyzer.analyze(op):
- i += 1
- continue
-
- if (not stackless_op and i == len(block.operations) - 1 and
- len(block.exits) == 1 and
- block.exits[0].target is self.curr_graph.returnblock and
- (block.exits[0].args[0].concretetype is lltype.Void or
- block.exits[0].args[0] is op.result)):
-# print "optimizing tail call %s in function %s" % (op, self.curr_graph.name)
- i += 1
- continue
-
- nextblock = self.insert_unwind_handling(block, i)
-
- if nextblock is None:
- return
-
- block = nextblock
- i = 0
- else:
- i += 1
-
- def generate_save_and_resume_blocks(self, varstosave, v_exception,
- v_result, links_to_resumption):
- frame_type, varsforcall, saver = self.frametyper.frame_type_for_vars(varstosave)
- if SAVE_STATISTICS:
- self.stats.rp_count += 1
- inc(self.stats.rp_type_counts, frame_type)
- inc(self.stats.rp_per_graph, gkey(self.curr_graph))
- inc(self.stats.rp_per_graph_type_counts.setdefault(gkey(self.curr_graph), {}), frame_type)
- exact_key = [v.concretetype for v in varstosave]
- exact_key.sort()
- exact_key = (tuple(exact_key), v_result.concretetype)
- inc(self.stats.cur_rp_exact_types, exact_key)
- inc(self.stats.cur_rp_erased_types, frame_type)
-
- varsforcall0 = varsforcall[:]
- c_restart = model.Constant(len(self.masterarray1) + len(self.resume_blocks), lltype.Signed)
- varsforcall.insert(0, c_restart)
- varsforcall = [v for v in varsforcall if v.concretetype != lltype.Void]
-
- self._make_resume_handling(frame_type, varsforcall0,
- v_result, links_to_resumption)
-
- return (self._generate_save_block(varsforcall, v_exception, saver),
- varsforcall)
-
- def _generate_save_block(self, varsforcall, v_unwind_exception, saver):
- conc_types = tuple([v.concretetype for v in varsforcall])
- if conc_types in self.curr_graph_save_blocks:
- return self.curr_graph_save_blocks[conc_types]
- rtyper = self.translator.rtyper
- edata = rtyper.getexceptiondata()
- etype = edata.lltype_of_exception_type
- evalue = edata.lltype_of_exception_value
- inputargs = [copyvar(v) for v in varsforcall]
- v_unwind_exception = copyvar(v_unwind_exception)
-
- save_state_block = model.Block(inputargs + [v_unwind_exception])
- saveops = LowLevelOpList()
-
- v_exc = gen_cast(saveops, self.unwind_exception_type, v_unwind_exception)
-
- realvarsforcall = [v_exc]
- for v in inputargs:
- realvarsforcall.append(gen_cast(saveops, storage_type(v.concretetype), v))
-
- saveops.genop('direct_call',
- [model.Constant(saver, lltype.typeOf(saver))] + realvarsforcall,
- resulttype=lltype.Void)
- save_state_block.operations = saveops
-
- type_repr = rclass.get_type_repr(rtyper)
- c_unwindexception = model.Constant(
- type_repr.convert_const(code.UnwindException), etype)
- if not hasattr(self.curr_graph.exceptblock.inputargs[0], 'concretetype'):
- self.curr_graph.exceptblock.inputargs[0].concretetype = etype
- if not hasattr(self.curr_graph.exceptblock.inputargs[1], 'concretetype'):
- self.curr_graph.exceptblock.inputargs[1].concretetype = evalue
- save_state_block.closeblock(model.Link(
- [c_unwindexception, v_unwind_exception],
- self.curr_graph.exceptblock))
- self.translator.rtyper._convert_link(
- save_state_block, save_state_block.exits[0])
- if SAVE_STATISTICS:
- self.stats.saveops += len(save_state_block.operations)
- self.curr_graph_save_blocks[conc_types] = save_state_block
- return save_state_block
-
- def _make_resume_handling(self, FRAME_TYPE, sorted_vars, v_retval, links_to_resumption):
- resume_substate = len(self.resume_blocks)
-
- erased_types = []
- for v in sorted_vars:
- if v.concretetype != lltype.Void:
- erased_types.append(storage_type(v.concretetype))
-
- retval_type = v_retval.concretetype
- erased_retval_type = storage_type(retval_type)
-
- retrieve_block, output_args = self._get_resume_retrieval_block(FRAME_TYPE, erased_types)
-
- return_block, switch_block = self._get_resume_return_block(
- erased_types, erased_retval_type, links_to_resumption[1:], sorted_vars)
-
- link = model.Link(output_args, return_block, resume_substate)
- link.llexitcase = link.exitcase
- retrieve_block.recloseblock(*(tuple(retrieve_block.exits) + (link,)))
-
- if erased_retval_type != lltype.Void:
- erased_types.append(erased_retval_type)
- cast_block, cast_args = self._get_resume_cast_block(
- erased_types,
- [v.concretetype for v in sorted_vars] + [retval_type])
-
- link = model.Link(switch_block.inputargs, cast_block, resume_substate)
- link.llexitcase = resume_substate
- switch_block.recloseblock(*(tuple(switch_block.exits) + (link,)))
-
- varmap = dict([(v, cast_args[sorted_vars.index(v)]) for v in sorted_vars])
- for k, v in varmap.items():
- assert k.concretetype == v.concretetype
-
- varmap[v_retval] = cast_args[-1]
-
- link = copy_link_with_varmap(links_to_resumption[0], varmap)
- link.exitcase = link.llexitcase = resume_substate
- cast_block.recloseblock(*(tuple(cast_block.exits) + (link,)))
-
- self.resume_blocks.append(retrieve_block)
-
- def _make_resume_retrieval_block(self, FRAME_TYPE, erased_types):
- retrieve_block = model.Block([varoftype(lltype.Signed)])
- retrieve_block.exitswitch = retrieve_block.inputargs[0]
-
- llops = LowLevelOpList()
- llops.genop("setfield",
- [self.ll_global_state, self.c_restart_substate_name, self.c_minus_one])
- v_state_hdr = llops.genop("getfield",
- [self.ll_global_state, self.c_inst_top_name],
- resulttype=lltype.Ptr(STATE_HEADER))
- v_state = gen_cast(llops, lltype.Ptr(FRAME_TYPE), v_state_hdr)
- llops.genop("setfield",
- [self.ll_global_state, self.c_inst_top_name, self.c_null_state])
- output_args = [retrieve_block.inputargs[0]]
- assert len(FRAME_TYPE._names[1:]) == len(erased_types)
- for fieldname, TYPE in zip(FRAME_TYPE._names[1:], erased_types):
- assert FRAME_TYPE._flds[fieldname] == TYPE
- output_args.append(llops.genop("getfield",
- [v_state, model.Constant(fieldname, lltype.Void)],
- resulttype=TYPE))
- retrieve_block.operations = llops
- return retrieve_block, output_args
-
- def _get_resume_retrieval_block(self, FRAME_TYPE, erased_types):
- key = tuple(erased_types)
- if key in self.curr_graph_resume_retrieval_blocks:
- retrieve_block, output_args = self.curr_graph_resume_retrieval_blocks[key]
- if SAVE_STATISTICS:
- self.stats.saved_retrieval_ops += len(retrieve_block.operations)
- return retrieve_block, output_args
- else:
- retrieve_block, output_args = self._make_resume_retrieval_block(
- FRAME_TYPE, erased_types)
- self.curr_graph_resume_retrieval_blocks[key] = retrieve_block, output_args
- return retrieve_block, output_args
-
- def _make_resume_return_block(self, erased_types, erased_retval_type, except_links, sorted_vars):
- inputargs = [varoftype(lltype.Signed)] + [varoftype(t) for t in erased_types]
- return_block = model.Block(inputargs)
- return_block.exitswitch = model.c_last_exception
- llops = LowLevelOpList()
-
- getretval = self.fetch_retvals[erased_retval_type]
- v_retval = llops.genop("direct_call", [getretval],
- resulttype=erased_retval_type)
-
- switch_block = model.Block([copyvar(v) for v in inputargs])
- switch_block.exitswitch = switch_block.inputargs[0]
-
- retlink = model.Link(inputargs, switch_block, None)
-
- if erased_retval_type != lltype.Void:
- retlink.args.append(v_retval)
- switch_block.inputargs.append(copyvar(v_retval))
-
- links = [retlink]
-
- for except_link in except_links:
- cast_block, cast_args = self._make_cast_block(
- erased_types, [v.concretetype for v in sorted_vars])
- varmap = dict([(v, cast_args[sorted_vars.index(v)]) for v in sorted_vars])
-
- link = model.Link(inputargs[1:], cast_block, except_link.exitcase)
- link.llexitcase = except_link.llexitcase
- for attr in "last_exception", "last_exc_value":
- old = getattr(except_link, attr)
- new = copyvar(old)
- setattr(link, attr, new)
- link.args.append(new)
- newnew = copyvar(new)
- cast_block.inputargs.append(newnew)
- varmap[old] = newnew
- links.append(link)
-
- link = copy_link_with_varmap(except_link, varmap)
- link.exitcase = link.llexitcase = None
- link.last_exception = link.last_exc_value = None
- cast_block.closeblock(link)
-
- return_block.operations = llops
- return_block.closeblock(*links)
-
- return return_block, switch_block
-
- def _get_resume_return_block(self, erased_types, erased_retval_type, except_links, sorted_vars):
- key = (erased_retval_type,)
- key += tuple(erased_types)
- key += tuple([(elink.exitcase, elink.target) for elink in except_links])
- if except_links and max([len(elink.args) for elink in except_links]) > 2:
- key = None
- if key in self.curr_graph_resume_return_blocks:
- return_block, switch_block = self.curr_graph_resume_return_blocks[key]
- if SAVE_STATISTICS:
- self.stats.saved_return_ops += len(return_block.operations)
- return return_block, switch_block
- else:
- return_block, switch_block = self._make_resume_return_block(erased_types, erased_retval_type, except_links, sorted_vars)
- if key is not None:
- self.curr_graph_resume_return_blocks[key] = return_block, switch_block
- return return_block, switch_block
-
- def _make_cast_block(self, erased_types, exact_types):
- inputargs = [varoftype(t) for t in erased_types]
- cast_block = model.Block(inputargs)
- cast_block.operations = LowLevelOpList()
- output_args = []
- assert len(inputargs) == len([typ for typ in exact_types if typ != lltype.Void])
- i_arg = 0
- for typ in exact_types:
- if typ == lltype.Void:
- output_args.append(model.Constant(None, lltype.Void))
- else:
- arg = inputargs[i_arg]
- i_arg += 1
- output_args.append(gen_cast(cast_block.operations, typ, arg))
- assert i_arg == len(inputargs)
- return cast_block, output_args
-
- def _get_resume_cast_block(self, erased_vars, exact_types):
- # returns something that you should add a link to (with
- # recloseblock), and the output_args to use in that link.
- key = tuple(exact_types)
- if key in self.curr_graph_resume_cast_blocks:
- cast_block, output_args = self.curr_graph_resume_cast_blocks[key]
- if SAVE_STATISTICS:
- self.stats.saved_cast_ops += len(cast_block.operations)
- return cast_block, output_args
- else:
- cast_block, output_args = self._make_cast_block(erased_vars, exact_types)
- cast_block.inputargs.insert(0, varoftype(lltype.Signed))
- cast_block.exitswitch = cast_block.inputargs[0]
- self.curr_graph_resume_cast_blocks[key] = cast_block, output_args
- return cast_block, output_args
-
- def generate_restart_infos(self, graph):
- restartinfo = frame.RestartInfo(graph, len(self.resume_blocks))
- self.register_restart_info(restartinfo)
-
- def register_restart_info(self, restartinfo):
- assert not self.is_finished
- rtyper = self.translator.rtyper
- for frame_info in restartinfo.compress(self.signaturecodes, rtyper):
- self.masterarray1.append(frame_info)
-
- def finish(self):
- # compute the final masterarray by copying over the masterarray1,
- # which is a list of dicts of attributes
- if SAVE_STATISTICS:
- import cPickle
- cPickle.dump(self.stats, open('stackless-stats.pickle', 'wb'))
-
- # fun fun fun patching the call_function_retval_xyz() functions!
- for RESTYPE, typename in frame.STORAGE_TYPES_AND_FIELDS:
- rettype_index = STORAGE_TYPES.index(RESTYPE)
- cache = self.signaturecodes[rettype_index]
- if not cache:
- continue # not used anyway, don't produce a broken empty switch
- func = getattr(code, 'call_function_retval_' + typename)
- desc = self.translator.annotator.bookkeeper.getdesc(func)
- graph = desc.getuniquegraph()
-
- [v_fnaddr, v_signature_index] = graph.getargs()
- block = model.Block([v_fnaddr, v_signature_index])
- block.exitswitch = v_signature_index
- block.isstartblock = True
- graph.startblock = block
- switchlinks = []
-
- for ARGTYPES, signature_index in cache.items():
- # XXX because of type erasure, the following cast is
- # kind of invalid, but we hope that nobody will notice
- FUNCTYPE = lltype.Ptr(lltype.FuncType(ARGTYPES, RESTYPE))
- v_fnaddr1 = varoftype(v_fnaddr.concretetype)
- callblock = model.Block([v_fnaddr1])
- llops = LowLevelOpList()
- args_v = [model.Constant(TYPE._defl(), concretetype=TYPE)
- for TYPE in ARGTYPES]
- v_res = llops.genop('adr_call', [v_fnaddr1] + args_v,
- resulttype = RESTYPE)
- callblock.operations[:] = llops
- callblock.closeblock(model.Link([v_res], graph.returnblock))
- link = model.Link([v_fnaddr], callblock)
- link.exitcase = signature_index
- link.llexitcase = signature_index
- switchlinks.append(link)
-
- block.closeblock(*switchlinks)
- model.checkgraph(graph)
-
- self.is_finished = True
- masterarray = lltype.malloc(frame.FRAME_INFO_ARRAY,
- len(self.masterarray1),
- immortal=True)
- for dst, src in zip(masterarray, self.masterarray1):
- dst.fnaddr, dst.info = src
- # horrors in the same spirit as in rpython.memory.gctransform
- # (shorter, though)
- ll_global_state = self.ll_global_state.value
- ll_global_state.inst_masterarray = masterarray
- return [masterarray]
-
- def transform_gc_nocollect(self, graph):
- # for the framework gc: in stackless_explicit graphs, make sure
- # that the mallocs won't trigger a collect.
- for block in graph.iterblocks():
- for i, op in enumerate(block.operations):
- if op.opname.startswith('malloc'):
- newargs = op.args[:]
- newargs[1] = self.c_gc_nocollect
- newop = model.SpaceOperation(op.opname, newargs, op.result)
- block.operations[i] = newop
diff --git a/pypy/translator/test/test_stackcheck.py b/pypy/translator/test/test_stackcheck.py
--- a/pypy/translator/test/test_stackcheck.py
+++ b/pypy/translator/test/test_stackcheck.py
@@ -3,7 +3,6 @@
from pypy.translator.backendopt.all import backend_optimizations
from pypy.translator.transform import insert_ll_stackcheck
from pypy.rpython.memory.gctransform import framework
-from pypy.translator.stackless.transform import StacklessTransformer
def _follow_path_naive(block, cur_path, accum):
cur_path = (cur_path, block)
@@ -119,54 +118,3 @@
reload += 1
assert reload == 0
-
-def test_stackless():
- t = TranslationContext()
- a = t.buildannotator()
- a.build_types(g, [int])
- a.simplify()
- t.buildrtyper().specialize()
- backend_optimizations(t)
- t.checkgraphs()
- n = insert_ll_stackcheck(t)
- t.checkgraphs()
- assert n == 1
-
- t.config.translation.stackless = True
- stacklesstransf = StacklessTransformer(t, g)
-
- f_graph = graphof(t, f)
- stacklesstransf.transform_graph(f_graph)
- if conftest.option.view:
- f_graph.show()
-
- exctransf = t.getexceptiontransformer()
- exctransf.create_exception_handling(f_graph)
- if conftest.option.view:
- f_graph.show()
- check(f_graph, 'f', 'fetch_retval_void')
-
- class GCTransform(framework.FrameworkGCTransformer):
- from pypy.rpython.memory.gc.generation import GenerationGC as \
- GCClass
- GC_PARAMS = {}
-
- gctransf = GCTransform(t)
- gctransf.transform_graph(f_graph)
- if conftest.option.view:
- f_graph.show()
- relevant = check(f_graph, 'f', 'fetch_retval_void')
- for p in relevant:
- in_between = False
- reload = 0
- for spaceop in p:
- if spaceop.opname == 'direct_call':
- target = direct_target(spaceop)
- if target == 'f':
- in_between = False
- elif target == 'stack_check___':
- in_between = True
- if in_between and spaceop.opname == 'gc_reload_possibly_moved':
- reload += 1
-
- assert reload == 1
More information about the pypy-commit
mailing list