Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/memory.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
"""General purpose MemoryChunk types and related utilities"""
13
14
from __future__ import print_function, absolute_import
15
16
from .utils import je, reindent_lines as ri
17
18
19
def string_of_addr(a):
20
r"""
21
An address or a length from a parameter specification may be
22
either None, an integer, or a MemoryChunk. If the address or
23
length is an integer or a MemoryChunk, this function will convert
24
it to a string giving an expression that will evaluate to the correct
25
address or length. (See the docstring for params_gen for more
26
information on parameter specifications.)
27
28
EXAMPLES::
29
30
sage: from sage_setup.autogen.interpreters.internal import *
31
sage: mc_code = MemoryChunkConstants('code', ty_int)
32
sage: string_of_addr(mc_code)
33
'*code++'
34
sage: string_of_addr(42r)
35
'42'
36
"""
37
if isinstance(a, int):
38
return str(a)
39
assert isinstance(a, MemoryChunk)
40
return '*%s++' % a.name
41
42
43
class MemoryChunk(object):
44
r"""
45
Memory chunks control allocation, deallocation, initialization,
46
etc. of the vectors and objects in the interpreter. Basically,
47
there is one memory chunk per argument to the C interpreter.
48
49
There are three "generic" varieties of memory chunk: "constants",
50
"arguments", and "scratch". These are named after their most
51
common use, but they could be used for other things in some
52
interpreters.
53
54
All three kinds of chunks are allocated in the wrapper class.
55
Constants are initialized when the wrapper is constructed;
56
arguments are initialized in the __call__ method, from the
57
caller's arguments. "scratch" chunks are not initialized at all;
58
they are used for scratch storage (often, but not necessarily, for
59
a stack) in the interpreter.
60
61
Interpreters which need memory chunks that don't fit into these
62
categories can create new subclasses of MemoryChunk.
63
"""
64
65
def __init__(self, name, storage_type):
66
r"""
67
Initialize an instance of MemoryChunk.
68
69
This sets the properties "name" (the name of this memory chunk;
70
used in generated variable names, etc.) and "storage_type",
71
which is a StorageType object.
72
73
EXAMPLES::
74
75
sage: from sage_setup.autogen.interpreters.internal import *
76
sage: mc = MemoryChunkArguments('args', ty_mpfr)
77
sage: mc.name
78
'args'
79
sage: mc.storage_type is ty_mpfr
80
True
81
"""
82
self.name = name
83
self.storage_type = storage_type
84
85
def __repr__(self):
86
r"""
87
Give a string representation of this memory chunk.
88
89
EXAMPLES::
90
91
sage: from sage_setup.autogen.interpreters.internal import *
92
sage: mc = MemoryChunkArguments('args', ty_mpfr)
93
sage: mc
94
{MC:args}
95
sage: mc.__repr__()
96
'{MC:args}'
97
"""
98
return '{MC:%s}' % self.name
99
100
def declare_class_members(self):
101
r"""
102
Return a string giving the declarations of the class members
103
in a wrapper class for this memory chunk.
104
105
EXAMPLES::
106
107
sage: from sage_setup.autogen.interpreters.internal import *
108
sage: mc = MemoryChunkArguments('args', ty_mpfr)
109
sage: mc.declare_class_members()
110
' cdef int _n_args\n cdef mpfr_t* _args\n'
111
"""
112
return self.storage_type.declare_chunk_class_members(self.name)
113
114
def init_class_members(self):
115
r"""
116
Return a string to be put in the __init__ method of a wrapper
117
class using this memory chunk, to initialize the corresponding
118
class members.
119
120
EXAMPLES::
121
122
sage: from sage_setup.autogen.interpreters.internal import *
123
sage: mc = MemoryChunkArguments('args', ty_mpfr)
124
sage: print(mc.init_class_members())
125
count = args['args']
126
self._n_args = count
127
self._args = <mpfr_t*>check_allocarray(self._n_args, sizeof(mpfr_t))
128
for i in range(count):
129
mpfr_init2(self._args[i], self.domain.prec())
130
<BLANKLINE>
131
"""
132
return ""
133
134
def dealloc_class_members(self):
135
r"""
136
Return a string to be put in the __dealloc__ method of a wrapper
137
class using this memory chunk, to deallocate the corresponding
138
class members.
139
140
EXAMPLES::
141
142
sage: from sage_setup.autogen.interpreters.internal import *
143
sage: mc = MemoryChunkArguments('args', ty_mpfr)
144
sage: print(mc.dealloc_class_members())
145
if self._args:
146
for i in range(self._n_args):
147
mpfr_clear(self._args[i])
148
sig_free(self._args)
149
<BLANKLINE>
150
"""
151
return ""
152
153
def declare_parameter(self):
154
r"""
155
Return the string to use to declare the interpreter parameter
156
corresponding to this memory chunk.
157
158
EXAMPLES::
159
160
sage: from sage_setup.autogen.interpreters.internal import *
161
sage: mc = MemoryChunkArguments('args', ty_mpfr)
162
sage: mc.declare_parameter()
163
'mpfr_t* args'
164
"""
165
return '%s %s' % (self.storage_type.c_ptr_type(), self.name)
166
167
def declare_call_locals(self):
168
r"""
169
Return a string to put in the __call__ method of a wrapper
170
class using this memory chunk, to allocate local variables.
171
172
EXAMPLES::
173
174
sage: from sage_setup.autogen.interpreters.internal import *
175
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
176
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
177
sage: mc.declare_call_locals()
178
' cdef RealNumber retval = (self.domain)()\n'
179
"""
180
return ""
181
182
def pass_argument(self):
183
r"""
184
Return the string to pass the argument corresponding to this
185
memory chunk to the interpreter.
186
187
EXAMPLES::
188
189
sage: from sage_setup.autogen.interpreters.internal import *
190
sage: mc = MemoryChunkConstants('constants', ty_mpfr)
191
sage: mc.pass_argument()
192
'self._constants'
193
"""
194
raise NotImplementedError
195
196
def pass_call_c_argument(self):
197
r"""
198
Return the string to pass the argument corresponding to this
199
memory chunk to the interpreter, for use in the call_c method.
200
Almost always the same as pass_argument.
201
202
EXAMPLES::
203
204
sage: from sage_setup.autogen.interpreters.internal import *
205
sage: mc = MemoryChunkConstants('constants', ty_mpfr)
206
sage: mc.pass_call_c_argument()
207
'self._constants'
208
"""
209
return self.pass_argument()
210
211
def needs_cleanup_on_error(self):
212
r"""
213
In an interpreter that can terminate prematurely (due to an
214
exception from calling Python code, or divide by zero, or
215
whatever) it will just return at the end of the current instruction,
216
skipping the rest of the program. Thus, it may still have
217
values pushed on the stack, etc.
218
219
This method returns True if this memory chunk is modified by the
220
interpreter and needs some sort of cleanup when an error happens.
221
222
EXAMPLES::
223
224
sage: from sage_setup.autogen.interpreters.internal import *
225
sage: mc = MemoryChunkConstants('constants', ty_mpfr)
226
sage: mc.needs_cleanup_on_error()
227
False
228
"""
229
return False
230
231
def is_stack(self):
232
r"""
233
Says whether this memory chunk is a stack. This affects code
234
generation for instructions using this memory chunk.
235
236
It would be nicer to make this object-oriented somehow, so
237
that the code generator called MemoryChunk methods instead of
238
using::
239
240
if ch.is_stack():
241
... hardcoded stack code
242
else:
243
... hardcoded non-stack code
244
245
but that hasn't been done yet.
246
247
EXAMPLES::
248
249
sage: from sage_setup.autogen.interpreters.internal import *
250
sage: mc = MemoryChunkScratch('scratch', ty_mpfr)
251
sage: mc.is_stack()
252
False
253
sage: mc = MemoryChunkScratch('stack', ty_mpfr, is_stack=True)
254
sage: mc.is_stack()
255
True
256
"""
257
return False
258
259
def is_python_refcounted_stack(self):
260
r"""
261
Says whether this memory chunk refers to a stack where the entries
262
need to be INCREF/DECREF'ed.
263
264
It would be nice to make this object-oriented, so that the
265
code generator called MemoryChunk methods to do the potential
266
INCREF/DECREF and didn't have to explicitly test
267
is_python_refcounted_stack.
268
269
EXAMPLES::
270
271
sage: from sage_setup.autogen.interpreters.internal import *
272
sage: mc = MemoryChunkScratch('args', ty_python)
273
sage: mc.is_python_refcounted_stack()
274
False
275
sage: mc = MemoryChunkScratch('args', ty_python, is_stack=True)
276
sage: mc.is_python_refcounted_stack()
277
True
278
sage: mc = MemoryChunkScratch('args', ty_mpfr, is_stack=True)
279
sage: mc.is_python_refcounted_stack()
280
False
281
"""
282
return self.is_stack() and self.storage_type.python_refcounted()
283
284
285
class MemoryChunkLonglivedArray(MemoryChunk):
286
r"""
287
MemoryChunkLonglivedArray is a subtype of MemoryChunk that deals
288
with memory chunks that are both 1) allocated as class members (rather
289
than being allocated in __call__) and 2) are arrays.
290
"""
291
292
def init_class_members(self):
293
r"""
294
Return a string to be put in the __init__ method of a wrapper
295
class using this memory chunk, to initialize the corresponding
296
class members.
297
298
EXAMPLES::
299
300
sage: from sage_setup.autogen.interpreters.internal import *
301
sage: mc = MemoryChunkArguments('args', ty_double)
302
sage: print(mc.init_class_members())
303
count = args['args']
304
self._n_args = count
305
self._args = <double*>check_allocarray(self._n_args, sizeof(double))
306
<BLANKLINE>
307
"""
308
return je(ri(0, """
309
count = args['{{ myself.name }}']
310
{% print(myself.storage_type.alloc_chunk_data(myself.name, 'count')) %}
311
"""), myself=self)
312
313
def dealloc_class_members(self):
314
r"""
315
Return a string to be put in the __dealloc__ method of a wrapper
316
class using this memory chunk, to deallocate the corresponding
317
class members.
318
319
EXAMPLES::
320
321
sage: from sage_setup.autogen.interpreters.internal import *
322
sage: mc = MemoryChunkArguments('args', ty_mpfr)
323
sage: print(mc.dealloc_class_members())
324
if self._args:
325
for i in range(self._n_args):
326
mpfr_clear(self._args[i])
327
sig_free(self._args)
328
<BLANKLINE>
329
"""
330
return self.storage_type.dealloc_chunk_data(self.name)
331
332
def pass_argument(self):
333
r"""
334
Return the string to pass the argument corresponding to this
335
memory chunk to the interpreter.
336
337
EXAMPLES::
338
339
sage: from sage_setup.autogen.interpreters.internal import *
340
sage: mc = MemoryChunkConstants('constants', ty_mpfr)
341
sage: mc.pass_argument()
342
'self._constants'
343
"""
344
return 'self._%s' % self.name
345
346
347
class MemoryChunkConstants(MemoryChunkLonglivedArray):
348
r"""
349
MemoryChunkConstants is a subtype of MemoryChunkLonglivedArray.
350
351
MemoryChunkConstants chunks have their contents set in the
352
wrapper's __init__ method (and not changed afterward).
353
"""
354
355
def init_class_members(self):
356
r"""
357
Return a string to be put in the __init__ method of a wrapper
358
class using this memory chunk, to initialize the corresponding
359
class members.
360
361
EXAMPLES::
362
363
sage: from sage_setup.autogen.interpreters.internal import *
364
sage: mc = MemoryChunkConstants('constants', ty_mpfr)
365
sage: print(mc.init_class_members())
366
val = args['constants']
367
self._n_constants = len(val)
368
self._constants = <mpfr_t*>check_allocarray(self._n_constants, sizeof(mpfr_t))
369
for i in range(len(val)):
370
mpfr_init2(self._constants[i], self.domain.prec())
371
for i in range(len(val)):
372
rn = self.domain(val[i])
373
mpfr_set(self._constants[i], rn.value, MPFR_RNDN)
374
<BLANKLINE>
375
"""
376
return je(ri(0, """
377
val = args['{{ myself.name }}']
378
{% print(myself.storage_type.alloc_chunk_data(myself.name, 'len(val)')) %}
379
for i in range(len(val)):
380
{{ myself.storage_type.assign_c_from_py('self._%s[i]' % myself.name, 'val[i]') | i(12) }}
381
"""), myself=self)
382
383
384
class MemoryChunkArguments(MemoryChunkLonglivedArray):
385
r"""
386
MemoryChunkArguments is a subtype of MemoryChunkLonglivedArray,
387
for dealing with arguments to the wrapper's ``__call__`` method.
388
389
Currently the ``__call__`` method is declared to take a varargs
390
`*args` argument tuple. We assume that the MemoryChunk named `args`
391
will deal with that tuple.
392
"""
393
394
def setup_args(self):
395
r"""
396
Handle the arguments of __call__ -- copy them into a pre-allocated
397
array, ready to pass to the interpreter.
398
399
EXAMPLES::
400
401
sage: from sage_setup.autogen.interpreters.internal import *
402
sage: mc = MemoryChunkArguments('args', ty_mpfr)
403
sage: print(mc.setup_args())
404
cdef mpfr_t* c_args = self._args
405
cdef int i
406
for i from 0 <= i < len(args):
407
rn = self.domain(args[i])
408
mpfr_set(self._args[i], rn.value, MPFR_RNDN)
409
<BLANKLINE>
410
"""
411
return je(ri(0, """
412
cdef {{ myself.storage_type.c_ptr_type() }} c_args = self._args
413
cdef int i
414
for i from 0 <= i < len(args):
415
{{ myself.storage_type.assign_c_from_py('self._args[i]', 'args[i]') | i(4) }}
416
"""), myself=self)
417
418
def pass_argument(self):
419
r"""
420
Return the string to pass the argument corresponding to this
421
memory chunk to the interpreter.
422
423
EXAMPLES::
424
425
sage: from sage_setup.autogen.interpreters.internal import *
426
sage: mc = MemoryChunkArguments('args', ty_mpfr)
427
sage: mc.pass_argument()
428
'c_args'
429
"""
430
return 'c_args'
431
432
433
class MemoryChunkScratch(MemoryChunkLonglivedArray):
434
r"""
435
MemoryChunkScratch is a subtype of MemoryChunkLonglivedArray
436
for dealing with memory chunks that are allocated in the wrapper,
437
but only used in the interpreter -- stacks, scratch registers, etc.
438
439
(Currently these are only used as stacks.)
440
"""
441
442
def __init__(self, name, storage_type, is_stack=False):
443
r"""
444
Initialize an instance of MemoryChunkScratch.
445
446
Initializes the _is_stack property, as well as
447
the properties described in the documentation for
448
MemoryChunk.__init__.
449
450
EXAMPLES::
451
452
sage: from sage_setup.autogen.interpreters.internal import *
453
sage: mc = MemoryChunkScratch('stack', ty_double, is_stack=True)
454
sage: mc.name
455
'stack'
456
sage: mc.storage_type is ty_double
457
True
458
sage: mc._is_stack
459
True
460
"""
461
462
super(MemoryChunkScratch, self).__init__(name, storage_type)
463
self._is_stack = is_stack
464
465
def is_stack(self):
466
r"""
467
Says whether this memory chunk is a stack. This affects code
468
generation for instructions using this memory chunk.
469
470
EXAMPLES::
471
472
sage: from sage_setup.autogen.interpreters.internal import *
473
sage: mc = MemoryChunkScratch('stack', ty_mpfr, is_stack=True)
474
sage: mc.is_stack()
475
True
476
"""
477
return self._is_stack
478
479
def needs_cleanup_on_error(self):
480
r"""
481
In an interpreter that can terminate prematurely (due to an
482
exception from calling Python code, or divide by zero, or
483
whatever) it will just return at the end of the current instruction,
484
skipping the rest of the program. Thus, it may still have
485
values pushed on the stack, etc.
486
487
This method returns True if this memory chunk is modified by the
488
interpreter and needs some sort of cleanup when an error happens.
489
490
EXAMPLES::
491
492
sage: from sage_setup.autogen.interpreters.internal import *
493
sage: mc = MemoryChunkScratch('registers', ty_python)
494
sage: mc.needs_cleanup_on_error()
495
True
496
"""
497
return self.storage_type.python_refcounted()
498
499
def handle_cleanup(self):
500
r"""
501
Handle the cleanup if the interpreter exits with an error.
502
503
For scratch/stack chunks that hold Python-refcounted values,
504
we assume that they are filled with NULL on every entry to the
505
interpreter. If the interpreter exited with an error, it may
506
have left values in the chunk, so we need to go through
507
the chunk and Py_CLEAR it.
508
509
EXAMPLES::
510
511
sage: from sage_setup.autogen.interpreters.internal import *
512
sage: mc = MemoryChunkScratch('registers', ty_python)
513
sage: print(mc.handle_cleanup())
514
for i in range(self._n_registers):
515
Py_CLEAR(self._registers[i])
516
<BLANKLINE>
517
"""
518
# XXX This is a lot slower than it needs to be, because
519
# we don't have a "cdef int i" in scope here.
520
return je(ri(0, """
521
for i in range(self._n_{{ myself.name }}):
522
Py_CLEAR(self._{{ myself.name }}[i])
523
"""), myself=self)
524
525