From noreply at buildbot.pypy.org Wed Apr 1 09:52:15 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Wed, 1 Apr 2015 09:52:15 +0200 (CEST)
Subject: [pypy-commit] pypy.org extradoc: update the values
Message-ID: <20150401075215.5BB8E1C09A3@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo We generally consider things that are slower on PyPy than CPython to be bugs
of PyPy. If you find some issue that is not documented here,
-please report it to our bug tracker for investigation.
", "exec")
+
+ def test_exec_tuple(self):
+ # note: this is VERY different than testing exec("a = 42", d), because
+ # this specific case is handled specially by the AST compiler
+ d = {}
+ x = ("a = 42", d)
+ exec x
+ assert d['a'] == 42
diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py
--- a/pypy/interpreter/test/test_objspace.py
+++ b/pypy/interpreter/test/test_objspace.py
@@ -373,7 +373,7 @@
config = make_config(None)
space = make_objspace(config)
w_executable = space.wrap('executable')
- assert space.str_w(space.getattr(space.sys, w_executable)) == 'py.py'
+ assert space.findattr(space.sys, w_executable) is None
space.setattr(space.sys, w_executable, space.wrap('foobar'))
assert space.str_w(space.getattr(space.sys, w_executable)) == 'foobar'
space.startup()
diff --git a/pypy/interpreter/test/test_targetpypy.py b/pypy/interpreter/test/test_targetpypy.py
--- a/pypy/interpreter/test/test_targetpypy.py
+++ b/pypy/interpreter/test/test_targetpypy.py
@@ -8,7 +8,7 @@
entry_point = get_entry_point(config)[0]
entry_point(['pypy-c' , '-S', '-c', 'print 3'])
-def test_exeucte_source(space):
+def test_execute_source(space):
_, d = create_entry_point(space, None)
execute_source = d['pypy_execute_source']
lls = rffi.str2charp("import sys; sys.modules['xyz'] = 3")
diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py
--- a/pypy/interpreter/unicodehelper.py
+++ b/pypy/interpreter/unicodehelper.py
@@ -24,13 +24,9 @@
self.end = end
self.reason = reason
- at specialize.memo()
-def rpy_encode_error_handler():
- # A RPython version of the "strict" error handler.
- def raise_unicode_exception_encode(errors, encoding, msg, u,
- startingpos, endingpos):
- raise RUnicodeEncodeError(encoding, u, startingpos, endingpos, msg)
- return raise_unicode_exception_encode
+def raise_unicode_exception_encode(errors, encoding, msg, u,
+ startingpos, endingpos):
+ raise RUnicodeEncodeError(encoding, u, startingpos, endingpos, msg)
# ____________________________________________________________
@@ -67,5 +63,5 @@
# This is not the case with Python3.
return runicode.unicode_encode_utf_8(
uni, len(uni), "strict",
- errorhandler=rpy_encode_error_handler(),
+ errorhandler=raise_unicode_exception_encode,
allow_surrogates=True)
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -82,6 +82,8 @@
'strategy' : 'interp_magic.strategy', # dict,set,list
'set_debug' : 'interp_magic.set_debug',
'locals_to_fast' : 'interp_magic.locals_to_fast',
+ 'save_module_content_for_future_reload':
+ 'interp_magic.save_module_content_for_future_reload',
}
if sys.platform == 'win32':
interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp'
diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py
--- a/pypy/module/__pypy__/interp_magic.py
+++ b/pypy/module/__pypy__/interp_magic.py
@@ -1,6 +1,7 @@
from pypy.interpreter.error import OperationError, wrap_oserror
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.pyframe import PyFrame
+from pypy.interpreter.mixedmodule import MixedModule
from rpython.rlib.objectmodel import we_are_translated
from pypy.objspace.std.dictmultiobject import W_DictMultiObject
from pypy.objspace.std.listobject import W_ListObject
@@ -130,3 +131,7 @@
def locals_to_fast(space, w_frame):
assert isinstance(w_frame, PyFrame)
w_frame.locals2fast()
+
+ at unwrap_spec(w_module=MixedModule)
+def save_module_content_for_future_reload(space, w_module):
+ w_module.save_module_content_for_future_reload()
diff --git a/pypy/module/__pypy__/test/test_magic.py b/pypy/module/__pypy__/test/test_magic.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__pypy__/test/test_magic.py
@@ -0,0 +1,15 @@
+
+class AppTestMagic:
+ spaceconfig = dict(usemodules=['__pypy__'])
+
+ def test_save_module_content_for_future_reload(self):
+ import sys, __pypy__
+ d = sys.dont_write_bytecode
+ sys.dont_write_bytecode = "hello world"
+ __pypy__.save_module_content_for_future_reload(sys)
+ sys.dont_write_bytecode = d
+ reload(sys)
+ assert sys.dont_write_bytecode == "hello world"
+ #
+ sys.dont_write_bytecode = d
+ __pypy__.save_module_content_for_future_reload(sys)
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -2,13 +2,15 @@
from pypy.interpreter.mixedmodule import MixedModule
from rpython.rlib import rdynload
+VERSION = "0.9.2"
+
class Module(MixedModule):
appleveldefs = {
}
interpleveldefs = {
- '__version__': 'space.wrap("0.8.6+")',
+ '__version__': 'space.wrap("%s")' % VERSION,
'load_library': 'libraryobj.load_library',
diff --git a/pypy/module/_cffi_backend/cbuffer.py b/pypy/module/_cffi_backend/cbuffer.py
--- a/pypy/module/_cffi_backend/cbuffer.py
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -81,4 +81,5 @@
if size < 0:
raise oefmt(space.w_TypeError,
"don't know the size pointed to by '%s'", ctype.name)
- return space.wrap(MiniBuffer(LLBuffer(w_cdata._cdata, size), w_cdata))
+ ptr = w_cdata.unsafe_escaping_ptr() # w_cdata kept alive by MiniBuffer()
+ return space.wrap(MiniBuffer(LLBuffer(ptr, size), w_cdata))
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -48,9 +48,12 @@
raise oefmt(space.w_NotImplementedError,
"%s: callback with unsupported argument or "
"return type or with '...'", self.getfunctype().name)
- res = clibffi.c_ffi_prep_closure(self.get_closure(), cif_descr.cif,
- invoke_callback,
- rffi.cast(rffi.VOIDP, self.unique_id))
+ with self as ptr:
+ closure_ptr = rffi.cast(clibffi.FFI_CLOSUREP, ptr)
+ unique_id = rffi.cast(rffi.VOIDP, self.unique_id)
+ res = clibffi.c_ffi_prep_closure(closure_ptr, cif_descr.cif,
+ invoke_callback,
+ unique_id)
if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
raise OperationError(space.w_SystemError,
space.wrap("libffi failed to build this callback"))
@@ -62,12 +65,9 @@
from pypy.module.thread.os_thread import setup_threads
setup_threads(space)
- def get_closure(self):
- return rffi.cast(clibffi.FFI_CLOSUREP, self._cdata)
-
#@rgc.must_be_light_finalizer
def __del__(self):
- clibffi.closureHeap.free(self.get_closure())
+ clibffi.closureHeap.free(rffi.cast(clibffi.FFI_CLOSUREP, self._ptr))
From noreply at buildbot.pypy.org Thu Apr 9 14:02:28 2015
From: noreply at buildbot.pypy.org (fijal)
Date: Thu, 9 Apr 2015 14:02:28 +0200 (CEST)
Subject: [pypy-commit] pypy default: enable this to be called from somewhere
else too
Message-ID: <20150409120228.85FDA1C0173@cobra.cs.uni-duesseldorf.de>
Author: Maciej Fijalkowski
Branch:
Changeset: r76753:4f4ec424db3b
Date: 2015-04-09 14:02 +0200
http://bitbucket.org/pypy/pypy/changeset/4f4ec424db3b/
Log: enable this to be called from somewhere else too
diff --git a/rpython/translator/c/src/debug_print.c b/rpython/translator/c/src/debug_print.c
--- a/rpython/translator/c/src/debug_print.c
+++ b/rpython/translator/c/src/debug_print.c
@@ -26,9 +26,8 @@
static char *debug_filename = NULL;
static char *debug_filename_with_fork = NULL;
-static void pypy_debug_open(void)
+static void _pypy_debug_open(char *filename)
{
- char *filename = getenv("PYPYLOG");
if (filename && filename[0])
{
char *colon = strchr(filename, ':');
@@ -77,6 +76,11 @@
debug_ready = 1;
}
+static void pypy_debug_open(void)
+{
+ _pypy_debug_open(getenv("PYPYLOG"));
+}
+
long pypy_debug_offset(void)
{
if (!debug_ready)
From noreply at buildbot.pypy.org Thu Apr 9 14:07:32 2015
From: noreply at buildbot.pypy.org (rlamy)
Date: Thu, 9 Apr 2015 14:07:32 +0200 (CEST)
Subject: [pypy-commit] pypy default: Add test for an invariant that typeOf()
and .convert_const() should obey
Message-ID: <20150409120732.915991C010B@cobra.cs.uni-duesseldorf.de>
Author: Ronan Lamy
Branch:
Changeset: r76754:14453a4c1b2b
Date: 2015-04-09 13:07 +0100
http://bitbucket.org/pypy/pypy/changeset/14453a4c1b2b/
Log: Add test for an invariant that typeOf() and .convert_const() should
obey
diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py b/rpython/rtyper/lltypesystem/test/test_lltype.py
--- a/rpython/rtyper/lltypesystem/test/test_lltype.py
+++ b/rpython/rtyper/lltypesystem/test/test_lltype.py
@@ -1,9 +1,11 @@
-from __future__ import with_statement
import py
+import sys
from rpython.rtyper.lltypesystem.lltype import *
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.tool.identity_dict import identity_dict
from rpython.tool import leakfinder
+from rpython.annotator.annrpython import RPythonAnnotator
+from rpython.rtyper.rtyper import RPythonTyper
def isweak(p, T):
try:
@@ -539,6 +541,24 @@
assert S == Sprime
+class Frozen(object):
+ def _freeze_(self):
+ return True
+
+ at py.test.mark.parametrize('x', [
+ 1, sys.maxint, 1.5, 'a', 'abc', u'abc', None, [],
+ py.test.mark.xfail(lambda: None),
+ {1.23: 'abc'},
+ (1, 'x', [2, 3.],),
+ py.test.mark.xfail(Frozen()),])
+def test_typeOf_const(x):
+ a = RPythonAnnotator()
+ bk = a.bookkeeper
+ rtyper = RPythonTyper(a)
+ s_x = bk.immutablevalue(x)
+ r_x = rtyper.getrepr(s_x)
+ assert typeOf(r_x.convert_const(x)) == r_x.lowleveltype
+
def test_cast_primitive():
cases = [
(Float, 1, 1.0),
From noreply at buildbot.pypy.org Thu Apr 9 14:08:42 2015
From: noreply at buildbot.pypy.org (fijal)
Date: Thu, 9 Apr 2015 14:08:42 +0200 (CEST)
Subject: [pypy-commit] pypy default: pass jitdriver_sd instead of is_portal
to jitcode
Message-ID: <20150409120842.DDA741C010B@cobra.cs.uni-duesseldorf.de>
Author: Maciej Fijalkowski
Branch:
Changeset: r76755:97dab258e764
Date: 2015-04-09 14:02 +0200
http://bitbucket.org/pypy/pypy/changeset/97dab258e764/
Log: pass jitdriver_sd instead of is_portal to jitcode
diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py
--- a/rpython/jit/codewriter/call.py
+++ b/rpython/jit/codewriter/call.py
@@ -143,7 +143,7 @@
def grab_initial_jitcodes(self):
for jd in self.jitdrivers_sd:
jd.mainjitcode = self.get_jitcode(jd.portal_graph)
- jd.mainjitcode.is_portal = True
+ jd.mainjitcode.jitdriver_sd = jd
def enum_pending_graphs(self):
while self.unfinished_graphs:
diff --git a/rpython/jit/codewriter/jitcode.py b/rpython/jit/codewriter/jitcode.py
--- a/rpython/jit/codewriter/jitcode.py
+++ b/rpython/jit/codewriter/jitcode.py
@@ -12,7 +12,7 @@
self.name = name
self.fnaddr = fnaddr
self.calldescr = calldescr
- self.is_portal = False
+ self.jitdriver_sd = None # None for non-portals
self._called_from = called_from # debugging
self._ssarepr = None # debugging
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1624,7 +1624,7 @@
def _handle_jitexception(blackholeinterp, exc):
# See comments in _handle_jitexception_in_portal().
- while not blackholeinterp.jitcode.is_portal:
+ while blackholeinterp.jitcode.jitdriver_sd is None:
blackholeinterp.builder.release_interp(blackholeinterp)
blackholeinterp = blackholeinterp.nextblackholeinterp
if blackholeinterp.nextblackholeinterp is None:
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -1778,7 +1778,7 @@
return self.jitdriver_sd is not None and jitcode is self.jitdriver_sd.mainjitcode
def newframe(self, jitcode, greenkey=None):
- if jitcode.is_portal:
+ if jitcode.jitdriver_sd:
self.portal_call_depth += 1
self.call_ids.append(self.current_call_id)
self.current_call_id += 1
@@ -1796,7 +1796,7 @@
def popframe(self):
frame = self.framestack.pop()
jitcode = frame.jitcode
- if jitcode.is_portal:
+ if jitcode.jitdriver_sd:
self.portal_call_depth -= 1
self.call_ids.pop()
if frame.greenkey is not None and self.is_main_jitcode(jitcode):
@@ -1860,17 +1860,14 @@
portal_call_depth = -1
for frame in self.framestack:
jitcode = frame.jitcode
- assert jitcode.is_portal == len([
- jd for jd in self.staticdata.jitdrivers_sd
- if jd.mainjitcode is jitcode])
- if jitcode.is_portal:
+ if jitcode.jitdriver_sd:
portal_call_depth += 1
if portal_call_depth != self.portal_call_depth:
print "portal_call_depth problem!!!"
print portal_call_depth, self.portal_call_depth
for frame in self.framestack:
jitcode = frame.jitcode
- if jitcode.is_portal:
+ if jitcode.jitdriver_sd:
print "P",
else:
print " ",
diff --git a/rpython/jit/metainterp/test/test_blackhole.py b/rpython/jit/metainterp/test/test_blackhole.py
--- a/rpython/jit/metainterp/test/test_blackhole.py
+++ b/rpython/jit/metainterp/test/test_blackhole.py
@@ -119,7 +119,7 @@
"\x01\x02", # int_return/i
[],
num_regs_i=3, num_regs_r=0, num_regs_f=0)
- jitcode.is_portal = True
+ jitcode.jitdriver_sd = "foo" # not none
pc = 1
registers_i = [history.BoxInt(40), history.ConstInt(2), None]
class MyMetaInterp:
From noreply at buildbot.pypy.org Thu Apr 9 14:08:44 2015
From: noreply at buildbot.pypy.org (fijal)
Date: Thu, 9 Apr 2015 14:08:44 +0200 (CEST)
Subject: [pypy-commit] pypy default: merge
Message-ID: <20150409120844.218E01C010B@cobra.cs.uni-duesseldorf.de>
Author: Maciej Fijalkowski
Branch:
Changeset: r76756:196823d2c4e7
Date: 2015-04-09 14:08 +0200
http://bitbucket.org/pypy/pypy/changeset/196823d2c4e7/
Log: merge
diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py b/rpython/rtyper/lltypesystem/test/test_lltype.py
--- a/rpython/rtyper/lltypesystem/test/test_lltype.py
+++ b/rpython/rtyper/lltypesystem/test/test_lltype.py
@@ -1,9 +1,11 @@
-from __future__ import with_statement
import py
+import sys
from rpython.rtyper.lltypesystem.lltype import *
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.tool.identity_dict import identity_dict
from rpython.tool import leakfinder
+from rpython.annotator.annrpython import RPythonAnnotator
+from rpython.rtyper.rtyper import RPythonTyper
def isweak(p, T):
try:
@@ -539,6 +541,24 @@
assert S == Sprime
+class Frozen(object):
+ def _freeze_(self):
+ return True
+
+ at py.test.mark.parametrize('x', [
+ 1, sys.maxint, 1.5, 'a', 'abc', u'abc', None, [],
+ py.test.mark.xfail(lambda: None),
+ {1.23: 'abc'},
+ (1, 'x', [2, 3.],),
+ py.test.mark.xfail(Frozen()),])
+def test_typeOf_const(x):
+ a = RPythonAnnotator()
+ bk = a.bookkeeper
+ rtyper = RPythonTyper(a)
+ s_x = bk.immutablevalue(x)
+ r_x = rtyper.getrepr(s_x)
+ assert typeOf(r_x.convert_const(x)) == r_x.lowleveltype
+
def test_cast_primitive():
cases = [
(Float, 1, 1.0),
From noreply at buildbot.pypy.org Thu Apr 9 14:26:24 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 14:26:24 +0200 (CEST)
Subject: [pypy-commit] pypy default: Test and fix for END_FINALLY's stack
usage
Message-ID: <20150409122624.E5B1A1C010B@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76757:e66f14f4e3d9
Date: 2015-04-09 14:26 +0200
http://bitbucket.org/pypy/pypy/changeset/e66f14f4e3d9/
Log: Test and fix for END_FINALLY's stack usage
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -593,7 +593,9 @@
ops.WITH_CLEANUP: -1,
ops.POP_BLOCK: 0,
- ops.END_FINALLY: -1,
+ ops.END_FINALLY: -3, # assume always 3: we pretend that SETUP_FINALLY
+ # pushes 3. In truth, it would only push 1 and
+ # the corresponding END_FINALLY only pops 1.
ops.SETUP_WITH: 1,
ops.SETUP_FINALLY: 0,
ops.SETUP_EXCEPT: 0,
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -772,6 +772,24 @@
code = compile_with_astcompiler(source, 'exec', self.space)
assert code.co_stacksize == 2
+ def test_stackeffect_bug3(self):
+ source = """if 1:
+ try: pass
+ finally: pass
+ try: pass
+ finally: pass
+ try: pass
+ finally: pass
+ try: pass
+ finally: pass
+ try: pass
+ finally: pass
+ try: pass
+ finally: pass
+ """
+ code = compile_with_astcompiler(source, 'exec', self.space)
+ assert code.co_stacksize == 3
+
def test_lambda(self):
yield self.st, "y = lambda x: x", "y(4)", 4
From noreply at buildbot.pypy.org Thu Apr 9 14:39:08 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 14:39:08 +0200 (CEST)
Subject: [pypy-commit] pypy default: Test and fix
Message-ID: <20150409123908.9CDB91C00FA@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76758:72049e6e23d8
Date: 2015-04-09 14:37 +0200
http://bitbucket.org/pypy/pypy/changeset/72049e6e23d8/
Log: Test and fix
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -406,7 +406,9 @@
elif (jump_op == ops.SETUP_FINALLY or
jump_op == ops.SETUP_EXCEPT or
jump_op == ops.SETUP_WITH):
- target_depth += 3
+ if jump_op == ops.SETUP_WITH:
+ target_depth -= 1 # ignore the w_result just pushed
+ target_depth += 3 # add [exc_type, exc, unroller]
if target_depth > self._max_depth:
self._max_depth = target_depth
elif (jump_op == ops.JUMP_IF_TRUE_OR_POP or
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -790,6 +790,18 @@
code = compile_with_astcompiler(source, 'exec', self.space)
assert code.co_stacksize == 3
+ def test_stackeffect_bug4(self):
+ source = """if 1:
+ with a: pass
+ with a: pass
+ with a: pass
+ with a: pass
+ with a: pass
+ with a: pass
+ """
+ code = compile_with_astcompiler(source, 'exec', self.space)
+ assert code.co_stacksize == 4
+
def test_lambda(self):
yield self.st, "y = lambda x: x", "y(4)", 4
From noreply at buildbot.pypy.org Thu Apr 9 14:45:09 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 14:45:09 +0200 (CEST)
Subject: [pypy-commit] pypy default: Test and fix
Message-ID: <20150409124509.1271F1C116D@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76759:d77902fffe58
Date: 2015-04-09 14:45 +0200
http://bitbucket.org/pypy/pypy/changeset/d77902fffe58/
Log: Test and fix
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -565,10 +565,10 @@
ops.INPLACE_OR: -1,
ops.INPLACE_XOR: -1,
- ops.SLICE+0: 1,
- ops.SLICE+1: 0,
- ops.SLICE+2: 0,
- ops.SLICE+3: -1,
+ ops.SLICE+0: 0,
+ ops.SLICE+1: -1,
+ ops.SLICE+2: -1,
+ ops.SLICE+3: -2,
ops.STORE_SLICE+0: -2,
ops.STORE_SLICE+1: -3,
ops.STORE_SLICE+2: -3,
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -802,6 +802,16 @@
code = compile_with_astcompiler(source, 'exec', self.space)
assert code.co_stacksize == 4
+ def test_stackeffect_bug5(self):
+ source = """if 1:
+ a[:]; a[:]; a[:]; a[:]; a[:]; a[:]
+ a[1:]; a[1:]; a[1:]; a[1:]; a[1:]; a[1:]
+ a[:2]; a[:2]; a[:2]; a[:2]; a[:2]; a[:2]
+ a[1:2]; a[1:2]; a[1:2]; a[1:2]; a[1:2]; a[1:2]
+ """
+ code = compile_with_astcompiler(source, 'exec', self.space)
+ assert code.co_stacksize == 3
+
def test_lambda(self):
yield self.st, "y = lambda x: x", "y(4)", 4
From noreply at buildbot.pypy.org Thu Apr 9 14:52:52 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 14:52:52 +0200 (CEST)
Subject: [pypy-commit] pypy default: Test and fix
Message-ID: <20150409125252.40F561C116D@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76760:f57c8be4e18c
Date: 2015-04-09 14:52 +0200
http://bitbucket.org/pypy/pypy/changeset/f57c8be4e18c/
Log: Test and fix
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -608,7 +608,6 @@
ops.YIELD_VALUE: 0,
ops.BUILD_CLASS: -2,
ops.BUILD_MAP: 1,
- ops.BUILD_SET: 1,
ops.COMPARE_OP: -1,
ops.LOOKUP_METHOD: 1,
@@ -663,6 +662,9 @@
def _compute_BUILD_LIST(arg):
return 1 - arg
+def _compute_BUILD_SET(arg):
+ return 1 - arg
+
def _compute_MAKE_CLOSURE(arg):
return -arg - 1
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -812,6 +812,13 @@
code = compile_with_astcompiler(source, 'exec', self.space)
assert code.co_stacksize == 3
+ def test_stackeffect_bug6(self):
+ source = """if 1:
+ {1}; {1}; {1}; {1}; {1}; {1}; {1}
+ """
+ code = compile_with_astcompiler(source, 'exec', self.space)
+ assert code.co_stacksize == 1
+
def test_lambda(self):
yield self.st, "y = lambda x: x", "y(4)", 4
From noreply at buildbot.pypy.org Thu Apr 9 14:59:10 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 14:59:10 +0200 (CEST)
Subject: [pypy-commit] pypy default: Check in the test that found the bugs
in f57c8be4e18c, d77902fffe58,
Message-ID: <20150409125910.31EF41C118B@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76761:46d0755129c5
Date: 2015-04-09 14:59 +0200
http://bitbucket.org/pypy/pypy/changeset/46d0755129c5/
Log: Check in the test that found the bugs in f57c8be4e18c, d77902fffe58,
72049e6e23d8, e66f14f4e3d9.
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -9,6 +9,10 @@
from pypy.tool import stdlib_opcode as ops
+class StackDepthComputationError(Exception):
+ pass
+
+
class Instruction(object):
"""Represents a single opcode."""
@@ -55,11 +59,13 @@
reaches the end of the block, it continues to next_block.
"""
+ marked = False
+ have_return = False
+ auto_inserted_return = False
+
def __init__(self):
self.instructions = []
self.next_block = None
- self.marked = False
- self.have_return = False
def _post_order_see(self, stack, nextblock):
if nextblock.marked == 0:
@@ -384,7 +390,9 @@
# look into a block when all the previous blocks have been done.
self._max_depth = 0
for block in blocks:
- self._do_stack_depth_walk(block)
+ depth = self._do_stack_depth_walk(block)
+ if block.auto_inserted_return and depth != 0:
+ raise StackDepthComputationError # fatal error
return self._max_depth
def _next_stack_depth_walk(self, nextblock, depth):
@@ -421,6 +429,7 @@
break
if block.next_block and not done:
self._next_stack_depth_walk(block.next_block, depth)
+ return depth
def _build_lnotab(self, blocks):
"""Build the line number table for tracebacks and tracing."""
@@ -473,6 +482,7 @@
if self.add_none_to_final_return:
self.load_const(self.space.w_None)
self.emit_op(ops.RETURN_VALUE)
+ self.current_block.auto_inserted_return = True
# Set the first lineno if it is not already explicitly set.
if self.first_lineno == -1:
if self.first_block.instructions:
From noreply at buildbot.pypy.org Thu Apr 9 15:41:01 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 15:41:01 +0200 (CEST)
Subject: [pypy-commit] stmgc c8-locking: This is also a kind of lock...
Message-ID: <20150409134101.AC4C31C0173@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: c8-locking
Changeset: r1750:f7482306f0d4
Date: 2015-04-09 15:41 +0200
http://bitbucket.org/pypy/stmgc/changeset/f7482306f0d4/
Log: This is also a kind of lock...
diff --git a/c8/LOCKS b/c8/LOCKS
--- a/c8/LOCKS
+++ b/c8/LOCKS
@@ -69,3 +69,13 @@
The C_AT_SAFE_POINT and C_REQUEST_REMOVED condition codes are used by
synchronize_all_threads(). That's used only in rare cases, for
example because we want to start a major collection.
+
+
+
+usleep loop
+===========
+
+core.c: wait_for_other_inevitable()
+sync.c: stm_wait_for_current_inevitable_transaction()
+
+Must be fixed!
From noreply at buildbot.pypy.org Thu Apr 9 15:58:40 2015
From: noreply at buildbot.pypy.org (mattip)
Date: Thu, 9 Apr 2015 15:58:40 +0200 (CEST)
Subject: [pypy-commit] pypy object-dtype2: add tests that match -A behaviour,
hack till they pass (cleanup needed)
Message-ID: <20150409135840.F0AB31C15B6@cobra.cs.uni-duesseldorf.de>
Author: mattip
Branch: object-dtype2
Changeset: r76762:48af64138cf8
Date: 2015-04-09 10:51 +0300
http://bitbucket.org/pypy/pypy/changeset/48af64138cf8/
Log: add tests that match -A behaviour, hack till they pass (cleanup
needed)
diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py
--- a/pypy/module/micronumpy/boxes.py
+++ b/pypy/module/micronumpy/boxes.py
@@ -614,6 +614,8 @@
self.w_obj = w_obj
def convert_to(self, space, dtype):
+ if dtype.is_bool():
+ return W_BoolBox(space.bool_w(self.w_obj))
return self # XXX
def descr__getattr__(self, space, w_key):
diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -202,11 +202,14 @@
return self
elif isinstance(w_idx, W_NDimArray) and w_idx.get_dtype().is_bool() \
and w_idx.ndims() > 0:
- return self.getitem_filter(space, w_idx)
+ w_res = self.getitem_filter(space, w_idx)
try:
- return self.implementation.descr_getitem(space, self, w_idx)
+ w_res = self.implementation.descr_getitem(space, self, w_idx)
except ArrayArgumentException:
- return self.getitem_array_int(space, w_idx)
+ w_res = self.getitem_array_int(space, w_idx)
+ if w_res.is_scalar() and w_res.get_dtype(space).is_object():
+ return w_res.get_dtype(space).itemtype.unbox(w_res)
+ return w_res
def getitem(self, space, index_list):
return self.implementation.getitem_index(space, index_list)
@@ -889,7 +892,10 @@
"The truth value of an array with more than one element "
"is ambiguous. Use a.any() or a.all()"))
iter, state = self.create_iter()
- return space.wrap(space.is_true(iter.getitem(state)))
+ w_val = iter.getitem(state)
+ if self.get_dtype().is_object():
+ w_val = self.get_dtype().itemtype.unbox(w_val)
+ return space.wrap(space.is_true(w_val))
def _binop_impl(ufunc_name):
def impl(self, space, w_other, w_out=None):
diff --git a/pypy/module/micronumpy/test/test_object_arrays.py b/pypy/module/micronumpy/test/test_object_arrays.py
--- a/pypy/module/micronumpy/test/test_object_arrays.py
+++ b/pypy/module/micronumpy/test/test_object_arrays.py
@@ -33,16 +33,26 @@
a = np.array(["foo"], dtype=object)
b = a and complex(1, -1)
assert b == complex(1, -1)
- b = complex(1, -1) and a
+ b = np.array(complex(1, -1)) and a
assert (b == a).all()
def test_logical_ufunc(self):
import numpy as np
a = np.array(["foo"], dtype=object)
b = np.array([1], dtype=object)
- raises(TypeError, np.logical_and, a, 1)
- raises(TypeError, np.logical_and, b, complex(1, -1))
+ d = np.array([complex(1, 10)], dtype=object)
+ c = np.logical_and(a, 1)
+ assert c.dtype == np.dtype('object')
+ assert c == 1
+ c = np.logical_and(b, complex(1, -1))
+ assert c.dtype == np.dtype('object')
+ assert c == complex(1, -1)
+ c = np.logical_and(d, b)
+ assert c == 1
c = b & 1
+ assert c.dtype == np.dtype('object')
+ assert (c == 1).all()
+ c = np.array(1) & b
assert (c == b).all()
def test_reduce(self):
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -149,9 +149,8 @@
return self.box(array[0])
def unbox(self, box):
- if not isinstance(box, self.BoxType):
- # i.e. box is an ObjectBox
- raise oefmt(self.space.w_AttributeError, '')
+ if isinstance(box, ObjectType.BoxType):
+ return box.w_obj
return box.value
def coerce(self, space, dtype, w_item):
@@ -1701,12 +1700,15 @@
@specialize.argtype(1, 2)
def box_complex(self, real, imag):
+ if isinstance(real, rffi.r_singlefloat):
+ real = rffi.cast(rffi.DOUBLE, real)
+ if isinstance(imag, rffi.r_singlefloat):
+ imag = rffi.cast(rffi.DOUBLE, imag)
w_obj = self.space.newcomplex(real, imag)
return self.BoxType(w_obj)
def str_format(self, box):
- return 'Object as string'
- #return space.str_w(space.repr(self.unbox(box)))
+ return self.space.str_w(self.space.repr(self.unbox(box)))
def to_builtin_type(self, space, box):
assert isinstance(box, self.BoxType)
@@ -1716,7 +1718,7 @@
def for_computation(v):
return v
- @raw_binary_op
+ @simple_binary_op
def eq(self, v1, v2):
return self.space.eq_w(v1, v2)
@@ -1735,28 +1737,32 @@
def arctan2(self, v1, v2):
raise oefmt(self.space.w_AttributeError, 'arctan2')
- @raw_unary_op
+ @simple_unary_op
def bool(self,v):
- return not self.space.is_w(v, self.space.w_None) and \
- not self.space.eq_w(v, self.space.wrap(0)) and \
- not self.space.len_w(v) == 0
+ return self._bool(v)
def _bool(self, v):
- return self.space.bool_w(v)
+ if self.space.is_true(v):
+ return True
+ return False
- @raw_binary_op
+ @simple_binary_op
def logical_and(self, v1, v2):
- return self._bool(v1) and self._bool(v2)
+ if self._bool(v1):
+ return v2
+ return v1
- @raw_binary_op
+ @simple_binary_op
def logical_or(self, v1, v2):
- return self._bool(v1) or self._bool(v2)
+ if self._bool(v1):
+ return v1
+ return v2
- @raw_unary_op
+ @simple_unary_op
def logical_not(self, v):
return not self._bool(v)
- @raw_binary_op
+ @simple_binary_op
def logical_xor(self, v1, v2):
a = self._bool(v1)
b = self._bool(v2)
@@ -1808,7 +1814,7 @@
def func(self, w_v):
w_impl = self.space.lookup(w_v, op)
if w_impl is None:
- raise oefmt(self.space.w_AttributeError, op)
+ raise oefmt(self.space.w_AttributeError, 'unknown op "%s" on object' % op)
return self.space.get_and_call_function(w_impl, w_v)
func.__name__ = 'object_' + op
setattr(cls, op, func)
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -378,6 +378,8 @@
w_val = self.func(calc_dtype,
w_obj.get_scalar_value().convert_to(space, calc_dtype))
if out is None:
+ if res_dtype.is_object():
+ w_val = w_obj.get_scalar_value()
return w_val
w_val = res_dtype.coerce(space, w_val)
if out.is_scalar():
@@ -481,7 +483,7 @@
else:
out = w_out
calc_dtype = out.get_dtype()
- if self.comparison_func:
+ if self.comparison_func and not calc_dtype.is_object():
res_dtype = get_dtype_cache(space).w_booldtype
else:
res_dtype = calc_dtype
@@ -495,6 +497,8 @@
out.set_scalar_value(arr)
else:
out.fill(space, arr)
+ elif calc_dtype.is_object():
+ out = arr.get_scalar_value()
else:
out = arr
return out
@@ -1130,6 +1134,8 @@
def impl(res_dtype, lvalue, rvalue):
res = get_op(res_dtype)(lvalue, rvalue)
if comparison_func:
+ if res_dtype.is_object():
+ return res
return dtype_cache.w_booldtype.box(res)
return res
return func_with_new_name(impl, ufunc_name)
From noreply at buildbot.pypy.org Thu Apr 9 15:58:42 2015
From: noreply at buildbot.pypy.org (mattip)
Date: Thu, 9 Apr 2015 15:58:42 +0200 (CEST)
Subject: [pypy-commit] pypy object-dtype2: fix logic
Message-ID: <20150409135842.36D871C15B6@cobra.cs.uni-duesseldorf.de>
Author: mattip
Branch: object-dtype2
Changeset: r76763:fc9ba3c58dda
Date: 2015-04-09 12:26 +0300
http://bitbucket.org/pypy/pypy/changeset/fc9ba3c58dda/
Log: fix logic
diff --git a/pypy/module/micronumpy/base.py b/pypy/module/micronumpy/base.py
--- a/pypy/module/micronumpy/base.py
+++ b/pypy/module/micronumpy/base.py
@@ -125,7 +125,7 @@
def get_shape(self):
return self.implementation.get_shape()
- def get_dtype(self):
+ def get_dtype(self, space=None):
return self.implementation.dtype
def get_order(self):
diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -203,10 +203,11 @@
elif isinstance(w_idx, W_NDimArray) and w_idx.get_dtype().is_bool() \
and w_idx.ndims() > 0:
w_res = self.getitem_filter(space, w_idx)
- try:
- w_res = self.implementation.descr_getitem(space, self, w_idx)
- except ArrayArgumentException:
- w_res = self.getitem_array_int(space, w_idx)
+ else:
+ try:
+ w_res = self.implementation.descr_getitem(space, self, w_idx)
+ except ArrayArgumentException:
+ w_res = self.getitem_array_int(space, w_idx)
if w_res.is_scalar() and w_res.get_dtype(space).is_object():
return w_res.get_dtype(space).itemtype.unbox(w_res)
return w_res
From noreply at buildbot.pypy.org Thu Apr 9 15:58:43 2015
From: noreply at buildbot.pypy.org (mattip)
Date: Thu, 9 Apr 2015 15:58:43 +0200 (CEST)
Subject: [pypy-commit] pypy object-dtype2: more tests,
mimic numpy 1.9 ufunc behaviour
Message-ID: <20150409135843.70EEC1C15B6@cobra.cs.uni-duesseldorf.de>
Author: mattip
Branch: object-dtype2
Changeset: r76764:dfa0d26cc06b
Date: 2015-04-09 16:03 +0300
http://bitbucket.org/pypy/pypy/changeset/dfa0d26cc06b/
Log: more tests, mimic numpy 1.9 ufunc behaviour
diff --git a/pypy/module/micronumpy/test/test_object_arrays.py b/pypy/module/micronumpy/test/test_object_arrays.py
--- a/pypy/module/micronumpy/test/test_object_arrays.py
+++ b/pypy/module/micronumpy/test/test_object_arrays.py
@@ -66,6 +66,20 @@
b = np.maximum.reduce(a)
assert b is not None
+ def test_complex_op(self):
+ import numpy as np
+ a = np.array(['abc', 'def'], dtype=object)
+ b = np.array([1, 2, 3], dtype=object)
+ c = np.array([complex(1, 1), complex(1, -1)], dtype=object)
+ for arg in (a,b,c):
+ assert (arg == np.real(arg)).all()
+ assert (0 == np.imag(arg)).all()
+ raises(AttributeError, np.conj, a)
+ res = np.conj(b)
+ assert (res == b).all()
+ res = np.conj(c)
+ assert res[0] == c[1] and res[1] == c[0]
+
def test_keep_object_alive(self):
# only translated does it really test the gc
import numpy as np
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -414,6 +414,9 @@
u(* [array] * u.nin)
except AttributeError:
pass
+ except NotImplementedError:
+ print s
+ uncallable.add(s)
except TypeError:
assert s not in uncallable
uncallable.add(s)
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -1734,9 +1734,6 @@
return v1
return v2
- def arctan2(self, v1, v2):
- raise oefmt(self.space.w_AttributeError, 'arctan2')
-
@simple_unary_op
def bool(self,v):
return self._bool(v)
@@ -1800,22 +1797,39 @@
else:
return zero
+ @simple_unary_op
+ def real(self, v):
+ return v
+ @simple_unary_op
+ def imag(self, v):
+ return 0
+
+ @simple_unary_op
+ def square(self, v):
+ return self.space.mul(v, v)
+
+def add_attributeerr_op(cls, op):
+ def func(self, *args):
+ raise oefmt(self.space.w_AttributeError,
+ "%s", op)
+ func.__name__ = 'object_' + op
+ setattr(cls, op, func)
+
def add_unsupported_op(cls, op):
def func(self, *args):
raise oefmt(self.space.w_TypeError,
"ufunc '%s' not supported for input types", op)
func.__name__ = 'object_' + op
setattr(cls, op, func)
-
-def add_unary_op(cls, op):
+def add_unary_op(cls, op, method):
@simple_unary_op
def func(self, w_v):
- w_impl = self.space.lookup(w_v, op)
+ w_impl = getattr(w_v, method, None)
if w_impl is None:
raise oefmt(self.space.w_AttributeError, 'unknown op "%s" on object' % op)
- return self.space.get_and_call_function(w_impl, w_v)
+ return w_impl(self.space)
func.__name__ = 'object_' + op
setattr(cls, op, func)
@@ -1841,10 +1855,15 @@
setattr(cls, op, func)
for op in ('copysign', 'isfinite', 'isinf', 'isnan', 'logaddexp', 'logaddexp2',
- 'signbit',):
+ 'signbit'):
add_unsupported_op(ObjectType, op)
-for op in ('conj', 'real', 'imag', 'rint'):
- add_unary_op(ObjectType, op)
+for op in ('arctan2', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan',
+ 'arctanh', 'ceil', 'floor', 'cos', 'sin', 'tan', 'cosh', 'sinh',
+ 'tanh', 'radians', 'degrees', 'exp','exp2', 'expm1', 'fabs',
+ 'log', 'log10', 'log1p', 'log2', 'sqrt', 'trunc'):
+ add_attributeerr_op(ObjectType, op)
+for op, method in (('conj', 'descr_conjugate'), ('rint', 'descr_rint')):
+ add_unary_op(ObjectType, op, method)
for op in ('abs', 'neg', 'pos', 'invert'):
add_space_unary_op(ObjectType, op)
for op in ('add', 'floordiv', 'div', 'mod', 'mul', 'sub', 'lshift', 'rshift'):
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -497,10 +497,13 @@
out.set_scalar_value(arr)
else:
out.fill(space, arr)
- elif calc_dtype.is_object():
- out = arr.get_scalar_value()
+ elif isinstance(arr, res_dtype.itemtype.BoxType):
+ if res_dtype.is_object():
+ out = arr.get_scalar_value()
+ else:
+ out = arr
else:
- out = arr
+ out = space.wrap(arr)
return out
if isinstance(w_lhs, boxes.W_GenericBox):
w_lhs = W_NDimArray.from_scalar(space, w_lhs)
@@ -1042,6 +1045,8 @@
@jit.unroll_safe
def find_unaryop_result_dtype(space, dt, promote_to_float=False,
promote_bools=False, promote_to_largest=False):
+ if dt.is_object():
+ return dt
if promote_to_largest:
if dt.kind == NPY.GENBOOLLTR or dt.kind == NPY.SIGNEDLTR:
if dt.elsize * 8 < LONG_BIT:
@@ -1128,6 +1133,9 @@
def impl(res_dtype, value):
res = get_op(res_dtype)(value)
if bool_result:
+ if res_dtype.is_object() and isinstance(res, W_Root):
+ space = res_dtype.itemtype.space
+ res = space.bool_w(res)
return dtype_cache.w_booldtype.box(res)
return res
elif nin == 2:
From noreply at buildbot.pypy.org Thu Apr 9 16:01:40 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 16:01:40 +0200 (CEST)
Subject: [pypy-commit] stmgc c8-locking: Two extra points (thanks remi)
Message-ID: <20150409140140.366EC1C15B6@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: c8-locking
Changeset: r1751:dc7653c95182
Date: 2015-04-09 16:02 +0200
http://bitbucket.org/pypy/stmgc/changeset/dc7653c95182/
Log: Two extra points (thanks remi)
diff --git a/c8/LOCKS b/c8/LOCKS
--- a/c8/LOCKS
+++ b/c8/LOCKS
@@ -34,6 +34,12 @@
the current segment modification_lock is also acquired briefly
whenever we change our segment's 'modified_old_objects'.
+_validate_and_attach() needs to have its own segment's
+modification_lock *around* the compare-and-swap, so that
+_stm_validate() sees either the commit not done and the backup copies
+still in modified_old_objects, or the commit done and no backup copies
+any more.
+
privatization_lock
@@ -70,6 +76,9 @@
synchronize_all_threads(). That's used only in rare cases, for
example because we want to start a major collection.
+The mutex also needs to be acquired for rewind_longjmp's setjmp() and
+longjmp() equivalent.
+
usleep loop
From noreply at buildbot.pypy.org Thu Apr 9 16:03:33 2015
From: noreply at buildbot.pypy.org (Raemi)
Date: Thu, 9 Apr 2015 16:03:33 +0200 (CEST)
Subject: [pypy-commit] stmgc c8-hashtable: Close c8-hashtable branch ready
to be merged
Message-ID: <20150409140333.D00101C1513@cobra.cs.uni-duesseldorf.de>
Author: Remi Meier
Branch: c8-hashtable
Changeset: r1752:052ea809ba6f
Date: 2015-04-09 15:18 +0200
http://bitbucket.org/pypy/stmgc/changeset/052ea809ba6f/
Log: Close c8-hashtable branch ready to be merged
From noreply at buildbot.pypy.org Thu Apr 9 16:03:35 2015
From: noreply at buildbot.pypy.org (Raemi)
Date: Thu, 9 Apr 2015 16:03:35 +0200 (CEST)
Subject: [pypy-commit] stmgc default: Merge with c8-hashtable
Message-ID: <20150409140335.1F5A91C1513@cobra.cs.uni-duesseldorf.de>
Author: Remi Meier
Branch:
Changeset: r1753:a92ba2549987
Date: 2015-04-09 15:18 +0200
http://bitbucket.org/pypy/stmgc/changeset/a92ba2549987/
Log: Merge with c8-hashtable
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -280,8 +280,14 @@
struct stm_undo_s *end = undo + cl->written_count;
for (; undo < end; undo++) {
if (undo->type == TYPE_POSITION_MARKER) {
- fprintf(stderr, " marker %p %lu\n",
- undo->marker_object, undo->marker_odd_number);
+ if (undo->type2 == TYPE_MODIFIED_HASHTABLE) {
+ fprintf(stderr, " hashtable %p\n",
+ undo->modif_hashtable);
+ }
+ else {
+ fprintf(stderr, " marker %p %lu\n",
+ undo->marker_object, undo->marker_odd_number);
+ }
continue;
}
fprintf(stderr, " obj %p, size %d, ofs %lu: ", undo->object,
@@ -383,21 +389,40 @@
struct stm_undo_s *undo = cl->written;
struct stm_undo_s *end = cl->written + cl->written_count;
for (; undo < end; undo++) {
- if (undo->type == TYPE_POSITION_MARKER)
+ object_t *obj;
+
+ if (undo->type != TYPE_POSITION_MARKER) {
+ /* common case: 'undo->object' was written to
+ in this past commit, so we must check that
+ it was not read by us. */
+ obj = undo->object;
+ }
+ else if (undo->type2 != TYPE_MODIFIED_HASHTABLE)
continue;
- if (_stm_was_read(undo->object)) {
- /* first reset all modified objects from the backup
- copies as soon as the first conflict is detected;
- then we will proceed below to update our segment from
- the old (but unmodified) version to the newer version.
- */
- reset_modified_from_backup_copies(my_segnum);
- timing_write_read_contention(cl->written, undo);
- needs_abort = true;
+ else {
+ /* the previous stm_undo_s is about a written
+ 'entry' object, which belongs to the hashtable
+ given now. Check that we haven't read the
+ hashtable (via stm_hashtable_list()). */
+ obj = undo->modif_hashtable;
+ }
- dprintf(("_stm_validate() failed for obj %p\n", undo->object));
- break;
- }
+ if (LIKELY(!_stm_was_read(obj)))
+ continue;
+
+ /* conflict! */
+ dprintf(("_stm_validate() failed for obj %p\n", obj));
+
+ /* first reset all modified objects from the backup
+ copies as soon as the first conflict is detected;
+ then we will proceed below to update our segment
+ from the old (but unmodified) version to the newer
+ version.
+ */
+ reset_modified_from_backup_copies(my_segnum);
+ timing_write_read_contention(cl->written, undo);
+ needs_abort = true;
+ break;
}
}
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -190,9 +190,15 @@
uintptr_t marker_odd_number; /* the odd number part of the marker */
object_t *marker_object; /* the object part of the marker */
};
+ struct {
+ intptr_t type1; /* TYPE_POSITION_MARKER (again) */
+ intptr_t type2; /* TYPE_MODIFIED_HASHTABLE */
+ object_t *modif_hashtable; /* modified entry is previous stm_undo_s */
+ };
};
};
#define TYPE_POSITION_MARKER (-1)
+#define TYPE_MODIFIED_HASHTABLE (-2)
#define SLICE_OFFSET(slice) ((slice) >> 16)
#define SLICE_SIZE(slice) ((int)((slice) & 0xFFFF))
#define NEW_SLICE(offset, size) (((uint64_t)(offset)) << 16 | (size))
@@ -251,6 +257,14 @@
return stm_object_pages + segment_num * (NB_PAGES * 4096UL);
}
+static inline long get_num_segment_containing_address(char *addr)
+{
+ uintptr_t delta = addr - stm_object_pages;
+ uintptr_t result = delta / (NB_PAGES * 4096UL);
+ assert(result < NB_SEGMENTS);
+ return result;
+}
+
static inline
struct stm_segment_info_s *get_segment(long segment_num) {
return (struct stm_segment_info_s *)REAL_ADDRESS(
@@ -285,6 +299,17 @@
static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
static bool _stm_validate();
+static inline bool was_read_remote(char *base, object_t *obj)
+{
+ uint8_t other_transaction_read_version =
+ ((struct stm_segment_info_s *)REAL_ADDRESS(base, STM_PSEGMENT))
+ ->transaction_read_version;
+ uint8_t rm = ((struct stm_read_marker_s *)
+ (base + (((uintptr_t)obj) >> 4)))->rm;
+ assert(rm <= other_transaction_read_version);
+ return rm == other_transaction_read_version;
+}
+
static inline void _duck(void) {
/* put a call to _duck() between two instructions that set 0 into
a %gs-prefixed address and that may otherwise be replaced with
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -130,6 +130,58 @@
return o;
}
+static void _fill_preexisting_slice(long segnum, char *dest,
+ const char *src, uintptr_t size)
+{
+ uintptr_t np = dest - get_segment_base(segnum);
+ if (get_page_status_in(segnum, np / 4096) != PAGE_NO_ACCESS)
+ memcpy(dest, src, size);
+}
+
+object_t *stm_allocate_preexisting(ssize_t size_rounded_up,
+ const char *initial_data)
+{
+ stm_char *np = allocate_outside_nursery_large(size_rounded_up);
+ uintptr_t nobj = (uintptr_t)np;
+ dprintf(("allocate_preexisting: %p\n", (object_t *)nobj));
+
+ char *nobj_seg0 = stm_object_pages + nobj;
+ memcpy(nobj_seg0, initial_data, size_rounded_up);
+ ((struct object_s *)nobj_seg0)->stm_flags = GCFLAG_WRITE_BARRIER;
+
+ acquire_privatization_lock(STM_SEGMENT->segment_num);
+ DEBUG_EXPECT_SEGFAULT(false);
+
+ long j;
+ for (j = 1; j < NB_SEGMENTS; j++) {
+ const char *src = nobj_seg0;
+ char *dest = get_segment_base(j) + nobj;
+ char *end = dest + size_rounded_up;
+
+ while (((uintptr_t)dest) / 4096 != ((uintptr_t)end - 1) / 4096) {
+ uintptr_t count = 4096 - (((uintptr_t)dest) & 4095);
+ _fill_preexisting_slice(j, dest, src, count);
+ src += count;
+ dest += count;
+ }
+ _fill_preexisting_slice(j, dest, src, end - dest);
+
+#ifdef STM_TESTS
+ /* can't really enable this check outside tests, because there is
+ a change that the transaction_state changes in parallel */
+ if (get_priv_segment(j)->transaction_state != TS_NONE) {
+ assert(!was_read_remote(get_segment_base(j), (object_t *)nobj));
+ }
+#endif
+ }
+
+ DEBUG_EXPECT_SEGFAULT(true);
+ release_privatization_lock(STM_SEGMENT->segment_num);
+
+ write_fence(); /* make sure 'nobj' is fully initialized from
+ all threads here */
+ return (object_t *)nobj;
+}
/************************************************************/
@@ -249,6 +301,8 @@
}
+#define TRACE_FOR_MAJOR_COLLECTION (&mark_record_trace)
+
static void mark_and_trace(
object_t *obj,
char *segment_base, /* to trace obj in */
@@ -408,7 +462,8 @@
struct stm_undo_s *modified = (struct stm_undo_s *)lst->items;
struct stm_undo_s *end = (struct stm_undo_s *)(lst->items + lst->count);
for (; modified < end; modified++) {
- if (modified->type == TYPE_POSITION_MARKER)
+ if (modified->type == TYPE_POSITION_MARKER &&
+ modified->type2 != TYPE_MODIFIED_HASHTABLE)
mark_visit_possibly_new_object(modified->marker_object, pseg);
}
}
@@ -541,6 +596,31 @@
list_set_item(lst, n, list_pop_item(lst));
}
}
+
+ /* Remove from 'modified_old_objects' all old hashtables that die */
+ {
+ lst = pseg->modified_old_objects;
+ uintptr_t j, k = 0, limit = list_count(lst);
+ for (j = 0; j < limit; j += 3) {
+ uintptr_t e0 = list_item(lst, j + 0);
+ uintptr_t e1 = list_item(lst, j + 1);
+ uintptr_t e2 = list_item(lst, j + 2);
+ if (e0 == TYPE_POSITION_MARKER &&
+ e1 == TYPE_MODIFIED_HASHTABLE &&
+ !mark_visited_test((object_t *)e2)) {
+ /* hashtable object dies */
+ }
+ else {
+ if (j != k) {
+ list_set_item(lst, k + 0, e0);
+ list_set_item(lst, k + 1, e1);
+ list_set_item(lst, k + 2, e2);
+ }
+ k += 3;
+ }
+ }
+ lst->count = k;
+ }
}
#pragma pop_macro("STM_SEGMENT")
#pragma pop_macro("STM_PSEGMENT")
diff --git a/c8/stm/hashtable.c b/c8/stm/hashtable.c
new file mode 100644
--- /dev/null
+++ b/c8/stm/hashtable.c
@@ -0,0 +1,532 @@
+/*
+Design of stmgc's "hashtable" objects
+=====================================
+
+A "hashtable" is theoretically a lazily-filled array of objects of
+length 2**64. Initially it is full of NULLs. It's obviously
+implemented as a dictionary in which NULL objects are not needed.
+
+A real dictionary can be implemented on top of it, by using the index
+`hash(key)` in the hashtable, and storing a list of `(key, value)`
+pairs at that index (usually only one, unless there is a hash
+collision).
+
+The main operations on a hashtable are reading or writing an object at a
+given index. It also supports fetching the list of non-NULL entries.
+
+There are two markers for every index (a read and a write marker).
+This is unlike regular arrays, which have only two markers in total.
+
+Additionally, we use the read marker for the hashtable object itself
+to mean "we have read the complete list of keys". This plays the role
+of a "global" read marker: when any thread adds a new key/value object
+to the hashtable, this new object's read marker is initialized with a
+copy of the "global" read marker --- in all segments.
+
+
+Implementation
+--------------
+
+First idea: have the hashtable in raw memory, pointing to "entry"
+objects (which are regular, GC- and STM-managed objects). The entry
+objects themselves point to the user-specified objects. The entry
+objects hold the read/write markers. Every entry object, once
+created, stays around. It is only removed by the next major GC if it
+points to NULL and its read/write markers are not set in any
+currently-running transaction.
+
+References
+----------
+
+Inspired by: http://ppl.stanford.edu/papers/podc011-bronson.pdf
+*/
+
+
+uint32_t stm_hashtable_entry_userdata;
+
+
+#define INITIAL_HASHTABLE_SIZE 8
+#define PERTURB_SHIFT 5
+#define RESIZING_LOCK 0
+
+typedef struct {
+ uintptr_t mask;
+
+ /* 'resize_counter' start at an odd value, and is decremented (by
+ 6) for every new item put in 'items'. When it crosses 0, we
+ instead allocate a bigger table and change 'resize_counter' to
+ be a regular pointer to it (which is then even). The whole
+ structure is immutable then.
+
+ The field 'resize_counter' also works as a write lock: changes
+ go via the intermediate value RESIZING_LOCK (0).
+ */
+ uintptr_t resize_counter;
+
+ stm_hashtable_entry_t *items[INITIAL_HASHTABLE_SIZE];
+} stm_hashtable_table_t;
+
+#define IS_EVEN(p) (((p) & 1) == 0)
+
+struct stm_hashtable_s {
+ stm_hashtable_table_t *table;
+ stm_hashtable_table_t initial_table;
+ uint64_t additions;
+};
+
+
+static inline void init_table(stm_hashtable_table_t *table, uintptr_t itemcount)
+{
+ table->mask = itemcount - 1;
+ table->resize_counter = itemcount * 4 + 1;
+ memset(table->items, 0, itemcount * sizeof(stm_hashtable_entry_t *));
+}
+
+stm_hashtable_t *stm_hashtable_create(void)
+{
+ stm_hashtable_t *hashtable = malloc(sizeof(stm_hashtable_t));
+ assert(hashtable);
+ hashtable->table = &hashtable->initial_table;
+ hashtable->additions = 0;
+ init_table(&hashtable->initial_table, INITIAL_HASHTABLE_SIZE);
+ return hashtable;
+}
+
+void stm_hashtable_free(stm_hashtable_t *hashtable)
+{
+ uintptr_t rc = hashtable->initial_table.resize_counter;
+ free(hashtable);
+ while (IS_EVEN(rc)) {
+ assert(rc != RESIZING_LOCK);
+
+ stm_hashtable_table_t *table = (stm_hashtable_table_t *)rc;
+ rc = table->resize_counter;
+ free(table);
+ }
+}
+
+static bool _stm_was_read_by_anybody(object_t *obj)
+{
+ /* can only be safely called during major GC, when all other threads
+ are suspended */
+ long i;
+ for (i = 1; i < NB_SEGMENTS; i++) {
+ if (get_priv_segment(i)->transaction_state == TS_NONE)
+ continue;
+ if (was_read_remote(get_segment_base(i), obj))
+ return true;
+ }
+ return false;
+}
+
+#define VOLATILE_HASHTABLE(p) ((volatile stm_hashtable_t *)(p))
+#define VOLATILE_TABLE(p) ((volatile stm_hashtable_table_t *)(p))
+
+static void _insert_clean(stm_hashtable_table_t *table,
+ stm_hashtable_entry_t *entry,
+ uintptr_t index)
+{
+ uintptr_t mask = table->mask;
+ uintptr_t i = index & mask;
+ if (table->items[i] == NULL) {
+ table->items[i] = entry;
+ return;
+ }
+
+ uintptr_t perturb = index;
+ while (1) {
+ i = (i << 2) + i + perturb + 1;
+ i &= mask;
+ if (table->items[i] == NULL) {
+ table->items[i] = entry;
+ return;
+ }
+
+ perturb >>= PERTURB_SHIFT;
+ }
+}
+
+static void _stm_rehash_hashtable(stm_hashtable_t *hashtable,
+ uintptr_t biggercount,
+ char *segment_base)
+{
+ dprintf(("rehash %p to size %ld, segment_base=%p\n",
+ hashtable, biggercount, segment_base));
+
+ size_t size = (offsetof(stm_hashtable_table_t, items)
+ + biggercount * sizeof(stm_hashtable_entry_t *));
+ stm_hashtable_table_t *biggertable = malloc(size);
+ assert(biggertable); // XXX
+
+ stm_hashtable_table_t *table = hashtable->table;
+ table->resize_counter = (uintptr_t)biggertable;
+ /* ^^^ this unlocks the table by writing a non-zero value to
+ table->resize_counter, but the new value is a pointer to the
+ new bigger table, so IS_EVEN() is still true */
+ assert(IS_EVEN(table->resize_counter));
+
+ init_table(biggertable, biggercount);
+
+ uintptr_t j, mask = table->mask;
+ uintptr_t rc = biggertable->resize_counter;
+ for (j = 0; j <= mask; j++) {
+ stm_hashtable_entry_t *entry = table->items[j];
+ if (entry == NULL)
+ continue;
+ if (segment_base != NULL) {
+ if (((struct stm_hashtable_entry_s *)
+ REAL_ADDRESS(segment_base, entry))->object == NULL &&
+ !_stm_was_read_by_anybody((object_t *)entry)) {
+ dprintf((" removing dead %p\n", entry));
+ continue;
+ }
+ }
+
+ uintptr_t eindex;
+ if (segment_base == NULL)
+ eindex = entry->index; /* read from STM_SEGMENT */
+ else
+ eindex = ((struct stm_hashtable_entry_s *)
+ REAL_ADDRESS(segment_base, entry))->index;
+
+ dprintf((" insert_clean %p at index=%ld\n",
+ entry, eindex));
+ _insert_clean(biggertable, entry, eindex);
+ assert(rc > 6);
+ rc -= 6;
+ }
+ biggertable->resize_counter = rc;
+
+ write_fence(); /* make sure that 'biggertable' is valid here,
+ and make sure 'table->resize_counter' is updated
+ ('table' must be immutable from now on). */
+ VOLATILE_HASHTABLE(hashtable)->table = biggertable;
+}
+
+stm_hashtable_entry_t *stm_hashtable_lookup(object_t *hashtableobj,
+ stm_hashtable_t *hashtable,
+ uintptr_t index)
+{
+ stm_hashtable_table_t *table;
+ uintptr_t mask;
+ uintptr_t i;
+ stm_hashtable_entry_t *entry;
+
+ restart:
+ /* classical dict lookup logic */
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+ mask = table->mask; /* read-only field */
+ i = index & mask;
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ if (entry->index == index)
+ return entry; /* found at the first try */
+
+ uintptr_t perturb = index;
+ while (1) {
+ i = (i << 2) + i + perturb + 1;
+ i &= mask;
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ if (entry->index == index)
+ return entry; /* found */
+ }
+ else
+ break;
+ perturb >>= PERTURB_SHIFT;
+ }
+ }
+ /* here, we didn't find the 'entry' with the correct index. Note
+ that even if the same 'table' is modified or resized by other
+ threads concurrently, any new item found from a race condition
+ would anyway contain NULL in the present segment (ensured by
+ the first write_fence() below). If the 'table' grows an entry
+ just after we checked above, then we go ahead and lock the
+ table; but after we get the lock, we will notice the new entry
+ (ensured by the second write_fence() below) and restart the
+ whole process.
+ */
+
+ uintptr_t rc = VOLATILE_TABLE(table)->resize_counter;
+
+ /* if rc is RESIZING_LOCK (which is 0, so even), a concurrent thread
+ is writing to the hashtable. Or, if rc is another even number, it is
+ actually a pointer to the next version of the table, installed
+ just now. In both cases, this thread must simply spin loop.
+ */
+ if (IS_EVEN(rc)) {
+ spin_loop();
+ goto restart;
+ }
+ /* in the other cases, we need to grab the RESIZING_LOCK.
+ */
+ if (!__sync_bool_compare_and_swap(&table->resize_counter,
+ rc, RESIZING_LOCK)) {
+ goto restart;
+ }
+ /* we now have the lock. The only table with a non-even value of
+ 'resize_counter' should be the last one in the chain, so if we
+ succeeded in locking it, check this. */
+ assert(table == hashtable->table);
+
+ /* Check that 'table->items[i]' is still NULL,
+ i.e. hasn't been populated under our feet.
+ */
+ if (table->items[i] != NULL) {
+ table->resize_counter = rc; /* unlock */
+ goto restart;
+ }
+ /* if rc is greater than 6, there is enough room for a new
+ item in the current table.
+ */
+ if (rc > 6) {
+ /* we can only enter here once! If we allocate stuff, we may
+ run the GC, and so 'hashtableobj' might move afterwards. */
+ if (_is_in_nursery(hashtableobj)) {
+ entry = (stm_hashtable_entry_t *)
+ stm_allocate(sizeof(stm_hashtable_entry_t));
+ entry->userdata = stm_hashtable_entry_userdata;
+ entry->index = index;
+ entry->object = NULL;
+ }
+ else {
+ /* for a non-nursery 'hashtableobj', we pretend that the
+ 'entry' object we're about to return was already
+ existing all along, with NULL in all segments. If the
+ caller of this function is going to modify the 'object'
+ field, it will call stm_write(entry) first, which will
+ correctly schedule 'entry' for write propagation. We
+ do that even if 'hashtableobj' was created by the
+ running transaction: the new 'entry' object is created
+ as if it was older than the transaction.
+
+ Note the following difference: if 'hashtableobj' is
+ still in the nursery (case above), the 'entry' object
+ is also allocated from the nursery, and after a minor
+ collection it ages as an old-but-created-by-the-
+ current-transaction object. We could try to emulate
+ this here, or to create young 'entry' objects, but
+ doing either of these would require careful
+ synchronization with other pieces of the code that may
+ change.
+ */
+ struct stm_hashtable_entry_s initial = {
+ .userdata = stm_hashtable_entry_userdata,
+ .index = index,
+ .object = NULL
+ };
+ entry = (stm_hashtable_entry_t *)
+ stm_allocate_preexisting(sizeof(stm_hashtable_entry_t),
+ (char *)&initial.header);
+ hashtable->additions++;
+ }
+ table->items[i] = entry;
+ write_fence(); /* make sure 'table->items' is written here */
+ VOLATILE_TABLE(table)->resize_counter = rc - 6; /* unlock */
+ return entry;
+ }
+ else {
+ /* if rc is smaller than 6, we must allocate a new bigger table.
+ */
+ uintptr_t biggercount = table->mask + 1;
+ if (biggercount < 50000)
+ biggercount *= 4;
+ else
+ biggercount *= 2;
+ _stm_rehash_hashtable(hashtable, biggercount, /*segment_base=*/NULL);
+ goto restart;
+ }
+}
+
+object_t *stm_hashtable_read(object_t *hobj, stm_hashtable_t *hashtable,
+ uintptr_t key)
+{
+ stm_hashtable_entry_t *e = stm_hashtable_lookup(hobj, hashtable, key);
+ stm_read((object_t *)e);
+ return e->object;
+}
+
+void stm_hashtable_write_entry(object_t *hobj, stm_hashtable_entry_t *entry,
+ object_t *nvalue)
+{
+ if (_STM_WRITE_CHECK_SLOWPATH((object_t *)entry)) {
+
+ stm_write((object_t *)entry);
+
+ uintptr_t i = list_count(STM_PSEGMENT->modified_old_objects);
+ if (i > 0 && list_item(STM_PSEGMENT->modified_old_objects, i - 3)
+ == (uintptr_t)entry) {
+ /* The stm_write() above recorded a write to 'entry'. Here,
+ we add another stm_undo_s to modified_old_objects with
+ TYPE_MODIFIED_HASHTABLE. It is ignored everywhere except
+ in _stm_validate().
+
+ The goal is that this TYPE_MODIFIED_HASHTABLE ends up in
+ the commit log's 'cl_written' array. Later, another
+ transaction validating that log will check two things:
+
+ - the regular stm_undo_s entry put by stm_write() above
+ will make the other transaction check that it didn't
+ read the same 'entry' object;
+
+ - the TYPE_MODIFIED_HASHTABLE entry we're adding now
+ will make the other transaction check that it didn't
+ do any stm_hashtable_list() on the complete hashtable.
+ */
+ STM_PSEGMENT->modified_old_objects = list_append3(
+ STM_PSEGMENT->modified_old_objects,
+ TYPE_POSITION_MARKER, /* type1 */
+ TYPE_MODIFIED_HASHTABLE, /* type2 */
+ (uintptr_t)hobj); /* modif_hashtable */
+ }
+ }
+ entry->object = nvalue;
+}
+
+void stm_hashtable_write(object_t *hobj, stm_hashtable_t *hashtable,
+ uintptr_t key, object_t *nvalue,
+ stm_thread_local_t *tl)
+{
+ STM_PUSH_ROOT(*tl, nvalue);
+ STM_PUSH_ROOT(*tl, hobj);
+ stm_hashtable_entry_t *e = stm_hashtable_lookup(hobj, hashtable, key);
+ STM_POP_ROOT(*tl, hobj);
+ STM_POP_ROOT(*tl, nvalue);
+ stm_hashtable_write_entry(hobj, e, nvalue);
+}
+
+long stm_hashtable_length_upper_bound(stm_hashtable_t *hashtable)
+{
+ stm_hashtable_table_t *table;
+ uintptr_t rc;
+
+ restart:
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+ rc = VOLATILE_TABLE(table)->resize_counter;
+ if (IS_EVEN(rc)) {
+ spin_loop();
+ goto restart;
+ }
+
+ uintptr_t initial_rc = (table->mask + 1) * 4 + 1;
+ uintptr_t num_entries_times_6 = initial_rc - rc;
+ return num_entries_times_6 / 6;
+}
+
+long stm_hashtable_list(object_t *hobj, stm_hashtable_t *hashtable,
+ stm_hashtable_entry_t **results)
+{
+ /* Set the read marker. It will be left as long as we're running
+ the same transaction.
+ */
+ stm_read(hobj);
+
+ /* Get the table. No synchronization is needed: we may miss some
+ entries that are being added, but they would contain NULL in
+ this segment anyway. */
+ stm_hashtable_table_t *table = VOLATILE_HASHTABLE(hashtable)->table;
+
+ /* Read all entries, check which ones are not NULL, count them,
+ and optionally list them in 'results'.
+ */
+ uintptr_t i, mask = table->mask;
+ stm_hashtable_entry_t *entry;
+ long nresult = 0;
+
+ if (results != NULL) {
+ /* collect the results in the provided list */
+ for (i = 0; i <= mask; i++) {
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ stm_read((object_t *)entry);
+ if (entry->object != NULL)
+ results[nresult++] = entry;
+ }
+ }
+ }
+ else {
+ /* don't collect, just get the exact number of results */
+ for (i = 0; i <= mask; i++) {
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ stm_read((object_t *)entry);
+ if (entry->object != NULL)
+ nresult++;
+ }
+ }
+ }
+ return nresult;
+}
+
+static void _stm_compact_hashtable(struct object_s *hobj,
+ stm_hashtable_t *hashtable)
+{
+ stm_hashtable_table_t *table = hashtable->table;
+ uintptr_t rc = table->resize_counter;
+ assert(!IS_EVEN(rc));
+
+ if (hashtable->additions * 4 > table->mask) {
+ hashtable->additions = 0;
+
+ /* If 'hobj' was created in some current transaction, i.e. if it is
+ now an overflow object, then we have the risk that some of its
+ entry objects were not created with stm_allocate_preexisting().
+ In that situation, a valid workaround is to read all entry
+ objects in the segment of the running transaction. Otherwise,
+ the base case is to read them all from segment zero.
+ */
+ long segnum = get_num_segment_containing_address((char *)hobj);
+ if (!IS_OVERFLOW_OBJ(get_priv_segment(segnum), hobj))
+ segnum = 0;
+
+ uintptr_t initial_rc = (table->mask + 1) * 4 + 1;
+ uintptr_t num_entries_times_6 = initial_rc - rc;
+ uintptr_t count = INITIAL_HASHTABLE_SIZE;
+ while (count * 4 < num_entries_times_6)
+ count *= 2;
+ /* sanity-check: 'num_entries_times_6 < initial_rc', and so 'count'
+ can never grow larger than the current table size. */
+ assert(count <= table->mask + 1);
+
+ dprintf(("compact with %ld items:\n", num_entries_times_6 / 6));
+ _stm_rehash_hashtable(hashtable, count, get_segment_base(segnum));
+ }
+
+ table = hashtable->table;
+ assert(!IS_EVEN(table->resize_counter));
+
+ if (table != &hashtable->initial_table) {
+ uintptr_t rc = hashtable->initial_table.resize_counter;
+ while (1) {
+ assert(IS_EVEN(rc));
+ assert(rc != RESIZING_LOCK);
+
+ stm_hashtable_table_t *old_table = (stm_hashtable_table_t *)rc;
+ if (old_table == table)
+ break;
+ rc = old_table->resize_counter;
+ free(old_table);
+ }
+ hashtable->initial_table.resize_counter = (uintptr_t)table;
+ assert(IS_EVEN(hashtable->initial_table.resize_counter));
+ }
+}
+
+void stm_hashtable_tracefn(struct object_s *hobj, stm_hashtable_t *hashtable,
+ void trace(object_t **))
+{
+ if (trace == TRACE_FOR_MAJOR_COLLECTION)
+ _stm_compact_hashtable(hobj, hashtable);
+
+ stm_hashtable_table_t *table;
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+
+ uintptr_t j, mask = table->mask;
+ for (j = 0; j <= mask; j++) {
+ stm_hashtable_entry_t *volatile *pentry;
+ pentry = &VOLATILE_TABLE(table)->items[j];
+ if (*pentry != NULL) {
+ trace((object_t **)pentry);
+ }
+ }
+}
diff --git a/c8/stm/marker.c b/c8/stm/marker.c
--- a/c8/stm/marker.c
+++ b/c8/stm/marker.c
@@ -42,7 +42,8 @@
*/
while (contention != start) {
--contention;
- if (contention->type == TYPE_POSITION_MARKER) {
+ if (contention->type == TYPE_POSITION_MARKER &&
+ contention->type2 != TYPE_MODIFIED_HASHTABLE) {
out_marker->odd_number = contention->marker_odd_number;
out_marker->object = contention->marker_object;
return;
@@ -69,6 +70,9 @@
return; /* already up-to-date */
}
+ /* -2 is not odd */
+ assert(marker.odd_number != (uintptr_t)TYPE_MODIFIED_HASHTABLE);
+
STM_PSEGMENT->position_markers_last = list_count(list);
STM_PSEGMENT->modified_old_objects = list_append3(
list,
diff --git a/c8/stm/misc.c b/c8/stm/misc.c
--- a/c8/stm/misc.c
+++ b/c8/stm/misc.c
@@ -31,10 +31,7 @@
bool _stm_was_read(object_t *obj)
{
- uint8_t rm = ((struct stm_read_marker_s *)
- (STM_SEGMENT->segment_base + (((uintptr_t)obj) >> 4)))->rm;
- assert(rm <= STM_SEGMENT->transaction_read_version);
- return rm == STM_SEGMENT->transaction_read_version;
+ return was_read_remote(STM_SEGMENT->segment_base, obj);
}
bool _stm_was_written(object_t *obj)
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -424,6 +424,7 @@
struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
for (; undo < end; undo++) {
+ /* this logic also works if type2 == TYPE_MODIFIED_HASHTABLE */
if (undo->type == TYPE_POSITION_MARKER)
minor_trace_if_young(&undo->marker_object);
}
diff --git a/c8/stm/pages.h b/c8/stm/pages.h
--- a/c8/stm/pages.h
+++ b/c8/stm/pages.h
@@ -62,7 +62,11 @@
static inline bool get_page_status_in(long segnum, uintptr_t pagenum)
{
- /* reading page status requires "read"-lock: */
+ /* reading page status requires "read"-lock, which is defined as
+ "any segment has the privatization_lock". This is enough to
+ prevent the "write"-lock from being acquired by somebody else
+ (defined as "_all_ segments have the privatization_lock").
+ */
assert(STM_PSEGMENT->privatization_lock);
OPT_ASSERT(segnum < 8 * sizeof(struct page_shared_s));
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -250,8 +250,6 @@
set_gs_register(get_segment_base(num + 1));
s_mutex_unlock();
- DEBUG_EXPECT_SEGFAULT(true);
-
if (num == 0) {
dprintf(("STM_GC_NURSERY: %d\n", STM_GC_NURSERY));
dprintf(("NB_PAGES: %d\n", NB_PAGES));
diff --git a/c8/stm/setup.h b/c8/stm/setup.h
--- a/c8/stm/setup.h
+++ b/c8/stm/setup.h
@@ -3,8 +3,8 @@
static pthread_t *_get_cpth(stm_thread_local_t *);
#ifndef NDEBUG
-static __thread long _stm_segfault_expected = 0;
-#define DEBUG_EXPECT_SEGFAULT(v) do {if (v) _stm_segfault_expected++; else _stm_segfault_expected--;} while (0)
+static __thread long _stm_segfault_expected = 1;
+#define DEBUG_EXPECT_SEGFAULT(v) do {if (v) _stm_segfault_expected++; else _stm_segfault_expected--; assert(_stm_segfault_expected <= 1);} while (0)
#else
#define DEBUG_EXPECT_SEGFAULT(v) {}
#endif
diff --git a/c8/stmgc.c b/c8/stmgc.c
--- a/c8/stmgc.c
+++ b/c8/stmgc.c
@@ -39,3 +39,4 @@
#include "stm/prof.c"
#include "stm/rewind_setjmp.c"
#include "stm/finalizer.c"
+#include "stm/hashtable.c"
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -196,10 +196,13 @@
STM_SEGMENT->transaction_read_version;
}
+#define _STM_WRITE_CHECK_SLOWPATH(obj) \
+ UNLIKELY(((obj)->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0)
+
__attribute__((always_inline))
static inline void stm_write(object_t *obj)
{
- if (UNLIKELY((obj->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0))
+ if (_STM_WRITE_CHECK_SLOWPATH(obj))
_stm_write_slowpath(obj);
}
@@ -208,7 +211,7 @@
static inline void stm_write_card(object_t *obj, uintptr_t index)
{
/* if GCFLAG_WRITE_BARRIER is set, then don't do anything more. */
- if (UNLIKELY((obj->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0)) {
+ if (_STM_WRITE_CHECK_SLOWPATH(obj)) {
/* GCFLAG_WRITE_BARRIER is not set. This might be because
it's the first time we see a given small array; or it might
@@ -479,6 +482,38 @@
/* dummies for now: */
static inline void stm_flush_timing(stm_thread_local_t *tl, int verbose) {}
+
+/* Hashtables. Keys are 64-bit unsigned integers, values are
+ 'object_t *'. Note that the type 'stm_hashtable_t' is not an
+ object type at all; you need to allocate and free it explicitly.
+ If you want to embed the hashtable inside an 'object_t' you
+ probably need a light finalizer to do the freeing. */
+typedef struct stm_hashtable_s stm_hashtable_t;
+typedef TLPREFIX struct stm_hashtable_entry_s stm_hashtable_entry_t;
+
+stm_hashtable_t *stm_hashtable_create(void);
+void stm_hashtable_free(stm_hashtable_t *);
+stm_hashtable_entry_t *stm_hashtable_lookup(object_t *, stm_hashtable_t *,
+ uintptr_t key);
+object_t *stm_hashtable_read(object_t *, stm_hashtable_t *, uintptr_t key);
+void stm_hashtable_write(object_t *, stm_hashtable_t *, uintptr_t key,
+ object_t *nvalue, stm_thread_local_t *);
+void stm_hashtable_write_entry(object_t *hobj, stm_hashtable_entry_t *entry,
+ object_t *nvalue);
+long stm_hashtable_length_upper_bound(stm_hashtable_t *);
+long stm_hashtable_list(object_t *, stm_hashtable_t *,
+ stm_hashtable_entry_t **results);
+extern uint32_t stm_hashtable_entry_userdata;
+void stm_hashtable_tracefn(struct object_s *, stm_hashtable_t *,
+ void (object_t **));
+
+struct stm_hashtable_entry_s {
+ struct object_s header;
+ uint32_t userdata;
+ uintptr_t index;
+ object_t *object;
+};
+
/* ==================== END ==================== */
static void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -197,6 +197,26 @@
void stm_enable_light_finalizer(object_t *);
void (*stmcb_finalizer)(object_t *);
+
+typedef struct stm_hashtable_s stm_hashtable_t;
+typedef ... stm_hashtable_entry_t;
+stm_hashtable_t *stm_hashtable_create(void);
+void stm_hashtable_free(stm_hashtable_t *);
+bool _check_hashtable_read(object_t *, stm_hashtable_t *, uintptr_t key);
+object_t *hashtable_read_result;
+bool _check_hashtable_write(object_t *, stm_hashtable_t *, uintptr_t key,
+ object_t *nvalue, stm_thread_local_t *tl);
+long stm_hashtable_length_upper_bound(stm_hashtable_t *);
+long stm_hashtable_list(object_t *, stm_hashtable_t *,
+ stm_hashtable_entry_t **results);
+uint32_t stm_hashtable_entry_userdata;
+void stm_hashtable_tracefn(struct object_s *, stm_hashtable_t *,
+ void trace(object_t **));
+
+void _set_hashtable(object_t *obj, stm_hashtable_t *h);
+stm_hashtable_t *_get_hashtable(object_t *obj);
+uintptr_t _get_entry_index(stm_hashtable_entry_t *entry);
+object_t *_get_entry_object(stm_hashtable_entry_t *entry);
""")
@@ -299,6 +319,19 @@
CHECKED(stm_validate());
}
+object_t *hashtable_read_result;
+
+bool _check_hashtable_read(object_t *hobj, stm_hashtable_t *h, uintptr_t key)
+{
+ CHECKED(hashtable_read_result = stm_hashtable_read(hobj, h, key));
+}
+
+bool _check_hashtable_write(object_t *hobj, stm_hashtable_t *h, uintptr_t key,
+ object_t *nvalue, stm_thread_local_t *tl)
+{
+ CHECKED(stm_hashtable_write(hobj, h, key, nvalue, tl));
+}
+
#undef CHECKED
@@ -326,6 +359,32 @@
return *WEAKREF_PTR(obj, size);
}
+void _set_hashtable(object_t *obj, stm_hashtable_t *h)
+{
+ stm_char *field_addr = ((stm_char*)obj);
+ field_addr += SIZEOF_MYOBJ; /* header */
+ *(stm_hashtable_t *TLPREFIX *)field_addr = h;
+}
+
+stm_hashtable_t *_get_hashtable(object_t *obj)
+{
+ stm_char *field_addr = ((stm_char*)obj);
+ field_addr += SIZEOF_MYOBJ; /* header */
+ return *(stm_hashtable_t *TLPREFIX *)field_addr;
+}
+
+uintptr_t _get_entry_index(stm_hashtable_entry_t *entry)
+{
+ stm_read((object_t *)entry);
+ return entry->index;
+}
+
+object_t *_get_entry_object(stm_hashtable_entry_t *entry)
+{
+ stm_read((object_t *)entry);
+ return entry->object;
+}
+
void _set_ptr(object_t *obj, int n, object_t *v)
{
long nrefs = (long)((myobj_t*)obj)->type_id - 421420;
@@ -351,11 +410,17 @@
}
-
ssize_t stmcb_size_rounded_up(struct object_s *obj)
{
struct myobj_s *myobj = (struct myobj_s*)obj;
+ assert(myobj->type_id != 0);
if (myobj->type_id < 421420) {
+ if (myobj->type_id == 421419) { /* hashtable */
+ return sizeof(struct myobj_s) + 1 * sizeof(void*);
+ }
+ if (myobj->type_id == 421418) { /* hashtable entry */
+ return sizeof(struct stm_hashtable_entry_s);
+ }
/* basic case: tid equals 42 plus the size of the object */
assert(myobj->type_id >= 42 + sizeof(struct myobj_s));
assert((myobj->type_id - 42) >= 16);
@@ -371,11 +436,21 @@
}
}
-
void stmcb_trace(struct object_s *obj, void visit(object_t **))
{
int i;
struct myobj_s *myobj = (struct myobj_s*)obj;
+ if (myobj->type_id == 421419) {
+ /* hashtable */
+ stm_hashtable_t *h = *((stm_hashtable_t **)(myobj + 1));
+ stm_hashtable_tracefn(obj, h, visit);
+ return;
+ }
+ if (myobj->type_id == 421418) {
+ /* hashtable entry */
+ object_t **ref = &((struct stm_hashtable_entry_s *)myobj)->object;
+ visit(ref);
+ }
if (myobj->type_id < 421420) {
/* basic case: no references */
return;
@@ -396,6 +471,7 @@
{
int i;
struct myobj_s *myobj = (struct myobj_s*)obj;
+ assert(myobj->type_id != 0);
assert(myobj->type_id != 421419);
assert(myobj->type_id != 421418);
if (myobj->type_id < 421420) {
@@ -413,6 +489,9 @@
uintptr_t offset_itemsize[2])
{
struct myobj_s *myobj = (struct myobj_s*)obj;
+ assert(myobj->type_id != 0);
+ assert(myobj->type_id != 421419);
+ assert(myobj->type_id != 421418);
if (myobj->type_id < 421420) {
offset_itemsize[0] = SIZEOF_MYOBJ;
offset_itemsize[1] = 1;
@@ -468,6 +547,7 @@
CARD_CLEAR = 0
CARD_MARKED = lib._STM_CARD_MARKED
CARD_MARKED_OLD = lib._stm_get_transaction_read_version
+lib.stm_hashtable_entry_userdata = 421418
class Conflict(Exception):
@@ -530,6 +610,18 @@
lib._set_type_id(o, tid)
return o
+def stm_allocate_hashtable():
+ o = lib.stm_allocate(16)
+ tid = 421419
+ lib._set_type_id(o, tid)
+ h = lib.stm_hashtable_create()
+ lib._set_hashtable(o, h)
+ return o
+
+def get_hashtable(o):
+ assert lib._get_type_id(o) == 421419
+ return lib._get_hashtable(o)
+
def stm_get_weakref(o):
return lib._get_weakref(o)
diff --git a/c8/test/test_hashtable.py b/c8/test/test_hashtable.py
new file mode 100644
--- /dev/null
+++ b/c8/test/test_hashtable.py
@@ -0,0 +1,561 @@
+from support import *
+import random
+import py, sys
+
+
+def htget(o, key):
+ h = get_hashtable(o)
+ res = lib._check_hashtable_read(o, h, key)
+ if res:
+ raise Conflict
+ return lib.hashtable_read_result
+
+def htset(o, key, nvalue, tl):
+ h = get_hashtable(o)
+ res = lib._check_hashtable_write(o, h, key, nvalue, tl)
+ if res:
+ raise Conflict
+
+def ht_length_upper_bound(o):
+ h = get_hashtable(o)
+ return lib.stm_hashtable_length_upper_bound(h)
+
+def htitems(o):
+ h = get_hashtable(o)
+ upper_bound = lib.stm_hashtable_length_upper_bound(h)
+ entries = ffi.new("stm_hashtable_entry_t *[]", upper_bound)
+ count = lib.stm_hashtable_list(o, h, entries)
+ assert count <= upper_bound
+ return [(lib._get_entry_index(entries[i]),
+ lib._get_entry_object(entries[i])) for i in range(count)]
+
+def htlen(o):
+ h = get_hashtable(o)
+ count = lib.stm_hashtable_list(o, h, ffi.NULL)
+ return count
+
+
+class BaseTestHashtable(BaseTest):
+
+ def setup_method(self, meth):
+ BaseTest.setup_method(self, meth)
+ #
+ @ffi.callback("void(object_t *)")
+ def light_finalizer(obj):
+ print 'light_finalizer:', obj
+ try:
+ assert lib._get_type_id(obj) == 421419
+ self.seen_hashtables -= 1
+ except:
+ self.errors.append(sys.exc_info()[2])
+ raise
+
+ lib.stmcb_light_finalizer = light_finalizer
+ self._light_finalizer_keepalive = light_finalizer
+ self.seen_hashtables = 0
+ self.errors = []
+
+ def teardown_method(self, meth):
+ BaseTest.teardown_method(self, meth)
+ lib.stmcb_light_finalizer = ffi.NULL
+ assert self.errors == []
+ assert self.seen_hashtables == 0
+
+ def allocate_hashtable(self):
+ h = stm_allocate_hashtable()
+ lib.stm_enable_light_finalizer(h)
+ self.seen_hashtables += 1
+ return h
+
+
+class TestHashtable(BaseTestHashtable):
+
+ def test_empty(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ for i in range(100):
+ index = random.randrange(0, 1<<64)
+ got = htget(h, index)
+ assert got == ffi.NULL
+
+ def test_set_value(self):
+ self.start_transaction()
+ tl0 = self.tls[self.current_thread]
+ h = self.allocate_hashtable()
+ lp1 = stm_allocate(16)
+ htset(h, 12345678901, lp1, tl0)
+ assert htget(h, 12345678901) == lp1
+ for i in range(64):
+ index = 12345678901 ^ (1 << i)
+ assert htget(h, index) == ffi.NULL
+ assert htget(h, 12345678901) == lp1
+
+ def test_no_conflict(self):
+ lp1 = stm_allocate_old(16)
+ lp2 = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ tl0 = self.tls[self.current_thread]
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ stm_set_char(lp1, 'A')
+ htset(h, 1234, lp1, tl0)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ h = self.pop_root()
+ stm_set_char(lp2, 'B')
+ self.switch(1)
+ self.start_transaction()
+ self.switch(0)
+ htset(h, 9991234, lp2, tl0)
+ #
+ self.switch(1)
+ lp1b = htget(h, 1234)
+ assert lp1b != ffi.NULL
+ assert stm_get_char(lp1b) == 'A'
+ assert lp1b == lp1
+ self.commit_transaction()
+ #
+ self.switch(0)
+ assert htget(h, 9991234) == lp2
+ assert stm_get_char(lp2) == 'B'
+ assert htget(h, 1234) == lp1
+ htset(h, 1234, ffi.NULL, tl0)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ stm_major_collect() # to get rid of the hashtable object
+
+ def test_conflict(self):
+ lp1 = stm_allocate_old(16)
+ lp2 = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ h = self.pop_root()
+ self.push_root(h)
+ tl0 = self.tls[self.current_thread]
+ htset(h, 1234, lp1, tl0)
+ #
+ self.switch(1)
+ self.start_transaction()
+ tl1 = self.tls[self.current_thread]
+ htset(h, 1234, lp2, tl1)
+ #
+ self.switch(0)
+ self.commit_transaction()
+ #
+ py.test.raises(Conflict, self.switch, 1)
+ #
+ self.switch(0)
+ self.start_transaction()
+ self.pop_root()
+ stm_major_collect() # to get rid of the hashtable object
+ self.commit_transaction()
+
+ def test_keepalive_minor(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ lp1 = stm_allocate(16)
+ stm_set_char(lp1, 'N')
+ tl0 = self.tls[self.current_thread]
+ htset(h, 1234, lp1, tl0)
+ stm_minor_collect()
+ h = self.pop_root()
+ lp1b = htget(h, 1234)
+ assert lp1b != ffi.NULL
+ assert stm_get_char(lp1b) == 'N'
+ assert lp1b != lp1
+
+ def test_keepalive_major(self):
+ lp1 = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ stm_set_char(lp1, 'N')
+ tl0 = self.tls[self.current_thread]
+ htset(h, 1234, lp1, tl0)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ stm_major_collect()
+ h = self.pop_root()
+ lp1b = htget(h, 1234)
+ assert lp1b == lp1
+ assert stm_get_char(lp1b) == 'N'
+ #
+ stm_major_collect() # to get rid of the hashtable object
+ self.commit_transaction()
+
+ def test_minor_collect_bug1(self):
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ self.push_root(lp1)
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ stm_minor_collect()
+ h = self.pop_root()
+ lp1 = self.pop_root()
+ print 'h', h # 0xa040010
+ print 'lp1', lp1 # 0xa040040
+ tl0 = self.tls[self.current_thread]
+ htset(h, 1, lp1, tl0)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ assert htget(h, 1) == lp1
+ stm_major_collect() # to get rid of the hashtable object
+
+ def test_minor_collect_bug1_different_thread(self):
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ self.push_root(lp1)
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ stm_minor_collect()
+ h = self.pop_root()
+ lp1 = self.pop_root()
+ print 'h', h # 0xa040010
+ print 'lp1', lp1 # 0xa040040
+ tl0 = self.tls[self.current_thread]
+ htset(h, 1, lp1, tl0)
+ self.commit_transaction()
+ #
+ self.switch(1) # in a different thread
+ self.start_transaction()
+ assert htget(h, 1) == lp1
+ stm_major_collect() # to get rid of the hashtable object
+
+ def test_major_collect_bug2(self):
+ self.start_transaction()
+ lp1 = stm_allocate(24)
+ self.push_root(lp1)
+ self.commit_transaction()
+ lp1 = self.pop_root()
+ #
+ self.switch(1)
+ self.start_transaction()
+ stm_write(lp1) # force this page to be shared
+ #
+ self.switch(0)
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ tl0 = self.tls[self.current_thread]
+ htset(h, 10, stm_allocate(32), tl0)
+ htset(h, 11, stm_allocate(32), tl0)
+ htset(h, 12, stm_allocate(32), tl0)
+ self.push_root(h)
+ #
+ self.switch(1) # in a different thread
+ stm_major_collect() # force a _stm_rehash_hashtable()
+ #
+ self.switch(0) # back to the original thread
+ h = self.pop_root()
+ assert htget(h, 10) != ffi.NULL
+ assert htget(h, 11) != ffi.NULL
+ assert htget(h, 12) != ffi.NULL
+
+ def test_list_1(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ tl0 = self.tls[self.current_thread]
+ for i in range(32):
+ assert ht_length_upper_bound(h) == i
+ htset(h, 19 ^ i, stm_allocate(32), tl0)
+ assert ht_length_upper_bound(h) == 32
+
+ def test_list_2(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ tl0 = self.tls[self.current_thread]
+ expected = []
+ for i in range(29):
+ lp = stm_allocate(32)
+ htset(h, 19 ^ i, lp, tl0)
+ expected.append((19 ^ i, lp))
+ lst = htitems(h)
+ assert len(lst) == 29
+ assert sorted(lst) == sorted(expected)
+
+ def test_list_3(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ tl0 = self.tls[self.current_thread]
+ for i in range(29):
+ htset(h, 19 ^ i, stm_allocate(32), tl0)
+ assert htlen(h) == 29
+
+ def test_len_conflicts_with_additions(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ h = self.pop_root()
+ self.push_root(h)
+ tl0 = self.tls[self.current_thread]
+ htset(h, 10, stm_allocate(32), tl0)
+ #
+ self.switch(1)
+ self.start_transaction()
+ assert htlen(h) == 0
+ #
+ self.switch(0)
+ self.commit_transaction()
+ #
+ py.test.raises(Conflict, self.switch, 1)
+ #
+ self.switch(0)
+ self.start_transaction()
+ self.pop_root()
+ stm_major_collect() # to get rid of the hashtable object
+ self.commit_transaction()
+
+ def test_grow_without_conflict(self):
+ self.start_transaction()
+ h = self.allocate_hashtable()
+ self.push_root(h)
+ self.commit_transaction()
+ h = self.pop_root()
+ self.push_root(h)
+ #
+ STEPS = 50
+ for i in range(STEPS):
+ self.switch(1)
+ self.start_transaction()
+ tl0 = self.tls[self.current_thread]
+ htset(h, i + STEPS, stm_allocate(32), tl0)
+ #
+ self.switch(0)
+ self.start_transaction()
+ tl0 = self.tls[self.current_thread]
+ htset(h, i, stm_allocate(24), tl0)
+ #
+ self.switch(1)
+ self.commit_transaction()
+ #
+ self.switch(0)
+ self.commit_transaction()
+ #
+ self.pop_root()
+ self.start_transaction()
+ stm_major_collect() # to get rid of the hashtable object
+
+
+class TestRandomHashtable(BaseTestHashtable):
+
+ def setup_method(self, meth):
+ BaseTestHashtable.setup_method(self, meth)
+ self.values = []
+ self.mirror = None
+ self.roots = []
+ self.other_thread = ([], [])
+
+ def push_roots(self):
+ assert self.roots is None
+ self.roots = []
+ for k, hitems in self.mirror.items():
+ assert lib._get_type_id(k) == 421419
+ for key, value in hitems.items():
+ assert lib._get_type_id(value) < 1000
+ self.push_root(value)
+ self.roots.append(key)
+ self.push_root(k)
+ self.roots.append(None)
+ for v in self.values:
+ self.push_root(v)
+ self.mirror = None
+
+ def pop_roots(self):
+ assert self.mirror is None
+ for i in reversed(range(len(self.values))):
+ self.values[i] = self.pop_root()
+ assert stm_get_char(self.values[i]) == chr((i + 1) & 255)
+ self.mirror = {}
+ for r in reversed(self.roots):
+ obj = self.pop_root()
+ if r is None:
+ assert lib._get_type_id(obj) == 421419
+ self.mirror[obj] = curhitems = {}
+ else:
+ assert lib._get_type_id(obj) < 1000
+ curhitems[r] = obj
+ self.roots = None
+
+ def exchange_threads(self):
+ old_thread = (self.values, self.roots)
+ self.switch(1 - self.current_thread)
+ (self.values, self.roots) = self.other_thread
+ self.mirror = None
+ self.other_thread = old_thread
+ print "----- switch to %s -----" % (self.current_thread,)
+
+ def test_random_single_thread(self):
+ import random
+ #
+ for i in range(100):
+ print "start_transaction"
+ self.start_transaction()
+ self.pop_roots()
+ for j in range(10):
+ r = random.random()
+ if r < 0.05:
+ h = self.allocate_hashtable()
+ print "allocate_hashtable ->", h
+ self.mirror[h] = {}
+ elif r < 0.10:
+ print "stm_minor_collect"
+ self.push_roots()
+ stm_minor_collect()
+ self.pop_roots()
+ elif r < 0.11:
+ print "stm_major_collect"
+ self.push_roots()
+ stm_major_collect()
+ self.pop_roots()
+ elif r < 0.5:
+ if not self.mirror: continue
+ h = random.choice(self.mirror.keys())
+ if not self.mirror[h]: continue
+ key = random.choice(self.mirror[h].keys())
+ value = self.mirror[h][key]
+ print "htget(%r, %r) == %r" % (h, key, value)
+ self.push_roots()
+ self.push_root(value)
+ result = htget(h, key)
+ value = self.pop_root()
+ assert result == value
+ self.pop_roots()
+ elif r < 0.6:
+ if not self.mirror: continue
+ h = random.choice(self.mirror.keys())
+ key = random.randrange(0, 40)
+ if key in self.mirror[h]: continue
+ print "htget(%r, %r) == NULL" % (h, key)
+ self.push_roots()
+ assert htget(h, key) == ffi.NULL
+ self.pop_roots()
+ elif r < 0.63:
+ if not self.mirror: continue
+ h, _ = self.mirror.popitem()
+ print "popped", h
+ elif r < 0.75:
+ obj = stm_allocate(32)
+ self.values.append(obj)
+ stm_set_char(obj, chr(len(self.values) & 255))
+ else:
+ if not self.mirror or not self.values: continue
+ h = random.choice(self.mirror.keys())
+ key = random.randrange(0, 32)
+ value = random.choice(self.values)
+ print "htset(%r, %r, %r)" % (h, key, value)
+ self.push_roots()
+ tl = self.tls[self.current_thread]
+ htset(h, key, value, tl)
+ self.pop_roots()
+ self.mirror[h][key] = value
+ self.push_roots()
+ print "commit_transaction"
+ self.commit_transaction()
+ #
+ self.start_transaction()
+ self.become_inevitable()
+ self.pop_roots()
+ stm_major_collect() # to get rid of the hashtable objects
+
+ def test_random_multiple_threads(self):
+ from random import randrange, Random
+ seed = randrange(0, 10000)
+ print "----------------------------------------- seed:", seed
+ random = Random(seed)
+ #
+ self.start_transaction()
+ self.exchange_threads()
+ self.start_transaction()
+ self.pop_roots()
+ #
+ for j in range(1000):
+ r = random.random()
+ if r > 0.9:
+ if r > 0.95:
+ self.push_roots()
+ self.commit_transaction()
+ self.start_transaction()
+ self.pop_roots()
+ else:
+ self.push_roots()
+ self.exchange_threads()
+ self.pop_roots()
+ continue
+
+ if r < 0.05:
+ h = self.allocate_hashtable()
+ print "allocate_hashtable -> %r/%r" % (h, get_hashtable(h))
+ self.mirror[h] = {}
+ elif r < 0.10:
+ print "stm_minor_collect"
+ self.push_roots()
+ stm_minor_collect()
+ self.pop_roots()
+ elif r < 0.11:
+ print "stm_major_collect"
+ self.push_roots()
+ stm_major_collect()
+ self.pop_roots()
+ elif r < 0.5:
+ if not self.mirror: continue
+ h = random.choice(self.mirror.keys())
+ if not self.mirror[h]: continue
+ key = random.choice(self.mirror[h].keys())
+ value = self.mirror[h][key]
+ print "htget(%r/%r, %r) == %r" % (h, get_hashtable(h), key, value)
+ self.push_roots()
+ self.push_root(value)
+ result = htget(h, key)
+ value = self.pop_root()
+ assert result == value
+ self.pop_roots()
+ elif r < 0.6:
+ if not self.mirror: continue
+ h = random.choice(self.mirror.keys())
+ key = random.randrange(0, 40)
+ if key in self.mirror[h]: continue
+ print "htget(%r/%r, %r) == NULL" % (h, get_hashtable(h), key)
+ self.push_roots()
+ assert htget(h, key) == ffi.NULL
+ self.pop_roots()
+ elif r < 0.63:
+ if not self.mirror: continue
+ h, _ = self.mirror.popitem()
+ print "popped", h
+ elif r < 0.75:
+ obj = stm_allocate(32)
+ self.values.append(obj)
+ stm_set_char(obj, chr(len(self.values) & 255))
+ else:
+ if not self.mirror or not self.values: continue
+ h = random.choice(self.mirror.keys())
+ key = random.randrange(0, 32)
+ value = random.choice(self.values)
+ print "htset(%r/%r, %r, %r)" % (h, get_hashtable(h), key, value)
+ self.push_roots()
+ tl = self.tls[self.current_thread]
+ htset(h, key, value, tl)
+ self.pop_roots()
+ self.mirror[h][key] = value
+ #
+ print 'closing down...'
+ self.become_inevitable()
+ self.commit_transaction()
+ self.exchange_threads()
+ self.pop_roots()
+ self.become_inevitable()
+ stm_major_collect() # to get rid of the hashtable objects
From noreply at buildbot.pypy.org Thu Apr 9 16:32:13 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 16:32:13 +0200 (CEST)
Subject: [pypy-commit] pypy default: Print more location info to stderr
Message-ID: <20150409143213.F2B3C1C1513@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76765:b8efc7b61f61
Date: 2015-04-09 16:32 +0200
http://bitbucket.org/pypy/pypy/changeset/b8efc7b61f61/
Log: Print more location info to stderr
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -1,5 +1,6 @@
"""Python control flow graph generation and bytecode assembly."""
+import os
from rpython.rlib import rfloat
from rpython.rlib.objectmodel import we_are_translated
@@ -392,6 +393,8 @@
for block in blocks:
depth = self._do_stack_depth_walk(block)
if block.auto_inserted_return and depth != 0:
+ os.write(2, "StackDepthComputationError in %s at %s:%s\n" % (
+ self.compile_info.filename, self.name, self.first_lineno))
raise StackDepthComputationError # fatal error
return self._max_depth
From noreply at buildbot.pypy.org Thu Apr 9 16:57:10 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Thu, 9 Apr 2015 16:57:10 +0200 (CEST)
Subject: [pypy-commit] pypy default: Next test and fix
Message-ID: <20150409145710.328121C116D@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76766:bcdcaac84be7
Date: 2015-04-09 16:56 +0200
http://bitbucket.org/pypy/pypy/changeset/bcdcaac84be7/
Log: Next test and fix
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -404,14 +404,13 @@
def _do_stack_depth_walk(self, block):
depth = block.initial_depth
- done = False
for instr in block.instructions:
depth += _opcode_stack_effect(instr.opcode, instr.arg)
if depth >= self._max_depth:
self._max_depth = depth
+ jump_op = instr.opcode
if instr.has_jump:
target_depth = depth
- jump_op = instr.opcode
if jump_op == ops.FOR_ITER:
target_depth -= 2
elif (jump_op == ops.SETUP_FINALLY or
@@ -428,10 +427,13 @@
self._next_stack_depth_walk(instr.jump[0], target_depth)
if jump_op == ops.JUMP_ABSOLUTE or jump_op == ops.JUMP_FORWARD:
# Nothing more can occur.
- done = True
break
- if block.next_block and not done:
- self._next_stack_depth_walk(block.next_block, depth)
+ elif jump_op == ops.RETURN_VALUE or jump_op == ops.RAISE_VARARGS:
+ # Nothing more can occur.
+ break
+ else:
+ if block.next_block:
+ self._next_stack_depth_walk(block.next_block, depth)
return depth
def _build_lnotab(self, blocks):
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -819,6 +819,13 @@
code = compile_with_astcompiler(source, 'exec', self.space)
assert code.co_stacksize == 1
+ def test_stackeffect_bug7(self):
+ source = '''def f():
+ for i in a:
+ return
+ '''
+ code = compile_with_astcompiler(source, 'exec', self.space)
+
def test_lambda(self):
yield self.st, "y = lambda x: x", "y(4)", 4
From noreply at buildbot.pypy.org Thu Apr 9 17:00:46 2015
From: noreply at buildbot.pypy.org (Raemi)
Date: Thu, 9 Apr 2015 17:00:46 +0200 (CEST)
Subject: [pypy-commit] stmgc default: add some comments
Message-ID: <20150409150046.B279C1C116D@cobra.cs.uni-duesseldorf.de>
Author: Remi Meier
Branch:
Changeset: r1754:e55658d12179
Date: 2015-04-09 17:03 +0200
http://bitbucket.org/pypy/stmgc/changeset/e55658d12179/
Log: add some comments
diff --git a/c8/stm/hashtable.c b/c8/stm/hashtable.c
--- a/c8/stm/hashtable.c
+++ b/c8/stm/hashtable.c
@@ -109,6 +109,8 @@
{
/* can only be safely called during major GC, when all other threads
are suspended */
+ assert(_has_mutex());
+
long i;
for (i = 1; i < NB_SEGMENTS; i++) {
if (get_priv_segment(i)->transaction_state == TS_NONE)
@@ -174,6 +176,7 @@
if (entry == NULL)
continue;
if (segment_base != NULL) {
+ /* -> compaction during major GC */
if (((struct stm_hashtable_entry_s *)
REAL_ADDRESS(segment_base, entry))->object == NULL &&
!_stm_was_read_by_anybody((object_t *)entry)) {
@@ -283,6 +286,9 @@
/* we can only enter here once! If we allocate stuff, we may
run the GC, and so 'hashtableobj' might move afterwards. */
if (_is_in_nursery(hashtableobj)) {
+ /* this also means that the hashtable is from this
+ transaction and not visible to other segments yet, so
+ the new entry can be nursery-allocated. */
entry = (stm_hashtable_entry_t *)
stm_allocate(sizeof(stm_hashtable_entry_t));
entry->userdata = stm_hashtable_entry_userdata;
From noreply at buildbot.pypy.org Thu Apr 9 17:12:28 2015
From: noreply at buildbot.pypy.org (Raemi)
Date: Thu, 9 Apr 2015 17:12:28 +0200 (CEST)
Subject: [pypy-commit] pypy stmgc-c8: Merge with stmgc-c8-hashtable
Message-ID: <20150409151228.28CF21C0173@cobra.cs.uni-duesseldorf.de>
Author: Remi Meier
Branch: stmgc-c8
Changeset: r76767:0524ad7cc770
Date: 2015-04-09 17:10 +0200
http://bitbucket.org/pypy/pypy/changeset/0524ad7cc770/
Log: Merge with stmgc-c8-hashtable
diff --git a/rpython/rlib/rstm.py b/rpython/rlib/rstm.py
--- a/rpython/rlib/rstm.py
+++ b/rpython/rlib/rstm.py
@@ -259,14 +259,13 @@
'freelist': _ll_hashtable_freelist,
'lookup': _ll_hashtable_lookup,
'writeobj': _ll_hashtable_writeobj})
-# NULL_HASHTABLE = lltype.nullptr(_HASHTABLE_OBJ)
-NULL_HASHTABLE = None
+NULL_HASHTABLE = lltype.nullptr(_HASHTABLE_OBJ)
def _ll_hashtable_trace(gc, obj, callback, arg):
from rpython.memory.gctransform.stmframework import get_visit_function
visit_fn = get_visit_function(callback, arg)
addr = obj + llmemory.offsetof(_HASHTABLE_OBJ, 'll_raw_hashtable')
- llop.stm_hashtable_tracefn(lltype.Void, addr.address[0], visit_fn)
+ llop.stm_hashtable_tracefn(lltype.Void, obj, addr.address[0], visit_fn)
lambda_hashtable_trace = lambda: _ll_hashtable_trace
def _ll_hashtable_finalizer(h):
@@ -280,66 +279,22 @@
def create_hashtable():
if not we_are_translated():
return HashtableForTest() # for tests
- return HashtableEmulation()
- # rgc.register_custom_light_finalizer(_HASHTABLE_OBJ, lambda_hashtable_finlz)
- # rgc.register_custom_trace_hook(_HASHTABLE_OBJ, lambda_hashtable_trace)
- # # Pass a null pointer to _STM_HASHTABLE_ENTRY to stm_hashtable_create().
- # # Make sure we see a malloc() of it, so that its typeid is correctly
- # # initialized. It can be done in a NonConstant(False) path so that
- # # the C compiler will actually drop it.
- # if _false:
- # p = lltype.malloc(_STM_HASHTABLE_ENTRY)
- # else:
- # p = lltype.nullptr(_STM_HASHTABLE_ENTRY)
- # h = lltype.malloc(_HASHTABLE_OBJ, zero=True)
- # h.ll_raw_hashtable = llop.stm_hashtable_create(_STM_HASHTABLE_P, p)
- # return h
+ rgc.register_custom_light_finalizer(_HASHTABLE_OBJ, lambda_hashtable_finlz)
+ rgc.register_custom_trace_hook(_HASHTABLE_OBJ, lambda_hashtable_trace)
+ # Pass a null pointer to _STM_HASHTABLE_ENTRY to stm_hashtable_create().
+ # Make sure we see a malloc() of it, so that its typeid is correctly
+ # initialized. It can be done in a NonConstant(False) path so that
+ # the C compiler will actually drop it.
+ if _false:
+ p = lltype.malloc(_STM_HASHTABLE_ENTRY)
+ else:
+ p = lltype.nullptr(_STM_HASHTABLE_ENTRY)
+ h = lltype.malloc(_HASHTABLE_OBJ, zero=True)
+ h.ll_raw_hashtable = llop.stm_hashtable_create(_STM_HASHTABLE_P, p)
+ return h
NULL_GCREF = lltype.nullptr(llmemory.GCREF.TO)
-class HashtableEmulation(object):
- def __init__(self):
- self._content = {} # dict {integer: GCREF}
-
- def get(self, key):
- return self._content.get(key, NULL_GCREF)
-
- def set(self, key, value):
- if value:
- self._content[key] = value
- else:
- try:
- del self._content[key]
- except KeyError:
- pass
-
- def len(self):
- return len(self._content)
-
- def list(self):
- items = []
- for key in self._content.keys():
- items.append(self.lookup(key))
- count = len(items)
- return items, count
-
- def freelist(self, array):
- pass
-
- def lookup(self, key):
- return EntryObjectEmulation(self, key)
-
- def writeobj(self, entry, nvalue):
- self.set(entry.key, nvalue)
-
-class EntryObjectEmulation(object):
- def __init__(self, hashtable, key):
- self.hashtable = hashtable
- self.key = key
- self.index = r_uint(key)
- self.object = hashtable.get(key)
-
-
class HashtableForTest(object):
def __init__(self):
self._content = {} # dict {integer: GCREF}
diff --git a/rpython/translator/stm/funcgen.py b/rpython/translator/stm/funcgen.py
--- a/rpython/translator/stm/funcgen.py
+++ b/rpython/translator/stm/funcgen.py
@@ -343,8 +343,9 @@
arg1 = funcgen.expr(op.args[1])
arg2 = funcgen.expr(op.args[2])
result = funcgen.expr(op.result)
- return '%s = stm_hashtable_lookup((object_t *)%s, %s, %s);' % (
- result, arg0, arg1, arg2)
+ typename = cdecl(funcgen.lltypename(op.result), '')
+ return '%s = (%s)stm_hashtable_lookup((object_t *)%s, %s, %s);' % (
+ result, typename, arg0, arg1, arg2)
def stm_hashtable_length_upper_bound(funcgen, op):
arg0 = funcgen.expr(op.args[0])
@@ -357,10 +358,12 @@
arg1 = funcgen.expr(op.args[1])
arg2 = funcgen.expr(op.args[2])
result = funcgen.expr(op.result)
- return '%s = stm_hashtable_list((object_t *)%s, %s, %s);' % (
- result, arg0, arg1, arg2)
+ return ('%s = stm_hashtable_list((object_t *)%s, %s, '
+ '(stm_hashtable_entry_t **)%s);' % (result, arg0, arg1, arg2))
def stm_hashtable_tracefn(funcgen, op):
arg0 = funcgen.expr(op.args[0])
arg1 = funcgen.expr(op.args[1])
- return 'stm_hashtable_tracefn((stm_hashtable_t *)%s, %s);' % (arg0, arg1)
+ arg2 = funcgen.expr(op.args[2])
+ return ('stm_hashtable_tracefn(%s, (stm_hashtable_t *)%s, '
+ ' (void(*)(object_t**))%s);' % (arg0, arg1, arg2))
diff --git a/rpython/translator/stm/src_stm/stm/core.c b/rpython/translator/stm/src_stm/stm/core.c
--- a/rpython/translator/stm/src_stm/stm/core.c
+++ b/rpython/translator/stm/src_stm/stm/core.c
@@ -280,8 +280,14 @@
struct stm_undo_s *end = undo + cl->written_count;
for (; undo < end; undo++) {
if (undo->type == TYPE_POSITION_MARKER) {
- fprintf(stderr, " marker %p %lu\n",
- undo->marker_object, undo->marker_odd_number);
+ if (undo->type2 == TYPE_MODIFIED_HASHTABLE) {
+ fprintf(stderr, " hashtable %p\n",
+ undo->modif_hashtable);
+ }
+ else {
+ fprintf(stderr, " marker %p %lu\n",
+ undo->marker_object, undo->marker_odd_number);
+ }
continue;
}
fprintf(stderr, " obj %p, size %d, ofs %lu: ", undo->object,
@@ -383,21 +389,40 @@
struct stm_undo_s *undo = cl->written;
struct stm_undo_s *end = cl->written + cl->written_count;
for (; undo < end; undo++) {
- if (undo->type == TYPE_POSITION_MARKER)
+ object_t *obj;
+
+ if (undo->type != TYPE_POSITION_MARKER) {
+ /* common case: 'undo->object' was written to
+ in this past commit, so we must check that
+ it was not read by us. */
+ obj = undo->object;
+ }
+ else if (undo->type2 != TYPE_MODIFIED_HASHTABLE)
continue;
- if (_stm_was_read(undo->object)) {
- /* first reset all modified objects from the backup
- copies as soon as the first conflict is detected;
- then we will proceed below to update our segment from
- the old (but unmodified) version to the newer version.
- */
- reset_modified_from_backup_copies(my_segnum);
- timing_write_read_contention(cl->written, undo);
- needs_abort = true;
+ else {
+ /* the previous stm_undo_s is about a written
+ 'entry' object, which belongs to the hashtable
+ given now. Check that we haven't read the
+ hashtable (via stm_hashtable_list()). */
+ obj = undo->modif_hashtable;
+ }
- dprintf(("_stm_validate() failed for obj %p\n", undo->object));
- break;
- }
+ if (LIKELY(!_stm_was_read(obj)))
+ continue;
+
+ /* conflict! */
+ dprintf(("_stm_validate() failed for obj %p\n", obj));
+
+ /* first reset all modified objects from the backup
+ copies as soon as the first conflict is detected;
+ then we will proceed below to update our segment
+ from the old (but unmodified) version to the newer
+ version.
+ */
+ reset_modified_from_backup_copies(my_segnum);
+ timing_write_read_contention(cl->written, undo);
+ needs_abort = true;
+ break;
}
}
diff --git a/rpython/translator/stm/src_stm/stm/core.h b/rpython/translator/stm/src_stm/stm/core.h
--- a/rpython/translator/stm/src_stm/stm/core.h
+++ b/rpython/translator/stm/src_stm/stm/core.h
@@ -190,9 +190,15 @@
uintptr_t marker_odd_number; /* the odd number part of the marker */
object_t *marker_object; /* the object part of the marker */
};
+ struct {
+ intptr_t type1; /* TYPE_POSITION_MARKER (again) */
+ intptr_t type2; /* TYPE_MODIFIED_HASHTABLE */
+ object_t *modif_hashtable; /* modified entry is previous stm_undo_s */
+ };
};
};
#define TYPE_POSITION_MARKER (-1)
+#define TYPE_MODIFIED_HASHTABLE (-2)
#define SLICE_OFFSET(slice) ((slice) >> 16)
#define SLICE_SIZE(slice) ((int)((slice) & 0xFFFF))
#define NEW_SLICE(offset, size) (((uint64_t)(offset)) << 16 | (size))
@@ -251,6 +257,14 @@
return stm_object_pages + segment_num * (NB_PAGES * 4096UL);
}
+static inline long get_num_segment_containing_address(char *addr)
+{
+ uintptr_t delta = addr - stm_object_pages;
+ uintptr_t result = delta / (NB_PAGES * 4096UL);
+ assert(result < NB_SEGMENTS);
+ return result;
+}
+
static inline
struct stm_segment_info_s *get_segment(long segment_num) {
return (struct stm_segment_info_s *)REAL_ADDRESS(
@@ -285,6 +299,17 @@
static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
static bool _stm_validate();
+static inline bool was_read_remote(char *base, object_t *obj)
+{
+ uint8_t other_transaction_read_version =
+ ((struct stm_segment_info_s *)REAL_ADDRESS(base, STM_PSEGMENT))
+ ->transaction_read_version;
+ uint8_t rm = ((struct stm_read_marker_s *)
+ (base + (((uintptr_t)obj) >> 4)))->rm;
+ assert(rm <= other_transaction_read_version);
+ return rm == other_transaction_read_version;
+}
+
static inline void _duck(void) {
/* put a call to _duck() between two instructions that set 0 into
a %gs-prefixed address and that may otherwise be replaced with
diff --git a/rpython/translator/stm/src_stm/stm/gcpage.c b/rpython/translator/stm/src_stm/stm/gcpage.c
--- a/rpython/translator/stm/src_stm/stm/gcpage.c
+++ b/rpython/translator/stm/src_stm/stm/gcpage.c
@@ -127,6 +127,58 @@
return o;
}
+static void _fill_preexisting_slice(long segnum, char *dest,
+ const char *src, uintptr_t size)
+{
+ uintptr_t np = dest - get_segment_base(segnum);
+ if (get_page_status_in(segnum, np / 4096) != PAGE_NO_ACCESS)
+ memcpy(dest, src, size);
+}
+
+object_t *stm_allocate_preexisting(ssize_t size_rounded_up,
+ const char *initial_data)
+{
+ stm_char *np = allocate_outside_nursery_large(size_rounded_up);
+ uintptr_t nobj = (uintptr_t)np;
+ dprintf(("allocate_preexisting: %p\n", (object_t *)nobj));
+
+ char *nobj_seg0 = stm_object_pages + nobj;
+ memcpy(nobj_seg0, initial_data, size_rounded_up);
+ ((struct object_s *)nobj_seg0)->stm_flags = GCFLAG_WRITE_BARRIER;
+
+ acquire_privatization_lock(STM_SEGMENT->segment_num);
+ DEBUG_EXPECT_SEGFAULT(false);
+
+ long j;
+ for (j = 1; j < NB_SEGMENTS; j++) {
+ const char *src = nobj_seg0;
+ char *dest = get_segment_base(j) + nobj;
+ char *end = dest + size_rounded_up;
+
+ while (((uintptr_t)dest) / 4096 != ((uintptr_t)end - 1) / 4096) {
+ uintptr_t count = 4096 - (((uintptr_t)dest) & 4095);
+ _fill_preexisting_slice(j, dest, src, count);
+ src += count;
+ dest += count;
+ }
+ _fill_preexisting_slice(j, dest, src, end - dest);
+
+#ifdef STM_TESTS
+ /* can't really enable this check outside tests, because there is
+ a change that the transaction_state changes in parallel */
+ if (get_priv_segment(j)->transaction_state != TS_NONE) {
+ assert(!was_read_remote(get_segment_base(j), (object_t *)nobj));
+ }
+#endif
+ }
+
+ DEBUG_EXPECT_SEGFAULT(true);
+ release_privatization_lock(STM_SEGMENT->segment_num);
+
+ write_fence(); /* make sure 'nobj' is fully initialized from
+ all threads here */
+ return (object_t *)nobj;
+}
/************************************************************/
@@ -246,6 +298,8 @@
}
+#define TRACE_FOR_MAJOR_COLLECTION (&mark_record_trace)
+
static void mark_and_trace(
object_t *obj,
char *segment_base, /* to trace obj in */
@@ -405,7 +459,8 @@
struct stm_undo_s *modified = (struct stm_undo_s *)lst->items;
struct stm_undo_s *end = (struct stm_undo_s *)(lst->items + lst->count);
for (; modified < end; modified++) {
- if (modified->type == TYPE_POSITION_MARKER)
+ if (modified->type == TYPE_POSITION_MARKER &&
+ modified->type2 != TYPE_MODIFIED_HASHTABLE)
mark_visit_possibly_new_object(modified->marker_object, pseg);
}
}
@@ -538,6 +593,31 @@
list_set_item(lst, n, list_pop_item(lst));
}
}
+
+ /* Remove from 'modified_old_objects' all old hashtables that die */
+ {
+ lst = pseg->modified_old_objects;
+ uintptr_t j, k = 0, limit = list_count(lst);
+ for (j = 0; j < limit; j += 3) {
+ uintptr_t e0 = list_item(lst, j + 0);
+ uintptr_t e1 = list_item(lst, j + 1);
+ uintptr_t e2 = list_item(lst, j + 2);
+ if (e0 == TYPE_POSITION_MARKER &&
+ e1 == TYPE_MODIFIED_HASHTABLE &&
+ !mark_visited_test((object_t *)e2)) {
+ /* hashtable object dies */
+ }
+ else {
+ if (j != k) {
+ list_set_item(lst, k + 0, e0);
+ list_set_item(lst, k + 1, e1);
+ list_set_item(lst, k + 2, e2);
+ }
+ k += 3;
+ }
+ }
+ lst->count = k;
+ }
}
#pragma pop_macro("STM_SEGMENT")
#pragma pop_macro("STM_PSEGMENT")
diff --git a/rpython/translator/stm/src_stm/stm/hashtable.c b/rpython/translator/stm/src_stm/stm/hashtable.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/src_stm/stm/hashtable.c
@@ -0,0 +1,532 @@
+/* Imported by rpython/translator/stm/import_stmgc.py */
+/*
+Design of stmgc's "hashtable" objects
+=====================================
+A "hashtable" is theoretically a lazily-filled array of objects of
+length 2**64. Initially it is full of NULLs. It's obviously
+implemented as a dictionary in which NULL objects are not needed.
+
+A real dictionary can be implemented on top of it, by using the index
+`hash(key)` in the hashtable, and storing a list of `(key, value)`
+pairs at that index (usually only one, unless there is a hash
+collision).
+
+The main operations on a hashtable are reading or writing an object at a
+given index. It also supports fetching the list of non-NULL entries.
+
+There are two markers for every index (a read and a write marker).
+This is unlike regular arrays, which have only two markers in total.
+
+Additionally, we use the read marker for the hashtable object itself
+to mean "we have read the complete list of keys". This plays the role
+of a "global" read marker: when any thread adds a new key/value object
+to the hashtable, this new object's read marker is initialized with a
+copy of the "global" read marker --- in all segments.
+
+
+Implementation
+--------------
+
+First idea: have the hashtable in raw memory, pointing to "entry"
+objects (which are regular, GC- and STM-managed objects). The entry
+objects themselves point to the user-specified objects. The entry
+objects hold the read/write markers. Every entry object, once
+created, stays around. It is only removed by the next major GC if it
+points to NULL and its read/write markers are not set in any
+currently-running transaction.
+
+References
+----------
+
+Inspired by: http://ppl.stanford.edu/papers/podc011-bronson.pdf
+*/
+
+
+uint32_t stm_hashtable_entry_userdata;
+
+
+#define INITIAL_HASHTABLE_SIZE 8
+#define PERTURB_SHIFT 5
+#define RESIZING_LOCK 0
+
+typedef struct {
+ uintptr_t mask;
+
+ /* 'resize_counter' start at an odd value, and is decremented (by
+ 6) for every new item put in 'items'. When it crosses 0, we
+ instead allocate a bigger table and change 'resize_counter' to
+ be a regular pointer to it (which is then even). The whole
+ structure is immutable then.
+
+ The field 'resize_counter' also works as a write lock: changes
+ go via the intermediate value RESIZING_LOCK (0).
+ */
+ uintptr_t resize_counter;
+
+ stm_hashtable_entry_t *items[INITIAL_HASHTABLE_SIZE];
+} stm_hashtable_table_t;
+
+#define IS_EVEN(p) (((p) & 1) == 0)
+
+struct stm_hashtable_s {
+ stm_hashtable_table_t *table;
+ stm_hashtable_table_t initial_table;
+ uint64_t additions;
+};
+
+
+static inline void init_table(stm_hashtable_table_t *table, uintptr_t itemcount)
+{
+ table->mask = itemcount - 1;
+ table->resize_counter = itemcount * 4 + 1;
+ memset(table->items, 0, itemcount * sizeof(stm_hashtable_entry_t *));
+}
+
+stm_hashtable_t *stm_hashtable_create(void)
+{
+ stm_hashtable_t *hashtable = malloc(sizeof(stm_hashtable_t));
+ assert(hashtable);
+ hashtable->table = &hashtable->initial_table;
+ hashtable->additions = 0;
+ init_table(&hashtable->initial_table, INITIAL_HASHTABLE_SIZE);
+ return hashtable;
+}
+
+void stm_hashtable_free(stm_hashtable_t *hashtable)
+{
+ uintptr_t rc = hashtable->initial_table.resize_counter;
+ free(hashtable);
+ while (IS_EVEN(rc)) {
+ assert(rc != RESIZING_LOCK);
+
+ stm_hashtable_table_t *table = (stm_hashtable_table_t *)rc;
+ rc = table->resize_counter;
+ free(table);
+ }
+}
+
+static bool _stm_was_read_by_anybody(object_t *obj)
+{
+ /* can only be safely called during major GC, when all other threads
+ are suspended */
+ long i;
+ for (i = 1; i < NB_SEGMENTS; i++) {
+ if (get_priv_segment(i)->transaction_state == TS_NONE)
+ continue;
+ if (was_read_remote(get_segment_base(i), obj))
+ return true;
+ }
+ return false;
+}
+
+#define VOLATILE_HASHTABLE(p) ((volatile stm_hashtable_t *)(p))
+#define VOLATILE_TABLE(p) ((volatile stm_hashtable_table_t *)(p))
+
+static void _insert_clean(stm_hashtable_table_t *table,
+ stm_hashtable_entry_t *entry,
+ uintptr_t index)
+{
+ uintptr_t mask = table->mask;
+ uintptr_t i = index & mask;
+ if (table->items[i] == NULL) {
+ table->items[i] = entry;
+ return;
+ }
+
+ uintptr_t perturb = index;
+ while (1) {
+ i = (i << 2) + i + perturb + 1;
+ i &= mask;
+ if (table->items[i] == NULL) {
+ table->items[i] = entry;
+ return;
+ }
+
+ perturb >>= PERTURB_SHIFT;
+ }
+}
+
+static void _stm_rehash_hashtable(stm_hashtable_t *hashtable,
+ uintptr_t biggercount,
+ char *segment_base)
+{
+ dprintf(("rehash %p to size %ld, segment_base=%p\n",
+ hashtable, biggercount, segment_base));
+
+ size_t size = (offsetof(stm_hashtable_table_t, items)
+ + biggercount * sizeof(stm_hashtable_entry_t *));
+ stm_hashtable_table_t *biggertable = malloc(size);
+ assert(biggertable); // XXX
+
+ stm_hashtable_table_t *table = hashtable->table;
+ table->resize_counter = (uintptr_t)biggertable;
+ /* ^^^ this unlocks the table by writing a non-zero value to
+ table->resize_counter, but the new value is a pointer to the
+ new bigger table, so IS_EVEN() is still true */
+ assert(IS_EVEN(table->resize_counter));
+
+ init_table(biggertable, biggercount);
+
+ uintptr_t j, mask = table->mask;
+ uintptr_t rc = biggertable->resize_counter;
+ for (j = 0; j <= mask; j++) {
+ stm_hashtable_entry_t *entry = table->items[j];
+ if (entry == NULL)
+ continue;
+ if (segment_base != NULL) {
+ if (((struct stm_hashtable_entry_s *)
+ REAL_ADDRESS(segment_base, entry))->object == NULL &&
+ !_stm_was_read_by_anybody((object_t *)entry)) {
+ dprintf((" removing dead %p\n", entry));
+ continue;
+ }
+ }
+
+ uintptr_t eindex;
+ if (segment_base == NULL)
+ eindex = entry->index; /* read from STM_SEGMENT */
+ else
+ eindex = ((struct stm_hashtable_entry_s *)
+ REAL_ADDRESS(segment_base, entry))->index;
+
+ dprintf((" insert_clean %p at index=%ld\n",
+ entry, eindex));
+ _insert_clean(biggertable, entry, eindex);
+ assert(rc > 6);
+ rc -= 6;
+ }
+ biggertable->resize_counter = rc;
+
+ write_fence(); /* make sure that 'biggertable' is valid here,
+ and make sure 'table->resize_counter' is updated
+ ('table' must be immutable from now on). */
+ VOLATILE_HASHTABLE(hashtable)->table = biggertable;
+}
+
+stm_hashtable_entry_t *stm_hashtable_lookup(object_t *hashtableobj,
+ stm_hashtable_t *hashtable,
+ uintptr_t index)
+{
+ stm_hashtable_table_t *table;
+ uintptr_t mask;
+ uintptr_t i;
+ stm_hashtable_entry_t *entry;
+
+ restart:
+ /* classical dict lookup logic */
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+ mask = table->mask; /* read-only field */
+ i = index & mask;
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ if (entry->index == index)
+ return entry; /* found at the first try */
+
+ uintptr_t perturb = index;
+ while (1) {
+ i = (i << 2) + i + perturb + 1;
+ i &= mask;
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ if (entry->index == index)
+ return entry; /* found */
+ }
+ else
+ break;
+ perturb >>= PERTURB_SHIFT;
+ }
+ }
+ /* here, we didn't find the 'entry' with the correct index. Note
+ that even if the same 'table' is modified or resized by other
+ threads concurrently, any new item found from a race condition
+ would anyway contain NULL in the present segment (ensured by
+ the first write_fence() below). If the 'table' grows an entry
+ just after we checked above, then we go ahead and lock the
+ table; but after we get the lock, we will notice the new entry
+ (ensured by the second write_fence() below) and restart the
+ whole process.
+ */
+
+ uintptr_t rc = VOLATILE_TABLE(table)->resize_counter;
+
+ /* if rc is RESIZING_LOCK (which is 0, so even), a concurrent thread
+ is writing to the hashtable. Or, if rc is another even number, it is
+ actually a pointer to the next version of the table, installed
+ just now. In both cases, this thread must simply spin loop.
+ */
+ if (IS_EVEN(rc)) {
+ spin_loop();
+ goto restart;
+ }
+ /* in the other cases, we need to grab the RESIZING_LOCK.
+ */
+ if (!__sync_bool_compare_and_swap(&table->resize_counter,
+ rc, RESIZING_LOCK)) {
+ goto restart;
+ }
+ /* we now have the lock. The only table with a non-even value of
+ 'resize_counter' should be the last one in the chain, so if we
+ succeeded in locking it, check this. */
+ assert(table == hashtable->table);
+
+ /* Check that 'table->items[i]' is still NULL,
+ i.e. hasn't been populated under our feet.
+ */
+ if (table->items[i] != NULL) {
+ table->resize_counter = rc; /* unlock */
+ goto restart;
+ }
+ /* if rc is greater than 6, there is enough room for a new
+ item in the current table.
+ */
+ if (rc > 6) {
+ /* we can only enter here once! If we allocate stuff, we may
+ run the GC, and so 'hashtableobj' might move afterwards. */
+ if (_is_in_nursery(hashtableobj)) {
+ entry = (stm_hashtable_entry_t *)
+ stm_allocate(sizeof(stm_hashtable_entry_t));
+ entry->userdata = stm_hashtable_entry_userdata;
+ entry->index = index;
+ entry->object = NULL;
+ }
+ else {
+ /* for a non-nursery 'hashtableobj', we pretend that the
+ 'entry' object we're about to return was already
+ existing all along, with NULL in all segments. If the
+ caller of this function is going to modify the 'object'
+ field, it will call stm_write(entry) first, which will
+ correctly schedule 'entry' for write propagation. We
+ do that even if 'hashtableobj' was created by the
+ running transaction: the new 'entry' object is created
+ as if it was older than the transaction.
+
+ Note the following difference: if 'hashtableobj' is
+ still in the nursery (case above), the 'entry' object
+ is also allocated from the nursery, and after a minor
+ collection it ages as an old-but-created-by-the-
+ current-transaction object. We could try to emulate
+ this here, or to create young 'entry' objects, but
+ doing either of these would require careful
+ synchronization with other pieces of the code that may
+ change.
+ */
+ struct stm_hashtable_entry_s initial = {
+ .userdata = stm_hashtable_entry_userdata,
+ .index = index,
+ .object = NULL
+ };
+ entry = (stm_hashtable_entry_t *)
+ stm_allocate_preexisting(sizeof(stm_hashtable_entry_t),
+ (char *)&initial.header);
+ hashtable->additions++;
+ }
+ table->items[i] = entry;
+ write_fence(); /* make sure 'table->items' is written here */
+ VOLATILE_TABLE(table)->resize_counter = rc - 6; /* unlock */
+ return entry;
+ }
+ else {
+ /* if rc is smaller than 6, we must allocate a new bigger table.
+ */
+ uintptr_t biggercount = table->mask + 1;
+ if (biggercount < 50000)
+ biggercount *= 4;
+ else
+ biggercount *= 2;
+ _stm_rehash_hashtable(hashtable, biggercount, /*segment_base=*/NULL);
+ goto restart;
+ }
+}
+
+object_t *stm_hashtable_read(object_t *hobj, stm_hashtable_t *hashtable,
+ uintptr_t key)
+{
+ stm_hashtable_entry_t *e = stm_hashtable_lookup(hobj, hashtable, key);
+ stm_read((object_t *)e);
+ return e->object;
+}
+
+void stm_hashtable_write_entry(object_t *hobj, stm_hashtable_entry_t *entry,
+ object_t *nvalue)
+{
+ if (_STM_WRITE_CHECK_SLOWPATH((object_t *)entry)) {
+
+ stm_write((object_t *)entry);
+
+ uintptr_t i = list_count(STM_PSEGMENT->modified_old_objects);
+ if (i > 0 && list_item(STM_PSEGMENT->modified_old_objects, i - 3)
+ == (uintptr_t)entry) {
+ /* The stm_write() above recorded a write to 'entry'. Here,
+ we add another stm_undo_s to modified_old_objects with
+ TYPE_MODIFIED_HASHTABLE. It is ignored everywhere except
+ in _stm_validate().
+
+ The goal is that this TYPE_MODIFIED_HASHTABLE ends up in
+ the commit log's 'cl_written' array. Later, another
+ transaction validating that log will check two things:
+
+ - the regular stm_undo_s entry put by stm_write() above
+ will make the other transaction check that it didn't
+ read the same 'entry' object;
+
+ - the TYPE_MODIFIED_HASHTABLE entry we're adding now
+ will make the other transaction check that it didn't
+ do any stm_hashtable_list() on the complete hashtable.
+ */
+ STM_PSEGMENT->modified_old_objects = list_append3(
+ STM_PSEGMENT->modified_old_objects,
+ TYPE_POSITION_MARKER, /* type1 */
+ TYPE_MODIFIED_HASHTABLE, /* type2 */
+ (uintptr_t)hobj); /* modif_hashtable */
+ }
+ }
+ entry->object = nvalue;
+}
+
+void stm_hashtable_write(object_t *hobj, stm_hashtable_t *hashtable,
+ uintptr_t key, object_t *nvalue,
+ stm_thread_local_t *tl)
+{
+ STM_PUSH_ROOT(*tl, nvalue);
+ STM_PUSH_ROOT(*tl, hobj);
+ stm_hashtable_entry_t *e = stm_hashtable_lookup(hobj, hashtable, key);
+ STM_POP_ROOT(*tl, hobj);
+ STM_POP_ROOT(*tl, nvalue);
+ stm_hashtable_write_entry(hobj, e, nvalue);
+}
+
+long stm_hashtable_length_upper_bound(stm_hashtable_t *hashtable)
+{
+ stm_hashtable_table_t *table;
+ uintptr_t rc;
+
+ restart:
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+ rc = VOLATILE_TABLE(table)->resize_counter;
+ if (IS_EVEN(rc)) {
+ spin_loop();
+ goto restart;
+ }
+
+ uintptr_t initial_rc = (table->mask + 1) * 4 + 1;
+ uintptr_t num_entries_times_6 = initial_rc - rc;
+ return num_entries_times_6 / 6;
+}
+
+long stm_hashtable_list(object_t *hobj, stm_hashtable_t *hashtable,
+ stm_hashtable_entry_t **results)
+{
+ /* Set the read marker. It will be left as long as we're running
+ the same transaction.
+ */
+ stm_read(hobj);
+
+ /* Get the table. No synchronization is needed: we may miss some
+ entries that are being added, but they would contain NULL in
+ this segment anyway. */
+ stm_hashtable_table_t *table = VOLATILE_HASHTABLE(hashtable)->table;
+
+ /* Read all entries, check which ones are not NULL, count them,
+ and optionally list them in 'results'.
+ */
+ uintptr_t i, mask = table->mask;
+ stm_hashtable_entry_t *entry;
+ long nresult = 0;
+
+ if (results != NULL) {
+ /* collect the results in the provided list */
+ for (i = 0; i <= mask; i++) {
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ stm_read((object_t *)entry);
+ if (entry->object != NULL)
+ results[nresult++] = entry;
+ }
+ }
+ }
+ else {
+ /* don't collect, just get the exact number of results */
+ for (i = 0; i <= mask; i++) {
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ stm_read((object_t *)entry);
+ if (entry->object != NULL)
+ nresult++;
+ }
+ }
+ }
+ return nresult;
+}
+
+static void _stm_compact_hashtable(struct object_s *hobj,
+ stm_hashtable_t *hashtable)
+{
+ stm_hashtable_table_t *table = hashtable->table;
+ uintptr_t rc = table->resize_counter;
+ assert(!IS_EVEN(rc));
+
+ if (hashtable->additions * 4 > table->mask) {
+ hashtable->additions = 0;
+
+ /* If 'hobj' was created in some current transaction, i.e. if it is
+ now an overflow object, then we have the risk that some of its
+ entry objects were not created with stm_allocate_preexisting().
+ In that situation, a valid workaround is to read all entry
+ objects in the segment of the running transaction. Otherwise,
+ the base case is to read them all from segment zero.
+ */
+ long segnum = get_num_segment_containing_address((char *)hobj);
+ if (!IS_OVERFLOW_OBJ(get_priv_segment(segnum), hobj))
+ segnum = 0;
+
+ uintptr_t initial_rc = (table->mask + 1) * 4 + 1;
+ uintptr_t num_entries_times_6 = initial_rc - rc;
+ uintptr_t count = INITIAL_HASHTABLE_SIZE;
+ while (count * 4 < num_entries_times_6)
+ count *= 2;
+ /* sanity-check: 'num_entries_times_6 < initial_rc', and so 'count'
+ can never grow larger than the current table size. */
+ assert(count <= table->mask + 1);
+
+ dprintf(("compact with %ld items:\n", num_entries_times_6 / 6));
+ _stm_rehash_hashtable(hashtable, count, get_segment_base(segnum));
+ }
+
+ table = hashtable->table;
+ assert(!IS_EVEN(table->resize_counter));
+
+ if (table != &hashtable->initial_table) {
+ uintptr_t rc = hashtable->initial_table.resize_counter;
+ while (1) {
+ assert(IS_EVEN(rc));
+ assert(rc != RESIZING_LOCK);
+
+ stm_hashtable_table_t *old_table = (stm_hashtable_table_t *)rc;
+ if (old_table == table)
+ break;
+ rc = old_table->resize_counter;
+ free(old_table);
+ }
+ hashtable->initial_table.resize_counter = (uintptr_t)table;
+ assert(IS_EVEN(hashtable->initial_table.resize_counter));
+ }
+}
+
+void stm_hashtable_tracefn(struct object_s *hobj, stm_hashtable_t *hashtable,
+ void trace(object_t **))
+{
+ if (trace == TRACE_FOR_MAJOR_COLLECTION)
+ _stm_compact_hashtable(hobj, hashtable);
+
+ stm_hashtable_table_t *table;
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+
+ uintptr_t j, mask = table->mask;
+ for (j = 0; j <= mask; j++) {
+ stm_hashtable_entry_t *volatile *pentry;
+ pentry = &VOLATILE_TABLE(table)->items[j];
+ if (*pentry != NULL) {
+ trace((object_t **)pentry);
+ }
+ }
+}
diff --git a/rpython/translator/stm/src_stm/stm/marker.c b/rpython/translator/stm/src_stm/stm/marker.c
--- a/rpython/translator/stm/src_stm/stm/marker.c
+++ b/rpython/translator/stm/src_stm/stm/marker.c
@@ -42,7 +42,8 @@
*/
while (contention != start) {
--contention;
- if (contention->type == TYPE_POSITION_MARKER) {
+ if (contention->type == TYPE_POSITION_MARKER &&
+ contention->type2 != TYPE_MODIFIED_HASHTABLE) {
out_marker->odd_number = contention->marker_odd_number;
out_marker->object = contention->marker_object;
return;
@@ -69,6 +70,9 @@
return; /* already up-to-date */
}
+ /* -2 is not odd */
+ assert(marker.odd_number != (uintptr_t)TYPE_MODIFIED_HASHTABLE);
+
STM_PSEGMENT->position_markers_last = list_count(list);
STM_PSEGMENT->modified_old_objects = list_append3(
list,
diff --git a/rpython/translator/stm/src_stm/stm/misc.c b/rpython/translator/stm/src_stm/stm/misc.c
--- a/rpython/translator/stm/src_stm/stm/misc.c
+++ b/rpython/translator/stm/src_stm/stm/misc.c
@@ -31,10 +31,7 @@
bool _stm_was_read(object_t *obj)
{
- uint8_t rm = ((struct stm_read_marker_s *)
- (STM_SEGMENT->segment_base + (((uintptr_t)obj) >> 4)))->rm;
- assert(rm <= STM_SEGMENT->transaction_read_version);
- return rm == STM_SEGMENT->transaction_read_version;
+ return was_read_remote(STM_SEGMENT->segment_base, obj);
}
bool _stm_was_written(object_t *obj)
diff --git a/rpython/translator/stm/src_stm/stm/nursery.c b/rpython/translator/stm/src_stm/stm/nursery.c
--- a/rpython/translator/stm/src_stm/stm/nursery.c
+++ b/rpython/translator/stm/src_stm/stm/nursery.c
@@ -424,6 +424,7 @@
struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
for (; undo < end; undo++) {
+ /* this logic also works if type2 == TYPE_MODIFIED_HASHTABLE */
if (undo->type == TYPE_POSITION_MARKER)
minor_trace_if_young(&undo->marker_object);
}
diff --git a/rpython/translator/stm/src_stm/stm/pages.h b/rpython/translator/stm/src_stm/stm/pages.h
--- a/rpython/translator/stm/src_stm/stm/pages.h
+++ b/rpython/translator/stm/src_stm/stm/pages.h
@@ -62,7 +62,11 @@
static inline bool get_page_status_in(long segnum, uintptr_t pagenum)
{
- /* reading page status requires "read"-lock: */
+ /* reading page status requires "read"-lock, which is defined as
+ "any segment has the privatization_lock". This is enough to
+ prevent the "write"-lock from being acquired by somebody else
+ (defined as "_all_ segments have the privatization_lock").
+ */
assert(STM_PSEGMENT->privatization_lock);
OPT_ASSERT(segnum < 8 * sizeof(struct page_shared_s));
diff --git a/rpython/translator/stm/src_stm/stm/setup.c b/rpython/translator/stm/src_stm/stm/setup.c
--- a/rpython/translator/stm/src_stm/stm/setup.c
+++ b/rpython/translator/stm/src_stm/stm/setup.c
@@ -250,8 +250,6 @@
set_gs_register(get_segment_base(num + 1));
s_mutex_unlock();
- DEBUG_EXPECT_SEGFAULT(true);
-
if (num == 0) {
dprintf(("STM_GC_NURSERY: %d\n", STM_GC_NURSERY));
dprintf(("NB_PAGES: %d\n", NB_PAGES));
diff --git a/rpython/translator/stm/src_stm/stm/setup.h b/rpython/translator/stm/src_stm/stm/setup.h
--- a/rpython/translator/stm/src_stm/stm/setup.h
+++ b/rpython/translator/stm/src_stm/stm/setup.h
@@ -3,8 +3,8 @@
static void setup_protection_settings(void);
static pthread_t *_get_cpth(stm_thread_local_t *);
#ifndef NDEBUG
-static __thread long _stm_segfault_expected = 0;
-#define DEBUG_EXPECT_SEGFAULT(v) do {if (v) _stm_segfault_expected++; else _stm_segfault_expected--;} while (0)
+static __thread long _stm_segfault_expected = 1;
+#define DEBUG_EXPECT_SEGFAULT(v) do {if (v) _stm_segfault_expected++; else _stm_segfault_expected--; assert(_stm_segfault_expected <= 1);} while (0)
#else
#define DEBUG_EXPECT_SEGFAULT(v) {}
#endif
diff --git a/rpython/translator/stm/src_stm/stmgc.c b/rpython/translator/stm/src_stm/stmgc.c
--- a/rpython/translator/stm/src_stm/stmgc.c
+++ b/rpython/translator/stm/src_stm/stmgc.c
@@ -39,3 +39,4 @@
#include "stm/prof.c"
#include "stm/rewind_setjmp.c"
#include "stm/finalizer.c"
+#include "stm/hashtable.c"
diff --git a/rpython/translator/stm/src_stm/stmgc.h b/rpython/translator/stm/src_stm/stmgc.h
--- a/rpython/translator/stm/src_stm/stmgc.h
+++ b/rpython/translator/stm/src_stm/stmgc.h
@@ -196,10 +196,13 @@
STM_SEGMENT->transaction_read_version;
}
+#define _STM_WRITE_CHECK_SLOWPATH(obj) \
+ UNLIKELY(((obj)->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0)
+
__attribute__((always_inline))
static inline void stm_write(object_t *obj)
{
- if (UNLIKELY((obj->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0))
+ if (_STM_WRITE_CHECK_SLOWPATH(obj))
_stm_write_slowpath(obj);
}
@@ -208,7 +211,7 @@
static inline void stm_write_card(object_t *obj, uintptr_t index)
{
/* if GCFLAG_WRITE_BARRIER is set, then don't do anything more. */
- if (UNLIKELY((obj->stm_flags & _STM_GCFLAG_WRITE_BARRIER) != 0)) {
+ if (_STM_WRITE_CHECK_SLOWPATH(obj)) {
/* GCFLAG_WRITE_BARRIER is not set. This might be because
it's the first time we see a given small array; or it might
@@ -479,6 +482,38 @@
/* dummies for now: */
static inline void stm_flush_timing(stm_thread_local_t *tl, int verbose) {}
+
+/* Hashtables. Keys are 64-bit unsigned integers, values are
+ 'object_t *'. Note that the type 'stm_hashtable_t' is not an
+ object type at all; you need to allocate and free it explicitly.
+ If you want to embed the hashtable inside an 'object_t' you
+ probably need a light finalizer to do the freeing. */
+typedef struct stm_hashtable_s stm_hashtable_t;
+typedef TLPREFIX struct stm_hashtable_entry_s stm_hashtable_entry_t;
+
+stm_hashtable_t *stm_hashtable_create(void);
+void stm_hashtable_free(stm_hashtable_t *);
+stm_hashtable_entry_t *stm_hashtable_lookup(object_t *, stm_hashtable_t *,
+ uintptr_t key);
+object_t *stm_hashtable_read(object_t *, stm_hashtable_t *, uintptr_t key);
+void stm_hashtable_write(object_t *, stm_hashtable_t *, uintptr_t key,
+ object_t *nvalue, stm_thread_local_t *);
+void stm_hashtable_write_entry(object_t *hobj, stm_hashtable_entry_t *entry,
+ object_t *nvalue);
+long stm_hashtable_length_upper_bound(stm_hashtable_t *);
+long stm_hashtable_list(object_t *, stm_hashtable_t *,
+ stm_hashtable_entry_t **results);
+extern uint32_t stm_hashtable_entry_userdata;
+void stm_hashtable_tracefn(struct object_s *, stm_hashtable_t *,
+ void (object_t **));
+
+struct stm_hashtable_entry_s {
+ struct object_s header;
+ uint32_t userdata;
+ uintptr_t index;
+ object_t *object;
+};
+
/* ==================== END ==================== */
static void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
From noreply at buildbot.pypy.org Thu Apr 9 17:12:29 2015
From: noreply at buildbot.pypy.org (Raemi)
Date: Thu, 9 Apr 2015 17:12:29 +0200 (CEST)
Subject: [pypy-commit] pypy stmgc-c8: import stmgc-c8 e55658d12179
Message-ID: <20150409151229.7A7B81C0173@cobra.cs.uni-duesseldorf.de>
Author: Remi Meier
Branch: stmgc-c8
Changeset: r76768:9789a066a768
Date: 2015-04-09 17:11 +0200
http://bitbucket.org/pypy/pypy/changeset/9789a066a768/
Log: import stmgc-c8 e55658d12179
diff --git a/rpython/translator/stm/src_stm/revision b/rpython/translator/stm/src_stm/revision
--- a/rpython/translator/stm/src_stm/revision
+++ b/rpython/translator/stm/src_stm/revision
@@ -1,1 +1,1 @@
-1062987da64f
+e55658d12179
diff --git a/rpython/translator/stm/src_stm/stm/gcpage.c b/rpython/translator/stm/src_stm/stm/gcpage.c
--- a/rpython/translator/stm/src_stm/stm/gcpage.c
+++ b/rpython/translator/stm/src_stm/stm/gcpage.c
@@ -106,6 +106,9 @@
object_t *_stm_allocate_old(ssize_t size_rounded_up)
{
/* only for tests xxx but stm_setup_prebuilt() uses this now too */
+ if (size_rounded_up <= GC_LAST_SMALL_SIZE)
+ return _stm_allocate_old_small(size_rounded_up);
+
stm_char *p = allocate_outside_nursery_large(size_rounded_up);
object_t *o = (object_t *)p;
diff --git a/rpython/translator/stm/src_stm/stm/hashtable.c b/rpython/translator/stm/src_stm/stm/hashtable.c
--- a/rpython/translator/stm/src_stm/stm/hashtable.c
+++ b/rpython/translator/stm/src_stm/stm/hashtable.c
@@ -109,6 +109,8 @@
{
/* can only be safely called during major GC, when all other threads
are suspended */
+ assert(_has_mutex());
+
long i;
for (i = 1; i < NB_SEGMENTS; i++) {
if (get_priv_segment(i)->transaction_state == TS_NONE)
@@ -174,6 +176,7 @@
if (entry == NULL)
continue;
if (segment_base != NULL) {
+ /* -> compaction during major GC */
if (((struct stm_hashtable_entry_s *)
REAL_ADDRESS(segment_base, entry))->object == NULL &&
!_stm_was_read_by_anybody((object_t *)entry)) {
@@ -283,6 +286,9 @@
/* we can only enter here once! If we allocate stuff, we may
run the GC, and so 'hashtableobj' might move afterwards. */
if (_is_in_nursery(hashtableobj)) {
+ /* this also means that the hashtable is from this
+ transaction and not visible to other segments yet, so
+ the new entry can be nursery-allocated. */
entry = (stm_hashtable_entry_t *)
stm_allocate(sizeof(stm_hashtable_entry_t));
entry->userdata = stm_hashtable_entry_userdata;
From noreply at buildbot.pypy.org Fri Apr 10 09:53:44 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 09:53:44 +0200 (CEST)
Subject: [pypy-commit] pypy default: Fix for issue #2019
Message-ID: <20150410075344.EDC181C0294@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76769:adfa94ce2ed9
Date: 2015-04-10 09:53 +0200
http://bitbucket.org/pypy/pypy/changeset/adfa94ce2ed9/
Log: Fix for issue #2019
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -501,12 +501,18 @@
return SomeInteger(nonneg=True)
def method_strip(self, chr=None):
+ if chr is None and isinstance(self, SomeUnicodeString):
+ raise AnnotatorError("unicode.strip() with no arg is not RPython")
return self.basestringclass(no_nul=self.no_nul)
def method_lstrip(self, chr=None):
+ if chr is None and isinstance(self, SomeUnicodeString):
+ raise AnnotatorError("unicode.lstrip() with no arg is not RPython")
return self.basestringclass(no_nul=self.no_nul)
def method_rstrip(self, chr=None):
+ if chr is None and isinstance(self, SomeUnicodeString):
+ raise AnnotatorError("unicode.rstrip() with no arg is not RPython")
return self.basestringclass(no_nul=self.no_nul)
def method_join(self, s_list):
diff --git a/rpython/rtyper/test/test_runicode.py b/rpython/rtyper/test/test_runicode.py
--- a/rpython/rtyper/test/test_runicode.py
+++ b/rpython/rtyper/test/test_runicode.py
@@ -306,3 +306,11 @@
assert res == False
res = self.interpret(f, [2])
assert res == True
+
+ def test_strip_no_arg(self):
+
+ def f():
+ return u'abcdef'.strip()
+
+ e = py.test.raises(Exception, self.interpret, f, [])
+ assert "unicode.strip() with no arg is not RPython" in str(e.value)
From noreply at buildbot.pypy.org Fri Apr 10 11:55:13 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 11:55:13 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Add PLAN.
Message-ID: <20150410095513.16AF71C0294@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1683:d96ec16eaef6
Date: 2015-04-10 11:55 +0200
http://bitbucket.org/cffi/cffi/changeset/d96ec16eaef6/
Log: Add PLAN.
diff --git a/new/PLAN b/new/PLAN
new file mode 100644
--- /dev/null
+++ b/new/PLAN
@@ -0,0 +1,114 @@
+
+==================================================
+CPython C extension module produced by recompile()
+==================================================
+
+Global variable:
+CTypeDescrObject *_cffi_types[];
+
+Every _cffi_types entry is initially an odd integer and is fixed
+to be a real `CTypeDescrObject *` later.
+
+The generated C functions are listed in _cffi_globals, a sorted array
+of entries which get turned lazily into real . Each entry in this array has an index in the _cffi_types
+array, which describe the function type (CTOP_FUNCTION opcode, see
+below). We turn the odd integers into real CTypeDescrObjects at the
+point where the entry is turned into a real builtin function object.
+
+The odd integers are "opcodes" that contain a type info in the lowest
+byte. The remaining N-1 bytes of the integer is an "arg" that depends
+on the type info:
+
+CTOP_PRIMITIVE
+ the arg tells which primitive type it is
+
+CTOP_POINTER
+ the arg is the index of the item type in the _cffi_types array.
+
+CTOP_ARRAY
+ the arg is the index of the item type in the _cffi_types array.
+ followed by another opcode that contains (uintptr_t)length_of_array.
+
+CTOP_OPEN_ARRAY
+ for syntax like "int[]". same as CTOP_ARRAY but without the length
+
+CTOP_STRUCT_UNION
+ the arg is the index of the struct/union in _cffi_structs_unions
+
+CTOP_ENUM
+ the arg is the index of the enum in _cffi_enums
+
+CTOP_TYPENAME
+ the arg is the index of the typename in _cffi_typenames
+
+CTOP_FUNCTION
+ the arg is the index of the result type in _cffi_types.
+ followed by other opcodes for the arguments.
+ terminated by CTOP_FUNCTION_END.
+
+CTOP_FUNCTION_END
+ the arg's lowest bit is set if there is a "..." argument.
+
+
+_cffi_globals
+-------------
+
+Lists global functions (with a type CTOP_FUNCTION), and also global
+variables (with a different type than CTOP_FUNCTION).
+
+struct {
+ const char *name;
+ void *address;
+ int type_index;
+}
+
+
+_cffi_structs_unions
+--------------------
+
+struct {
+ const char *name;
+ size_t size;
+ int alignment;
+ int flags; // CT_UNION?
+ int num_fields;
+ int first_field_index; // -> _cffi_fields array
+}
+
+struct _cffi_field_s {
+ const char *name;
+ size_t field_offset;
+ size_t field_size;
+ int field_bit_size;
+ int field_type; // -> _cffi_types
+}
+
+
+_cffi_enums
+-----------
+
+struct {
+ const char *name;
+ int integer_type; // -> _cffi_types
+}
+
+
+_cffi_constants
+---------------
+
+struct {
+ const char *name;
+ unsigned long long value;
+ int cinfo_or_type_index; // CINFO_POSITIVE_INT, CINFO_NONPOSITIVE_INT
+}
+
+
+
+Runtime type parsing
+==================================================
+
+For ffi.new() etc. This is done by turning the C declaration into an
+array of opcodes like above, and then turning these opcodes into real
+types. The array of opcodes is then freed. We use a cache from C
+declaration to final types.
From noreply at buildbot.pypy.org Fri Apr 10 11:58:13 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 11:58:13 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: add _cffi_typenames
Message-ID: <20150410095813.BB2D01C0294@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1684:bc6bab2c3d45
Date: 2015-04-10 11:58 +0200
http://bitbucket.org/cffi/cffi/changeset/bc6bab2c3d45/
Log: add _cffi_typenames
diff --git a/new/PLAN b/new/PLAN
--- a/new/PLAN
+++ b/new/PLAN
@@ -94,6 +94,15 @@
}
+_cffi_typenames
+---------------
+
+struct {
+ const char *name;
+ int type_index; // -> _cffi_types
+}
+
+
_cffi_constants
---------------
From noreply at buildbot.pypy.org Fri Apr 10 15:37:28 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 15:37:28 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: in-progress
Message-ID: <20150410133728.CD56A1C15B6@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1685:f53ba78aeffa
Date: 2015-04-10 15:38 +0200
http://bitbucket.org/cffi/cffi/changeset/f53ba78aeffa/
Log: in-progress
diff --git a/new/PLAN b/new/PLAN
--- a/new/PLAN
+++ b/new/PLAN
@@ -71,7 +71,7 @@
const char *name;
size_t size;
int alignment;
- int flags; // CT_UNION?
+ int flags; // CT_UNION? CT_IS_OPAQUE?
int num_fields;
int first_field_index; // -> _cffi_fields array
}
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -38,10 +38,12 @@
};
typedef struct {
+ struct _cffi_parse_info_s *info;
+ const char *p;
enum token_e kind;
- const char *p, **error_location, **error_message;
size_t size;
- ctype_opcode_t *opcodes, *opcodes_end;
+ _cffi_opcode_t *output;
+ unsigned long output_index;
} token_t;
static int is_space(char x)
@@ -153,54 +155,61 @@
}
}
-static void parse_error(token_t *tok, const char *msg)
+static int parse_error(token_t *tok, const char *msg)
{
if (tok->kind != TOK_ERROR) {
tok->kind = TOK_ERROR;
- if (tok->error_location)
- *tok->error_location = tok->p;
- if (tok->error_message)
- *tok->error_message = msg;
+ if (tok->info->error_location)
+ *tok->info->error_location = tok->p;
+ if (tok->info->error_message)
+ *tok->info->error_message = msg;
}
+ return -1;
}
-static ctype_opcode_t *alloc_ds(token_t *tok, size_t num)
+static int write_ds(token_t *tok, _cffi_opcode_t ds)
{
- ctype_opcode_t *result = tok->opcodes;
- if (num > tok->opcodes_end - result) {
- parse_error(tok, "type too lengthy");
- return NULL;
+ size_t index = tok->output_index;
+ if (index >= tok->info->output_size) {
+ parse_error(tok, "internal type complexity limit reached");
+ return -1;
}
- tok->opcodes += num;
- return result;
+ tok->output[index] = ds;
+ tok->output_index = index + 1;
+ return index;
}
-#if 0
-static void parse_complete(token_t *tok, _crx_qual_type *result);
+static int parse_complete(token_t *tok);
-static void parse_sequel(token_t *tok, intptr_t ds_end)
+static int parse_sequel(token_t *tok)
{
- intptr_t *ds;
- while (tok->kind == TOK_STAR || tok->kind == TOK_CONST ||
- tok->kind == TOK_VOLATILE) {
- ds = alloc_ds(tok, 1);
- if (ds == NULL)
- return;
- ds[0] = tok->kind;
+ header:
+ switch (tok->kind) {
+ case TOK_STAR:
+ write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, tok->output_index - 1));
next_token(tok);
+ goto header;
+ case TOK_CONST:
+ /* ignored for now */
+ next_token(tok);
+ goto header;
+ case TOK_VOLATILE:
+ /* ignored for now */
+ next_token(tok);
+ goto header;
+ default:
+ break;
}
+ _cffi_opcode_t slot_result = _CFFI_OP(0, tok->output_index - 1);
+ _cffi_opcode_t *p_current = &slot_result;
+
int check_for_grouping = -1;
if (tok->kind == TOK_IDENTIFIER) {
next_token(tok); /* skip a potential variable name */
check_for_grouping = 1;
}
- intptr_t *jump_slot = alloc_ds(tok, 1);
- if (jump_slot == NULL)
- return;
- *jump_slot = ds_end;
-
next_right_part:
check_for_grouping++;
@@ -209,6 +218,8 @@
case TOK_OPEN_PAREN:
next_token(tok);
+ abort();
+#if 0
if (check_for_grouping == 0 && (tok->kind == TOK_STAR ||
tok->kind == TOK_CONST ||
tok->kind == TOK_VOLATILE ||
@@ -261,48 +272,43 @@
}
next_token(tok);
goto next_right_part;
+#endif
case TOK_OPEN_BRACKET:
{
- uintptr_t length = (uintptr_t)-1;
+ _cffi_opcode_t prev = *p_current;
+ *p_current = _CFFI_OP(_CFFI_GETOP(prev), tok->output_index);
+ p_current = &tok->output[tok->output_index];
+
next_token(tok);
if (tok->kind != TOK_CLOSE_BRACKET) {
- if (tok->kind != TOK_INTEGER) {
- parse_error(tok, "expected a positive integer constant");
- return;
- }
+ size_t length;
- if (sizeof(uintptr_t) > sizeof(unsigned long))
+ if (tok->kind != TOK_INTEGER)
+ return parse_error(tok, "expected a positive integer constant");
+
+ if (sizeof(length) > sizeof(unsigned long))
length = strtoull(tok->p, NULL, 10);
else
length = strtoul(tok->p, NULL, 10);
- if (length == (uintptr_t)-1) {
- parse_error(tok, "number too large");
- return;
- }
next_token(tok);
+
+ write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, _CFFI_GETARG(prev)));
+ write_ds(tok, (_cffi_opcode_t)length);
}
+ else
+ write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, _CFFI_GETARG(prev)));
- if (tok->kind != TOK_CLOSE_BRACKET) {
- parse_error(tok, "expected ']'");
- return;
- }
+ if (tok->kind != TOK_CLOSE_BRACKET)
+ return parse_error(tok, "expected ']'");
next_token(tok);
-
- ds = alloc_ds(tok, 3);
- if (ds == NULL)
- return;
- ds[0] = TOK_OPEN_BRACKET;
- ds[1] = (intptr_t)length;
- ds[2] = *jump_slot;
- *jump_slot = -(ds - tok->all_delay_slots);
goto next_right_part;
}
default:
break;
}
+ return _CFFI_GETARG(slot_result);
}
-#endif
#if 0
static void fetch_delay_slots(token_t *tok, _crx_qual_type *result,
@@ -368,66 +374,55 @@
}
#endif
-static void parse_complete(token_t *tok)
+static int parse_complete(token_t *tok)
{
- int const_qualifier = 0, volatile_qualifier = 0;
-
qualifiers:
switch (tok->kind) {
case TOK_CONST:
- const_qualifier = 1;
+ /* ignored for now */
next_token(tok);
goto qualifiers;
case TOK_VOLATILE:
- volatile_qualifier = 1;
+ /* ignored for now */
next_token(tok);
goto qualifiers;
default:
;
}
- int t1;
+ unsigned int t0;
+ _cffi_opcode_t t1;
int modifiers_length = 0;
int modifiers_sign = 0;
modifiers:
switch (tok->kind) {
case TOK_SHORT:
- if (modifiers_length != 0) {
- parse_error(tok, "'short' after another 'short' or 'long'");
- return;
- }
+ if (modifiers_length != 0)
+ return parse_error(tok, "'short' after another 'short' or 'long'");
modifiers_length--;
next_token(tok);
goto modifiers;
case TOK_LONG:
- if (modifiers_length < 0) {
- parse_error(tok, "'long' after 'short'");
- return;
- }
- if (modifiers_length >= 2) {
- parse_error(tok, "'long long long' is too long");
- return;
- }
+ if (modifiers_length < 0)
+ return parse_error(tok, "'long' after 'short'");
+ if (modifiers_length >= 2)
+ return parse_error(tok, "'long long long' is too long");
modifiers_length++;
next_token(tok);
goto modifiers;
case TOK_SIGNED:
- if (modifiers_sign) {
- parse_error(tok, "multiple 'signed' or 'unsigned'");
- return;
- }
+ if (modifiers_sign)
+ return parse_error(tok, "multiple 'signed' or 'unsigned'");
modifiers_sign++;
next_token(tok);
goto modifiers;
case TOK_UNSIGNED:
- if (modifiers_sign) {
- parse_error(tok, "multiple 'signed' or 'unsigned'");
- return;
- }
+ if (modifiers_sign)
+ return parse_error(tok, "multiple 'signed' or 'unsigned'");
modifiers_sign--;
next_token(tok);
goto modifiers;
@@ -445,23 +440,18 @@
case TOK_FLOAT:
case TOK_STRUCT:
case TOK_UNION:
- parse_error(tok, "invalid combination of types");
- return;
+ return parse_error(tok, "invalid combination of types");
case TOK_DOUBLE:
- if (modifiers_sign != 0 || modifiers_length != 1) {
- parse_error(tok, "invalid combination of types");
- return;
- }
+ if (modifiers_sign != 0 || modifiers_length != 1)
+ return parse_error(tok, "invalid combination of types");
next_token(tok);
- t1 = CTOP_LONGDOUBLE;
+ t0 = _CFFI_PRIM_LONGDOUBLE;
break;
case TOK_CHAR:
- if (modifiers_length != 0) {
- parse_error(tok, "invalid combination of types");
- return;
- }
+ if (modifiers_length != 0)
+ return parse_error(tok, "invalid combination of types");
modifiers_length = -2;
/* fall-through */
case TOK_INT:
@@ -470,41 +460,42 @@
default:
if (modifiers_sign >= 0)
switch (modifiers_length) {
- case -2: t1 = CTOP_SCHAR; break;
- case -1: t1 = CTOP_SHORT; break;
- case 1: t1 = CTOP_LONG; break;
- case 2: t1 = CTOP_LONGLONG; break;
- default: t1 = CTOP_INT; break;
+ case -2: t0 = _CFFI_PRIM_SCHAR; break;
+ case -1: t0 = _CFFI_PRIM_SHORT; break;
+ case 1: t0 = _CFFI_PRIM_LONG; break;
+ case 2: t0 = _CFFI_PRIM_LONGLONG; break;
+ default: t0 = _CFFI_PRIM_INT; break;
}
else
switch (modifiers_length) {
- case -2: t1 = CTOP_UCHAR; break;
- case -1: t1 = CTOP_USHORT; break;
- case 1: t1 = CTOP_ULONG; break;
- case 2: t1 = CTOP_ULONGLONG; break;
- default: t1 = CTOP_UINT; break;
+ case -2: t0 = _CFFI_PRIM_UCHAR; break;
+ case -1: t0 = _CFFI_PRIM_USHORT; break;
+ case 1: t0 = _CFFI_PRIM_ULONG; break;
+ case 2: t0 = _CFFI_PRIM_ULONGLONG; break;
+ default: t0 = _CFFI_PRIM_UINT; break;
}
}
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, t0);
}
else {
switch (tok->kind) {
case TOK_INT:
- t1 = CTOP_INT;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT);
break;
case TOK_CHAR:
- t1 = CTOP_CHAR;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_CHAR);
break;
case TOK_VOID:
- t1 = CTOP_VOID;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_VOID);
break;
case TOK__BOOL:
- t1 = CTOP_BOOL;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_BOOL);
break;
case TOK_FLOAT:
- t1 = CTOP_FLOAT;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT);
break;
case TOK_DOUBLE:
- t1 = CTOP_DOUBLE;
+ t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE);
break;
case TOK_IDENTIFIER:
{
@@ -550,41 +541,32 @@
#endif
}
default:
- parse_error(tok, "identifier expected");
- return;
+ return parse_error(tok, "identifier expected");
}
next_token(tok);
}
- *alloc_ds(tok, 1) = t1;
- if (const_qualifier)
- *alloc_ds(tok, 1) = CTOP_CONST;
- if (volatile_qualifier)
- *alloc_ds(tok, 1) = CTOP_VOLATILE;
- //parse_sequel(tok, CTOP_END);
- *alloc_ds(tok, 1) = CTOP_END;
+ write_ds(tok, t1);
+ return parse_sequel(tok);
}
-int parse_c_type(const char *input,
- ctype_opcode_t *output, size_t output_size,
- const char **error_loc, const char **error_msg)
+int parse_c_type(struct _cffi_parse_info_s *info, const char *input)
{
+ int result;
token_t token;
+ token.info = info;
token.kind = TOK_START;
token.p = input;
- token.error_location = error_loc;
- token.error_message = error_msg;
token.size = 0;
- token.opcodes = output;
- token.opcodes_end = output + output_size;
+ token.output = info->output;
+ token.output_index = 0;
+
next_token(&token);
- parse_complete(&token);
+ result = parse_complete(&token);
- if (token.kind != TOK_END) {
- parse_error(&token, "unexpected symbol");
- return -1;
- }
- return 0;
+ if (token.kind != TOK_END)
+ return parse_error(&token, "unexpected symbol");
+ return result;
}
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -1,29 +1,100 @@
-typedef int ctype_opcode_t;
+typedef void *_cffi_opcode_t;
-#define CTOP_END 0
-#define CTOP_CONST 1
-#define CTOP_VOLATILE 2
+#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((unsigned long)(arg)) << 8))
+#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(unsigned long)cffi_opcode)
+#define _CFFI_GETARG(cffi_opcode) (((unsigned long)cffi_opcode) >> 8)
-#define CTOP_VOID 100
-#define CTOP_BOOL 101
-#define CTOP_CHAR 102
-#define CTOP_SCHAR 103
-#define CTOP_UCHAR 104
-#define CTOP_SHORT 105
-#define CTOP_USHORT 106
-#define CTOP_INT 107
-#define CTOP_UINT 108
-#define CTOP_LONG 109
-#define CTOP_ULONG 110
-#define CTOP_LONGLONG 111
-#define CTOP_ULONGLONG 112
-#define CTOP_FLOAT 113
-#define CTOP_DOUBLE 114
-#define CTOP_LONGDOUBLE 115
+#define _CFFI_OP_PRIMITIVE 1
+#define _CFFI_OP_POINTER 3
+#define _CFFI_OP_ARRAY 5
+#define _CFFI_OP_OPEN_ARRAY 7
+#define _CFFI_OP_STRUCT_UNION 9
+#define _CFFI_OP_ENUM 11
+#define _CFFI_OP_TYPENAME 13
+#define _CFFI_OP_FUNCTION 15
+#define _CFFI_OP_FUNCTION_END 17
+#define _CFFI_PRIM_VOID 0
+#define _CFFI_PRIM_BOOL 1
+#define _CFFI_PRIM_CHAR 2
+#define _CFFI_PRIM_SCHAR 3
+#define _CFFI_PRIM_UCHAR 4
+#define _CFFI_PRIM_SHORT 5
+#define _CFFI_PRIM_USHORT 6
+#define _CFFI_PRIM_INT 7
+#define _CFFI_PRIM_UINT 8
+#define _CFFI_PRIM_LONG 9
+#define _CFFI_PRIM_ULONG 10
+#define _CFFI_PRIM_LONGLONG 11
+#define _CFFI_PRIM_ULONGLONG 12
+#define _CFFI_PRIM_FLOAT 13
+#define _CFFI_PRIM_DOUBLE 14
+#define _CFFI_PRIM_LONGDOUBLE 15
-int parse_c_type(const char *input,
- ctype_opcode_t *output, size_t output_size,
- const char **error_loc, const char **error_msg);
+
+struct _cffi_global_s {
+ const char *name;
+ void *address;
+ int type_index;
+};
+
+struct _cffi_constant_s {
+ const char *name;
+ unsigned long long value;
+ int type_index_or_plain;
+};
+#define _CFFI_PLAIN_POSITIVE_INT (-1)
+#define _CFFI_PLAIN_NONPOSITIVE_INT (-2)
+
+struct _cffi_struct_union_s {
+ const char *name;
+ size_t size;
+ int alignment;
+ int flags; // CT_UNION? CT_IS_OPAQUE?
+ int num_fields;
+ int first_field_index; // -> _cffi_fields array
+};
+
+struct _cffi_field_s {
+ const char *name;
+ size_t field_offset;
+ size_t field_size;
+ int field_bit_size;
+ int field_type; // -> _cffi_types
+};
+
+struct _cffi_enum_s {
+ const char *name;
+ int integer_type; // -> _cffi_types
+};
+
+struct _cffi_typename_s {
+ const char *name;
+ int type_index; // -> _cffi_types
+};
+
+struct _cffi_type_context_s {
+ const struct _cffi_global_s *globals;
+ const struct _cffi_constant_s *constants;
+ const struct _cffi_struct_union_s *structs_unions;
+ const struct _cffi_field_s *fields;
+ const struct _cffi_enum_s *enums;
+ const struct _cffi_typename_s *typenames;
+ int num_globals;
+ int num_constants;
+ int num_structs_unions;
+ int num_enums;
+ int num_typenames;
+};
+
+struct _cffi_parse_info_s {
+ struct _cffi_type_context_s *ctx;
+ _cffi_opcode_t *output;
+ int output_size;
+ const char **error_location;
+ const char **error_message;
+};
+
+int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -1,53 +1,77 @@
+import re
import os
import cffi
+r_macro = re.compile(r"#define \w+[(][^\n]*")
+r_define = re.compile(r"(#define \w+) [^\n]*")
+header = open('parse_c_type.h').read()
+header = r_macro.sub(r"", header)
+header = r_define.sub(r"\1 ...", header)
+
ffi = cffi.FFI()
-ffi.cdef("""
-typedef int ctype_opcode_t;
-
-#define CTOP_END ...
-#define CTOP_CONST ...
-#define CTOP_VOLATILE ...
-
-#define CTOP_VOID ...
-#define CTOP_BOOL ...
-#define CTOP_CHAR ...
-#define CTOP_SCHAR ...
-#define CTOP_UCHAR ...
-#define CTOP_SHORT ...
-#define CTOP_USHORT ...
-#define CTOP_INT ...
-#define CTOP_UINT ...
-#define CTOP_LONG ...
-#define CTOP_ULONG ...
-#define CTOP_LONGLONG ...
-#define CTOP_ULONGLONG ...
-#define CTOP_FLOAT ...
-#define CTOP_DOUBLE ...
-#define CTOP_LONGDOUBLE ...
-
-int parse_c_type(const char *input,
- ctype_opcode_t *output, size_t output_size,
- const char **error_loc, const char **error_msg);
-""")
+ffi.cdef(header)
lib = ffi.verify(open('parse_c_type.c').read(),
include_dirs=[os.getcwd()])
+class ParseError(Exception):
+ pass
+
+def parse(input):
+ out = ffi.new("_cffi_opcode_t[]", 100)
+ info = ffi.new("struct _cffi_parse_info_s *")
+ info.output = out
+ info.output_size = len(out)
+ for j in range(len(out)):
+ out[j] = ffi.cast("void *", -424242)
+ c_input = ffi.new("char[]", input)
+ res = lib.parse_c_type(info, c_input)
+ if res < 0:
+ raise ParseError(ffi.string(info.error_message),
+ ffi.string(info.error_location) - c_input)
+ assert 0 <= res < len(out)
+ result = []
+ for j in range(len(out)):
+ if out[j] == ffi.cast("void *", -424242):
+ assert res < j
+ break
+ i = int(ffi.cast("intptr_t", out[j]))
+ if j == res:
+ result.append('->')
+ result.append(i)
+ return result
+
+def make_getter(name):
+ opcode = getattr(lib, '_CFFI_OP_' + name)
+ def getter(value):
+ return opcode | (value << 8)
+ return getter
+
+Prim = make_getter('PRIMITIVE')
+Array = make_getter('ARRAY')
+OpenArray = make_getter('OPEN_ARRAY')
+
def test_simple():
- out = ffi.new("ctype_opcode_t[]", 100)
for simple_type, expected in [
- ("int", lib.CTOP_INT),
- ("signed int", lib.CTOP_INT),
- (" long ", lib.CTOP_LONG),
- ("long int", lib.CTOP_LONG),
- ("unsigned short", lib.CTOP_USHORT),
- ("long double", lib.CTOP_LONGDOUBLE),
+ ("int", lib._CFFI_PRIM_INT),
+ ("signed int", lib._CFFI_PRIM_INT),
+ (" long ", lib._CFFI_PRIM_LONG),
+ ("long int", lib._CFFI_PRIM_LONG),
+ ("unsigned short", lib._CFFI_PRIM_USHORT),
+ ("long double", lib._CFFI_PRIM_LONGDOUBLE),
]:
- for j in range(len(out)):
- out[j] = -424242
- res = lib.parse_c_type(simple_type, out, 100, ffi.NULL, ffi.NULL)
- assert res == 0
- assert out[0] == expected
- assert out[1] == lib.CTOP_END
+ assert parse(simple_type) == ['->', Prim(expected)]
+
+def test_array():
+ assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5]
+ assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)]
+ assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Array(3),
+ 5,
+ Array(0),
+ 8]
+ assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ '->', OpenArray(2),
+ Array(0),
+ 8]
From noreply at buildbot.pypy.org Fri Apr 10 18:04:48 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 18:04:48 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: in-progress: simplify my life with
adding the occasional OP_NOOP
Message-ID: <20150410160448.63B351C01F3@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1686:5dd093128d4b
Date: 2015-04-10 18:05 +0200
http://bitbucket.org/cffi/cffi/changeset/5dd093128d4b/
Log: in-progress: simplify my life with adding the occasional OP_NOOP
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -181,12 +181,20 @@
static int parse_complete(token_t *tok);
-static int parse_sequel(token_t *tok)
+static int parse_sequel(token_t *tok, int outer)
{
+ /* Emit opcodes for the "sequel", which is the optional part of a
+ type declaration that follows the type name, i.e. everything
+ with '*', '[ ]', '( )'. Returns the entry point index pointing
+ the innermost opcode (the one that corresponds to the complete
+ type). The 'outer' argument is the index of the opcode outside
+ this "sequel".
+ */
+
header:
switch (tok->kind) {
case TOK_STAR:
- write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, tok->output_index - 1));
+ outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer));
next_token(tok);
goto header;
case TOK_CONST:
@@ -201,35 +209,36 @@
break;
}
- _cffi_opcode_t slot_result = _CFFI_OP(0, tok->output_index - 1);
- _cffi_opcode_t *p_current = &slot_result;
-
- int check_for_grouping = -1;
+ int check_for_grouping = 1;
if (tok->kind == TOK_IDENTIFIER) {
next_token(tok); /* skip a potential variable name */
- check_for_grouping = 1;
+ check_for_grouping = 0;
}
- next_right_part:
- check_for_grouping++;
+ _cffi_opcode_t result = 0;
+ _cffi_opcode_t *p_current = &result;
- switch (tok->kind) {
-
- case TOK_OPEN_PAREN:
+ while (tok->kind == TOK_OPEN_PAREN) {
next_token(tok);
- abort();
-#if 0
- if (check_for_grouping == 0 && (tok->kind == TOK_STAR ||
- tok->kind == TOK_CONST ||
- tok->kind == TOK_VOLATILE ||
- tok->kind == TOK_OPEN_BRACKET)) {
- /* just parentheses for grouping */
- ds = tok->delay_slots;
- parse_sequel(tok, *jump_slot);
- *jump_slot = -(ds - tok->all_delay_slots);
+ if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR ||
+ tok->kind == TOK_CONST ||
+ tok->kind == TOK_VOLATILE ||
+ tok->kind == TOK_OPEN_BRACKET)) {
+ /* just parentheses for grouping. Use a OP_NOOP to simplify */
+ int x;
+ assert(p_current == &result);
+ x = tok->output_index;
+ p_current = tok->output + x;
+
+ write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0));
+
+ x = parse_sequel(tok, x);
+ result = _CFFI_OP(_CFFI_GETOP(0), x);
}
else {
+ abort();
+#if 0
/* function type */
ds = alloc_ds(tok, 2);
if (ds == NULL)
@@ -264,21 +273,17 @@
assert(ds_next == ds + 2 + 2 * ds[1]);
*ds_next = *jump_slot;
*jump_slot = -(ds - tok->all_delay_slots);
+#endif
}
- if (tok->kind != TOK_CLOSE_PAREN) {
- parse_error(tok, "expected ')'");
- return;
- }
+ if (tok->kind != TOK_CLOSE_PAREN)
+ return parse_error(tok, "expected ')'");
next_token(tok);
- goto next_right_part;
-#endif
+ }
- case TOK_OPEN_BRACKET:
- {
- _cffi_opcode_t prev = *p_current;
- *p_current = _CFFI_OP(_CFFI_GETOP(prev), tok->output_index);
- p_current = &tok->output[tok->output_index];
+ while (tok->kind == TOK_OPEN_BRACKET) {
+ *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
+ p_current = tok->output + tok->output_index;
next_token(tok);
if (tok->kind != TOK_CLOSE_BRACKET) {
@@ -293,21 +298,19 @@
length = strtoul(tok->p, NULL, 10);
next_token(tok);
- write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, _CFFI_GETARG(prev)));
+ write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0));
write_ds(tok, (_cffi_opcode_t)length);
}
else
- write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, _CFFI_GETARG(prev)));
+ write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0));
if (tok->kind != TOK_CLOSE_BRACKET)
return parse_error(tok, "expected ']'");
next_token(tok);
- goto next_right_part;
}
- default:
- break;
- }
- return _CFFI_GETARG(slot_result);
+
+ *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer);
+ return _CFFI_GETARG(result);
}
#if 0
@@ -546,8 +549,7 @@
next_token(tok);
}
- write_ds(tok, t1);
- return parse_sequel(tok);
+ return parse_sequel(tok, write_ds(tok, t1));
}
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -15,6 +15,7 @@
#define _CFFI_OP_TYPENAME 13
#define _CFFI_OP_FUNCTION 15
#define _CFFI_OP_FUNCTION_END 17
+#define _CFFI_OP_NOOP 19
#define _CFFI_PRIM_VOID 0
#define _CFFI_PRIM_BOOL 1
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -48,8 +48,10 @@
return getter
Prim = make_getter('PRIMITIVE')
+Pointer = make_getter('POINTER')
Array = make_getter('ARRAY')
OpenArray = make_getter('OPEN_ARRAY')
+NoOp = make_getter('NOOP')
def test_simple():
@@ -72,6 +74,30 @@
Array(0),
8]
assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT),
- '->', OpenArray(2),
- Array(0),
- 8]
+ '->', OpenArray(2),
+ Array(0),
+ 8]
+
+def test_pointer():
+ assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)]
+ assert parse("int***") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1), '->', Pointer(2)]
+
+def test_grouping():
+ assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), '->', OpenArray(1)]
+ assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ '->', OpenArray(4), Array(2), 8]
+ assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1), OpenArray(0)]
+ assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ OpenArray(4), Array(0), 8]
+ assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ NoOp(2), Pointer(3), '->', Pointer(4)]
+ assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ NoOp(6), Pointer(3), '->', Pointer(4),
+ OpenArray(2)]
From noreply at buildbot.pypy.org Fri Apr 10 18:45:03 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 18:45:03 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Function types
Message-ID: <20150410164503.6F8881C01F3@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1687:db54ee477ad5
Date: 2015-04-10 18:45 +0200
http://bitbucket.org/cffi/cffi/changeset/db54ee477ad5/
Log: Function types
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -77,6 +77,22 @@
return *p;
}
+static int number_of_commas(token_t *tok)
+{
+ const char *p = tok->p;
+ int result = 0;
+ int nesting = 0;
+ while (1) {
+ switch (*p++) {
+ case ',': result++; break;
+ case '(': nesting++; break;
+ case ')': if ((--nesting) < 0) return result; break;
+ case 0: return result;
+ default: break;
+ }
+ }
+}
+
static void next_token(token_t *tok)
{
const char *p = tok->p + tok->size;
@@ -237,43 +253,47 @@
result = _CFFI_OP(_CFFI_GETOP(0), x);
}
else {
- abort();
-#if 0
/* function type */
- ds = alloc_ds(tok, 2);
- if (ds == NULL)
- return;
- ds[0] = TOK_OPEN_PAREN;
- ds[1] = 0;
+ int arg_total, base_index, arg_next, has_ellipsis=0;
+
if (tok->kind == TOK_VOID && get_following_char(tok) == ')') {
next_token(tok);
}
+
+ /* (over-)estimate 'arg_total'. May return 1 when it is really 0 */
+ arg_total = number_of_commas(tok) + 1;
+
+ *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
+ p_current = tok->output + tok->output_index;
+
+ base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0));
+ if (base_index < 0)
+ return -1;
+ /* reserve (arg_total + 1) slots for the arguments and the
+ final FUNCTION_END */
+ for (arg_next = 0; arg_next <= arg_total; arg_next++)
+ if (write_ds(tok, _CFFI_OP(0, 0)) < 0)
+ return -1;
+
+ arg_next = base_index + 1;
+
if (tok->kind != TOK_CLOSE_PAREN) {
while (1) {
if (tok->kind == TOK_DOTDOTDOT) {
- ds[0] = TOK_DOTDOTDOT;
+ has_ellipsis = 1;
next_token(tok);
break;
}
- intptr_t *ds_type = alloc_ds(tok, 2);
- if (ds_type == NULL)
- return;
- assert(ds_type == ds + 2 + 2 * ds[1]);
- assert(2 * sizeof(intptr_t) >= sizeof(_crx_qual_type));
- parse_complete(tok, (_crx_qual_type *)ds_type);
- ds[1]++;
+ int arg = parse_complete(tok);
+ assert(arg_next - base_index <= arg_total);
+ tok->output[arg_next++] = _CFFI_OP(_CFFI_OP_NOOP, arg);
if (tok->kind != TOK_COMMA)
break;
next_token(tok);
}
}
- intptr_t *ds_next = alloc_ds(tok, 1);
- if (ds_next == NULL)
- return;
- assert(ds_next == ds + 2 + 2 * ds[1]);
- *ds_next = *jump_slot;
- *jump_slot = -(ds - tok->all_delay_slots);
-#endif
+ tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END,
+ has_ellipsis);
}
if (tok->kind != TOK_CLOSE_PAREN)
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -52,6 +52,8 @@
Array = make_getter('ARRAY')
OpenArray = make_getter('OPEN_ARRAY')
NoOp = make_getter('NOOP')
+Func = make_getter('FUNCTION')
+FuncEnd = make_getter('FUNCTION_END')
def test_simple():
@@ -101,3 +103,25 @@
Pointer(0), Pointer(1),
NoOp(6), Pointer(3), '->', Pointer(4),
OpenArray(2)]
+
+def test_simple_function():
+ assert parse("int()") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), FuncEnd(0), 0]
+ assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(4), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_INT)]
+ assert parse("int(long, char)") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_CHAR)]
+ assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_INT),
+ Pointer(4)]
+ assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0),
+ '->', Func(1), FuncEnd(0), 0]
+ assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), FuncEnd(1), 0,
+ Prim(lib._CFFI_PRIM_INT)]
From noreply at buildbot.pypy.org Fri Apr 10 19:00:22 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 19:00:22 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: more tests, one fix
Message-ID: <20150410170022.E0B351C0294@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1688:739312aad65c
Date: 2015-04-10 19:00 +0200
http://bitbucket.org/cffi/cffi/changeset/739312aad65c/
Log: more tests, one fix
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -84,7 +84,7 @@
int nesting = 0;
while (1) {
switch (*p++) {
- case ',': result++; break;
+ case ',': result += !nesting; break;
case '(': nesting++; break;
case ')': if ((--nesting) < 0) return result; break;
case 0: return result;
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -41,6 +41,14 @@
result.append(i)
return result
+def parsex(input):
+ result = parse(input)
+ def str_if_int(x):
+ if isinstance(x, str):
+ return x
+ return '%d,%d' % (x & 255, x >> 8)
+ return ' '.join(map(str_if_int, result))
+
def make_getter(name):
opcode = getattr(lib, '_CFFI_OP_' + name)
def getter(value):
@@ -125,3 +133,20 @@
assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT),
'->', Func(0), NoOp(5), FuncEnd(1), 0,
Prim(lib._CFFI_PRIM_INT)]
+
+def test_internal_function():
+ assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ Func(0), FuncEnd(0), 0]
+ assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(6), Pointer(1),
+ '->', Func(2), FuncEnd(0), 0,
+ OpenArray(0)]
+ assert parse("int(char(*)(long, short))") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(6), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ NoOp(7), Pointer(5),
+ Func(4), NoOp(11), NoOp(12), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_SHORT)]
From noreply at buildbot.pypy.org Fri Apr 10 19:06:16 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 19:06:16 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Fix the argument types (function ->
ptr_to_function; array -> pointer)
Message-ID: <20150410170616.187991C0294@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1689:95d687f8e1bd
Date: 2015-04-10 19:06 +0200
http://bitbucket.org/cffi/cffi/changeset/95d687f8e1bd/
Log: Fix the argument types (function -> ptr_to_function; array ->
pointer)
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -285,8 +285,21 @@
break;
}
int arg = parse_complete(tok);
+ _cffi_opcode_t oarg;
assert(arg_next - base_index <= arg_total);
- tok->output[arg_next++] = _CFFI_OP(_CFFI_OP_NOOP, arg);
+ switch (_CFFI_GETOP(tok->output[arg])) {
+ case _CFFI_OP_ARRAY:
+ case _CFFI_OP_OPEN_ARRAY:
+ arg = _CFFI_GETARG(tok->output[arg]);
+ /* fall-through */
+ case _CFFI_OP_FUNCTION:
+ oarg = _CFFI_OP(_CFFI_OP_POINTER, arg);
+ break;
+ default:
+ oarg = _CFFI_OP(_CFFI_OP_NOOP, arg);
+ break;
+ }
+ tok->output[arg_next++] = oarg;
if (tok->kind != TOK_COMMA)
break;
next_token(tok);
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -150,3 +150,17 @@
Func(4), NoOp(11), NoOp(12), FuncEnd(0),
Prim(lib._CFFI_PRIM_LONG),
Prim(lib._CFFI_PRIM_SHORT)]
+
+def test_fix_arg_types():
+ assert parse("int(char(long, short))") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), Pointer(5), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ Func(4), NoOp(9), NoOp(10), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_SHORT)]
+ assert parse("int(char[])") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), Pointer(4), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ OpenArray(4)]
From noreply at buildbot.pypy.org Fri Apr 10 19:16:17 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 19:16:17 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: The non-private part of the accepted
grant proposal
Message-ID: <20150410171617.931911C0318@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1690:16dc660b4ee7
Date: 2015-04-10 19:16 +0200
http://bitbucket.org/cffi/cffi/changeset/16dc660b4ee7/
Log: The non-private part of the accepted grant proposal
diff --git a/new/cffi-1.0.rst b/new/cffi-1.0.rst
new file mode 100644
--- /dev/null
+++ b/new/cffi-1.0.rst
@@ -0,0 +1,124 @@
+
+===========================
+Grant Proposal for CFFI 1.0
+===========================
+
+*Accepted by the PSF board on April 4, 2015*
+
+This Grant Proposal is to give a boost towards "CFFI 1.0". Two main
+issues with the current CFFI need to be solved: the difficulties of
+installation, and the potentially large time taken at import.
+
+1. The difficulties of installation can be seen from outside by looking
+at various workarounds and 3rd-party documentation that have grown into
+existence. For example, the `setup.py` of projects like cryptography,
+PyNaCl and bcrypt deploys workarounds that are explicitly documented in
+https://caremad.io/2014/11/distributing-a-cffi-project/.
+
+2. The time taken at import is excessive in some cases. For example,
+importing `pygame-cffi` on a Raspberry Pi ARM board takes on the order
+of 10 to 20 seconds (and this is the "fast" case where the compiler
+doesn't need to be invoked any more).
+
+
+Technical Overview
+------------------
+
+"CFFI" is an existing Python project which complements the ctypes,
+SWIG and Cython approaches to ease writing C Extension Modules for
+Python. It has several advantages over the previous approaches, which
+are presented at the start of the documentation at
+http://cffi.readthedocs.org/en/latest/ . It has been very successful
+so far: http://pypi-ranking.info/alltime records almost 7 million
+downloads (for comparison, the #1 of all packages has almost 36
+million downloads). CFFI works on any Python >= 2.6, including 3.x,
+as well as on PyPy.
+
+One problem is that while getting started with CFFI is very easy, the
+installation process of a package that uses CFFI has got its rough
+edges. CFFI (at least in its "verify()" mode) is based on calling the
+C compiler to get information about the exact C types, structures,
+argument types to functions, and so on. The C compiler is invoked
+transparently at run-time, and the results cached. A
+correctly-installed package using CFFI should cache the results at
+installation time, but it can be difficult to ensure that no more
+run-time compiler invocation is needed; doing so requires following
+some extra guidelines or understanding some internal details. (The
+problem is particularly acute on Windows where a typical user might
+not have a proper C compiler installed.)
+
+To fix this, we have in mind adding a different CFFI mode (replacing
+"verify()"), while keeping the access to the underlying C library
+unmodified. In this mode, the code containing the cdef() and verify()
+invocations would be moved to a separate Python source file. Running
+that Python file would produce a dynamically-linked library. There
+would be no caching logic involved; you would need to run it
+explicitly during development whenever you made changes to it, to
+re-generate and re-compile the dynamically-linked library.
+
+When distributed, the same file would be run (once) during
+installation. This can be fully automated in setuptools-based
+setup.py files; alternatively, it can be done in distutils-based
+setup.py files by requiring prior manual installation of CFFI itself.
+
+A major difference with the existing verify() approach would be that
+the ``.so/.dll/.dylib`` file would not be immediately loaded into the
+process; you would load it only from the installed program at
+run-time, and get the ``ffi`` and ``lib`` objects in this way (these
+are the two objects that you use so far to access a C library with
+verify()).
+
+Additionally, this would solve another issue: every import of a large
+CFFI-using package takes a while so far. This is caused by CFFI
+needing to parse again the C source code given in the cdef() (adding a
+run-time dependency to the ``pycparser`` and ``ply`` packages). CFFI
+also computes a CRC to know if it can reuse its cache. In the
+proposed change, all the cdef() code would be pre-parsed and stored in
+the dynamically-linked library, and no CRC would be needed. This
+would massively reduce the import times.
+
+
+Grant objective
+---------------
+
+The objective is to give a boost towards "CFFI 1.0", which needs to have
+the functionalities described above in order to solve the two main
+issues with the current CFFI: the difficulties of installation, and the
+time taken at import.
+
+Included in the objective: the internal refactorings of CFFI that are
+needed to get it done cleanly. The goal is to avoid simply adding
+another layer on top of the old unchanged CFFI.
+
+This work may happen eventually in any case, but support from the PSF
+would help make it happen sooner rather than later.
+
+
+Grant size
+----------
+
+2'500 US$ for supporting the development time. This would cover 2.5
+weeks of full-time work at the part-time cost of 25 US$ per hour.
+
+The estimated work time until the CFFI 1.0 release is a bit larger
+than that (I estimate it at roughly 4 weeks), but 2.5 weeks should
+cover all the basics. An extended grant size of 4'000 US$ would be
+appreciated but not required ``:-)``
+
+
+Grant beneficiaries
+-------------------
+
+Armin Rigo, main author of CFFI, committing 2.5 weeks of full-time
+work.
+
+
+Grant follow-up
+---------------
+
+I will report on the success of the grant on the CFFI mailing list and
+on the blog I usually post to (the PyPy blog) and mention the PSF as
+providing the grant. The PSF will receive an email pointing to these
+postings once they are out. Moreover a full CFFI 1.0 release should
+follow (likely starting with beta versions); the PSF will receive
+another email pointing to it.
From noreply at buildbot.pypy.org Fri Apr 10 19:19:53 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Fri, 10 Apr 2015 19:19:53 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: move the assert next to the line it is
protecting
Message-ID: <20150410171953.432131C1016@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1691:8236b06c316b
Date: 2015-04-10 19:20 +0200
http://bitbucket.org/cffi/cffi/changeset/8236b06c316b/
Log: move the assert next to the line it is protecting
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -286,7 +286,6 @@
}
int arg = parse_complete(tok);
_cffi_opcode_t oarg;
- assert(arg_next - base_index <= arg_total);
switch (_CFFI_GETOP(tok->output[arg])) {
case _CFFI_OP_ARRAY:
case _CFFI_OP_OPEN_ARRAY:
@@ -299,6 +298,7 @@
oarg = _CFFI_OP(_CFFI_OP_NOOP, arg);
break;
}
+ assert(arg_next - base_index <= arg_total);
tok->output[arg_next++] = oarg;
if (tok->kind != TOK_COMMA)
break;
From noreply at buildbot.pypy.org Fri Apr 10 19:49:29 2015
From: noreply at buildbot.pypy.org (rlamy)
Date: Fri, 10 Apr 2015 19:49:29 +0200 (CEST)
Subject: [pypy-commit] pypy issue2018: fix SingleFrozenPBCR.convert_const()
Message-ID: <20150410174929.797D21C01F3@cobra.cs.uni-duesseldorf.de>
Author: Ronan Lamy
Branch: issue2018
Changeset: r76770:516fd6b31b0c
Date: 2015-04-10 18:46 +0100
http://bitbucket.org/pypy/pypy/changeset/516fd6b31b0c/
Log: fix SingleFrozenPBCR.convert_const()
diff --git a/rpython/rtyper/rpbc.py b/rpython/rtyper/rpbc.py
--- a/rpython/rtyper/rpbc.py
+++ b/rpython/rtyper/rpbc.py
@@ -383,6 +383,9 @@
assert frozendesc is self.frozendesc
return object() # lowleveltype is Void
+ def convert_const(self, value):
+ return None
+
def getstr(self):
return str(self.frozendesc)
getstr._annspecialcase_ = 'specialize:memo'
From noreply at buildbot.pypy.org Sat Apr 11 00:52:33 2015
From: noreply at buildbot.pypy.org (rlamy)
Date: Sat, 11 Apr 2015 00:52:33 +0200 (CEST)
Subject: [pypy-commit] pypy issue2018: unxfail passing tests
Message-ID: <20150410225233.E33F61C1EE7@cobra.cs.uni-duesseldorf.de>
Author: Ronan Lamy
Branch: issue2018
Changeset: r76771:c06ba587fc61
Date: 2015-04-10 19:46 +0100
http://bitbucket.org/pypy/pypy/changeset/c06ba587fc61/
Log: unxfail passing tests
diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py b/rpython/rtyper/lltypesystem/test/test_lltype.py
--- a/rpython/rtyper/lltypesystem/test/test_lltype.py
+++ b/rpython/rtyper/lltypesystem/test/test_lltype.py
@@ -547,10 +547,10 @@
@py.test.mark.parametrize('x', [
1, sys.maxint, 1.5, 'a', 'abc', u'abc', None, [],
- py.test.mark.xfail(lambda: None),
+ lambda: None,
{1.23: 'abc'},
(1, 'x', [2, 3.],),
- py.test.mark.xfail(Frozen()),])
+ Frozen(),])
def test_typeOf_const(x):
a = RPythonAnnotator()
bk = a.bookkeeper
From noreply at buildbot.pypy.org Sat Apr 11 09:26:31 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 09:26:31 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Structs and unions
Message-ID: <20150411072631.056B61C0ACA@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1692:3162ab4fa3b8
Date: 2015-04-11 09:27 +0200
http://bitbucket.org/cffi/cffi/changeset/3162ab4fa3b8/
Log: Structs and unions
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -1,4 +1,5 @@
#include
+#include
#include
#include "parse_c_type.h"
@@ -175,10 +176,8 @@
{
if (tok->kind != TOK_ERROR) {
tok->kind = TOK_ERROR;
- if (tok->info->error_location)
- *tok->info->error_location = tok->p;
- if (tok->info->error_message)
- *tok->info->error_message = msg;
+ tok->info->error_location = tok->p;
+ tok->info->error_message = msg;
}
return -1;
}
@@ -410,6 +409,25 @@
}
#endif
+static int search_struct_union(struct _cffi_type_context_s *ctx,
+ const char *search, size_t search_len)
+{
+ int left = 0, right = ctx->num_structs_unions;
+
+ while (left < right) {
+ int middle = (left + right) / 2;
+ const char *src = ctx->structs_unions[middle].name;
+ int diff = strncmp(src, search, search_len);
+ if (diff == 0 && src[search_len] == '\0')
+ return middle;
+ else if (diff >= 0)
+ right = middle;
+ else
+ left = middle + 1;
+ }
+ return -1;
+}
+
static int parse_complete(token_t *tok)
{
qualifiers:
@@ -554,27 +572,20 @@
case TOK_STRUCT:
case TOK_UNION:
{
- abort();
-#if 0
- char identifier[1024];
int kind = tok->kind;
next_token(tok);
- if (tok->kind != TOK_IDENTIFIER) {
- parse_error(tok, "struct or union name expected");
- return;
- }
- if (tok->size >= 1024) {
- parse_error(tok, "struct or union name too long");
- return;
- }
- memcpy(identifier, tok->p, tok->size);
- identifier[tok->size] = 0;
- if (kind == TOK_STRUCT)
- t1 = tok->cb->get_struct_type(tok->cb, identifier);
- else
- t1 = tok->cb->get_union_type(tok->cb, identifier);
+ if (tok->kind != TOK_IDENTIFIER)
+ return parse_error(tok, "struct or union name expected");
+
+ int n = search_struct_union(tok->info->ctx, tok->p, tok->size);
+ if (n < 0)
+ return parse_error(tok, "undefined struct/union name");
+ if (((tok->info->ctx->structs_unions[n].flags & CT_UNION) != 0)
+ ^ (kind == TOK_UNION))
+ return parse_error(tok, "wrong kind of tag: struct vs union");
+
+ t1 = _CFFI_OP(_CFFI_OP_STRUCT_UNION, n);
break;
-#endif
}
default:
return parse_error(tok, "identifier expected");
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -57,6 +57,8 @@
int num_fields;
int first_field_index; // -> _cffi_fields array
};
+#define CT_UNION 128
+#define CT_IS_OPAQUE 4096
struct _cffi_field_s {
const char *name;
@@ -94,8 +96,8 @@
struct _cffi_type_context_s *ctx;
_cffi_opcode_t *output;
int output_size;
- const char **error_location;
- const char **error_message;
+ const char *error_location;
+ const char *error_message;
};
int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -1,5 +1,6 @@
import re
import os
+import py
import cffi
r_macro = re.compile(r"#define \w+[(][^\n]*")
@@ -17,9 +18,23 @@
class ParseError(Exception):
pass
+struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"]
+assert struct_names == sorted(struct_names)
+
+ctx = ffi.new("struct _cffi_type_context_s *")
+c_names = [ffi.new("char[]", _n) for _n in struct_names]
+ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names))
+for _i in range(len(struct_names)):
+ ctx_structs[_i].name = c_names[_i]
+ctx_structs[3].flags = lib.CT_UNION
+ctx.structs_unions = ctx_structs
+ctx.num_structs_unions = len(struct_names)
+
+
def parse(input):
out = ffi.new("_cffi_opcode_t[]", 100)
info = ffi.new("struct _cffi_parse_info_s *")
+ info.ctx = ctx
info.output = out
info.output_size = len(out)
for j in range(len(out)):
@@ -28,7 +43,7 @@
res = lib.parse_c_type(info, c_input)
if res < 0:
raise ParseError(ffi.string(info.error_message),
- ffi.string(info.error_location) - c_input)
+ info.error_location - c_input)
assert 0 <= res < len(out)
result = []
for j in range(len(out)):
@@ -49,6 +64,11 @@
return '%d,%d' % (x & 255, x >> 8)
return ' '.join(map(str_if_int, result))
+def parse_error(input, expected_msg, expected_location):
+ e = py.test.raises(ParseError, parse, input)
+ assert e.value.args[0] == expected_msg
+ assert e.value.args[1] == expected_location
+
def make_getter(name):
opcode = getattr(lib, '_CFFI_OP_' + name)
def getter(value):
@@ -62,6 +82,7 @@
NoOp = make_getter('NOOP')
Func = make_getter('FUNCTION')
FuncEnd = make_getter('FUNCTION_END')
+Struct = make_getter('STRUCT_UNION')
def test_simple():
@@ -164,3 +185,14 @@
'->', Func(0), Pointer(4), FuncEnd(0),
Prim(lib._CFFI_PRIM_CHAR),
OpenArray(4)]
+
+def test_error():
+ parse_error("short short int", "'short' after another 'short' or 'long'", 6)
+
+def test_struct():
+ for i in range(len(struct_names)):
+ if i == 3:
+ tag = "union"
+ else:
+ tag = "struct"
+ assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)]
From noreply at buildbot.pypy.org Sat Apr 11 09:31:28 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 09:31:28 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Copy some tests from creflect
Message-ID: <20150411073128.069B01C0ACA@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1693:3ac82266ea94
Date: 2015-04-11 09:32 +0200
http://bitbucket.org/cffi/cffi/changeset/3ac82266ea94/
Log: Copy some tests from creflect
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -1,6 +1,7 @@
#include
#include
#include
+#include
#include "parse_c_type.h"
@@ -324,10 +325,13 @@
if (tok->kind != TOK_INTEGER)
return parse_error(tok, "expected a positive integer constant");
+ errno = 0;
if (sizeof(length) > sizeof(unsigned long))
length = strtoull(tok->p, NULL, 10);
else
length = strtoul(tok->p, NULL, 10);
+ if (errno == ERANGE)
+ return parse_error(tok, "number too large");
next_token(tok);
write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0));
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -188,6 +188,34 @@
def test_error():
parse_error("short short int", "'short' after another 'short' or 'long'", 6)
+ parse_error("long long long", "'long long long' is too long", 10)
+ parse_error("short long", "'long' after 'short'", 6)
+ parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7)
+ parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9)
+ parse_error("long char", "invalid combination of types", 5)
+ parse_error("short char", "invalid combination of types", 6)
+ parse_error("signed void", "invalid combination of types", 7)
+ parse_error("unsigned struct", "invalid combination of types", 9)
+ #
+ parse_error("", "identifier expected", 0)
+ parse_error("]", "identifier expected", 0)
+ parse_error("*", "identifier expected", 0)
+ parse_error("int ]**", "unexpected symbol", 4)
+ parse_error("char char", "unexpected symbol", 5)
+ parse_error("int(int]", "expected ')'", 7)
+ parse_error("int(*]", "expected ')'", 5)
+ parse_error("int(]", "identifier expected", 4)
+ parse_error("int[?]", "expected a positive integer constant", 4)
+ parse_error("int[24)", "expected ']'", 6)
+ parse_error("struct", "struct or union name expected", 6)
+ parse_error("struct 24", "struct or union name expected", 7)
+ parse_error("int[5](*)", "unexpected symbol", 6)
+ parse_error("int a(*)", "identifier expected", 6)
+ parse_error("int[123456789012345678901234567890]", "number too large", 4)
+
+def test_complexity_limit():
+ parse_error("int" + "[]" * 2500, "internal type complexity limit reached",
+ 202)
def test_struct():
for i in range(len(struct_names)):
From noreply at buildbot.pypy.org Sat Apr 11 09:39:20 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 09:39:20 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Typenames
Message-ID: <20150411073920.432121C0107@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1694:54d0281c9072
Date: 2015-04-11 09:39 +0200
http://bitbucket.org/cffi/cffi/changeset/54d0281c9072/
Log: Typenames
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -432,6 +432,25 @@
return -1;
}
+static int search_typename(struct _cffi_type_context_s *ctx,
+ const char *search, size_t search_len)
+{
+ int left = 0, right = ctx->num_typenames;
+
+ while (left < right) {
+ int middle = (left + right) / 2;
+ const char *src = ctx->typenames[middle].name;
+ int diff = strncmp(src, search, search_len);
+ if (diff == 0 && src[search_len] == '\0')
+ return middle;
+ else if (diff >= 0)
+ right = middle;
+ else
+ left = middle + 1;
+ }
+ return -1;
+}
+
static int parse_complete(token_t *tok)
{
qualifiers:
@@ -557,21 +576,12 @@
break;
case TOK_IDENTIFIER:
{
- abort();
-#if 0
- _crx_qual_type qt2;
- char identifier[1024];
- if (tok->size >= 1024) {
- parse_error(tok, "identifier name too long");
- return;
- }
- memcpy(identifier, tok->p, tok->size);
- identifier[tok->size] = 0;
- qt2 = tok->cb->get_user_type(tok->cb, identifier);
- t1 = qt2.type;
- result->qualifiers |= qt2.qualifiers;
+ int n = search_typename(tok->info->ctx, tok->p, tok->size);
+ if (n < 0)
+ return parse_error(tok, "undefined type name");
+
+ t1 = _CFFI_OP(_CFFI_OP_TYPENAME, n);
break;
-#endif
}
case TOK_STRUCT:
case TOK_UNION:
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -44,7 +44,7 @@
struct _cffi_constant_s {
const char *name;
unsigned long long value;
- int type_index_or_plain;
+ int type_index; // -> _cffi_types or _CFFI_PLAIN_*_INT
};
#define _CFFI_PLAIN_POSITIVE_INT (-1)
#define _CFFI_PLAIN_NONPOSITIVE_INT (-2)
diff --git a/new/test_parse_c_type.py b/new/test_parse_c_type.py
--- a/new/test_parse_c_type.py
+++ b/new/test_parse_c_type.py
@@ -21,15 +21,25 @@
struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"]
assert struct_names == sorted(struct_names)
+identifier_names = ["id", "id0", "id05", "id05b", "tail"]
+assert identifier_names == sorted(identifier_names)
+
ctx = ffi.new("struct _cffi_type_context_s *")
-c_names = [ffi.new("char[]", _n) for _n in struct_names]
+c_struct_names = [ffi.new("char[]", _n) for _n in struct_names]
ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names))
for _i in range(len(struct_names)):
- ctx_structs[_i].name = c_names[_i]
+ ctx_structs[_i].name = c_struct_names[_i]
ctx_structs[3].flags = lib.CT_UNION
ctx.structs_unions = ctx_structs
ctx.num_structs_unions = len(struct_names)
+c_identifier_names = [ffi.new("char[]", _n) for _n in identifier_names]
+ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names))
+for _i in range(len(identifier_names)):
+ ctx_identifiers[_i].name = c_identifier_names[_i]
+ctx.typenames = ctx_identifiers
+ctx.num_typenames = len(identifier_names)
+
def parse(input):
out = ffi.new("_cffi_opcode_t[]", 100)
@@ -83,6 +93,7 @@
Func = make_getter('FUNCTION')
FuncEnd = make_getter('FUNCTION_END')
Struct = make_getter('STRUCT_UNION')
+Typename = make_getter('TYPENAME')
def test_simple():
@@ -224,3 +235,11 @@
else:
tag = "struct"
assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)]
+ assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i),
+ '->', Pointer(0)]
+
+def test_identifier():
+ for i in range(len(identifier_names)):
+ assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)]
+ assert parse("%s*" % (identifier_names[i])) == [Typename(i),
+ '->', Pointer(0)]
From noreply at buildbot.pypy.org Sat Apr 11 11:30:40 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 11:30:40 +0200 (CEST)
Subject: [pypy-commit] extradoc extradoc: Add the abstract for STM for
EuroPython 2015
Message-ID: <20150411093040.DF1C81C0845@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: extradoc
Changeset: r5525:9fd26448fe0f
Date: 2015-04-11 11:31 +0200
http://bitbucket.org/pypy/extradoc/changeset/9fd26448fe0f/
Log: Add the abstract for STM for EuroPython 2015
diff --git a/talk/ep2015/stm-abstract.rst b/talk/ep2015/stm-abstract.rst
new file mode 100644
--- /dev/null
+++ b/talk/ep2015/stm-abstract.rst
@@ -0,0 +1,21 @@
+=========================
+The GIL is dead: PyPy-STM
+=========================
+
+Abstract
+--------
+
+Take a big, non-multithreaded program, and run in on multiple cores!
+
+PyPy, the Python implementation written in Python, experimentally
+supports Software Transactional Memory (STM). It runs without the
+Global Interpreter Lock (GIL).
+
+The strength of STM is not only to remove the GIL, but to also enable
+a novel use of multithreading, inheritently safe, and more useful in
+the general case than other approaches like OpenMP. The main news
+from last year's presentation is that there is now a way to get
+reports about the "STM conflicts", which is essential to go past toy
+examples. With it, you can incrementally remove conflicts from large
+code bases until you see a benefit from PyPy-STM. The goal of the
+talk is to give several concrete examples of doing that.
From noreply at buildbot.pypy.org Sat Apr 11 11:49:42 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 11:49:42 +0200 (CEST)
Subject: [pypy-commit] pypy default: Fix: there are roughly 80'000 symbols
'__gcmap_*' in an asmgcc pypy, and
Message-ID: <20150411094942.C38841C0845@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76772:2769d419e952
Date: 2015-04-11 10:39 +0200
http://bitbucket.org/pypy/pypy/changeset/2769d419e952/
Log: Fix: there are roughly 80'000 symbols '__gcmap_*' in an asmgcc pypy,
and they were not declared '.hidden', which means that they were
left in the binary. Moreover, they were relocated at start-up,
which took an appreciable fraction of second.
diff --git a/rpython/translator/c/gcc/trackgcroot.py b/rpython/translator/c/gcc/trackgcroot.py
--- a/rpython/translator/c/gcc/trackgcroot.py
+++ b/rpython/translator/c/gcc/trackgcroot.py
@@ -423,6 +423,7 @@
# the original value for gcmaptable.s. That's a hack.
self.lines.insert(call.lineno+1, '%s=.+%d\n' % (label,
self.OFFSET_LABELS))
+ self.lines.insert(call.lineno+1, '\t.hidden\t%s\n' % (label,))
self.lines.insert(call.lineno+1, '\t.globl\t%s\n' % (label,))
call.global_label = label
From noreply at buildbot.pypy.org Sat Apr 11 11:49:44 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 11:49:44 +0200 (CEST)
Subject: [pypy-commit] pypy default: fix test
Message-ID: <20150411094944.015641C0845@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch:
Changeset: r76773:60de3e60e9d5
Date: 2015-04-11 10:42 +0200
http://bitbucket.org/pypy/pypy/changeset/60de3e60e9d5/
Log: fix test
diff --git a/rpython/translator/c/gcc/test/test_trackgcroot.py b/rpython/translator/c/gcc/test/test_trackgcroot.py
--- a/rpython/translator/c/gcc/test/test_trackgcroot.py
+++ b/rpython/translator/c/gcc/test/test_trackgcroot.py
@@ -159,7 +159,8 @@
expectedlines.insert(i-2, 'PUBLIC\t%s\n' % (label,))
expectedlines.insert(i-1, '%s::\n' % (label,))
else:
- expectedlines.insert(i-2, '\t.globl\t%s\n' % (label,))
+ expectedlines.insert(i-3, '\t.globl\t%s\n' % (label,))
+ expectedlines.insert(i-2, '\t.hidden\t%s\n' % (label,))
expectedlines.insert(i-1, '%s=.+%d\n' % (label,
tracker.OFFSET_LABELS))
if format == 'msvc' and r_gcroot_constant.match(line):
From noreply at buildbot.pypy.org Sat Apr 11 18:25:16 2015
From: noreply at buildbot.pypy.org (arigo)
Date: Sat, 11 Apr 2015 18:25:16 +0200 (CEST)
Subject: [pypy-commit] cffi cffi-1.0: Realizing C types from parsed
_cffi_opcode arrays: write the basics,
Message-ID: <20150411162516.F2D041C08C1@cobra.cs.uni-duesseldorf.de>
Author: Armin Rigo
Branch: cffi-1.0
Changeset: r1695:d5b493dd8a62
Date: 2015-04-11 18:25 +0200
http://bitbucket.org/cffi/cffi/changeset/d5b493dd8a62/
Log: Realizing C types from parsed _cffi_opcode arrays: write the basics,
needs to be integrated with _cffi_backend.c
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -349,70 +349,6 @@
return _CFFI_GETARG(result);
}
-#if 0
-static void fetch_delay_slots(token_t *tok, _crx_qual_type *result,
- intptr_t *delay_slot)
-{
- if (tok->kind == TOK_ERROR)
- return;
- tok->delay_slots = delay_slot;
- while (1) {
- intptr_t tok_kind = *delay_slot++;
- if (tok_kind <= 0) {
- delay_slot = tok->all_delay_slots + (-tok_kind);
- continue;
- }
- switch (tok_kind) {
- case TOK_END:
- return; /* done */
- case TOK_STAR:
- result->type = tok->cb->get_pointer_type(tok->cb, result->type,
- result->qualifiers);
- result->qualifiers = 0;
- break;
- case TOK_CONST:
- result->qualifiers |= _CRX_CONST;
- break;
- case TOK_VOLATILE:
- result->qualifiers |= _CRX_VOLATILE;
- break;
- case TOK_OPEN_BRACKET: /* array */
- {
- uintptr_t length = (uintptr_t)*delay_slot++;
- if (length != (uintptr_t)-1)
- result->type = tok->cb->get_array_type(
- tok->cb, result->type, length);
- else
- result->type = tok->cb->get_incomplete_array_type(
- tok->cb, result->type);
- /* result->qualifiers remains unmodified */
- break;
- }
- case TOK_OPEN_PAREN: /* function */
- case TOK_DOTDOTDOT: /* function ending with a '...' */
- {
- intptr_t nbargs = *delay_slot++;
- _crx_type_t *t1;
- _crx_qual_type *argtypes = (_crx_qual_type *)delay_slot;
- delay_slot += 2 * nbargs;
- if (tok_kind == TOK_DOTDOTDOT)
- t1 = tok->cb->get_ellipsis_function_type(tok->cb,
- result->type,
- argtypes, nbargs);
- else
- t1 = tok->cb->get_function_type(tok->cb, result->type,
- argtypes, nbargs, NULL);
- result->type = t1;
- result->qualifiers = 0; /* drop qualifiers on the return type */
- break;
- }
- default:
- assert(!"missing delay slot case");
- }
- }
-}
-#endif
-
static int search_struct_union(struct _cffi_type_context_s *ctx,
const char *search, size_t search_len)
{
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -1,3 +1,4 @@
+#include
typedef void *_cffi_opcode_t;
@@ -33,6 +34,7 @@
#define _CFFI_PRIM_FLOAT 13
#define _CFFI_PRIM_DOUBLE 14
#define _CFFI_PRIM_LONGDOUBLE 15
+#define _CFFI__NUM_PRIM 16
struct _cffi_global_s {
@@ -79,6 +81,7 @@
};
struct _cffi_type_context_s {
+ _cffi_opcode_t *types;
const struct _cffi_global_s *globals;
const struct _cffi_constant_s *constants;
const struct _cffi_struct_union_s *structs_unions;
diff --git a/new/realize_c_type.c b/new/realize_c_type.c
new file mode 100644
--- /dev/null
+++ b/new/realize_c_type.c
@@ -0,0 +1,97 @@
+#include