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
7405 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
113
import os
114
from os.path import getmtime
115
116
from .generator import AUTOGEN_WARN, InterpreterGenerator
117
from .instructions import *
118
from .memory import *
119
from .specs.base import *
120
from .storage import *
121
from .utils import *
122
123
# Tuple of (filename_root, extension, method) where filename_root is the
124
# root of the filename to be joined with "_<interpreter_name>".ext and
125
# method is the name of a get_ method on InterpreterGenerator that returns
126
# the contents of that file
127
_INTERPRETER_SOURCES = [
128
('interp', 'c', 'interpreter'),
129
('wrapper', 'pxd', 'pxd'),
130
('wrapper', 'pyx', 'wrapper')
131
]
132
133
134
def build_interp(interp_spec, dir):
135
r"""
136
Given an InterpreterSpec, write the C interpreter and the Cython
137
wrapper (generate a pyx and a pxd file).
138
139
EXAMPLES::
140
141
sage: from sage_setup.autogen.interpreters.internal import *
142
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
143
sage: testdir = tmp_dir()
144
sage: rdf_interp = RDFInterpreter()
145
sage: build_interp(rdf_interp, testdir)
146
sage: with open(testdir + '/interp_rdf.c') as f:
147
....: f.readline()
148
'/* Automatically generated by ... */\n'
149
"""
150
151
ig = InterpreterGenerator(interp_spec)
152
153
for filename_root, ext, method in _INTERPRETER_SOURCES:
154
filename = '{}_{}.{}'.format(filename_root, interp_spec.name, ext)
155
method = getattr(ig, 'get_{}'.format(method))
156
path = os.path.join(dir, filename)
157
write_if_changed(path, method())
158
159
160
def rebuild(dirname, force=False, interpreters=None, distribution=None):
161
r"""
162
Check whether the interpreter and wrapper sources have been written
163
since the last time this module was changed. If not, write them.
164
165
INPUT:
166
167
- ``dirname`` -- name of the target directory for the generated sources
168
169
- ``force`` -- boolean (default ``False``); if ``True``, ignore timestamps
170
and regenerate the sources unconditionally
171
172
- ``interpreters`` -- an iterable of strings, or ``None`` (the default,
173
which means all interpreters); which interpreters to generate
174
175
- ``distribution`` -- a string (the distribution name such as
176
``"sagemath-categories"``) or ``None`` (the default, which means
177
the monolithic Sage library)
178
179
EXAMPLES:
180
181
Monolithic build::
182
183
sage: from sage_setup.autogen.interpreters.internal import *
184
sage: testdir = tmp_dir()
185
sage: rebuild(testdir)
186
Generating interpreters for fast_callable in ...
187
-> First build of interpreters
188
sage: with open(testdir + '/wrapper_el.pyx') as f:
189
....: f.readline()
190
'# Automatically generated by ...\n'
191
"""
192
# This line will show up in "sage -b" (once per upgrade, not every time
193
# you run it).
194
print(f"Generating interpreters for fast_callable in {dirname}")
195
196
if interpreters is None:
197
interpreters = ['CDF', 'Element', 'Python', 'RDF', 'RR', 'CC']
198
199
from importlib import import_module
200
201
_INTERPRETERS = [
202
getattr(
203
import_module(
204
".specs." + interpreter.lower(), package=__name__
205
),
206
interpreter + "Interpreter",
207
)
208
for interpreter in interpreters
209
]
210
211
if distribution is None:
212
all_py = 'all.py'
213
else:
214
all_py = f'all__{distribution.replace("-", "_")}.py'
215
216
try:
217
os.makedirs(dirname)
218
except OSError:
219
if not os.path.isdir(dirname):
220
raise
221
222
# Although multiple files are generated by this function, since
223
# they are all generated at once it suffices to make sure if just
224
# one of the generated files is older than the generator sources
225
class NeedToRebuild(Exception):
226
pass
227
try:
228
if force:
229
raise NeedToRebuild("-> Force rebuilding interpreters")
230
gen_file = os.path.join(dirname, all_py)
231
if not os.path.isfile(gen_file):
232
raise NeedToRebuild("-> First build of interpreters")
233
234
gen_timestamp = getmtime(gen_file)
235
src_dir = os.path.dirname(__file__)
236
for root, dirs, files in os.walk(src_dir):
237
for basename in files:
238
if basename.endswith(".py"):
239
src_file = os.path.join(root, basename)
240
src_timestamp = getmtime(src_file)
241
if src_timestamp > gen_timestamp:
242
raise NeedToRebuild("-> Rebuilding interpreters because {} changed".format(src_file))
243
except NeedToRebuild as E:
244
# Rebuild
245
print(E)
246
else:
247
return # Up-to-date
248
249
for interp in _INTERPRETERS:
250
build_interp(interp(), dirname)
251
252
with open(os.path.join(dirname, all_py), 'w') as f:
253
f.write("# " + AUTOGEN_WARN)
254
255
with open(os.path.join(dirname, '__init__.py'), 'w') as f:
256
f.write("# " + AUTOGEN_WARN)
257
258