[pypy-commit] pypy stm-thread-2: merge heads
arigo
noreply at buildbot.pypy.org
Tue Dec 11 15:54:20 CET 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread-2
Changeset: r59399:40d946c71c64
Date: 2012-12-11 15:52 +0100
http://bitbucket.org/pypy/pypy/changeset/40d946c71c64/
Log: merge heads
diff --git a/pypy/module/thread/ll_thread.py b/pypy/module/thread/ll_thread.py
--- a/pypy/module/thread/ll_thread.py
+++ b/pypy/module/thread/ll_thread.py
@@ -46,7 +46,8 @@
# importantly, reacquire it
# around the callback
c_thread_get_ident = llexternal('RPyThreadGetIdent', [], rffi.LONG,
- _nowrapper=True) # always call directly
+ _nowrapper=True,
+ transactionsafe=True) # always call directly
TLOCKP = rffi.COpaquePtr('struct RPyOpaque_ThreadLock',
compilation_info=eci)
diff --git a/pypy/rlib/rdtoa.py b/pypy/rlib/rdtoa.py
--- a/pypy/rlib/rdtoa.py
+++ b/pypy/rlib/rdtoa.py
@@ -52,9 +52,9 @@
compilation_info=eci, sandboxsafe=True)
def strtod(input):
- end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+ ll_input = rffi.str2charp(input)
try:
- ll_input = rffi.str2charp(input)
+ end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
try:
result = dg_strtod(ll_input, end_ptr)
@@ -66,9 +66,10 @@
return result
finally:
- rffi.free_charp(ll_input)
+ lltype.free(end_ptr, flavor='raw')
finally:
- lltype.free(end_ptr, flavor='raw')
+ rffi.free_charp(ll_input)
+
lower_special_strings = ['inf', '+inf', '-inf', 'nan']
upper_special_strings = ['INF', '+INF', '-INF', 'NAN']
diff --git a/pypy/rpython/lltypesystem/rffi.py b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -63,7 +63,8 @@
sandboxsafe=False, threadsafe='auto',
_nowrapper=False, calling_conv='c',
oo_primitive=None, elidable_function=False,
- macro=None, random_effects_on_gcobjs='auto'):
+ macro=None, random_effects_on_gcobjs='auto',
+ transactionsafe=False):
"""Build an external function that will invoke the C function 'name'
with the given 'args' types and 'result' type.
@@ -125,6 +126,7 @@
has_callback) # because the callback can do it
funcptr = lltype.functionptr(ext_type, name, external='C',
+ transactionsafe=transactionsafe,
compilation_info=compilation_info,
_callable=_callable,
_safe_not_sandboxed=sandboxsafe,
@@ -453,7 +455,7 @@
TYPES += ['__int128_t']
except CompilationError:
pass
-
+
_TYPES_ARE_UNSIGNED = set(['size_t', 'uintptr_t']) # plus "unsigned *"
if os.name != 'nt':
TYPES.append('mode_t')
@@ -712,6 +714,7 @@
i -= 1
return array
str2charp._annenforceargs_ = [strtype]
+ str2charp._always_inline_ = True
def free_charp(cp):
lltype.free(cp, flavor='raw')
diff --git a/pypy/translator/stm/inevitable.py b/pypy/translator/stm/inevitable.py
--- a/pypy/translator/stm/inevitable.py
+++ b/pypy/translator/stm/inevitable.py
@@ -1,13 +1,14 @@
-from pypy.rpython.lltypesystem import lltype, lloperation
+from pypy.rpython.lltypesystem import lltype, lloperation, rclass
from pypy.translator.stm.writebarrier import is_immutable
from pypy.objspace.flow.model import SpaceOperation, Constant
from pypy.translator.unsimplify import varoftype
+from pypy.translator.simplify import get_funcobj
ALWAYS_ALLOW_OPERATIONS = set([
- 'direct_call', 'force_cast', 'keepalive', 'cast_ptr_to_adr',
+ 'force_cast', 'keepalive', 'cast_ptr_to_adr',
'debug_print', 'debug_assert', 'cast_opaque_ptr', 'hint',
- 'indirect_call', 'stack_current', 'gc_stack_bottom',
+ 'stack_current', 'gc_stack_bottom',
'cast_current_ptr_to_int', # this variant of 'cast_ptr_to_int' is ok
'jit_force_virtual', 'jit_force_virtualizable',
'jit_force_quasi_immutable', 'jit_marker', 'jit_is_virtual',
@@ -25,10 +26,9 @@
SETTERS = set(['setfield', 'setarrayitem', 'setinteriorfield'])
MALLOCS = set(['malloc', 'malloc_varsize',
'malloc_nonmovable', 'malloc_nonmovable_varsize'])
-
# ____________________________________________________________
-def should_turn_inevitable_getter_setter(op):
+def should_turn_inevitable_getter_setter(op, fresh_mallocs):
# Getters and setters are allowed if their first argument is a GC pointer.
# If it is a RAW pointer, and it is a read from a non-immutable place,
# and it doesn't use the hint 'stm_dont_track_raw_accesses', then they
@@ -40,9 +40,9 @@
return False
if S._hints.get('stm_dont_track_raw_accesses', False):
return False
- return True
+ return not fresh_mallocs.is_fresh_malloc(op.args[0])
-def should_turn_inevitable(op):
+def should_turn_inevitable(op, block, fresh_mallocs):
# Always-allowed operations never cause a 'turn inevitable'
if op.opname in ALWAYS_ALLOW_OPERATIONS:
return False
@@ -51,16 +51,49 @@
if op.opname in GETTERS:
if op.result.concretetype is lltype.Void:
return False
- return should_turn_inevitable_getter_setter(op)
+ return should_turn_inevitable_getter_setter(op, fresh_mallocs)
if op.opname in SETTERS:
if op.args[-1].concretetype is lltype.Void:
return False
- return should_turn_inevitable_getter_setter(op)
+ return should_turn_inevitable_getter_setter(op, fresh_mallocs)
#
- # Mallocs
+ # Mallocs & Frees
if op.opname in MALLOCS:
- flags = op.args[1].value
- return flags['flavor'] != 'gc'
+ # flags = op.args[1].value
+ # return flags['flavor'] != 'gc'
+ return False # XXX: Produces memory leaks on aborts
+ if op.opname == 'free':
+ # We can only run a CFG in non-inevitable mode from start
+ # to end in one transaction (every free gets called once
+ # for every fresh malloc). No need to turn inevitable.
+ # If the transaction is splitted, the remaining parts of the
+ # CFG will always run in inevitable mode anyways.
+ return not fresh_mallocs.is_fresh_malloc(op.args[0])
+
+ #
+ # Function calls
+ if op.opname == 'direct_call':
+ funcptr = get_funcobj(op.args[0].value)
+ if not hasattr(funcptr, "external"):
+ return False
+ return not getattr(funcptr, "transactionsafe", False)
+
+ if op.opname == 'indirect_call':
+ tographs = op.args[-1].value
+ if tographs is not None:
+ # Set of RPython functions
+ return False
+ # special-case to detect 'instantiate'
+ v_func = op.args[0]
+ for op1 in block.operations:
+ if (v_func is op1.result and
+ op1.opname == 'getfield' and
+ op1.args[0].concretetype == rclass.CLASSTYPE and
+ op1.args[1].value == 'instantiate'):
+ return False
+ # unknown function
+ return True
+
#
# Entirely unsupported operations cause a 'turn inevitable'
return True
@@ -71,10 +104,12 @@
return SpaceOperation('stm_become_inevitable', [c_info],
varoftype(lltype.Void))
-def insert_turn_inevitable(translator, graph):
+def insert_turn_inevitable(graph):
+ from pypy.translator.backendopt.writeanalyze import FreshMallocs
+ fresh_mallocs = FreshMallocs(graph)
for block in graph.iterblocks():
for i in range(len(block.operations)-1, -1, -1):
op = block.operations[i]
- if should_turn_inevitable(op):
+ if should_turn_inevitable(op, block, fresh_mallocs):
inev_op = turn_inevitable_op(op.opname)
block.operations.insert(i, inev_op)
diff --git a/pypy/translator/stm/stmgcintf.py b/pypy/translator/stm/stmgcintf.py
--- a/pypy/translator/stm/stmgcintf.py
+++ b/pypy/translator/stm/stmgcintf.py
@@ -17,7 +17,8 @@
def _llexternal(name, args, result, **kwds):
return rffi.llexternal(name, args, result, compilation_info=eci,
- _nowrapper=True, **kwds)
+ _nowrapper=True, transactionsafe=True,
+ **kwds)
def smexternal(name, args, result):
return staticmethod(_llexternal(name, args, result))
diff --git a/pypy/translator/stm/test/test_inevitable.py b/pypy/translator/stm/test/test_inevitable.py
--- a/pypy/translator/stm/test/test_inevitable.py
+++ b/pypy/translator/stm/test/test_inevitable.py
@@ -1,4 +1,4 @@
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.llinterp import LLFrame
from pypy.rpython.test import test_llinterp
from pypy.rpython.test.test_llinterp import get_interpreter, clear_tcache
@@ -20,7 +20,7 @@
interp, self.graph = get_interpreter(fn, args, view=False)
interp.frame_class = LLSTMInevFrame
self.translator = interp.typer.annotator.translator
- insert_turn_inevitable(self.translator, self.graph)
+ insert_turn_inevitable(self.graph)
if option.view:
self.translator.view()
#
@@ -113,4 +113,103 @@
lltype.free(p, flavor='raw')
res = self.interpret_inevitable(f1, [])
- assert res == 'malloc'
+ assert res is None
+ assert 0, """we do not turn inevitable before
+ raw-mallocs which causes leaks on aborts"""
+
+ def test_unknown_raw_free(self):
+ X = lltype.Struct('X', ('foo', lltype.Signed))
+ def f2(p):
+ lltype.free(p, flavor='raw')
+
+ res = self.interpret_inevitable(f2, [lltype.malloc(X, flavor='raw')])
+ assert res == 'free'
+
+
+ def test_ext_direct_call_safe(self):
+ TYPE = lltype.FuncType([], lltype.Void)
+ extfunc = lltype.functionptr(TYPE, 'extfunc',
+ external='C',
+ transactionsafe=True,
+ _callable=lambda:0)
+ def f1():
+ extfunc()
+
+ res = self.interpret_inevitable(f1, [])
+ assert res is None
+
+
+ def test_ext_direct_call_unsafe(self):
+ TYPE = lltype.FuncType([], lltype.Void)
+ extfunc = lltype.functionptr(TYPE, 'extfunc',
+ external='C',
+ _callable=lambda:0)
+ def f1():
+ extfunc()
+
+ res = self.interpret_inevitable(f1, [])
+ assert res == 'direct_call'
+
+ def test_rpy_direct_call(self):
+ def f2():
+ pass
+ def f1():
+ f2()
+
+ res = self.interpret_inevitable(f1, [])
+ assert res is None
+
+ def test_rpy_indirect_call(self):
+ def f2():
+ pass
+ def f3():
+ pass
+ def f1(i):
+ if i:
+ f = f2
+ else:
+ f = f3
+ f()
+
+ res = self.interpret_inevitable(f1, [True])
+ assert res is None
+
+ def test_ext_indirect_call(self):
+ TYPE = lltype.FuncType([], lltype.Void)
+ extfunc = lltype.functionptr(TYPE, 'extfunc',
+ external='C',
+ _callable=lambda:0)
+ rpyfunc = lltype.functionptr(TYPE, 'rpyfunc',
+ _callable=lambda:0)
+
+
+ def f1(i):
+ if i:
+ f = extfunc
+ else:
+ f = rpyfunc
+ f()
+
+ res = self.interpret_inevitable(f1, [True])
+ assert res == 'indirect_call'
+
+ def test_instantiate_indirect_call(self):
+ # inits are necessary to generate indirect_call
+ class A:
+ def __init__(self): pass
+ class B(A):
+ def __init__(self): pass
+ class C(A):
+ def __init__(self): pass
+
+ def f1(i):
+ if i:
+ c = B
+ else:
+ c = C
+ c()
+
+ res = self.interpret_inevitable(f1, [True])
+ assert res is None
+
+
diff --git a/pypy/translator/stm/transform2.py b/pypy/translator/stm/transform2.py
--- a/pypy/translator/stm/transform2.py
+++ b/pypy/translator/stm/transform2.py
@@ -25,7 +25,7 @@
from pypy.translator.stm.inevitable import insert_turn_inevitable
#
for graph in self.translator.graphs:
- insert_turn_inevitable(self.translator, graph)
+ insert_turn_inevitable(graph)
def start_log(self):
from pypy.translator.c.support import log
More information about the pypy-commit
mailing list