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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
"""PyPy Translator Frontend
The Translator is a glue class putting together the various pieces of the
translation-related code. It can be used for interactive testing of the
translator; see pypy/bin/translator.py.
"""
import autopath, os, sys
from pypy.objspace.flow.model import *
from pypy.translator.simplify import simplify_graph
from pypy.translator.gensupp import uniquemodulename
from pypy.translator.tool.buildpyxmodule import make_module_from_pyxstring
from pypy.translator.tool.buildpyxmodule import make_module_from_c
from pypy.objspace.flow import FlowObjSpace
class Translator:
def __init__(self, func=None, verbose=False, simplifying=True,
do_imports_immediately=True,
builtins_can_raise_exceptions=False):
self.entrypoint = func
self.verbose = verbose
self.simplifying = simplifying
self.builtins_can_raise_exceptions = builtins_can_raise_exceptions
self.do_imports_immediately = do_imports_immediately
self.clear()
def clear(self):
"""Clear all annotations and all flow graphs."""
self.annotator = None
self.rtyper = None
self.flowgraphs = {} # {function: graph}
self.functions = [] # the keys of self.flowgraphs, in creation order
self.callgraph = {} # {opaque_tag: (caller, callee)}
self.frozen = False # when frozen, no more flowgraphs can be generated
#self.concretetypes = {} # see getconcretetype()
#self.ctlist = [] # "
if self.entrypoint:
self.getflowgraph()
def getflowgraph(self, func=None, called_by=None, call_tag=None):
"""Get the flow graph for a function (default: the entry point)."""
func = func or self.entrypoint
try:
graph = self.flowgraphs[func]
except KeyError:
if self.verbose:
print 'getflowgraph (%s:%d) %s' % (
func.func_globals.get('__name__', '?'),
func.func_code.co_firstlineno,
func.__name__),
sys.stdout.flush()
assert not self.frozen
space = FlowObjSpace()
space.builtins_can_raise_exceptions = self.builtins_can_raise_exceptions
space.do_imports_immediately = self.do_imports_immediately
graph = space.build_flow(func)
if self.simplifying:
simplify_graph(graph, self.simplifying)
if self.verbose:
print
self.flowgraphs[func] = graph
self.functions.append(func)
try:
import inspect
graph.func = func
graph.source = inspect.getsource(func)
except IOError:
pass # e.g. when func is defined interactively
if called_by:
self.callgraph[called_by, func, call_tag] = called_by, func
return graph
def gv(self, func=None):
"""Shows the control flow graph for a function (default: all)
-- requires 'dot' and 'gv'."""
import os
from pypy.translator.tool.make_dot import make_dot, make_dot_graphs
if func is None:
# show the graph of *all* functions at the same time
graphs = []
for func in self.functions:
graph = self.getflowgraph(func)
graphs.append((graph.name, graph))
dest = make_dot_graphs(self.entrypoint.__name__, graphs)
else:
graph = self.getflowgraph(func)
dest = make_dot(graph.name, graph)
os.system('gv %s' % str(dest))
def view(self, *functions):
"""Shows the control flow graph with annotations if computed.
Requires 'dot' and pygame."""
from pypy.translator.tool.graphpage import FlowGraphPage
FlowGraphPage(self).display()
def viewcg(self):
"""Shows the whole call graph and the class hierarchy, based on
the computed annotations."""
from pypy.translator.tool.graphpage import TranslatorPage
TranslatorPage(self).display()
def simplify(self, func=None, passes=True):
"""Simplifies the control flow graph (default: for all functions)."""
if func is None:
for func in self.flowgraphs.keys():
self.simplify(func)
else:
graph = self.getflowgraph(func)
simplify_graph(graph, passes)
def annotate(self, input_args_types, func=None, policy=None):
"""annotate(self, input_arg_types[, func]) -> Annotator
Provides type information of arguments. Returns annotator.
"""
func = func or self.entrypoint
if self.annotator is None:
from pypy.translator.annrpython import RPythonAnnotator
self.annotator = RPythonAnnotator(self, policy=policy)
graph = self.getflowgraph(func)
self.annotator.build_types(graph, input_args_types, func)
return self.annotator
def checkgraphs(self):
for graph in self.flowgraphs.itervalues():
checkgraph(graph)
def specialize(self):
if self.annotator is None:
raise ValueError("you need to call annotate() first")
if self.rtyper is not None:
raise ValueError("cannot specialize() several times")
from pypy.rpython.rtyper import RPythonTyper
self.rtyper = RPythonTyper(self.annotator)
self.rtyper.specialize()
def source(self, func=None):
"""Returns original Python source.
Returns <interactive> for functions written while the
interactive session.
"""
func = func or self.entrypoint
graph = self.getflowgraph(func)
return getattr(graph, 'source', '<interactive>')
def pyrex(self, input_arg_types=None, func=None):
"""pyrex(self[, input_arg_types][, func]) -> Pyrex translation
Returns Pyrex translation. If input_arg_types is provided,
returns type annotated translation. Subsequent calls are
not affected by this.
"""
from pypy.translator.pyrex.genpyrex import GenPyrex
return self.generatecode(GenPyrex, input_arg_types, func)
def cl(self, input_arg_types=None, func=None):
"""cl(self[, input_arg_types][, func]) -> Common Lisp translation
Returns Common Lisp translation. If input_arg_types is provided,
returns type annotated translation. Subsequent calls are
not affected by this.
"""
from pypy.translator.gencl import GenCL
return self.generatecode(GenCL, input_arg_types, func)
def c(self):
"""c(self) -> C (CPython) translation
Returns C (CPython) translation.
"""
from pypy.translator.c import genc
from cStringIO import StringIO
f = StringIO()
database, ignored = genc.translator2database(self)
genc.gen_readable_parts_of_main_c_file(f, database)
return f.getvalue()
def llvm(self):
"""llvm(self) -> LLVM translation
Returns LLVM translation.
"""
from pypy.translator.llvm import genllvm
if self.annotator is None:
raise genllvm.CompileError, "function has to be annotated."
gen = genllvm.LLVMGenerator(self)
return str(gen)
def generatecode(self, gencls, input_arg_types, func):
if input_arg_types is None:
ann = self.annotator
else:
from pypy.translator.annrpython import RPythonAnnotator
ann = RPythonAnnotator(self)
if func is None:
codes = [self.generatecode1(gencls, input_arg_types,
self.entrypoint, ann)]
for func in self.functions:
if func is not self.entrypoint:
code = self.generatecode1(gencls, None, func, ann,
public=False)
codes.append(code)
else:
codes = [self.generatecode1(gencls, input_arg_types, func, ann)]
code = self.generateglobaldecl(gencls, func, ann)
if code:
codes.insert(0, code)
return '\n\n#_________________\n\n'.join(codes)
def generatecode1(self, gencls, input_arg_types, func, ann, public=True):
graph = self.getflowgraph(func)
g = gencls(graph)
g.by_the_way_the_function_was = func # XXX
if input_arg_types is not None:
ann.build_types(graph, input_arg_types, func)
if ann is not None:
g.setannotator(ann)
return g.emitcode(public)
def generateglobaldecl(self, gencls, func, ann):
graph = self.getflowgraph(func)
g = gencls(graph)
if ann is not None:
g.setannotator(ann)
return g.globaldeclarations()
def pyrexcompile(self):
"""Returns compiled function, compiled using Pyrex.
"""
from pypy.tool.udir import udir
name = self.entrypoint.func_name
pyxcode = self.pyrex()
mod = make_module_from_pyxstring(name, udir, pyxcode)
return getattr(mod, name)
def ccompile(self, really_compile=True):
"""Returns compiled function, compiled using the C generator.
"""
from pypy.translator.c import genc
if self.annotator is not None:
self.frozen = True
result = genc.genc(self, compile=really_compile)
if really_compile: # result is the module
result = getattr(result, self.entrypoint.func_name)
return result
def llvmcompile(self, optimize=True):
"""llvmcompile(self, optimize=True) -> LLVM translation
Returns LLVM translation with or without optimization.
"""
from pypy.translator.llvm import genllvm
if self.annotator is None:
raise genllvm.CompileError, "function has to be annotated."
gen = genllvm.LLVMGenerator(self)
return gen.compile(optimize)
def call(self, *args):
"""Calls underlying Python function."""
return self.entrypoint(*args)
def dis(self, func=None):
"""Disassembles underlying Python function to bytecodes."""
from dis import dis
dis(func or self.entrypoint)
## def consider_call(self, ann, func, args):
## graph = self.getflowgraph(func)
## ann.addpendingblock(graph.startblock, args)
## result_var = graph.getreturnvar()
## try:
## return ann.binding(result_var)
## except KeyError:
## # typical case for the 1st call, because addpendingblock() did
## # not actually start the analysis of the called function yet.
## return impossiblevalue
## def getconcretetype(self, cls, *args):
## "DEPRECATED. To be removed"
## # Return a (cached) 'concrete type' object attached to this translator.
## # Concrete types are what is put in the 'concretetype' attribute of
## # the Variables and Constants of the flow graphs by typer.py to guide
## # the code generators.
## try:
## return self.concretetypes[cls, args]
## except KeyError:
## result = self.concretetypes[cls, args] = cls(self, *args)
## self.ctlist.append(result)
## return result
|