Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/instructions.py
7417 views
1
#*****************************************************************************
2
# Copyright (C) 2009 Carl Witty <[email protected]>
3
# Copyright (C) 2015 Jeroen Demeyer <[email protected]>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 2 of the License, or
8
# (at your option) any later version.
9
# http://www.gnu.org/licenses/
10
#*****************************************************************************
11
12
"""Implements generic interpreter instructions and related utilities."""
13
14
15
import re
16
17
from .storage import ty_int
18
19
20
def params_gen(**chunks):
21
r"""
22
Instructions have a parameter specification that says where they get
23
their inputs and where their outputs go. Each parameter has
24
the same form: it is a triple (chunk, addr, len). The chunk says
25
where the parameter is read from/written to. The addr says which
26
value in the chunk is used. If the chunk is a stack chunk, then
27
addr must be null; the value will be read from/written to the top
28
of the stack. Otherwise, addr must be an integer, or another chunk;
29
if addr is another chunk, then the next value is read from that chunk
30
to be the address.
31
32
The len says how many values to read/write. It can be either None
33
(meaning to read/write only a single value), an integer, or
34
another chunk; if it is a chunk, then the next value is read from that
35
chunk to be the len. Note that specifying len changes the types
36
given to the instruction, so len=None is different than len=1 even
37
though both mean to use a single value.
38
39
These parameter specifications are cumbersome to write by hand, so
40
there's also a simple string format for them. This (curried)
41
function parses the simple string format and produces parameter
42
specifications. The params_gen function takes keyword arguments
43
mapping single-character names to memory chunks. The string format
44
uses these names. The params_gen function returns another function,
45
that takes two strings and returns a pair of lists of parameter
46
specifications.
47
48
Each string is the concatenation of arbitrarily many specifications.
49
Each specification consists of an address and a length. The
50
address is either a single character naming a stack chunk,
51
or a string of the form 'A[B]' where A names a non-stack chunk
52
and B names the code chunk. The length is either empty, or '@n'
53
for a number n (meaning to use that many arguments), or '@C', where
54
C is the code chunk.
55
56
EXAMPLES::
57
58
sage: from sage_setup.autogen.interpreters.internal import *
59
sage: mc_stack = MemoryChunkScratch('stack', ty_double, is_stack=True)
60
sage: mc_args = MemoryChunkArguments('args', ty_double)
61
sage: mc_code = MemoryChunkConstants('code', ty_int)
62
63
sage: pg = params_gen(D=mc_code, A=mc_args, S=mc_stack)
64
sage: pg('S', '')
65
([({MC:stack}, None, None)], [])
66
sage: pg('A[D]', '')
67
([({MC:args}, {MC:code}, None)], [])
68
sage: pg('S@5', '')
69
([({MC:stack}, None, 5)], [])
70
sage: pg('S@D', '')
71
([({MC:stack}, None, {MC:code})], [])
72
sage: pg('A[D]@D', '')
73
([({MC:args}, {MC:code}, {MC:code})], [])
74
sage: pg('SSS@D', 'A[D]S@D')
75
([({MC:stack}, None, None), ({MC:stack}, None, None), ({MC:stack}, None, {MC:code})], [({MC:args}, {MC:code}, None), ({MC:stack}, None, {MC:code})])
76
"""
77
78
def make_params(s):
79
p = []
80
s = s.strip()
81
while s:
82
chunk_code = s[0]
83
s = s[1:]
84
chunk = chunks[chunk_code]
85
addr = None
86
ch_len = None
87
# shouldn't hardcode 'code' here
88
if chunk.is_stack() or chunk.name == 'code':
89
pass
90
else:
91
m = re.match(r'\[(?:([0-9]+)|([a-zA-Z]))\]', s)
92
if m.group(1):
93
addr = int(m.group(1))
94
else:
95
ch = chunks[m.group(2)]
96
assert ch.storage_type is ty_int
97
addr = ch
98
s = s[m.end():].strip()
99
if len(s) and s[0] == '@':
100
m = re.match(r'@(?:([0-9]+)|([a-zA-Z]))', s)
101
if m.group(1):
102
ch_len = int(m.group(1))
103
else:
104
ch = chunks[m.group(2)]
105
assert ch.storage_type is ty_int
106
ch_len = ch
107
s = s[m.end():].strip()
108
p.append((chunk, addr, ch_len))
109
return p
110
111
def params(s_ins, s_outs):
112
ins = make_params(s_ins)
113
outs = make_params(s_outs)
114
return (ins, outs)
115
116
return params
117
118
119
class InstrSpec:
120
r"""
121
Each instruction in an interpreter is represented as an InstrSpec.
122
This contains all the information that we need to generate code
123
to interpret the instruction; it also is used to build the tables
124
that fast_callable uses, so this is the nexus point between
125
users of the interpreter (possibly pure Python) and the
126
generated C interpreter.
127
128
The underlying instructions are matched to the caller by name.
129
For instance, fast_callable assumes that if the interpreter has an
130
instruction named 'cos', then it will take a single argument,
131
return a single result, and implement the cos() function.
132
133
The print representation of an instruction (which will probably
134
only be used when doctesting this file) consists of the name,
135
a simplified stack effect, and the code (truncated if it's long).
136
The stack effect has two parts, the input and the output, separated
137
by '->'; the input shows what will be popped from the stack,
138
the output what will be placed on the stack. Each consists of
139
a sequence of 'S' and '*' characters, where 'S' refers to a single
140
argument and '*' refers to a variable number of arguments.
141
142
The code for an instruction is a small snippet of C code. It has
143
available variables 'i0', 'i1', ..., 'o0', 'o1', ...; one variable
144
for each input and output; its job is to assign values to the output
145
variables, based on the values of the input variables.
146
147
Normally, in an interpreter that uses doubles, each of the input
148
and output variables will be a double. If i0 actually represents
149
a variable number of arguments, then it will be a pointer to
150
double instead, and there will be another variable n_i0 giving
151
the actual number of arguments.
152
153
When instructions refer to auto-reference types, they actually
154
get a pointer to the data in its original location; it is
155
not copied into a local variable. Mostly, this makes no difference,
156
but there is one potential problem to be aware of. It is possible
157
for an output variable to point to the same object as an input
158
variable; in fact, this usually will happen when you're working
159
with the stack. If the instruction maps to a single function call,
160
then this is fine; the standard auto-reference implementations
161
(GMP, MPFR, etc.) are careful to allow having the input and output
162
be the same. But if the instruction maps to multiple function
163
calls, you may need to use a temporary variable.
164
165
Here's an example of this issue. Suppose you want to make an
166
instruction that does ``out = a+b*c``. You write code like this::
167
168
out = b*c
169
out = a+out
170
171
But out will actually share the same storage as a; so the first line
172
modifies a, and you actually end up computing 2*(b+c). The fix
173
is to only write to the output once, at the very end of your
174
instruction.
175
176
Instructions are also allowed to access memory chunks (other than
177
the stack and code) directly. They are available as C variables
178
with the same name as the chunk. This is useful if some type of
179
memory chunk doesn't fit well with the params_gen interface.
180
181
There are additional reference-counting rules that must be
182
followed if your interpreter operates on Python objects; these
183
rules are described in the docstring of the PythonInterpreter
184
class.
185
186
EXAMPLES::
187
188
sage: from sage_setup.autogen.interpreters.internal import *
189
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
190
sage: pg = RDFInterpreter().pg
191
sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')
192
add: SS->S = 'o0 = i0+i1;'
193
"""
194
195
def __init__(self, name, io, code=None, uses_error_handler=False,
196
handles_own_decref=False):
197
r"""
198
Initialize an InstrSpec.
199
200
INPUT:
201
202
- name -- the name of the instruction
203
- io -- a pair of lists of parameter specifications for I/O of the
204
instruction
205
- code -- a string containing a snippet of C code to read
206
from the input variables and write to the output variables
207
- uses_error_handler -- True if the instruction calls Python
208
and jumps to error: on a Python error
209
- handles_own_decref -- True if the instruction handles Python
210
objects and includes its own
211
reference-counting
212
213
EXAMPLES::
214
215
sage: from sage_setup.autogen.interpreters.internal import *
216
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
217
sage: pg = RDFInterpreter().pg
218
sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')
219
add: SS->S = 'o0 = i0+i1;'
220
sage: instr = InstrSpec('py_call', pg('P[D]S@D', 'S'), code=('This is very complicated. ' + 'blah ' * 30)); instr
221
py_call: *->S = 'This is very compli... blah blah blah '
222
sage: instr.name
223
'py_call'
224
sage: instr.inputs
225
[({MC:py_constants}, {MC:code}, None), ({MC:stack}, None, {MC:code})]
226
sage: instr.outputs
227
[({MC:stack}, None, None)]
228
sage: instr.code
229
'This is very complicated. blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah '
230
sage: instr.parameters
231
['py_constants', 'n_inputs']
232
sage: instr.n_inputs
233
0
234
sage: instr.n_outputs
235
1
236
"""
237
self.name = name
238
self.inputs = io[0]
239
self.outputs = io[1]
240
self.uses_error_handler = uses_error_handler
241
self.handles_own_decref = handles_own_decref
242
if code is not None:
243
self.code = code
244
# XXX We assume that there is only one stack
245
n_inputs = 0
246
n_outputs = 0
247
in_effect = ''
248
out_effect = ''
249
p = []
250
for (ch, addr, len) in self.inputs:
251
if ch.is_stack():
252
if len is None:
253
n_inputs += 1
254
in_effect += 'S'
255
elif isinstance(len, int):
256
n_inputs += len
257
in_effect += 'S%d' % len
258
else:
259
p.append('n_inputs')
260
in_effect += '*'
261
else:
262
p.append(ch.name)
263
for (ch, addr, len) in self.outputs:
264
if ch.is_stack():
265
if len is None:
266
n_outputs += 1
267
out_effect += 'S'
268
elif isinstance(len, int):
269
n_outputs += len
270
out_effect += 'S%d' % len
271
else:
272
p.append('n_outputs')
273
out_effect += '*'
274
else:
275
p.append(ch.name)
276
self.parameters = p
277
self.n_inputs = n_inputs
278
self.n_outputs = n_outputs
279
self.in_effect = in_effect
280
self.out_effect = out_effect
281
282
def __repr__(self):
283
r"""
284
Produce a string representing a given instruction, consisting
285
of its name, a brief stack specification, and its code
286
(possibly abbreviated).
287
288
EXAMPLES::
289
290
sage: from sage_setup.autogen.interpreters.internal import *
291
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
292
sage: pg = RDFInterpreter().pg
293
sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')
294
add: SS->S = 'o0 = i0+i1;'
295
"""
296
rcode = repr(self.code)
297
if len(rcode) > 40:
298
rcode = rcode[:20] + '...' + rcode[-17:]
299
return '%s: %s->%s = %s' % \
300
(self.name, self.in_effect, self.out_effect, rcode)
301
302
303
# Now we have a series of helper functions that make it slightly easier
304
# to create instructions.
305
306
def instr_infix(name, io, op):
307
r"""
308
A helper function for creating instructions implemented by
309
a single infix binary operator.
310
311
EXAMPLES::
312
313
sage: from sage_setup.autogen.interpreters.internal import *
314
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
315
sage: pg = RDFInterpreter().pg
316
sage: instr_infix('mul', pg('SS', 'S'), '*')
317
mul: SS->S = 'o0 = i0 * i1;'
318
"""
319
return InstrSpec(name, io, code='o0 = i0 %s i1;' % op)
320
321
322
def instr_funcall_2args(name, io, op):
323
r"""
324
A helper function for creating instructions implemented by
325
a two-argument function call.
326
327
EXAMPLES::
328
329
sage: from sage_setup.autogen.interpreters.internal import *
330
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
331
sage: pg = RDFInterpreter().pg
332
sage: instr_funcall_2args('atan2', pg('SS', 'S'), 'atan2')
333
atan2: SS->S = 'o0 = atan2(i0, i1);'
334
"""
335
return InstrSpec(name, io, code='o0 = %s(i0, i1);' % op)
336
337
338
def instr_unary(name, io, op):
339
r"""
340
A helper function for creating instructions with one input
341
and one output.
342
343
EXAMPLES::
344
345
sage: from sage_setup.autogen.interpreters.internal import *
346
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
347
sage: pg = RDFInterpreter().pg
348
sage: instr_unary('sin', pg('S','S'), 'sin(i0)')
349
sin: S->S = 'o0 = sin(i0);'
350
sage: instr_unary('neg', pg('S','S'), '-i0')
351
neg: S->S = 'o0 = -i0;'
352
"""
353
return InstrSpec(name, io, code='o0 = ' + op + ';')
354
355
356
def instr_funcall_2args_mpfr(name, io, op):
357
r"""
358
A helper function for creating MPFR instructions with two inputs
359
and one output.
360
361
EXAMPLES::
362
363
sage: from sage_setup.autogen.interpreters.internal import *
364
sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter
365
sage: pg = RRInterpreter().pg
366
sage: instr_funcall_2args_mpfr('add', pg('SS','S'), 'mpfr_add')
367
add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'
368
"""
369
return InstrSpec(name, io, code='%s(o0, i0, i1, MPFR_RNDN);' % op)
370
371
372
def instr_funcall_1arg_mpfr(name, io, op):
373
r"""
374
A helper function for creating MPFR instructions with one input
375
and one output.
376
377
EXAMPLES::
378
379
sage: from sage_setup.autogen.interpreters.internal import *
380
sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter
381
sage: pg = RRInterpreter().pg
382
sage: instr_funcall_1arg_mpfr('exp', pg('S','S'), 'mpfr_exp')
383
exp: S->S = 'mpfr_exp(o0, i0, MPFR_RNDN);'
384
"""
385
return InstrSpec(name, io, code='%s(o0, i0, MPFR_RNDN);' % op)
386
387
388
def instr_funcall_2args_mpc(name, io, op):
389
r"""
390
A helper function for creating MPC instructions with two inputs
391
and one output.
392
393
EXAMPLES::
394
395
sage: from sage_setup.autogen.interpreters.internal import *
396
sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter
397
sage: pg = CCInterpreter().pg
398
sage: instr_funcall_2args_mpc('add', pg('SS','S'), 'mpc_add')
399
add: SS->S = 'mpc_add(o0, i0, i1, MPC_RNDNN);'
400
"""
401
return InstrSpec(name, io, code='%s(o0, i0, i1, MPC_RNDNN);' % op)
402
403
404
def instr_funcall_1arg_mpc(name, io, op):
405
r"""
406
A helper function for creating MPC instructions with one input
407
and one output.
408
409
EXAMPLES::
410
411
sage: from sage_setup.autogen.interpreters.internal import *
412
sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter
413
sage: pg = CCInterpreter().pg
414
sage: instr_funcall_1arg_mpc('exp', pg('S','S'), 'mpc_exp')
415
exp: S->S = 'mpc_exp(o0, i0, MPC_RNDNN);'
416
"""
417
return InstrSpec(name, io, code='%s(o0, i0, MPC_RNDNN);' % op)
418
419