1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
from rpython.flowspace.model import (Variable, Constant, Block, Link,
SpaceOperation, c_last_exception, checkgraph)
def varoftype(concretetype, name=None):
var = Variable(name)
var.concretetype = concretetype
return var
def insert_empty_block(link, newops=[]):
"""Insert and return a new block along the given link."""
vars = {}
for v in link.args:
if isinstance(v, Variable):
vars[v] = True
for op in newops:
for v in op.args:
if isinstance(v, Variable):
vars.setdefault(v, True)
vars[op.result] = False
vars = [v for v, keep in vars.items() if keep]
mapping = {}
for v in vars:
mapping[v] = v.copy()
newblock = Block(vars)
newblock.operations.extend(newops)
newblock.closeblock(Link(link.args, link.target))
newblock.renamevariables(mapping)
link.args[:] = vars
link.target = newblock
return newblock
def insert_empty_startblock(graph):
vars = [v.copy() for v in graph.startblock.inputargs]
newblock = Block(vars)
newblock.closeblock(Link(vars, graph.startblock))
graph.startblock = newblock
def starts_with_empty_block(graph):
return (not graph.startblock.operations
and graph.startblock.exitswitch is None
and graph.startblock.exits[0].args == graph.getargs())
def split_block(block, index, _forcelink=None):
"""return a link where prevblock is the block leading up but excluding the
index'th operation and target is a new block with the neccessary variables
passed on.
"""
assert 0 <= index <= len(block.operations)
if block.exitswitch == c_last_exception:
assert index < len(block.operations)
#varmap is the map between names in the new and the old block
#but only for variables that are produced in the old block and needed in
#the new one
varmap = {}
vars_produced_in_new_block = set()
def get_new_name(var):
if var is None:
return None
if isinstance(var, Constant):
return var
if var in vars_produced_in_new_block:
return var
if var not in varmap:
varmap[var] = var.copy()
return varmap[var]
moved_operations = block.operations[index:]
new_moved_ops = []
for op in moved_operations:
repl = dict((arg, get_new_name(arg)) for arg in op.args)
newop = op.replace(repl)
new_moved_ops.append(newop)
vars_produced_in_new_block.add(op.result)
moved_operations = new_moved_ops
links = block.exits
block.exits = None
for link in links:
for i, arg in enumerate(link.args):
#last_exception and last_exc_value are considered to be created
#when the link is entered
if link.args[i] not in [link.last_exception, link.last_exc_value]:
link.args[i] = get_new_name(link.args[i])
exitswitch = get_new_name(block.exitswitch)
#the new block gets all the attributes relevant to outgoing links
#from block the old block
if _forcelink is not None:
assert index == 0
linkargs = list(_forcelink)
for v in varmap:
if v not in linkargs:
# 'v' was not specified by _forcelink, but we found out that
# we need it! Hack: if it is 'concretetype is lltype.Void'
# then it's ok to recreate its value in the target block.
# If not, then we have a problem :-)
from rpython.rtyper.lltypesystem import lltype
if v.concretetype is not lltype.Void:
raise Exception(
"The variable %r of type %r was not explicitly listed"
" in _forcelink. This issue can be caused by a"
" jitdriver.jit_merge_point() where some variable"
" containing an int or str or instance is actually"
" known to be constant, e.g. always 42." % (
v, v.concretetype))
c = Constant(None, lltype.Void)
w = varmap[v]
newop = SpaceOperation('same_as', [c], w)
i = 0
while i < len(moved_operations):
if w in moved_operations[i].args:
break
i += 1
moved_operations.insert(i, newop)
else:
linkargs = varmap.keys()
newblock = Block([get_new_name(v) for v in linkargs])
newblock.operations = moved_operations
newblock.recloseblock(*links)
newblock.exitswitch = exitswitch
link = Link(linkargs, newblock)
block.operations = block.operations[:index]
block.recloseblock(link)
block.exitswitch = None
return link
def call_initial_function(translator, initial_func, annhelper=None):
"""Before the program starts, call 'initial_func()'."""
from rpython.annotator import model as annmodel
from rpython.rtyper.lltypesystem import lltype
from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
own_annhelper = (annhelper is None)
if own_annhelper:
annhelper = MixLevelHelperAnnotator(translator.rtyper)
c_initial_func = annhelper.constfunc(initial_func, [], annmodel.s_None)
if own_annhelper:
annhelper.finish()
entry_point = translator.entry_point_graph
args = [v.copy() for v in entry_point.getargs()]
extrablock = Block(args)
v_none = varoftype(lltype.Void)
newop = SpaceOperation('direct_call', [c_initial_func], v_none)
extrablock.operations = [newop]
extrablock.closeblock(Link(args, entry_point.startblock))
entry_point.startblock = extrablock
checkgraph(entry_point)
def call_final_function(translator, final_func, annhelper=None):
"""When the program finishes normally, call 'final_func()'."""
from rpython.annotator import model as annmodel
from rpython.rtyper.lltypesystem import lltype
from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
own_annhelper = (annhelper is None)
if own_annhelper:
annhelper = MixLevelHelperAnnotator(translator.rtyper)
c_final_func = annhelper.constfunc(final_func, [], annmodel.s_None)
if own_annhelper:
annhelper.finish()
entry_point = translator.entry_point_graph
v = entry_point.getreturnvar().copy()
extrablock = Block([v])
v_none = varoftype(lltype.Void)
newop = SpaceOperation('direct_call', [c_final_func], v_none)
extrablock.operations = [newop]
extrablock.closeblock(Link([v], entry_point.returnblock))
for block in entry_point.iterblocks():
if block is not extrablock:
for link in block.exits:
if link.target is entry_point.returnblock:
link.target = extrablock
checkgraph(entry_point)
|