File: writeanalyze.py

package info (click to toggle)
pypy3 7.3.19%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212,236 kB
  • sloc: python: 2,098,316; ansic: 540,565; sh: 21,462; asm: 14,419; cpp: 4,451; makefile: 4,209; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 12; awk: 4
file content (175 lines) | stat: -rw-r--r-- 6,958 bytes parent folder | download | duplicates (7)
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])