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