[pypy-commit] pypy stmgc-c4: try to reduce becomeinevitable because of indirect_calls in blackhole.py (call_stub)
Raemi
noreply at buildbot.pypy.org
Wed Sep 11 20:05:59 CEST 2013
Author: Remi Meier <remi.meier at gmail.com>
Branch: stmgc-c4
Changeset: r66906:32a78cfb3fbc
Date: 2013-09-11 20:04 +0200
http://bitbucket.org/pypy/pypy/changeset/32a78cfb3fbc/
Log: try to reduce becomeinevitable because of indirect_calls in
blackhole.py (call_stub)
diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -404,12 +404,27 @@
category = 'i'
else:
assert 0
- source = py.code.Source("""
- def call_stub(func, args_i, args_r, args_f):
- fnptr = rffi.cast(lltype.Ptr(FUNC), func)
- res = support.maybe_on_top_of_llinterp(rtyper, fnptr)(%(args)s)
- return %(result)s
- """ % locals())
+
+ llop1 = llop
+ if not stm or (
+ self.extrainfo and self.extrainfo.call_needs_inevitable()):
+ source = py.code.Source("""
+ def call_stub(func, args_i, args_r, args_f):
+ fnptr = rffi.cast(lltype.Ptr(FUNC), func)
+ res = support.maybe_on_top_of_llinterp(rtyper, fnptr)(%(args)s)
+ return %(result)s
+ """ % locals())
+ else:
+ # the above 'source' works on STM too, but always forces
+ # the transaction to become inevitable. Using jit_assembler_call
+ # in cases where it is not needed avoids that.
+ source = py.code.Source("""
+ def call_stub(func, args_i, args_r, args_f):
+ fnptr = rffi.cast(lltype.Ptr(FUNC), func)
+ fun = support.maybe_on_top_of_llinterp(rtyper, fnptr)
+ res = llop1.jit_assembler_call(RESULT, fun, %(args)s)
+ return %(result)s
+ """ % locals())
ARGS = [TYPE(arg) for arg in self.arg_classes]
FUNC = lltype.FuncType(ARGS, RESULT)
d = globals().copy()
diff --git a/rpython/jit/backend/llsupport/stmrewrite.py b/rpython/jit/backend/llsupport/stmrewrite.py
--- a/rpython/jit/backend/llsupport/stmrewrite.py
+++ b/rpython/jit/backend/llsupport/stmrewrite.py
@@ -1,4 +1,5 @@
from rpython.jit.backend.llsupport.rewrite import GcRewriterAssembler
+from rpython.jit.backend.llsupport.descr import CallDescr
from rpython.jit.metainterp.resoperation import ResOperation, rop
from rpython.jit.metainterp.history import BoxPtr, ConstPtr, ConstInt
from rpython.rlib.objectmodel import specialize
@@ -108,7 +109,13 @@
elif op.getopnum() == rop.CALL_ASSEMBLER:
self.handle_call_assembler(op)
else:
- self.newops.append(op)
+ descr = op.getdescr()
+ assert not descr or isinstance(descr, CallDescr)
+ if descr and descr.get_extra_info() and \
+ descr.get_extra_info().call_needs_inevitable():
+ self.fallback_inevitable(op)
+ else:
+ self.newops.append(op)
self.known_category.clear()
continue
# ---------- copystrcontent ----------
diff --git a/rpython/jit/backend/llsupport/test/test_descr.py b/rpython/jit/backend/llsupport/test/test_descr.py
--- a/rpython/jit/backend/llsupport/test/test_descr.py
+++ b/rpython/jit/backend/llsupport/test/test_descr.py
@@ -353,6 +353,32 @@
descr5f = get_call_descr(c0, [lltype.Char], lltype.SingleFloat)
assert repr_of_descr(descr5f) == '<CallS 4 i>'
+
+def test_call_stubs_inevitable():
+ for inev in (True, False):
+ class fakeextrainfo:
+ def call_needs_inevitable(self):
+ return inev
+ class fakertyper:
+ class annotator:
+ class translator:
+ class config:
+ class translation:
+ stm = True
+
+ c0 = GcCache(False, rtyper=fakertyper())
+ ARGS = [lltype.Char, lltype.Signed]
+ RES = lltype.Char
+ descr1 = get_call_descr(c0, ARGS, RES, extrainfo=fakeextrainfo())
+ def f(a, b):
+ return 'c'
+
+ fnptr = llhelper(lltype.Ptr(lltype.FuncType(ARGS, RES)), f)
+
+ res = descr1.call_stub_i(rffi.cast(lltype.Signed, fnptr),
+ [1, 2], None, None)
+ assert res == ord('c')
+
def test_call_stubs_1():
c0 = GcCache(False)
ARGS = [lltype.Char, lltype.Signed]
diff --git a/rpython/jit/backend/llsupport/test/test_stmrewrite.py b/rpython/jit/backend/llsupport/test/test_stmrewrite.py
--- a/rpython/jit/backend/llsupport/test/test_stmrewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_stmrewrite.py
@@ -62,6 +62,29 @@
RewriteTests.check_rewrite(self, frm_operations, to_operations,
**namespace)
+ def test_inevitable_calls(self):
+ c1 = GcCache(True)
+ T = lltype.GcStruct('T')
+ U = lltype.GcStruct('U', ('x', lltype.Signed))
+ for inev in (True, False):
+ class fakeextrainfo:
+ def call_needs_inevitable(self):
+ return inev
+
+ calldescr = get_call_descr(c1, [lltype.Ptr(T)], lltype.Ptr(U),
+ fakeextrainfo())
+
+ self.check_rewrite("""
+ []
+ call(123, descr=cd)
+ jump()
+ ""","""
+ []
+ %s
+ call(123, descr=cd)
+ jump()
+ """ % ("$INEV" if inev else "",), cd=calldescr)
+
def test_rewrite_one_setfield_gc(self):
self.check_rewrite("""
[p1, p2]
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
@@ -196,6 +196,12 @@
elidable = False
loopinvariant = False
call_release_gil_target = llmemory.NULL
+ needs_inevitable = False
+ if op.opname == 'indirect_call' or op.opname == 'direct_call':
+ from rpython.translator.stm.inevitable import (
+ should_turn_inevitable_call)
+ needs_inevitable = bool(should_turn_inevitable_call(op))
+
if op.opname == "direct_call":
funcobj = op.args[0].value._obj
assert getattr(funcobj, 'calling_conv', 'c') == 'c', (
@@ -206,7 +212,8 @@
if loopinvariant:
assert not NON_VOID_ARGS, ("arguments not supported for "
"loop-invariant function!")
- if getattr(func, "_call_aroundstate_target_", None):
+ funcptr = getattr(func, "_call_aroundstate_target_", None)
+ if funcptr:
call_release_gil_target = func._call_aroundstate_target_
call_release_gil_target = llmemory.cast_ptr_to_adr(
call_release_gil_target)
@@ -234,6 +241,7 @@
effectinfo = effectinfo_from_writeanalyze(
self.readwrite_analyzer.analyze(op), self.cpu, extraeffect,
oopspecindex, can_invalidate, call_release_gil_target,
+ needs_inevitable
)
#
assert effectinfo is not None
diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -96,14 +96,16 @@
extraeffect=EF_CAN_RAISE,
oopspecindex=OS_NONE,
can_invalidate=False,
- call_release_gil_target=llmemory.NULL):
+ call_release_gil_target=llmemory.NULL,
+ needs_inevitable=False):
key = (frozenset_or_none(readonly_descrs_fields),
frozenset_or_none(readonly_descrs_arrays),
frozenset_or_none(write_descrs_fields),
frozenset_or_none(write_descrs_arrays),
extraeffect,
oopspecindex,
- can_invalidate)
+ can_invalidate,
+ needs_inevitable)
if call_release_gil_target:
key += (object(),) # don't care about caching in this case
if key in cls._cache:
@@ -131,6 +133,7 @@
result.write_descrs_arrays = write_descrs_arrays
result.extraeffect = extraeffect
result.can_invalidate = can_invalidate
+ result.needs_inevitable = needs_inevitable
result.oopspecindex = oopspecindex
result.call_release_gil_target = call_release_gil_target
if result.check_can_raise():
@@ -157,6 +160,9 @@
def is_call_release_gil(self):
return bool(self.call_release_gil_target)
+ def call_needs_inevitable(self):
+ return self.needs_inevitable
+
def frozenset_or_none(x):
if x is None:
@@ -172,7 +178,8 @@
extraeffect=EffectInfo.EF_CAN_RAISE,
oopspecindex=EffectInfo.OS_NONE,
can_invalidate=False,
- call_release_gil_target=llmemory.NULL):
+ call_release_gil_target=llmemory.NULL,
+ needs_inevitable=False):
from rpython.translator.backendopt.writeanalyze import top_set
if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
readonly_descrs_fields = None
@@ -221,7 +228,8 @@
extraeffect,
oopspecindex,
can_invalidate,
- call_release_gil_target)
+ call_release_gil_target,
+ needs_inevitable)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
diff --git a/rpython/jit/codewriter/test/test_call.py b/rpython/jit/codewriter/test/test_call.py
--- a/rpython/jit/codewriter/test/test_call.py
+++ b/rpython/jit/codewriter/test/test_call.py
@@ -198,6 +198,33 @@
call_descr = cc.getcalldescr(op)
assert call_descr.extrainfo.has_random_effects()
assert call_descr.extrainfo.is_call_release_gil() is False
+ assert call_descr.extrainfo.call_needs_inevitable() is False
+
+def test_releases_gil_analyzer_needs_inevitable():
+ from rpython.jit.backend.llgraph.runner import LLGraphCPU
+
+ for transactionsafe in (True, False):
+ T = rffi.CArrayPtr(rffi.TIME_T)
+ external = rffi.llexternal("time", [T], rffi.TIME_T,
+ _nowrapper=True,
+ threadsafe=False,
+ transactionsafe=transactionsafe)
+
+ @jit.dont_look_inside
+ def f():
+ return external(lltype.nullptr(T.TO))
+
+ rtyper = support.annotate(f, [])
+ jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
+ cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
+ res = cc.find_all_graphs(FakePolicy())
+ [f_graph] = [x for x in res if x.func is f]
+ [block, _] = list(f_graph.iterblocks())
+ [op] = block.operations
+ call_descr = cc.getcalldescr(op)
+ assert call_descr.extrainfo.is_call_release_gil() is False
+ needs_inev = not transactionsafe
+ assert call_descr.extrainfo.call_needs_inevitable() is needs_inev
def test_call_release_gil():
from rpython.jit.backend.llgraph.runner import LLGraphCPU
diff --git a/rpython/translator/stm/inevitable.py b/rpython/translator/stm/inevitable.py
--- a/rpython/translator/stm/inevitable.py
+++ b/rpython/translator/stm/inevitable.py
@@ -7,7 +7,9 @@
ALWAYS_ALLOW_OPERATIONS = set([
'force_cast', 'keepalive', 'cast_ptr_to_adr',
'cast_adr_to_int',
- 'debug_print', 'debug_assert', 'cast_opaque_ptr', 'hint',
+ 'debug_print', 'debug_assert',
+ 'debug_start', 'debug_stop', 'have_debug_prints',
+ 'cast_opaque_ptr', 'hint',
'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',
@@ -53,6 +55,29 @@
return False
return not fresh_mallocs.is_fresh_malloc(op.args[0])
+def should_turn_inevitable_call(op):
+ if op.opname == 'direct_call':
+ funcptr = op.args[0].value._obj
+ if not hasattr(funcptr, "external"):
+ return False
+ if getattr(funcptr, "transactionsafe", False):
+ return False
+ try:
+ return funcptr._name + '()'
+ except AttributeError:
+ return True
+
+ elif op.opname == 'indirect_call':
+ tographs = op.args[-1].value
+ if tographs is not None:
+ # Set of RPython functions
+ return False
+ # unknown function
+ return True
+
+ assert False
+
+
def should_turn_inevitable(op, block, fresh_mallocs):
# Always-allowed operations never cause a 'turn inevitable'
if op.opname in ALWAYS_ALLOW_OPERATIONS:
@@ -80,25 +105,8 @@
return not fresh_mallocs.is_fresh_malloc(op.args[0])
#
# Function calls
- if op.opname == 'direct_call':
- funcptr = op.args[0].value._obj
- if not hasattr(funcptr, "external"):
- return False
- if getattr(funcptr, "transactionsafe", False):
- return False
- try:
- return funcptr._name + '()'
- except AttributeError:
- return True
-
- if op.opname == 'indirect_call':
- tographs = op.args[-1].value
- if tographs is not None:
- # Set of RPython functions
- return False
- # unknown function
- return True
-
+ if op.opname == 'direct_call' or op.opname == 'indirect_call':
+ return should_turn_inevitable_call(op)
#
# Entirely unsupported operations cause a 'turn inevitable'
return True
More information about the pypy-commit
mailing list