[pypy-commit] pypy stacklet: - kill the references to the stackless option
arigo
noreply at buildbot.pypy.org
Sat Aug 6 16:02:06 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46323:2fa405969a86
Date: 2011-08-06 16:01 +0200
http://bitbucket.org/pypy/pypy/changeset/2fa405969a86/
Log: - kill the references to the stackless option
- in-progress: asmgcc support for stacklets (not working correctly
so far)
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -24,10 +24,10 @@
translation_optiondescription = OptionDescription(
"translation", "Translation Options", [
- BoolOption("stackless", "enable stackless features during compilation",
- default=False, cmdline="--stackless",
+ BoolOption("stacklet", "enable stackless features during compilation",
+ default=False, cmdline="--stacklet",
requires=[("translation.type_system", "lltype"),
- ("translation.gcremovetypeptr", False)]), # XXX?
+ ("translation.gcrootfinder", "asmgcc")]), # XXX temp
ChoiceOption("type_system", "Type system to use when RTyping",
["lltype", "ootype"], cmdline=None, default="lltype",
requires={
@@ -385,8 +385,6 @@
config.translation.suggest(withsmallfuncsets=5)
elif word == 'jit':
config.translation.suggest(jit=True)
- if config.translation.stackless:
- raise NotImplementedError("JIT conflicts with stackless for now")
elif word == 'removetypeptr':
config.translation.suggest(gcremovetypeptr=True)
else:
diff --git a/pypy/rlib/_stacklet_asmgcc.py b/pypy/rlib/_stacklet_asmgcc.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/_stacklet_asmgcc.py
@@ -0,0 +1,199 @@
+from pypy.rlib import rstacklet
+from pypy.rlib.debug import ll_assert
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
+from pypy.rpython.annlowlevel import llhelper
+
+
+_asmstackrootwalker = None # BIG HACK: monkey-patched by asmgcroot.py
+_stackletrootwalker = None
+
+def get_stackletrootwalker():
+ # lazily called, to make the following imports lazy
+ global _stackletrootwalker
+ if _stackletrootwalker is not None:
+ return _stackletrootwalker
+
+ from pypy.rpython.memory.gctransform.asmgcroot import (
+ WALKFRAME, CALLEE_SAVED_REGS, sizeofaddr)
+
+ assert _asmstackrootwalker is not None, "should have been monkey-patched"
+ basewalker = _asmstackrootwalker
+
+ class StackletRootWalker(object):
+ _alloc_flavor_ = "raw"
+
+ enumerating = False
+
+ def setup(self, obj):
+ # initialization: read the SUSPSTACK object
+ p = llmemory.cast_adr_to_ptr(obj, lltype.Ptr(SUSPSTACK))
+ if not p.handle:
+ return False
+ self.context = p.handle
+ anchor = p.anchor
+ del p
+ self.curframe = lltype.malloc(WALKFRAME, flavor='raw')
+ self.otherframe = lltype.malloc(WALKFRAME, flavor='raw')
+ initialframedata = anchor[1]
+ ll_assert(initialframedata != llmemory.cast_ptr_to_adr(anchor),
+ "no anchored stacklet stack found")
+ ll_assert(initialframedata == anchor[0],
+ "more than one anchored stacklet stack found")
+ self.fill_initial_frame(self.curframe, initialframedata)
+ return True
+
+ def fill_initial_frame(self, curframe, initialframedata):
+ # Copy&paste :-(
+ initialframedata += 2*sizeofaddr
+ reg = 0
+ while reg < CALLEE_SAVED_REGS:
+ curframe.regs_stored_at[reg] = initialframedata+reg*sizeofaddr
+ reg += 1
+ retaddraddr = initialframedata + CALLEE_SAVED_REGS * sizeofaddr
+ retaddraddr = self.translateptr(retaddraddr)
+ curframe.frame_address = retaddraddr.address[0]
+
+ def teardown(self):
+ lltype.free(self.curframe, flavor='raw')
+ lltype.free(self.otherframe, flavor='raw')
+ self.context = lltype.nullptr(rstacklet.handle.TO)
+ return llmemory.NULL
+
+ def next(self, obj, prev):
+ #
+ # Pointers to the stack can be "translated" or not:
+ #
+ # * Non-translated pointers point to where the data would be
+ # if the stack was installed and running.
+ #
+ # * Translated pointers correspond to where the data
+ # is now really in memory.
+ #
+ # Note that 'curframe' contains non-translated pointers, and
+ # of course the stack itself is full of non-translated pointers.
+ #
+ while True:
+ if not self.enumerating:
+ if not prev:
+ if not self.setup(obj): # one-time initialization
+ return llmemory.NULL
+ prev = obj # random value, but non-NULL
+ callee = self.curframe
+ retaddraddr = self.translateptr(callee.frame_address)
+ retaddr = retaddraddr.address[0]
+ basewalker.locate_caller_based_on_retaddr(retaddr)
+ self.enumerating = True
+ #
+ # not really a loop, but kept this way for similarity
+ # with asmgcroot:
+ callee = self.curframe
+ while True:
+ location = basewalker._shape_decompressor.next()
+ if location == 0:
+ break
+ addr = basewalker.getlocation(callee, location)
+ # yield the translated addr of the next GCREF in the stack
+ return self.translateptr(addr)
+ #
+ self.enumerating = False
+ caller = self.otherframe
+ reg = CALLEE_SAVED_REGS - 1
+ while reg >= 0:
+ location = basewalker._shape_decompressor.next()
+ addr = basewalker.getlocation(callee, location)
+ caller.regs_stored_at[reg] = addr # non-translated
+ reg -= 1
+
+ location = basewalker._shape_decompressor.next()
+ caller.frame_address = basewalker.getlocation(callee, location)
+ # ^^^ non-translated
+ if caller.frame_address == llmemory.NULL:
+ return self.teardown() # completely done with this stack
+ #
+ self.otherframe = callee
+ self.curframe = caller
+ # loop back
+
+ def translateptr(self, addr):
+ return rstacklet._translate_pointer(self.context, addr)
+
+ _stackletrootwalker = StackletRootWalker()
+ return _stackletrootwalker
+get_stackletrootwalker._annspecialcase_ = 'specialize:memo'
+
+
+def customtrace(obj, prev):
+ stackletrootwalker = get_stackletrootwalker()
+ return stackletrootwalker.next(obj, prev)
+
+
+ASM_FRAMEDATA_HEAD_PTR = lltype.Ptr(lltype.FixedSizeArray(llmemory.Address, 2))
+SUSPSTACK = lltype.GcStruct('SuspStack',
+ ('handle', rstacklet.handle),
+ ('anchor', ASM_FRAMEDATA_HEAD_PTR),
+ ('my_index', lltype.Signed),
+ ('next_unused', lltype.Signed),
+ rtti=True)
+CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
+ llmemory.Address)
+customtraceptr = llhelper(lltype.Ptr(CUSTOMTRACEFUNC), customtrace)
+lltype.attachRuntimeTypeInfo(SUSPSTACK, customtraceptr=customtraceptr)
+NULL_SUSPSTACK = lltype.Ptr(SUSPSTACK)
+
+
+class SuspendedStacks:
+
+ def __init__(self):
+ self.lst = []
+ self.first_unused = -1
+ self.current_index = -1
+
+ def acquire(self):
+ if self.first_unused == -1:
+ p = lltype.malloc(SUSPSTACK)
+ p.handle = lltype.nullptr(rstacklet.handle.TO)
+ p.my_index = len(self.lst)
+ p.next_unused = -42000000
+ p.anchor = lltype.malloc(ASM_FRAMEDATA_HEAD_PTR.TO, flavor='raw',
+ track_allocation=False)
+ self.lst.append(p)
+ else:
+ p = self.lst[self.first_unused]
+ self.first_unused = p.next_unused
+ p.anchor[0] = p.anchor[1] = llmemory.cast_ptr_to_adr(p.anchor)
+ self.current_index = p.my_index
+ return p
+
+ def release(self, p):
+ p.next_unused = self.first_unused
+ self.first_unused = p.my_index
+
+suspendedstacks = SuspendedStacks()
+
+FUNCNOARG_P = lltype.Ptr(lltype.FuncType([], rstacklet.handle))
+
+pypy_asm_stackwalk2 = rffi.llexternal('pypy_asm_stackwalk',
+ [FUNCNOARG_P,
+ ASM_FRAMEDATA_HEAD_PTR],
+ rstacklet.handle, sandboxsafe=True,
+ _nowrapper=True)
+
+class StackletGcRootFinder:
+
+ @staticmethod
+ def stack_protected_call(callback):
+ p = suspendedstacks.acquire()
+ llop.gc_assume_young_pointers(lltype.Void,
+ llmemory.cast_ptr_to_adr(p))
+ r = pypy_asm_stackwalk2(callback, p.anchor)
+ p.handle = lltype.nullptr(rstacklet.handle.TO)
+ suspendedstacks.release(p)
+ return r
+
+ @staticmethod
+ def set_handle_on_most_recent(h):
+ index = suspendedstacks.current_index
+ if index >= 0:
+ suspendedstacks.lst[index].handle = h
+ suspendedstacks.current_index = -1
diff --git a/pypy/rlib/_stacklet_n_a.py b/pypy/rlib/_stacklet_n_a.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/_stacklet_n_a.py
@@ -0,0 +1,11 @@
+
+
+class StackletGcRootFinder:
+
+ @staticmethod
+ def stack_protected_call(callback):
+ return callback()
+
+ @staticmethod
+ def set_handle_on_most_recent(h):
+ pass
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/rstacklet.py
@@ -1,7 +1,9 @@
import py
from pypy.tool.autopath import pypydir
-from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.rpython.annlowlevel import llhelper
###
### Note: stacklets do not reliably work on top of CPython, but well,
@@ -23,7 +25,7 @@
def llexternal(name, args, result):
return rffi.llexternal(name, args, result, compilation_info=eci,
- sandboxsafe=True)
+ _nowrapper=True)
# ----- types -----
@@ -42,7 +44,57 @@
newthread = llexternal('stacklet_newthread', [], thread_handle)
deletethread = llexternal('stacklet_deletethread',[thread_handle], lltype.Void)
-new = llexternal('stacklet_new', [thread_handle, run_fn, rffi.VOIDP],
- handle)
-switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
-destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
+_new = llexternal('stacklet_new', [thread_handle, run_fn, rffi.VOIDP],
+ handle)
+_switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
+_destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
+
+_translate_pointer = llexternal("_stacklet_translate_pointer",
+ [handle, llmemory.Address],
+ llmemory.Address)
+
+# ____________________________________________________________
+
+def getgcclass(gcrootfinder):
+ gcrootfinder = gcrootfinder.replace('/', '_')
+ module = __import__('pypy.rlib._stacklet_%s' % gcrootfinder,
+ None, None, ['__doc__'])
+ return module.StackletGcRootFinder
+getgcclass._annspecialcase_ = 'specialize:memo'
+
+FUNCNOARG_P = lltype.Ptr(lltype.FuncType([], handle))
+
+class Starter:
+ pass
+starter = Starter()
+
+def new(gcrootfinder, thrd, runfn, arg):
+ starter.thrd = thrd
+ starter.runfn = llhelper(run_fn, runfn)
+ starter.arg = arg
+ c = getgcclass(gcrootfinder)
+ starter.c = c
+ return c.stack_protected_call(llhelper(FUNCNOARG_P, _new_callback))
+new._annspecialcase_ = 'specialize:arg(2)'
+
+def _new_callback():
+ return _new(starter.thrd, llhelper(run_fn, _new_runfn),
+ lltype.nullptr(rffi.VOIDP.TO))
+
+def _new_runfn(h, arg):
+ llop.gc_stack_bottom(lltype.Void) # marker for trackgcroot.py
+ starter.c.set_handle_on_most_recent(h)
+ h = starter.runfn(h, starter.arg)
+ starter.c.set_handle_on_most_recent(h)
+ return h
+
+def switch(gcrootfinder, thrd, h):
+ starter.thrd = thrd
+ starter.switchto = h
+ c = getgcclass(gcrootfinder)
+ return c.stack_protected_call(llhelper(FUNCNOARG_P, _switch_callback))
+
+def _switch_callback():
+ h = _switch(starter.thrd, starter.switchto)
+ starter.c.set_handle_on_most_recent(h)
+ return h
diff --git a/pypy/rlib/test/test_rstacklet.py b/pypy/rlib/test/test_rstacklet.py
--- a/pypy/rlib/test/test_rstacklet.py
+++ b/pypy/rlib/test/test_rstacklet.py
@@ -6,6 +6,8 @@
STATUSMAX = 5000
+GCROOTFINDER = "n/a"
+
class Runner:
@@ -24,7 +26,7 @@
@here_is_a_test
def test_new(self):
print 'start'
- h = rstacklet.new(self.thrd, empty_callback,
+ h = rstacklet.new(GCROOTFINDER, self.thrd, empty_callback,
rffi.cast(rffi.VOIDP, 123))
print 'end', h
assert rstacklet.is_empty_handle(h)
@@ -38,11 +40,11 @@
@here_is_a_test
def test_simple_switch(self):
self.status = 0
- h = rstacklet.new(self.thrd, switchbackonce_callback,
+ h = rstacklet.new(GCROOTFINDER, self.thrd, switchbackonce_callback,
rffi.cast(rffi.VOIDP, 321))
assert not rstacklet.is_empty_handle(h)
self.nextstatus(2)
- h = rstacklet.switch(runner.thrd, h)
+ h = rstacklet.switch(GCROOTFINDER, runner.thrd, h)
self.nextstatus(4)
print 'end', h
assert rstacklet.is_empty_handle(h)
@@ -89,14 +91,15 @@
if not task.h:
# start a new stacklet
print "NEW", n
- h = rstacklet.new(runner.thrd, variousstackdepths_callback,
+ h = rstacklet.new(GCROOTFINDER, runner.thrd,
+ variousstackdepths_callback,
rffi.cast(rffi.VOIDP, n))
else:
# switch to this stacklet
print "switch to", n
h = task.h
task.h = lltype.nullptr(rstacklet.handle.TO)
- h = rstacklet.switch(runner.thrd, h)
+ h = rstacklet.switch(GCROOTFINDER, runner.thrd, h)
print "back in self.n = %d, coming from %d" % (self.n,
runner.comefrom)
@@ -131,7 +134,7 @@
assert rffi.cast(lltype.Signed, arg) == 321
runner.nextstatus(1)
assert not rstacklet.is_empty_handle(h)
- h = rstacklet.switch(runner.thrd, h)
+ h = rstacklet.switch(GCROOTFINDER, runner.thrd, h)
runner.nextstatus(3)
assert not rstacklet.is_empty_handle(h)
return h
@@ -189,17 +192,20 @@
class BaseTestStacklet(StandaloneTests):
def setup_class(cls):
+ global GCROOTFINDER
from pypy.config.pypyoption import get_pypy_config
config = get_pypy_config(translating=True)
config.translation.gc = cls.gc
if cls.gcrootfinder is not None:
+ config.translation.stacklet = True
config.translation.gcrootfinder = cls.gcrootfinder
- #try:
- # config.translation.tealet = True
- #except ConflictConfigError, e:
- # py.test.skip(str(e))
+ GCROOTFINDER = cls.gcrootfinder
cls.config = config
+ def teardown_class(cls):
+ global GCROOTFINDER
+ GCROOTFINDER = 'n/a'
+
def test_demo1(self):
t, cbuilder = self.compile(entry_point)
@@ -215,6 +221,10 @@
gc = 'boehm'
gcrootfinder = None
+class TestStackletAsmGcc(BaseTestStacklet):
+ gc = 'minimark'
+ gcrootfinder = 'asmgcc'
+
def target(*args):
return entry_point, None
diff --git a/pypy/rpython/memory/gctransform/asmgcroot.py b/pypy/rpython/memory/gctransform/asmgcroot.py
--- a/pypy/rpython/memory/gctransform/asmgcroot.py
+++ b/pypy/rpython/memory/gctransform/asmgcroot.py
@@ -147,6 +147,11 @@
self._extra_gcmapend = lambda: llmemory.NULL
self._extra_mark_sorted = lambda: True
+ def need_stacklet_support(self):
+ # stacklet support: BIG HACK for rlib.rstacklet
+ from pypy.rlib import _stacklet_asmgcc
+ _stacklet_asmgcc._asmstackrootwalker = self # as a global! argh
+
def need_thread_support(self, gctransformer, getfn):
# Threads supported "out of the box" by the rest of the code.
# The whole code in this function is only there to support
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
@@ -475,6 +475,8 @@
annmodel.SomeInteger())
# thread support
+ if translator.config.translation.stacklet:
+ root_walker.need_stacklet_support()
if translator.config.translation.thread:
root_walker.need_thread_support(self, getfn)
@@ -1297,6 +1299,10 @@
if collect_stack_root:
self.walk_stack_roots(collect_stack_root) # abstract
+ def need_stacklet_support(self):
+ raise Exception("%s does not support stacklets" % (
+ self.__class__.__name__,))
+
def need_thread_support(self, gctransformer, getfn):
raise Exception("%s does not support threads" % (
self.__class__.__name__,))
diff --git a/pypy/translator/c/database.py b/pypy/translator/c/database.py
--- a/pypy/translator/c/database.py
+++ b/pypy/translator/c/database.py
@@ -29,13 +29,11 @@
def __init__(self, translator=None, standalone=False,
gcpolicyclass=None,
- stacklesstransformer=None,
thread_enabled=False,
sandbox=False):
self.translator = translator
self.standalone = standalone
self.sandbox = sandbox
- self.stacklesstransformer = stacklesstransformer
if gcpolicyclass is None:
gcpolicyclass = gc.RefcountingGcPolicy
self.gcpolicy = gcpolicyclass(self, thread_enabled)
@@ -251,8 +249,8 @@
else:
show_i = -1
- # The order of database completion is fragile with stackless and
- # gc transformers. Here is what occurs:
+ # The order of database completion is fragile with gc transformers.
+ # Here is what occurs:
#
# 1. follow dependencies recursively from the entry point: data
# structures pointing to other structures or functions, and
@@ -270,24 +268,12 @@
# ll_finalize(). New FuncNodes are built for them. No more
# FuncNodes can show up after this step.
#
- # 4. stacklesstransform.finish() - freeze the stackless resume point
- # table.
+ # 4. gctransformer.finish_tables() - freeze the gc types table.
#
- # 5. follow new dependencies (this should be only the new frozen
- # table, which contains only numbers and already-seen function
- # pointers).
- #
- # 6. gctransformer.finish_tables() - freeze the gc types table.
- #
- # 7. follow new dependencies (this should be only the gc type table,
+ # 5. follow new dependencies (this should be only the gc type table,
# which contains only numbers and pointers to ll_finalizer
# functions seen in step 3).
#
- # I think that there is no reason left at this point that force
- # step 4 to be done before step 6, nor to have a follow-new-
- # dependencies step inbetween. It is important though to have step 3
- # before steps 4 and 6.
- #
# This is implemented by interleaving the follow-new-dependencies
# steps with calls to the next 'finish' function from the following
# list:
@@ -295,10 +281,6 @@
if self.gctransformer:
finish_callbacks.append(('GC transformer: finished helpers',
self.gctransformer.finish_helpers))
- if self.stacklesstransformer:
- finish_callbacks.append(('Stackless transformer: finished',
- self.stacklesstransformer.finish))
- if self.gctransformer:
finish_callbacks.append(('GC transformer: finished tables',
self.gctransformer.get_finish_tables()))
diff --git a/pypy/translator/c/funcgen.py b/pypy/translator/c/funcgen.py
--- a/pypy/translator/c/funcgen.py
+++ b/pypy/translator/c/funcgen.py
@@ -46,9 +46,6 @@
self.gcpolicy = db.gcpolicy
self.exception_policy = exception_policy
self.functionname = functionname
- # apply the stackless transformation
- if db.stacklesstransformer:
- db.stacklesstransformer.transform_graph(graph)
# apply the exception transformation
if self.db.exctransformer:
self.db.exctransformer.create_exception_handling(self.graph)
diff --git a/pypy/translator/c/gc.py b/pypy/translator/c/gc.py
--- a/pypy/translator/c/gc.py
+++ b/pypy/translator/c/gc.py
@@ -11,7 +11,6 @@
from pypy.translator.tool.cbuild import ExternalCompilationInfo
class BasicGcPolicy(object):
- requires_stackless = False
stores_hash_at_the_end = False
def __init__(self, db, thread_enabled=False):
diff --git a/pypy/translator/c/genc.py b/pypy/translator/c/genc.py
--- a/pypy/translator/c/genc.py
+++ b/pypy/translator/c/genc.py
@@ -120,8 +120,6 @@
self.originalentrypoint = entrypoint
self.config = config
self.gcpolicy = gcpolicy # for tests only, e.g. rpython/memory/
- if gcpolicy is not None and gcpolicy.requires_stackless:
- config.translation.stackless = True
self.eci = self.get_eci()
self.secondary_entrypoints = secondary_entrypoints
@@ -139,21 +137,8 @@
if not self.standalone:
raise NotImplementedError("--gcrootfinder=asmgcc requires standalone")
- if self.config.translation.stackless:
- if not self.standalone:
- raise Exception("stackless: only for stand-alone builds")
-
- from pypy.translator.stackless.transform import StacklessTransformer
- stacklesstransformer = StacklessTransformer(
- translator, self.originalentrypoint,
- stackless_gc=gcpolicyclass.requires_stackless)
- self.entrypoint = stacklesstransformer.slp_entry_point
- else:
- stacklesstransformer = None
-
db = LowLevelDatabase(translator, standalone=self.standalone,
gcpolicyclass=gcpolicyclass,
- stacklesstransformer=stacklesstransformer,
thread_enabled=self.config.translation.thread,
sandbox=self.config.translation.sandbox)
self.db = db
diff --git a/pypy/translator/c/src/stacklet/stacklet.c b/pypy/translator/c/src/stacklet/stacklet.c
--- a/pypy/translator/c/src/stacklet/stacklet.c
+++ b/pypy/translator/c/src/stacklet/stacklet.c
@@ -288,7 +288,7 @@
free(target);
}
-char **_tealet_translate_pointer(stacklet_handle context, char **ptr)
+char **_stacklet_translate_pointer(stacklet_handle context, char **ptr)
{
char *p = (char *)ptr;
long delta = p - context->stack_start;
diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -173,15 +173,15 @@
# command-line directly instead of via --allworkingmodules.
config.objspace.usemodules.thread = False
- if config.translation.stackless:
- config.objspace.usemodules._stackless = True
- elif config.objspace.usemodules._stackless:
+ if config.translation.stacklet:
+ config.objspace.usemodules._stacklet = True
+ elif config.objspace.usemodules._stacklet:
try:
- config.translation.stackless = True
+ config.translation.stacklet = True
except ConflictConfigError:
- raise ConflictConfigError("please use the --stackless option "
+ raise ConflictConfigError("please use the --stacklet option "
"to translate.py instead of "
- "--withmod-_stackless directly")
+ "--withmod-_stacklet directly")
if not config.translation.rweakref:
config.objspace.usemodules._weakref = False
More information about the pypy-commit
mailing list