Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/__init__.py
4086 views
1
r"""
2
Generate interpreters for fast_callable
3
4
AUTHORS:
5
6
- Carl Witty
7
8
This file is part of the Sage support for "planned" computations;
9
that is, computations that are separated into a planning stage and
10
a plan-execution stage. Here, we generate fast interpreters for plan
11
executions.
12
13
There are at least two kinds of computations that are often planned in
14
this fashion. First is arithmetic expression evaluation, where we
15
take an arbitrary user-specified arithmetic expression and compile it
16
into a bytecode form for fast interpretation. Second is things like
17
FFTs and large multiplications, where large problems are split into
18
multiple smaller problems... we can do the logical "splitting" for a
19
given size only once, producing a plan which can be reused as often as
20
we want for different problems of the same size. Currently only
21
arithmetic expression evaluation is implemented, but other kinds of
22
planned computations should be easy to add.
23
24
Typically, for arithmetic expressions, we want the storage of
25
intermediate results to be handled automatically (on a stack); for
26
FFTs/multiplications/etc., the planner will keep track of intermediate
27
results itself.
28
29
For arithmetic expression evaluation, we want to have lots of
30
interpreters (at least one, and possibly several, per
31
specially-handled type). Also, for any given type, we have many
32
possible variants of instruction encoding, etc.; some of these could
33
be handled with conditional compilation, but some are more
34
complicated. So we end up writing an interpreter generator.
35
36
We want to share as much code as possible across all of these
37
interpreters, while still maintaining the freedom to make drastic
38
changes in the interpretation strategy (which may change the
39
generated code, the calling convention for the interpreter, etc.)
40
41
To make this work, the interpreter back-end is divided into three
42
parts:
43
44
1. The interpreter itself, in C or C++.
45
46
2. The wrapper, which is a Cython object holding the
47
constants, code, etc., and which actually calls the interpreter.
48
49
3. The code generator.
50
51
We generate parts 1 and 2. The code generator is table-driven,
52
and we generate the tables for the code generator.
53
54
There are a lot of techniques for fast interpreters that we do not yet
55
use; hopefully at least some of these will eventually be implemented:
56
57
- using gcc's "labels as values" extension where available
58
59
- top-of-stack caching
60
61
- superinstructions and/or superoperators
62
63
- static stack caching
64
65
- context threading/subrouting threading
66
67
- selective inlining/dynamic superinstructions
68
69
- automatic replication
70
71
Interpreters may be stack-based or register-based. Recent research
72
suggests that register-based interpreters are better, but the
73
researchers are investigating interpreters for entire programming
74
languages, rather than interpreters for expressions. I suspect
75
that stack-based expression interpreters may be better. However,
76
we'll implement both varieties and see what's best.
77
78
The relative costs of stack- and register-based interpreters will
79
depend on the costs of moving values. For complicated types (like
80
mpz_t), a register-based interpreter will quite likely be better,
81
since it will avoid moving values.
82
83
We will NOT support any sort of storage of bytecode; instead, the
84
code must be re-generated from expression trees in every Sage run.
85
This means that we can trivially experiment with different styles of
86
interpreter, or even use quite different interpreters depending on
87
the architecture, without having to worry about forward and backward
88
compatibility.
89
"""
90
91
#*****************************************************************************
92
# Copyright (C) 2009 Carl Witty <[email protected]>
93
# Copyright (C) 2015 Jeroen Demeyer <[email protected]>
94
#
95
# This program is free software: you can redistribute it and/or modify
96
# it under the terms of the GNU General Public License as published by
97
# the Free Software Foundation, either version 2 of the License, or
98
# (at your option) any later version.
99
# http://www.gnu.org/licenses/
100
#*****************************************************************************
101
102
#####################################################################
103
# This module is used during the Sage build process, so it should not
104
# use any other Sage modules. (In particular, it MUST NOT use any
105
# Cython modules -- they won't be built yet!)
106
# Also, we have some trivial dependency tracking, where we don't
107
# rebuild the interpreters if this file hasn't changed; if
108
# interpreter configuration is split out into a separate file,
109
# that will have to be changed.
110
#####################################################################
111
112
from __future__ import absolute_import, print_function
113
114
import os
115
from os.path import getmtime
116
117
from .generator import AUTOGEN_WARN, InterpreterGenerator
118
from .instructions import *
119
from .memory import *
120
from .specs.base import *
121
from .storage import *
122
from .utils import *
123
124
# Tuple of (filename_root, extension, method) where filename_root is the
125
# root of the filename to be joined with "_<interpreter_name>".ext and
126
# method is the name of a get_ method on InterpreterGenerator that returns
127
# the contents of that file
128
_INTERPRETER_SOURCES = [
129
('interp', 'c', 'interpreter'),
130
('wrapper', 'pxd', 'pxd'),
131
('wrapper', 'pyx', 'wrapper')
132
]
133
134
135
def build_interp(interp_spec, dir):
136
r"""
137
Given an InterpreterSpec, write the C interpreter and the Cython
138
wrapper (generate a pyx and a pxd file).
139
140
EXAMPLES::
141
142
sage: from sage_setup.autogen.interpreters.internal import *
143
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
144
sage: testdir = tmp_dir()
145
sage: rdf_interp = RDFInterpreter()
146
sage: build_interp(rdf_interp, testdir)
147
sage: with open(testdir + '/interp_rdf.c') as f:
148
....: f.readline()
149
'/* Automatically generated by ... */\n'
150
"""
151
152
ig = InterpreterGenerator(interp_spec)
153
154
for filename_root, ext, method in _INTERPRETER_SOURCES:
155
filename = '{}_{}.{}'.format(filename_root, interp_spec.name, ext)
156
method = getattr(ig, 'get_{}'.format(method))
157
path = os.path.join(dir, filename)
158
write_if_changed(path, method())
159
160
161
def rebuild(dirname, force=False, interpreters=None, distribution=None):
162
r"""
163
Check whether the interpreter and wrapper sources have been written
164
since the last time this module was changed. If not, write them.
165
166
INPUT:
167
168
- ``dirname`` -- name of the target directory for the generated sources
169
170
- ``force`` -- boolean (default ``False``); if ``True``, ignore timestamps
171
and regenerate the sources unconditionally
172
173
- ``interpreters`` -- an iterable of strings, or ``None`` (the default,
174
which means all interpreters); which interpreters to generate
175
176
- ``distribution`` -- a string (the distribution name such as
177
``"sagemath-categories"``) or ``None`` (the default, which means
178
the monolithic Sage library)
179
180
EXAMPLES:
181
182
Monolithic build::
183
184
sage: from sage_setup.autogen.interpreters.internal import *
185
sage: testdir = tmp_dir()
186
sage: rebuild(testdir)
187
Generating interpreters for fast_callable in ...
188
-> First build of interpreters
189
sage: with open(testdir + '/wrapper_el.pyx') as f:
190
....: f.readline()
191
'# Automatically generated by ...\n'
192
193
Modularized build::
194
195
sage: testdir = tmp_dir()
196
sage: rebuild(testdir, interpreters=['Element', 'Python'],
197
....: distribution='sagemath-categories')
198
Generating interpreters for fast_callable in ...
199
-> First build of interpreters
200
sage: with open(testdir + '/all__sagemath_categories.py') as f:
201
....: f.readline()
202
'# Automatically generated by ...'
203
"""
204
# This line will show up in "sage -b" (once per upgrade, not every time
205
# you run it).
206
print(f"Generating interpreters for fast_callable in {dirname}")
207
208
if interpreters is None:
209
interpreters = ['CDF', 'Element', 'Python', 'RDF', 'RR', 'CC']
210
211
from importlib import import_module
212
213
_INTERPRETERS = [
214
getattr(
215
import_module(
216
".specs." + interpreter.lower(), package=__name__
217
),
218
interpreter + "Interpreter",
219
)
220
for interpreter in interpreters
221
]
222
223
if distribution is None:
224
all_py = 'all.py'
225
else:
226
all_py = f'all__{distribution.replace("-", "_")}.py'
227
228
try:
229
os.makedirs(dirname)
230
except OSError:
231
if not os.path.isdir(dirname):
232
raise
233
234
# Although multiple files are generated by this function, since
235
# they are all generated at once it suffices to make sure if just
236
# one of the generated files is older than the generator sources
237
class NeedToRebuild(Exception):
238
pass
239
try:
240
if force:
241
raise NeedToRebuild("-> Force rebuilding interpreters")
242
gen_file = os.path.join(dirname, all_py)
243
if not os.path.isfile(gen_file):
244
raise NeedToRebuild("-> First build of interpreters")
245
246
gen_timestamp = getmtime(gen_file)
247
src_dir = os.path.dirname(__file__)
248
for root, dirs, files in os.walk(src_dir):
249
for basename in files:
250
if basename.endswith(".py"):
251
src_file = os.path.join(root, basename)
252
src_timestamp = getmtime(src_file)
253
if src_timestamp > gen_timestamp:
254
raise NeedToRebuild("-> Rebuilding interpreters because {} changed".format(src_file))
255
except NeedToRebuild as E:
256
# Rebuild
257
print(E)
258
else:
259
return # Up-to-date
260
261
for interp in _INTERPRETERS:
262
build_interp(interp(), dirname)
263
264
with open(os.path.join(dirname, all_py), 'w') as f:
265
f.write("# " + AUTOGEN_WARN)
266
267
with open(os.path.join(dirname, '__init__.py'), 'w') as f:
268
f.write("# " + AUTOGEN_WARN)
269
270