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 174 175
|
from rpython.flowspace.model import Variable, Constant
from rpython.translator.backendopt import graphanalyze
from rpython.rtyper.lltypesystem import lltype, llmemory
top_set = object()
empty_set = frozenset()
# CUTOFF is disabled, as it gave a strangely not-working-any-more effect
# if the size of the result grows past that bound. The main user was
# optimizeopt/heap.py (force_from_effectinfo), which has been rewritten
# to be happy with any size now.
#CUTOFF = 3000
class WriteAnalyzer(graphanalyze.GraphAnalyzer):
#cutoff = CUTOFF
def bottom_result(self):
return empty_set
def top_result(self):
return top_set
def is_top_result(self, result):
return result is top_set
def result_builder(self):
return set()
def add_to_result(self, result, other):
if other is top_set:
return top_set
result.update(other)
#if len(result) > self.cutoff:
# return top_set
return result
def finalize_builder(self, result):
if result is top_set:
return result
return frozenset(result)
def join_two_results(self, result1, result2):
if result1 is top_set or result2 is top_set:
return top_set
return result1.union(result2)
def _getinteriorname(self, op):
if (isinstance(op.args[1], Constant) and
isinstance(op.args[1].value, str)):
return op.args[1].value
return op.args[2].value
def analyze_simple_operation(self, op, graphinfo):
if op.opname == "setfield":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
return frozenset([
("struct", op.args[0].concretetype, op.args[1].value)])
elif op.opname == "setarrayitem":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
return self._array_result(op.args[0].concretetype)
elif op.opname == "setinteriorfield":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
name = self._getinteriorname(op)
return self._interiorfield_result(op.args[0].concretetype, name)
elif op.opname == "gc_store_indexed":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
return self._gc_store_indexed_result(op)
elif op.opname == 'gc_add_memory_pressure':
# special_memory_pressure would be overwritten by zero, because
# the JIT cannot see the field write, which is why we assume
# it can write anything
return top_set
return empty_set
def _array_result(self, TYPE):
return frozenset([("array", TYPE)])
def _interiorfield_result(self, TYPE, fieldname):
return frozenset([("interiorfield", TYPE, fieldname)])
def _gc_store_indexed_result(self, op):
base_ofs = op.args[4].value
effect = self._get_effect_for_offset(base_ofs)
return frozenset([effect])
def _get_effect_for_offset(self, ofs, prefix=''):
# gc_{load,store}_indexed are generic operation which operate on
# various data types: depending on the symbolic offset, they can be
# equivalent as reading/writing an array, and interiorfield, etc. The
# following logic tries to catch all the known usages. If you see an
# 'implement me', please update the logic accordingly
if isinstance(ofs, llmemory.CompositeOffset):
# get the effect for the first component and modify it if
# necessary
sub_offsets = ofs.offsets
effect = self._get_effect_for_offset(sub_offsets[0], prefix)
for sub_ofs in sub_offsets[1:]:
if isinstance(sub_ofs, llmemory.ArrayItemsOffset):
# reading from the middle of an array is the same as
# reading from the beginning, so we don't need to change
# the effect
pass
else:
assert False, 'implement me'
return effect
elif isinstance(ofs, llmemory.FieldOffset):
T = ofs.TYPE
return (prefix + 'interiorfield', lltype.Ptr(T), ofs.fldname)
elif isinstance(ofs, llmemory.ArrayItemsOffset):
return (prefix + 'array', lltype.Ptr(ofs.TYPE))
else:
assert False, 'implement me'
def compute_graph_info(self, graph):
return FreshMallocs(graph)
class FreshMallocs(object):
def __init__(self, graph):
self.nonfresh = set(graph.getargs())
pendingblocks = list(graph.iterblocks())
self.allvariables = set()
for block in pendingblocks:
self.allvariables.update(block.inputargs)
pendingblocks.reverse()
while pendingblocks:
block = pendingblocks.pop()
for op in block.operations:
self.allvariables.add(op.result)
if (op.opname == 'malloc' or op.opname == 'malloc_varsize'
or op.opname == 'new'):
continue
elif op.opname in ('cast_pointer', 'same_as'):
if self.is_fresh_malloc(op.args[0]):
continue
self.nonfresh.add(op.result)
for link in block.exits:
self.nonfresh.update(link.getextravars())
self.allvariables.update(link.getextravars())
prevlen = len(self.nonfresh)
for v1, v2 in zip(link.args, link.target.inputargs):
if not self.is_fresh_malloc(v1):
self.nonfresh.add(v2)
if len(self.nonfresh) > prevlen:
pendingblocks.append(link.target)
def is_fresh_malloc(self, v):
if not isinstance(v, Variable):
return False
assert v in self.allvariables
return v not in self.nonfresh
class ReadWriteAnalyzer(WriteAnalyzer):
def analyze_simple_operation(self, op, graphinfo):
if op.opname == "getfield":
return frozenset([
("readstruct", op.args[0].concretetype, op.args[1].value)])
elif op.opname == "getarrayitem":
return frozenset([
("readarray", op.args[0].concretetype)])
elif op.opname == "getinteriorfield":
name = self._getinteriorname(op)
return frozenset([("readinteriorfield", op.args[0].concretetype,
name)])
elif op.opname == "gc_load_indexed":
return self._gc_load_indexed_result(op)
return WriteAnalyzer.analyze_simple_operation(self, op, graphinfo)
def _gc_load_indexed_result(self, op):
base_offset = op.args[3].value
effect = self._get_effect_for_offset(base_offset, prefix='read')
return frozenset([effect])
|