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