Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/generator.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
# https://www.gnu.org/licenses/
10
# ***************************************************************************
11
12
"""Implements the generic interpreter generator."""
13
14
from __future__ import print_function, absolute_import
15
16
from collections import defaultdict
17
from io import StringIO
18
19
from .memory import string_of_addr
20
from .utils import je, indent_lines, reindent_lines as ri
21
22
23
AUTOGEN_WARN = "Automatically generated by {}. Do not edit!".format(__file__)
24
25
26
class InterpreterGenerator:
27
r"""
28
This class takes an InterpreterSpec and generates the corresponding
29
C interpreter and Cython wrapper.
30
31
See the documentation for methods get_wrapper and get_interpreter
32
for more information.
33
"""
34
35
def __init__(self, spec):
36
r"""
37
Initialize an InterpreterGenerator.
38
39
INPUT:
40
41
- ``spec`` -- an InterpreterSpec
42
43
EXAMPLES::
44
45
sage: from sage_setup.autogen.interpreters.internal import *
46
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
47
sage: interp = RDFInterpreter()
48
sage: gen = InterpreterGenerator(interp)
49
sage: gen._spec is interp
50
True
51
sage: gen.uses_error_handler
52
False
53
"""
54
self._spec = spec
55
self.uses_error_handler = False
56
57
def gen_code(self, instr_desc, write):
58
r"""
59
Generate code for a single instruction.
60
61
INPUT:
62
63
- instr_desc -- an InstrSpec
64
- write -- a Python callable
65
66
This function calls its write parameter successively with
67
strings; when these strings are concatenated, the result is
68
the code for the given instruction.
69
70
See the documentation for the get_interpreter method for more
71
information.
72
73
EXAMPLES::
74
75
sage: from sage_setup.autogen.interpreters.internal import *
76
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
77
sage: interp = RDFInterpreter()
78
sage: gen = InterpreterGenerator(interp)
79
sage: from io import StringIO
80
sage: buff = StringIO()
81
sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])
82
sage: gen.gen_code(instrs['div'], buff.write)
83
sage: print(buff.getvalue())
84
case ...: /* div */
85
{
86
double i1 = *--stack;
87
double i0 = *--stack;
88
double o0;
89
o0 = i0 / i1;
90
*stack++ = o0;
91
}
92
break;
93
<BLANKLINE>
94
"""
95
96
d = instr_desc
97
w = write
98
99
if d.uses_error_handler:
100
self.uses_error_handler = True
101
102
w(je(ri(4, """
103
case {{ d.opcode }}: /* {{ d.name }} */
104
{
105
"""), d=d))
106
107
# If the inputs to an instruction come from the stack,
108
# then we want to generate code for the inputs in reverse order:
109
# for instance, the divide instruction, which takes inputs A and B
110
# and generates A/B, needs to pop B off the stack first.
111
# On the other hand, if the inputs come from the constant pool,
112
# then we want to generate code for the inputs in normal order,
113
# because the addresses in the code stream will be in that order.
114
# We handle this by running through the inputs in two passes:
115
# first a forward pass, where we handle non-stack inputs
116
# (and lengths for stack inputs), and then a reverse pass,
117
# where we handle stack inputs.
118
for i in range(len(d.inputs)):
119
(ch, addr, input_len) = d.inputs[i]
120
chst = ch.storage_type
121
if addr is not None:
122
w(" int ai%d = %s;\n" % (i, string_of_addr(addr)))
123
if input_len is not None:
124
w(" int n_i%d = %s;\n" % (i, string_of_addr(input_len)))
125
if not ch.is_stack():
126
# Shouldn't hardcode 'code' here
127
if ch.name == 'code':
128
w(" %s i%d = %s;\n" % (chst.c_local_type(), i, string_of_addr(ch)))
129
elif input_len is not None:
130
w(" %s i%d = %s + ai%d;\n" %
131
(chst.c_ptr_type(), i, ch.name, i))
132
else:
133
w(" %s i%d = %s[ai%d];\n" %
134
(chst.c_local_type(), i, ch.name, i))
135
136
for i in reversed(range(len(d.inputs))):
137
(ch, addr, input_len) = d.inputs[i]
138
chst = ch.storage_type
139
if ch.is_stack():
140
if input_len is not None:
141
w(" %s -= n_i%d;\n" % (ch.name, i))
142
w(" %s i%d = %s;\n" % (chst.c_ptr_type(), i, ch.name))
143
else:
144
w(" %s i%d = *--%s;\n" % (chst.c_local_type(), i, ch.name))
145
if ch.is_python_refcounted_stack():
146
w(" *%s = NULL;\n" % ch.name)
147
148
for i in range(len(d.outputs)):
149
(ch, addr, output_len) = d.outputs[i]
150
chst = ch.storage_type
151
if addr is not None:
152
w(" int ao%d = %s;\n" % (i, string_of_addr(addr)))
153
if output_len is not None:
154
w(" int n_o%d = %s;\n" % (i, string_of_addr(output_len)))
155
if ch.is_stack():
156
w(" %s o%d = %s;\n" %
157
(chst.c_ptr_type(), i, ch.name))
158
w(" %s += n_o%d;\n" % (ch.name, i))
159
else:
160
w(" %s o%d = %s + ao%d;\n" %
161
(chst.c_ptr_type(), i, ch.name, i))
162
else:
163
if not chst.cheap_copies():
164
if ch.is_stack():
165
w(" %s o%d = *%s++;\n" %
166
(chst.c_local_type(), i, ch.name))
167
else:
168
w(" %s o%d = %s[ao%d];\n" %
169
(chst.c_local_type(), i, ch.name, i))
170
else:
171
w(" %s o%d;\n" % (chst.c_local_type(), i))
172
w(indent_lines(8, d.code.rstrip('\n') + '\n'))
173
174
stack_offsets = defaultdict(int)
175
for i in range(len(d.inputs)):
176
(ch, addr, input_len) = d.inputs[i]
177
if ch.is_python_refcounted_stack() and not d.handles_own_decref:
178
if input_len is None:
179
w(" Py_DECREF(i%d);\n" % i)
180
stack_offsets[ch] += 1
181
else:
182
w(je(ri(8, """
183
int {{ iter }};
184
for ({{ iter }} = 0; {{ iter }} < n_i{{ i }}; {{ iter }}++) {
185
Py_CLEAR(i{{ i }}[{{ iter }}]);
186
}
187
"""), iter='_interp_iter_%d' % i, i=i))
188
189
for i in range(len(d.outputs)):
190
ch = d.outputs[i][0]
191
chst = ch.storage_type
192
if chst.python_refcounted():
193
# We don't yet support code chunks
194
# that produce multiple Python values, because of
195
# the way it complicates error handling.
196
assert i == 0
197
w(" if (!CHECK(o%d)) {\n" % i)
198
w(" Py_XDECREF(o%d);\n" % i)
199
w(" goto error;\n")
200
w(" }\n")
201
self.uses_error_handler = True
202
if chst.cheap_copies():
203
if ch.is_stack():
204
w(" *%s++ = o%d;\n" % (ch.name, i))
205
else:
206
w(" %s[ao%d] = o%d;\n" % (ch.name, i, i))
207
208
w(je(ri(6,
209
"""\
210
}
211
break;
212
""")))
213
214
def func_header(self, cython=False):
215
r"""
216
Generates the function header for the declaration (in the Cython
217
wrapper) or the definition (in the C interpreter) of the interpreter
218
function.
219
220
EXAMPLES::
221
222
sage: from sage_setup.autogen.interpreters.internal import *
223
sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter
224
sage: interp = ElementInterpreter()
225
sage: gen = InterpreterGenerator(interp)
226
sage: print(gen.func_header())
227
PyObject* interp_el(PyObject** args,
228
PyObject** constants,
229
PyObject** stack,
230
PyObject* domain,
231
int* code)
232
sage: print(gen.func_header(cython=True))
233
object interp_el(PyObject** args,
234
PyObject** constants,
235
PyObject** stack,
236
PyObject* domain,
237
int* code)
238
"""
239
s = self._spec
240
ret_ty = 'bint' if cython else 'int'
241
if s.return_type:
242
ret_ty = s.return_type.c_decl_type()
243
if cython:
244
ret_ty = s.return_type.cython_decl_type()
245
return je(ri(0, """\
246
{{ ret_ty }} interp_{{ s.name }}(
247
{%- for ch in s.chunks %}
248
{% if not loop.first %},
249
{% endif %}{{ ch.declare_parameter() }}
250
{%- endfor %})"""), ret_ty=ret_ty, s=s)
251
252
def write_interpreter(self, write):
253
r"""
254
Generate the code for the C interpreter.
255
256
This function calls its write parameter successively with
257
strings; when these strings are concatenated, the result is
258
the code for the interpreter.
259
260
See the documentation for the get_interpreter method for more
261
information.
262
263
EXAMPLES::
264
265
sage: from sage_setup.autogen.interpreters.internal import *
266
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
267
sage: interp = RDFInterpreter()
268
sage: gen = InterpreterGenerator(interp)
269
sage: from io import StringIO
270
sage: buff = StringIO()
271
sage: gen.write_interpreter(buff.write)
272
sage: print(buff.getvalue())
273
/* Automatically generated by ...
274
"""
275
s = self._spec
276
w = write
277
w(je(ri(0, """
278
/* {{ warn }} */
279
#include <Python.h>
280
281
{% print(s.c_header) %}
282
283
{{ myself.func_header() }} {
284
while (1) {
285
switch (*code++) {
286
"""), s=s, myself=self, i=indent_lines, warn=AUTOGEN_WARN))
287
288
for instr_desc in s.instr_descs:
289
self.gen_code(instr_desc, w)
290
w(je(ri(0, """
291
}
292
}
293
{% if myself.uses_error_handler %}
294
error:
295
return {{ s.err_return }};
296
{% endif %}
297
}
298
299
"""), s=s, i=indent_lines, myself=self))
300
301
def write_wrapper(self, write):
302
r"""
303
Generate the code for the Cython wrapper.
304
This function calls its write parameter successively with
305
strings; when these strings are concatenated, the result is
306
the code for the wrapper.
307
308
See the documentation for the get_wrapper method for more
309
information.
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: interp = RDFInterpreter()
316
sage: gen = InterpreterGenerator(interp)
317
sage: from io import StringIO
318
sage: buff = StringIO()
319
sage: gen.write_wrapper(buff.write)
320
sage: print(buff.getvalue())
321
# Automatically generated by ...
322
"""
323
s = self._spec
324
w = write
325
types = set()
326
do_cleanup = False
327
for ch in s.chunks:
328
if ch.storage_type is not None:
329
types.add(ch.storage_type)
330
do_cleanup = do_cleanup or ch.needs_cleanup_on_error()
331
for ch in s.chunks:
332
if ch.name == 'args':
333
arg_ch = ch
334
335
the_call = je(ri(0, """
336
{% if s.return_type %}return {% endif -%}
337
{% if s.adjust_retval %}{{ s.adjust_retval }}({% endif %}
338
interp_{{ s.name }}({{ arg_ch.pass_argument() }}
339
{% for ch in s.chunks[1:] %}
340
, {{ ch.pass_argument() }}
341
{% endfor %}
342
){% if s.adjust_retval %}){% endif %}
343
344
"""), s=s, arg_ch=arg_ch)
345
346
the_call_c = je(ri(0, """
347
{% if s.return_type %}result[0] = {% endif %}
348
interp_{{ s.name }}(args
349
{% for ch in s.chunks[1:] %}
350
, {{ ch.pass_call_c_argument() }}
351
{% endfor %}
352
)
353
354
"""), s=s, arg_ch=arg_ch)
355
356
w(je(ri(0, """
357
# {{ warn }}
358
{{ s.pyx_header }}
359
360
from cpython.ref cimport PyObject
361
cdef extern from "Python.h":
362
void Py_DECREF(PyObject *o)
363
void Py_INCREF(PyObject *o)
364
void Py_CLEAR(PyObject *o)
365
366
object PyList_New(Py_ssize_t len)
367
ctypedef struct PyListObject:
368
PyObject **ob_item
369
370
ctypedef struct PyTupleObject:
371
PyObject **ob_item
372
373
from cysignals.memory cimport check_allocarray, sig_free
374
375
from sage.ext.fast_callable cimport Wrapper
376
377
cdef extern from "interp_{{ s.name }}.c":
378
{{ myself.func_header(cython=true) -}}
379
380
{% if s.err_return != 'NULL' %}
381
except? {{ s.err_return }}
382
{% endif %}
383
384
cdef class Wrapper_{{ s.name }}(Wrapper):
385
# attributes are declared in corresponding .pxd file
386
387
def __init__(self, args):
388
Wrapper.__init__(self, args, metadata)
389
cdef int i
390
cdef int count
391
{% for ty in types %}
392
{% print(indent_lines(8, ty.local_declarations)) %}
393
{% print(indent_lines(8, ty.class_member_initializations)) %}
394
{% endfor %}
395
{% for ch in s.chunks %}
396
{% print(ch.init_class_members()) %}
397
{% endfor %}
398
{% print(indent_lines(8, s.extra_members_initialize)) %}
399
400
def __dealloc__(self):
401
cdef int i
402
{% for ch in s.chunks %}
403
{% print(ch.dealloc_class_members()) %}
404
{% endfor %}
405
406
def __call__(self, *args):
407
if self._n_args != len(args): raise ValueError
408
{% for ty in types %}
409
{% print(indent_lines(8, ty.local_declarations)) %}
410
{% endfor %}
411
{% print(indent_lines(8, arg_ch.setup_args())) %}
412
{% for ch in s.chunks %}
413
{% print(ch.declare_call_locals()) %}
414
{% endfor %}
415
{% if do_cleanup %}
416
try:
417
{% print(indent_lines(4, the_call)) %}
418
except BaseException:
419
{% for ch in s.chunks %}
420
{% if ch.needs_cleanup_on_error() %}
421
{% print(indent_lines(12, ch.handle_cleanup())) %}
422
{% endif %}
423
{% endfor %}
424
raise
425
{% else %}
426
{% print(the_call) %}
427
{% endif %}
428
{% if not s.return_type %}
429
return retval
430
{% endif %}
431
432
{% if s.implement_call_c %}
433
cdef bint call_c(self,
434
{{ arg_ch.storage_type.c_ptr_type() }} args,
435
{{ arg_ch.storage_type.c_reference_type() }} result) except 0:
436
{% if do_cleanup %}
437
try:
438
{% print(indent_lines(4, the_call_c)) %}
439
except BaseException:
440
{% for ch in s.chunks %}
441
{% if ch.needs_cleanup_on_error() %}
442
{% print(indent_lines(12, ch.handle_cleanup())) %}
443
{% endif %}
444
{% endfor %}
445
raise
446
{% else %}
447
{% print(the_call_c) %}
448
{% endif %}
449
return 1
450
{% endif %}
451
452
from sage.ext.fast_callable import CompilerInstrSpec, InterpreterMetadata
453
metadata = InterpreterMetadata(by_opname={
454
{% for instr in s.instr_descs %}
455
'{{ instr.name }}':
456
(CompilerInstrSpec({{ instr.n_inputs }}, {{ instr.n_outputs }}, {{ instr.parameters }}), {{ instr.opcode }}),
457
{% endfor %}
458
},
459
by_opcode=[
460
{% for instr in s.instr_descs %}
461
('{{ instr.name }}',
462
CompilerInstrSpec({{ instr.n_inputs }}, {{ instr.n_outputs }}, {{ instr.parameters }})),
463
{% endfor %}
464
],
465
ipow_range={{ s.ipow_range }})
466
"""), s=s, myself=self, types=types, arg_ch=arg_ch,
467
indent_lines=indent_lines, the_call=the_call,
468
the_call_c=the_call_c, do_cleanup=do_cleanup,
469
warn=AUTOGEN_WARN))
470
471
def write_pxd(self, write):
472
r"""
473
Generate the pxd file for the Cython wrapper.
474
475
This function calls its write parameter successively with
476
strings; when these strings are concatenated, the result is
477
the code for the pxd file.
478
479
See the documentation for the get_pxd method for more
480
information.
481
482
EXAMPLES::
483
484
sage: from sage_setup.autogen.interpreters.internal import *
485
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
486
sage: interp = RDFInterpreter()
487
sage: gen = InterpreterGenerator(interp)
488
sage: from io import StringIO
489
sage: buff = StringIO()
490
sage: gen.write_pxd(buff.write)
491
sage: print(buff.getvalue())
492
# Automatically generated by ...
493
"""
494
s = self._spec
495
w = write
496
types = set()
497
for ch in s.chunks:
498
if ch.storage_type is not None:
499
types.add(ch.storage_type)
500
for ch in s.chunks:
501
if ch.name == 'args':
502
arg_ch = ch
503
504
w(je(ri(0, """
505
# {{ warn }}
506
507
from cpython.ref cimport PyObject
508
509
from sage.ext.fast_callable cimport Wrapper
510
{% print(s.pxd_header) %}
511
512
cdef class Wrapper_{{ s.name }}(Wrapper):
513
{% for ty in types %}
514
{% print(indent_lines(4, ty.class_member_declarations)) %}
515
{% endfor %}
516
{% for ch in s.chunks %}
517
{% print(ch.declare_class_members()) %}
518
{% endfor %}
519
{% print(indent_lines(4, s.extra_class_members)) %}
520
{% if s.implement_call_c %}
521
cdef bint call_c(self,
522
{{ arg_ch.storage_type.c_ptr_type() }} args,
523
{{ arg_ch.storage_type.c_reference_type() }} result) except 0
524
{% endif %}
525
"""), s=s, myself=self, types=types, indent_lines=indent_lines,
526
arg_ch=arg_ch, warn=AUTOGEN_WARN))
527
528
def get_interpreter(self):
529
r"""
530
Return the code for the C interpreter.
531
532
EXAMPLES:
533
534
First we get the InterpreterSpec for several interpreters::
535
536
sage: from sage_setup.autogen.interpreters.internal import *
537
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
538
sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter
539
sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter
540
sage: rdf_spec = RDFInterpreter()
541
sage: rr_spec = RRInterpreter()
542
sage: el_spec = ElementInterpreter()
543
544
Then we get the actual interpreter code::
545
546
sage: rdf_interp = InterpreterGenerator(rdf_spec).get_interpreter()
547
sage: rr_interp = InterpreterGenerator(rr_spec).get_interpreter()
548
sage: el_interp = InterpreterGenerator(el_spec).get_interpreter()
549
550
Now we can look through these interpreters.
551
552
Each interpreter starts with a file header; this can be
553
customized on a per-interpreter basis::
554
555
sage: print(rr_interp)
556
/* Automatically generated by ... */
557
...
558
559
Next is the function header, with one argument per memory chunk
560
in the interpreter spec::
561
562
sage: print(el_interp)
563
/* ... */ ...
564
PyObject* interp_el(PyObject** args,
565
PyObject** constants,
566
PyObject** stack,
567
PyObject* domain,
568
int* code) {
569
...
570
571
Currently, the interpreters have a very simple structure; just
572
grab the next instruction and execute it, in a switch
573
statement::
574
575
sage: print(rdf_interp)
576
/* ... */ ...
577
while (1) {
578
switch (*code++) {
579
...
580
581
Then comes the code for each instruction. Here is one of the
582
simplest instructions::
583
584
sage: print(rdf_interp)
585
/* ... */ ...
586
case ...: /* neg */
587
{
588
double i0 = *--stack;
589
double o0;
590
o0 = -i0;
591
*stack++ = o0;
592
}
593
break;
594
...
595
596
We simply pull the top of the stack into a variable, negate it,
597
and write the result back onto the stack.
598
599
Let's look at the MPFR-based version of this instruction.
600
This is an example of an interpreter with an auto-reference
601
type::
602
603
sage: print(rr_interp)
604
/* ... */ ...
605
case ...: /* neg */
606
{
607
mpfr_ptr i0 = *--stack;
608
mpfr_ptr o0 = *stack++;
609
mpfr_neg(o0, i0, MPFR_RNDN);
610
}
611
break;
612
...
613
614
Here we see that the input and output variables are actually
615
just pointers into the stack. But due to the auto-reference
616
trick, the actual code snippet, ``mpfr_neg(o0, i0, MPFR_RNDN);``,
617
is exactly the same as if i0 and o0 were declared as local
618
mpfr_t variables.
619
620
For completeness, let's look at this instruction in the
621
Python-object element interpreter::
622
623
sage: print(el_interp)
624
/* ... */ ...
625
case ...: /* neg */
626
{
627
PyObject* i0 = *--stack;
628
*stack = NULL;
629
PyObject* o0;
630
o0 = PyNumber_Negative(i0);
631
Py_DECREF(i0);
632
if (!CHECK(o0)) {
633
Py_XDECREF(o0);
634
goto error;
635
}
636
*stack++ = o0;
637
}
638
break;
639
...
640
641
The original code snippet was only ``o0 = PyNumber_Negative(i0);``;
642
all the rest is automatically generated. For ElementInterpreter,
643
the CHECK macro actually checks for an exception (makes sure that
644
o0 is not NULL), tests if the o0 is an element with the correct
645
parent, and if not converts it into the correct parent. (That is,
646
it can potentially modify the variable o0.)
647
"""
648
649
buff = StringIO()
650
self.write_interpreter(buff.write)
651
return buff.getvalue()
652
653
def get_wrapper(self):
654
r"""
655
Return the code for the Cython wrapper.
656
657
EXAMPLES:
658
659
First we get the InterpreterSpec for several interpreters::
660
661
sage: from sage_setup.autogen.interpreters.internal import *
662
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
663
sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter
664
sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter
665
sage: rdf_spec = RDFInterpreter()
666
sage: rr_spec = RRInterpreter()
667
sage: el_spec = ElementInterpreter()
668
669
Then we get the actual wrapper code::
670
671
sage: rdf_wrapper = InterpreterGenerator(rdf_spec).get_wrapper()
672
sage: rr_wrapper = InterpreterGenerator(rr_spec).get_wrapper()
673
sage: el_wrapper = InterpreterGenerator(el_spec).get_wrapper()
674
675
Now we can look through these wrappers.
676
677
Each wrapper starts with a file header; this can be
678
customized on a per-interpreter basis (some blank lines have been
679
elided below)::
680
681
sage: print(rdf_wrapper)
682
# Automatically generated by ...
683
from cpython.ref cimport PyObject
684
cdef extern from "Python.h":
685
void Py_DECREF(PyObject *o)
686
void Py_INCREF(PyObject *o)
687
void Py_CLEAR(PyObject *o)
688
<BLANKLINE>
689
object PyList_New(Py_ssize_t len)
690
ctypedef struct PyListObject:
691
PyObject **ob_item
692
<BLANKLINE>
693
ctypedef struct PyTupleObject:
694
PyObject **ob_item
695
<BLANKLINE>
696
from cysignals.memory cimport check_allocarray, sig_free
697
<BLANKLINE>
698
from sage.ext.fast_callable cimport Wrapper
699
...
700
701
We need a way to propagate exceptions back to the wrapper,
702
even though we only return a double from interp_rdf. The
703
``except? -1094648009105371`` (that's a randomly chosen
704
number) means that we will return that number if there's an
705
exception, but the wrapper still has to check whether that's a
706
legitimate return or an exception. (Cython does this
707
automatically.)
708
709
Next comes the actual wrapper class. The member declarations
710
are in the corresponding pxd file; see the documentation for
711
get_pxd to see them::
712
713
sage: print(rdf_wrapper)
714
# ...
715
cdef class Wrapper_rdf(Wrapper):
716
# attributes are declared in corresponding .pxd file
717
...
718
719
Next is the __init__ method, which starts like this::
720
721
sage: print(rdf_wrapper)
722
# ...
723
def __init__(self, args):
724
Wrapper.__init__(self, args, metadata)
725
cdef int i
726
cdef int count
727
...
728
729
To make it possible to generate code for all expression
730
interpreters with a single code generator, all wrappers
731
have the same API. The __init__ method takes a single
732
argument (here called *args*), which is a dictionary holding
733
all the information needed to initialize this wrapper.
734
735
We call Wrapper.__init__, which saves a copy of this arguments
736
object and of the interpreter metadata in the wrapper. (This is
737
only used for debugging.)
738
739
Now we allocate memory for each memory chunk. (We allocate
740
the memory here, and reuse it on each call of the
741
wrapper/interpreter. This is for speed reasons; in a fast
742
interpreter like RDFInterpreter, there are no memory allocations
743
involved in a call of the wrapper, except for the ones that
744
are required by the Python calling convention. Eventually
745
we will support alternate Cython-only entry points that do
746
absolutely no memory allocation.)
747
748
Basically the same code is repeated, with minor variations, for
749
each memory chunk; for brevity, we'll only show the code
750
for 'constants'::
751
752
sage: print(rdf_wrapper)
753
# ...
754
val = args['constants']
755
self._n_constants = len(val)
756
self._constants = <double*>check_allocarray(self._n_constants, sizeof(double))
757
for i in range(len(val)):
758
self._constants[i] = val[i]
759
...
760
761
Recall that _n_constants is an int, and _constants is a
762
double*.
763
764
The RRInterpreter version is more complicated, because it has to
765
call mpfr_init::
766
767
sage: print(rr_wrapper)
768
# ...
769
cdef RealNumber rn
770
...
771
val = args['constants']
772
self._n_constants = len(val)
773
self._constants = <mpfr_t*>check_allocarray(self._n_constants, sizeof(mpfr_t))
774
for i in range(len(val)):
775
mpfr_init2(self._constants[i], self.domain.prec())
776
for i in range(len(val)):
777
rn = self.domain(val[i])
778
mpfr_set(self._constants[i], rn.value, MPFR_RNDN)
779
...
780
781
And as described in the documentation for get_pxd, in
782
Python-object based interpreters we actually allocate the
783
memory as a Python list::
784
785
sage: print(el_wrapper)
786
# ...
787
val = args['constants']
788
self._n_constants = len(val)
789
self._list_constants = PyList_New(self._n_constants)
790
self._constants = (<PyListObject *>self._list_constants).ob_item
791
for i in range(len(val)):
792
self._constants[i] = <PyObject *>val[i]; Py_INCREF(self._constants[i])
793
...
794
795
Of course, once we've allocated the memory, we eventually have
796
to free it. (Again, we'll only look at 'constants'.)::
797
798
sage: print(rdf_wrapper)
799
# ...
800
def __dealloc__(self):
801
...
802
if self._constants:
803
sig_free(self._constants)
804
...
805
806
The RRInterpreter code is more complicated again because it has
807
to call mpfr_clear::
808
809
sage: print(rr_wrapper)
810
# ...
811
def __dealloc__(self):
812
cdef int i
813
...
814
if self._constants:
815
for i in range(self._n_constants):
816
mpfr_clear(self._constants[i])
817
sig_free(self._constants)
818
...
819
820
But the ElementInterpreter code is extremely simple --
821
it doesn't have to do anything to deallocate constants!
822
(Since the memory for constants is actually allocated as a
823
Python list, and Cython knows how to deallocate Python lists.)
824
825
Finally we get to the __call__ method. We grab the arguments
826
passed by the caller, stuff them in our pre-allocated
827
argument array, and then call the C interpreter.
828
829
We optionally adjust the return value of the interpreter
830
(currently only the RDF/float interpreter performs this step;
831
this is the only place where domain=RDF differs than
832
domain=float)::
833
834
sage: print(rdf_wrapper)
835
# ...
836
def __call__(self, *args):
837
if self._n_args != len(args): raise ValueError
838
cdef double* c_args = self._args
839
cdef int i
840
for i from 0 <= i < len(args):
841
self._args[i] = args[i]
842
return self._domain(interp_rdf(c_args
843
, self._constants
844
, self._py_constants
845
, self._stack
846
, self._code
847
))
848
...
849
850
In Python-object based interpreters, the call to the C
851
interpreter has to be a little more complicated. We don't
852
want to hold on to Python objects from an old computation by
853
leaving them referenced from the stack. In normal operation,
854
the C interpreter clears out the stack as it runs, leaving the
855
stack totally clear when the interpreter finishes. However,
856
this doesn't happen if the C interpreter raises an exception.
857
In that case, we have to clear out any remnants from the stack
858
in the wrapper::
859
860
sage: print(el_wrapper)
861
# ...
862
try:
863
return interp_el((<PyListObject*>mapped_args).ob_item
864
, self._constants
865
, self._stack
866
, <PyObject*>self._domain
867
, self._code
868
)
869
except BaseException:
870
for i in range(self._n_stack):
871
Py_CLEAR(self._stack[i])
872
raise
873
...
874
875
Finally, we define a cdef call_c method, for quickly calling
876
this object from Cython. (The method is omitted from
877
Python-object based interpreters.)::
878
879
sage: print(rdf_wrapper)
880
# ...
881
cdef bint call_c(self,
882
double* args,
883
double* result) except 0:
884
result[0] = interp_rdf(args
885
, self._constants
886
, self._py_constants
887
, self._stack
888
, self._code
889
)
890
return 1
891
...
892
893
The method for the RR interpreter is slightly different, because
894
the interpreter takes a pointer to a result location instead of
895
returning the value::
896
897
sage: print(rr_wrapper)
898
# ...
899
cdef bint call_c(self,
900
mpfr_t* args,
901
mpfr_t result) except 0:
902
interp_rr(args
903
, result
904
, self._constants
905
, self._py_constants
906
, self._stack
907
, self._code
908
, <PyObject*>self._domain
909
)
910
return 1
911
...
912
913
That's it for the wrapper class. The only thing remaining is
914
the interpreter metadata. This is the information necessary
915
for the code generator to map instruction names to opcodes; it
916
also gives information about stack usage, etc. This is fully
917
documented at InterpreterMetadata; for now, we'll just show
918
what it looks like.
919
920
Currently, there are three parts to the metadata; the first maps
921
instruction names to instruction descriptions. The second one
922
maps opcodes to instruction descriptions. Note that we don't
923
use InstrSpec objects here; instead, we use CompilerInstrSpec
924
objects, which are much simpler and contain only the information
925
we'll need at runtime. The third part says what range the
926
ipow instruction is defined over.
927
928
First the part that maps instruction names to
929
(CompilerInstrSpec, opcode) pairs::
930
931
sage: print(rdf_wrapper)
932
# ...
933
from sage.ext.fast_callable import CompilerInstrSpec, InterpreterMetadata
934
metadata = InterpreterMetadata(by_opname={
935
...
936
'return':
937
(CompilerInstrSpec(1, 0, []), 2),
938
'py_call':
939
(CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']), 3),
940
'pow':
941
(CompilerInstrSpec(2, 1, []), 4),
942
'add':
943
(CompilerInstrSpec(2, 1, []), 5),
944
...
945
}, ...)
946
947
There's also a table that maps opcodes to (instruction name,
948
CompilerInstrSpec) pairs::
949
950
sage: print(rdf_wrapper)
951
# ...
952
metadata = InterpreterMetadata(..., by_opcode=[
953
...
954
('return',
955
CompilerInstrSpec(1, 0, [])),
956
('py_call',
957
CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])),
958
('pow',
959
CompilerInstrSpec(2, 1, [])),
960
('add',
961
CompilerInstrSpec(2, 1, [])),
962
...
963
], ...)
964
965
And then the ipow range::
966
967
sage: print(rdf_wrapper)
968
# ...
969
metadata = InterpreterMetadata(...,
970
ipow_range=(-2147483648, 2147483647))
971
972
And that's it for the wrapper.
973
"""
974
975
buff = StringIO()
976
self.write_wrapper(buff.write)
977
return buff.getvalue()
978
979
def get_pxd(self):
980
r"""
981
Return the code for the Cython .pxd file.
982
983
EXAMPLES:
984
985
First we get the InterpreterSpec for several interpreters::
986
987
sage: from sage_setup.autogen.interpreters.internal import *
988
sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter
989
sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter
990
sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter
991
sage: rdf_spec = RDFInterpreter()
992
sage: rr_spec = RRInterpreter()
993
sage: el_spec = ElementInterpreter()
994
995
Then we get the corresponding .pxd::
996
997
sage: rdf_pxd = InterpreterGenerator(rdf_spec).get_pxd()
998
sage: rr_pxd = InterpreterGenerator(rr_spec).get_pxd()
999
sage: el_pxd = InterpreterGenerator(el_spec).get_pxd()
1000
1001
Now we can look through these pxd files.
1002
1003
Each .pxd starts with a file header; this can be
1004
customized on a per-interpreter basis (some blank lines have been
1005
elided below)::
1006
1007
sage: print(rdf_pxd)
1008
# Automatically generated by ...
1009
from cpython.ref cimport PyObject
1010
from sage.ext.fast_callable cimport Wrapper
1011
...
1012
sage: print(rr_pxd)
1013
# ...
1014
from sage.rings.real_mpfr cimport RealField_class, RealNumber
1015
from sage.libs.mpfr cimport *
1016
...
1017
1018
Next and last is the declaration of the wrapper class, which
1019
starts off with a list of member declarations::
1020
1021
sage: print(rdf_pxd)
1022
# ...
1023
cdef class Wrapper_rdf(Wrapper):
1024
cdef int _n_args
1025
cdef double* _args
1026
cdef int _n_constants
1027
cdef double* _constants
1028
cdef object _list_py_constants
1029
cdef int _n_py_constants
1030
cdef PyObject** _py_constants
1031
cdef int _n_stack
1032
cdef double* _stack
1033
cdef int _n_code
1034
cdef int* _code
1035
...
1036
1037
Contrast the declaration of ``_stack`` here with the
1038
ElementInterpreter version. To simplify our handling of
1039
reference counting and garbage collection, in a Python-object
1040
based interpreter, we allocate arrays as Python lists,
1041
and then pull the array out of the innards of the list::
1042
1043
sage: print(el_pxd)
1044
# ...
1045
cdef object _list_stack
1046
cdef int _n_stack
1047
cdef PyObject** _stack
1048
...
1049
1050
Then, at the end of the wrapper class, we declare a cdef method
1051
for quickly calling the wrapper object from Cython. (This method
1052
is omitted from Python-object based interpreters.)::
1053
1054
sage: print(rdf_pxd)
1055
# ...
1056
cdef bint call_c(self,
1057
double* args,
1058
double* result) except 0
1059
sage: print(rr_pxd)
1060
# ...
1061
cdef bint call_c(self,
1062
mpfr_t* args,
1063
mpfr_t result) except 0
1064
1065
"""
1066
1067
buff = StringIO()
1068
self.write_pxd(buff.write)
1069
return buff.getvalue()
1070
1071