Author: Armin Rigo <[email protected]>
Branch: stmgc-static-barrier
Changeset: r66288:7a6758a0e339
Date: 2013-08-22 16:56 +0200
http://bitbucket.org/pypy/pypy/changeset/7a6758a0e339/
Log: Do some whole-graph analysis.
diff --git a/rpython/rtyper/lltypesystem/lloperation.py
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -624,6 +624,7 @@
'debug_reraise_traceback': LLOp(),
'debug_print_traceback': LLOp(),
'debug_nonnull_pointer': LLOp(canrun=True),
+ 'debug_stm_flush_barrier': LLOp(canrun=True),
# __________ instrumentation _________
'instrument_count': LLOp(),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -673,6 +673,9 @@
def op_nop(x):
pass
+def op_debug_stm_flush_barrier():
+ pass
+
# ____________________________________________________________
def get_op_impl(opname):
diff --git a/rpython/translator/stm/test/test_writebarrier.py
b/rpython/translator/stm/test/test_writebarrier.py
--- a/rpython/translator/stm/test/test_writebarrier.py
+++ b/rpython/translator/stm/test/test_writebarrier.py
@@ -1,5 +1,6 @@
from rpython.rlib.rstm import register_invoke_around_extcall
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.translator.stm.test.transform_support import BaseTestTransform
@@ -294,16 +295,15 @@
x = Z()
x.foo = 815
x.zbar = 'A'
- external_any_gcobj()
+ llop.debug_stm_flush_barrier(lltype.Void)
result = x.foo # 1
if isinstance(x, Y): # 2
- result += x.ybar # 3
+ result += x.ybar # 3: optimized
return result
res = self.interpret(f1, [10])
assert res == 42 + 10
- assert self.barriers == ['a2r', 'a2i', 'a2r'] # from 3 blocks (could be
- # optimized later)
+ assert self.barriers == ['a2r', 'a2i']
res = self.interpret(f1, [-10])
assert res == 815
assert self.barriers == ['a2r', 'a2i']
@@ -318,7 +318,7 @@
return y
def f1(i):
y = make_y(i)
- external_any_gcobj()
+ llop.debug_stm_flush_barrier(lltype.Void)
prev = y.ybar # a2r
handle(y) # inside handle(): a2r, r2v
return prev + y.ybar # q2r
@@ -343,7 +343,7 @@
else:
x = Z(); x.foo = 815; x.zbar = 'A'
y = Y(); y.foo = -13; y.ybar = i
- external_any_gcobj()
+ llop.debug_stm_flush_barrier(lltype.Void)
prev = x.foo # a2r
handle(y) # inside handle(): a2r, r2v
return prev + x.foo # q2r
@@ -366,7 +366,7 @@
else:
y = lltype.nullptr(Y)
x = lltype.cast_opaque_ptr(llmemory.GCREF, y)
- external_any_gcobj()
+ llop.debug_stm_flush_barrier(lltype.Void)
prev = lltype.cast_opaque_ptr(YPTR, x).foo # a2r
handle(y) # inside handle(): a2r, r2v
return prev + lltype.cast_opaque_ptr(YPTR, x).ybar # q2r?
@@ -387,7 +387,7 @@
def f1(i):
x.a = x2 # write barrier
y = X() # malloc
- x.a = x3 # write barrier again
+ x.a = x3 # repeat write barrier
return y
res = self.interpret(f1, [10])
@@ -399,8 +399,10 @@
def f1(n):
x = Foo()
+ llop.debug_stm_flush_barrier(lltype.Void)
if n > 1:
x.foo = n
+ llop.debug_stm_flush_barrier(lltype.Void)
return x.foo
res = self.interpret(f1, [4])
diff --git a/rpython/translator/stm/test/transform_support.py
b/rpython/translator/stm/test/transform_support.py
--- a/rpython/translator/stm/test/transform_support.py
+++ b/rpython/translator/stm/test/transform_support.py
@@ -77,7 +77,7 @@
def check_category(self, p, expected):
cat = self.get_category_or_null(p)
- assert cat in 'AIQRVW' or cat is None
+ assert cat is None or cat in 'AIQRVW'
if expected is not None:
assert cat is not None and cat >= expected
return cat
diff --git a/rpython/translator/stm/writebarrier.py
b/rpython/translator/stm/writebarrier.py
--- a/rpython/translator/stm/writebarrier.py
+++ b/rpython/translator/stm/writebarrier.py
@@ -1,6 +1,6 @@
from rpython.flowspace.model import SpaceOperation, Constant, Variable
-from rpython.flowspace.model import mkentrymap
from rpython.translator.unsimplify import varoftype, insert_empty_block
+from rpython.translator.unsimplify import insert_empty_startblock
from rpython.rtyper.lltypesystem import lltype
from rpython.translator.backendopt.writeanalyze import top_set
@@ -38,15 +38,21 @@
return to > frm
+class Renaming(object):
+ def __init__(self, newvar, category):
+ self.newvar = newvar # a Variable or a Constant
+ self.TYPE = newvar.concretetype
+ self.category = category
+
+
class BlockTransformer(object):
- def __init__(self, stmtransformer, block, entrylinks):
+ def __init__(self, stmtransformer, block):
self.stmtransformer = stmtransformer
self.block = block
- self.inputargs_category = {}
self.patch = None
- for link in entrylinks:
- self.inputargs_category[link] = ['A'] * len(link.args)
+ self.inputargs_category = [None] * len(block.inputargs)
+ self.inputargs_category_per_link = {}
def analyze_inside_block(self):
@@ -98,54 +104,71 @@
#
self.wants_a_barrier = wants_a_barrier
self.expand_comparison = expand_comparison
- return bool(wants_a_barrier or expand_comparison)
def flow_through_block(self, graphinfo):
- def get_category(v):
- if isinstance(v, Constant):
- default = 'I' # prebuilt objects cannot be stubs
- else:
- default = 'A'
- return category.get(v, default)
+ def renfetch(v):
+ try:
+ return renamings[v]
+ except KeyError:
+ if isinstance(v, Variable):
+ ren = Renaming(v, 'A')
+ else:
+ ren = Renaming(v, 'I') # prebuilt objects cannot be stubs
+ renamings[v] = ren
+ return ren
def get_category_or_null(v):
- if isinstance(v, Constant) and not v.value:
+ # 'v' is an original variable here, or a constant
+ if isinstance(v, Constant) and not v.value: # a NULL constant
return None
- return category.get(v, 'A')
+ if v in renamings:
+ return renamings[v].category
+ if isinstance(v, Constant):
+ return 'I'
+ else:
+ return 'A'
def renamings_get(v):
- if v not in renamings:
- return v
- v2 = renamings[v][0]
+ try:
+ ren = renamings[v]
+ except KeyError:
+ return v # unmodified
+ v2 = ren.newvar
if v2.concretetype == v.concretetype:
return v2
v3 = varoftype(v.concretetype)
newoperations.append(SpaceOperation('cast_pointer', [v2], v3))
+ if lltype.castable(ren.TYPE, v3.concretetype) > 0:
+ ren.TYPE = v3.concretetype
return v3
# note: 'renamings' maps old vars to new vars, but cast_pointers
# are done lazily. It means that the two vars may not have
# exactly the same type.
- renamings = {} # {original-var: [var-in-newoperations] (len 1)}
- category = {} # {var-in-newoperations: LETTER}
+ renamings = {} # {original-var: Renaming(newvar, category)}
newoperations = []
stmtransformer = self.stmtransformer
+ # make the initial trivial renamings needed to have some precise
+ # categories for the input args
+ for v, cat in zip(self.block.inputargs, self.inputargs_category):
+ if (cat is not None and
+ isinstance(v.concretetype, lltype.Ptr) and
+ v.concretetype.TO._gckind == 'gc'):
+ renamings[v] = Renaming(v, cat)
+
for op in self.block.operations:
#
- if op.opname == 'cast_pointer':
- v = op.args[0]
- renamings[op.result] = renamings.setdefault(v, [v])
+ if op.opname in ('cast_pointer', 'same_as'):
+ renamings[op.result] = renfetch(op.args[0])
continue
#
to = self.wants_a_barrier.get(op)
if to is not None:
- v = op.args[0]
- v_holder = renamings.setdefault(v, [v])
- v = v_holder[0]
- frm = get_category(v)
+ ren = renfetch(op.args[0])
+ frm = ren.category
if needs_barrier(frm, to):
try:
b = stmtransformer.barrier_counts[frm, to]
@@ -155,11 +178,12 @@
stmtransformer.barrier_counts[frm, to] = b
b[0] += 1
c_info = b[1]
+ v = ren.newvar
w = varoftype(v.concretetype)
newop = SpaceOperation('stm_barrier', [c_info, v], w)
newoperations.append(newop)
- v_holder[0] = w
- category[w] = to
+ ren.newvar = w
+ ren.category = to
#
newop = SpaceOperation(op.opname,
[renamings_get(v) for v in op.args],
@@ -167,8 +191,8 @@
newoperations.append(newop)
#
if op in self.expand_comparison:
- cats = (get_category_or_null(newop.args[0]),
- get_category_or_null(newop.args[1]))
+ cats = (get_category_or_null(op.args[0]),
+ get_category_or_null(op.args[1]))
if None not in cats and (cats[0] < 'V' or cats[1] < 'V'):
if newop.opname == 'ptr_ne':
v = varoftype(lltype.Bool)
@@ -183,17 +207,21 @@
# all pointers are lowered to 'I', because a non-
# stub cannot suddenly point to a stub, but we
# cannot guarantee anything more
- for v, cat in category.items():
- if cat > 'I':
- category[v] = 'I'
+ for ren in renamings.values():
+ if ren.category > 'I':
+ ren.category = 'I'
+
+ if op.opname == 'debug_stm_flush_barrier':
+ for ren in renamings.values():
+ ren.category = 'A'
if stmtransformer.collect_analyzer.analyze(op):
# this operation can collect: we bring all 'W'
# categories back to 'V', because we would need
# a repeat_write_barrier on them afterwards
- for v, cat in category.items():
- if cat == 'W':
- category[v] = 'V'
+ for ren in renamings.values():
+ if ren.category == 'W':
+ ren.category = 'V'
effectinfo = stmtransformer.write_analyzer.analyze(
op, graphinfo=graphinfo)
@@ -202,9 +230,9 @@
# this operation can perform random writes: any
# 'R'-category object falls back to 'Q' because
# we would need a repeat_read_barrier()
- for v, cat in category.items():
- if cat == 'R':
- category[v] = 'Q'
+ for ren in renamings.values():
+ if ren.category == 'R':
+ ren.category = 'Q'
else:
# the same, but only on objects of the right types
# -- we need to consider 'types' or any base type
@@ -216,34 +244,82 @@
if not isinstance(TYPE, lltype.Struct):
break
_, TYPE = TYPE._first_struct()
- for v in category.keys():
- if (v.concretetype.TO in types and
- category[v] == 'R'):
- category[v] = 'Q'
+ for ren in renamings.values():
+ if ren.TYPE.TO in types and ren.category == 'R':
+ ren.category = 'Q'
if op.opname in MALLOCS:
- category[op.result] = 'W'
+ assert op.result not in renamings
+ renamings[op.result] = Renaming(op.result, 'W')
+ if isinstance(self.block.exitswitch, Variable):
+ switchv = renamings_get(self.block.exitswitch)
+ else:
+ switchv = None
blockoperations = newoperations
linkoperations = []
for link in self.block.exits:
+ output_categories = []
+ for v in link.args:
+ if (isinstance(v.concretetype, lltype.Ptr) and
+ v.concretetype.TO._gckind == 'gc'):
+ cat = get_category_or_null(v)
+ else:
+ cat = None
+ output_categories.append(cat)
newoperations = []
newargs = [renamings_get(v) for v in link.args]
- linkoperations.append((newargs, newoperations))
+ linkoperations.append((newargs, newoperations, output_categories))
#
# Record how we'd like to patch the block, but don't do any
# patching yet
- self.patch = (blockoperations, linkoperations)
+ self.patch = (blockoperations, switchv, linkoperations)
+
+
+ def update_targets(self, block_transformers):
+ (_, _, linkoperations) = self.patch
+ assert len(linkoperations) == len(self.block.exits)
+ targetbts = []
+ for link, (_, _, output_categories) in zip(self.block.exits,
+ linkoperations):
+ targetblock = link.target
+ if targetblock not in block_transformers:
+ continue # ignore the exit block
+ targetbt = block_transformers[targetblock]
+ targetbt.inputargs_category_per_link[link] = output_categories
+ if targetbt.update_inputargs_category():
+ targetbts.append(targetbt)
+ return set(targetbts)
+
+ def update_inputargs_category(self):
+ values = self.inputargs_category_per_link.values()
+ newcats = []
+ for i in range(len(self.block.inputargs)):
+ cat = None
+ for output_categories in values:
+ cat2 = output_categories[i]
+ if cat is None:
+ cat = cat2
+ elif cat2 is not None:
+ cat = min(cat, cat2)
+ newcats.append(cat)
+ if newcats != self.inputargs_category:
+ self.inputargs_category = newcats
+ return True
+ else:
+ return False
def patch_now(self):
if self.patch is None:
return
- newoperations, linkoperations = self.patch
+ newoperations, switchv, linkoperations = self.patch
self.block.operations = newoperations
+ if switchv is not None:
+ self.block.exitswitch = switchv
assert len(linkoperations) == len(self.block.exits)
- for link, (newargs, newoperations) in zip(self.block.exits,
- linkoperations):
+ for link, (newargs, newoperations, _) in zip(self.block.exits,
+ linkoperations):
link.args[:] = newargs
if newoperations:
# must put them in a fresh block along the link
@@ -266,22 +342,24 @@
pointer from category x to category y if and only if y > x.
"""
graphinfo = stmtransformer.write_analyzer.compute_graph_info(graph)
+ annotator = stmtransformer.translator.annotator
+ insert_empty_startblock(annotator, graph)
block_transformers = {}
- entrymap = mkentrymap(graph)
pending = set()
for block in graph.iterblocks():
if block.operations == ():
continue
- bt = BlockTransformer(stmtransformer, block, entrymap[block])
- if bt.analyze_inside_block():
- pending.add(bt)
+ bt = BlockTransformer(stmtransformer, block)
+ bt.analyze_inside_block()
block_transformers[block] = bt
+ pending.add(bt)
while pending:
bt = pending.pop()
bt.flow_through_block(graphinfo)
+ pending |= bt.update_targets(block_transformers)
for bt in block_transformers.values():
bt.patch_now()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit