Path: blob/develop/src/sage_setup/autogen/interpreters/internal/memory.py
4086 views
#*****************************************************************************1# Copyright (C) 2009 Carl Witty <[email protected]>2# Copyright (C) 2015 Jeroen Demeyer <[email protected]>3#4# This program is free software: you can redistribute it and/or modify5# it under the terms of the GNU General Public License as published by6# the Free Software Foundation, either version 2 of the License, or7# (at your option) any later version.8# http://www.gnu.org/licenses/9#*****************************************************************************1011"""General purpose MemoryChunk types and related utilities"""1213from __future__ import print_function, absolute_import1415from .utils import je, reindent_lines as ri161718def string_of_addr(a):19r"""20An address or a length from a parameter specification may be21either None, an integer, or a MemoryChunk. If the address or22length is an integer or a MemoryChunk, this function will convert23it to a string giving an expression that will evaluate to the correct24address or length. (See the docstring for params_gen for more25information on parameter specifications.)2627EXAMPLES::2829sage: from sage_setup.autogen.interpreters.internal import *30sage: mc_code = MemoryChunkConstants('code', ty_int)31sage: string_of_addr(mc_code)32'*code++'33sage: string_of_addr(42r)34'42'35"""36if isinstance(a, int):37return str(a)38assert isinstance(a, MemoryChunk)39return '*%s++' % a.name404142class MemoryChunk(object):43r"""44Memory chunks control allocation, deallocation, initialization,45etc. of the vectors and objects in the interpreter. Basically,46there is one memory chunk per argument to the C interpreter.4748There are three "generic" varieties of memory chunk: "constants",49"arguments", and "scratch". These are named after their most50common use, but they could be used for other things in some51interpreters.5253All three kinds of chunks are allocated in the wrapper class.54Constants are initialized when the wrapper is constructed;55arguments are initialized in the __call__ method, from the56caller's arguments. "scratch" chunks are not initialized at all;57they are used for scratch storage (often, but not necessarily, for58a stack) in the interpreter.5960Interpreters which need memory chunks that don't fit into these61categories can create new subclasses of MemoryChunk.62"""6364def __init__(self, name, storage_type):65r"""66Initialize an instance of MemoryChunk.6768This sets the properties "name" (the name of this memory chunk;69used in generated variable names, etc.) and "storage_type",70which is a StorageType object.7172EXAMPLES::7374sage: from sage_setup.autogen.interpreters.internal import *75sage: mc = MemoryChunkArguments('args', ty_mpfr)76sage: mc.name77'args'78sage: mc.storage_type is ty_mpfr79True80"""81self.name = name82self.storage_type = storage_type8384def __repr__(self):85r"""86Give a string representation of this memory chunk.8788EXAMPLES::8990sage: from sage_setup.autogen.interpreters.internal import *91sage: mc = MemoryChunkArguments('args', ty_mpfr)92sage: mc93{MC:args}94sage: mc.__repr__()95'{MC:args}'96"""97return '{MC:%s}' % self.name9899def declare_class_members(self):100r"""101Return a string giving the declarations of the class members102in a wrapper class for this memory chunk.103104EXAMPLES::105106sage: from sage_setup.autogen.interpreters.internal import *107sage: mc = MemoryChunkArguments('args', ty_mpfr)108sage: mc.declare_class_members()109' cdef int _n_args\n cdef mpfr_t* _args\n'110"""111return self.storage_type.declare_chunk_class_members(self.name)112113def init_class_members(self):114r"""115Return a string to be put in the __init__ method of a wrapper116class using this memory chunk, to initialize the corresponding117class members.118119EXAMPLES::120121sage: from sage_setup.autogen.interpreters.internal import *122sage: mc = MemoryChunkArguments('args', ty_mpfr)123sage: print(mc.init_class_members())124count = args['args']125self._n_args = count126self._args = <mpfr_t*>check_allocarray(self._n_args, sizeof(mpfr_t))127for i in range(count):128mpfr_init2(self._args[i], self.domain.prec())129<BLANKLINE>130"""131return ""132133def dealloc_class_members(self):134r"""135Return a string to be put in the __dealloc__ method of a wrapper136class using this memory chunk, to deallocate the corresponding137class members.138139EXAMPLES::140141sage: from sage_setup.autogen.interpreters.internal import *142sage: mc = MemoryChunkArguments('args', ty_mpfr)143sage: print(mc.dealloc_class_members())144if self._args:145for i in range(self._n_args):146mpfr_clear(self._args[i])147sig_free(self._args)148<BLANKLINE>149"""150return ""151152def declare_parameter(self):153r"""154Return the string to use to declare the interpreter parameter155corresponding to this memory chunk.156157EXAMPLES::158159sage: from sage_setup.autogen.interpreters.internal import *160sage: mc = MemoryChunkArguments('args', ty_mpfr)161sage: mc.declare_parameter()162'mpfr_t* args'163"""164return '%s %s' % (self.storage_type.c_ptr_type(), self.name)165166def declare_call_locals(self):167r"""168Return a string to put in the __call__ method of a wrapper169class using this memory chunk, to allocate local variables.170171EXAMPLES::172173sage: from sage_setup.autogen.interpreters.internal import *174sage: from sage_setup.autogen.interpreters.internal.specs.rr import *175sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)176sage: mc.declare_call_locals()177' cdef RealNumber retval = (self.domain)()\n'178"""179return ""180181def pass_argument(self):182r"""183Return the string to pass the argument corresponding to this184memory chunk to the interpreter.185186EXAMPLES::187188sage: from sage_setup.autogen.interpreters.internal import *189sage: mc = MemoryChunkConstants('constants', ty_mpfr)190sage: mc.pass_argument()191'self._constants'192"""193raise NotImplementedError194195def pass_call_c_argument(self):196r"""197Return the string to pass the argument corresponding to this198memory chunk to the interpreter, for use in the call_c method.199Almost always the same as pass_argument.200201EXAMPLES::202203sage: from sage_setup.autogen.interpreters.internal import *204sage: mc = MemoryChunkConstants('constants', ty_mpfr)205sage: mc.pass_call_c_argument()206'self._constants'207"""208return self.pass_argument()209210def needs_cleanup_on_error(self):211r"""212In an interpreter that can terminate prematurely (due to an213exception from calling Python code, or divide by zero, or214whatever) it will just return at the end of the current instruction,215skipping the rest of the program. Thus, it may still have216values pushed on the stack, etc.217218This method returns True if this memory chunk is modified by the219interpreter and needs some sort of cleanup when an error happens.220221EXAMPLES::222223sage: from sage_setup.autogen.interpreters.internal import *224sage: mc = MemoryChunkConstants('constants', ty_mpfr)225sage: mc.needs_cleanup_on_error()226False227"""228return False229230def is_stack(self):231r"""232Says whether this memory chunk is a stack. This affects code233generation for instructions using this memory chunk.234235It would be nicer to make this object-oriented somehow, so236that the code generator called MemoryChunk methods instead of237using::238239if ch.is_stack():240... hardcoded stack code241else:242... hardcoded non-stack code243244but that hasn't been done yet.245246EXAMPLES::247248sage: from sage_setup.autogen.interpreters.internal import *249sage: mc = MemoryChunkScratch('scratch', ty_mpfr)250sage: mc.is_stack()251False252sage: mc = MemoryChunkScratch('stack', ty_mpfr, is_stack=True)253sage: mc.is_stack()254True255"""256return False257258def is_python_refcounted_stack(self):259r"""260Says whether this memory chunk refers to a stack where the entries261need to be INCREF/DECREF'ed.262263It would be nice to make this object-oriented, so that the264code generator called MemoryChunk methods to do the potential265INCREF/DECREF and didn't have to explicitly test266is_python_refcounted_stack.267268EXAMPLES::269270sage: from sage_setup.autogen.interpreters.internal import *271sage: mc = MemoryChunkScratch('args', ty_python)272sage: mc.is_python_refcounted_stack()273False274sage: mc = MemoryChunkScratch('args', ty_python, is_stack=True)275sage: mc.is_python_refcounted_stack()276True277sage: mc = MemoryChunkScratch('args', ty_mpfr, is_stack=True)278sage: mc.is_python_refcounted_stack()279False280"""281return self.is_stack() and self.storage_type.python_refcounted()282283284class MemoryChunkLonglivedArray(MemoryChunk):285r"""286MemoryChunkLonglivedArray is a subtype of MemoryChunk that deals287with memory chunks that are both 1) allocated as class members (rather288than being allocated in __call__) and 2) are arrays.289"""290291def init_class_members(self):292r"""293Return a string to be put in the __init__ method of a wrapper294class using this memory chunk, to initialize the corresponding295class members.296297EXAMPLES::298299sage: from sage_setup.autogen.interpreters.internal import *300sage: mc = MemoryChunkArguments('args', ty_double)301sage: print(mc.init_class_members())302count = args['args']303self._n_args = count304self._args = <double*>check_allocarray(self._n_args, sizeof(double))305<BLANKLINE>306"""307return je(ri(0, """308count = args['{{ myself.name }}']309{% print(myself.storage_type.alloc_chunk_data(myself.name, 'count')) %}310"""), myself=self)311312def dealloc_class_members(self):313r"""314Return a string to be put in the __dealloc__ method of a wrapper315class using this memory chunk, to deallocate the corresponding316class members.317318EXAMPLES::319320sage: from sage_setup.autogen.interpreters.internal import *321sage: mc = MemoryChunkArguments('args', ty_mpfr)322sage: print(mc.dealloc_class_members())323if self._args:324for i in range(self._n_args):325mpfr_clear(self._args[i])326sig_free(self._args)327<BLANKLINE>328"""329return self.storage_type.dealloc_chunk_data(self.name)330331def pass_argument(self):332r"""333Return the string to pass the argument corresponding to this334memory chunk to the interpreter.335336EXAMPLES::337338sage: from sage_setup.autogen.interpreters.internal import *339sage: mc = MemoryChunkConstants('constants', ty_mpfr)340sage: mc.pass_argument()341'self._constants'342"""343return 'self._%s' % self.name344345346class MemoryChunkConstants(MemoryChunkLonglivedArray):347r"""348MemoryChunkConstants is a subtype of MemoryChunkLonglivedArray.349350MemoryChunkConstants chunks have their contents set in the351wrapper's __init__ method (and not changed afterward).352"""353354def init_class_members(self):355r"""356Return a string to be put in the __init__ method of a wrapper357class using this memory chunk, to initialize the corresponding358class members.359360EXAMPLES::361362sage: from sage_setup.autogen.interpreters.internal import *363sage: mc = MemoryChunkConstants('constants', ty_mpfr)364sage: print(mc.init_class_members())365val = args['constants']366self._n_constants = len(val)367self._constants = <mpfr_t*>check_allocarray(self._n_constants, sizeof(mpfr_t))368for i in range(len(val)):369mpfr_init2(self._constants[i], self.domain.prec())370for i in range(len(val)):371rn = self.domain(val[i])372mpfr_set(self._constants[i], rn.value, MPFR_RNDN)373<BLANKLINE>374"""375return je(ri(0, """376val = args['{{ myself.name }}']377{% print(myself.storage_type.alloc_chunk_data(myself.name, 'len(val)')) %}378for i in range(len(val)):379{{ myself.storage_type.assign_c_from_py('self._%s[i]' % myself.name, 'val[i]') | i(12) }}380"""), myself=self)381382383class MemoryChunkArguments(MemoryChunkLonglivedArray):384r"""385MemoryChunkArguments is a subtype of MemoryChunkLonglivedArray,386for dealing with arguments to the wrapper's ``__call__`` method.387388Currently the ``__call__`` method is declared to take a varargs389`*args` argument tuple. We assume that the MemoryChunk named `args`390will deal with that tuple.391"""392393def setup_args(self):394r"""395Handle the arguments of __call__ -- copy them into a pre-allocated396array, ready to pass to the interpreter.397398EXAMPLES::399400sage: from sage_setup.autogen.interpreters.internal import *401sage: mc = MemoryChunkArguments('args', ty_mpfr)402sage: print(mc.setup_args())403cdef mpfr_t* c_args = self._args404cdef int i405for i from 0 <= i < len(args):406rn = self.domain(args[i])407mpfr_set(self._args[i], rn.value, MPFR_RNDN)408<BLANKLINE>409"""410return je(ri(0, """411cdef {{ myself.storage_type.c_ptr_type() }} c_args = self._args412cdef int i413for i from 0 <= i < len(args):414{{ myself.storage_type.assign_c_from_py('self._args[i]', 'args[i]') | i(4) }}415"""), myself=self)416417def pass_argument(self):418r"""419Return the string to pass the argument corresponding to this420memory chunk to the interpreter.421422EXAMPLES::423424sage: from sage_setup.autogen.interpreters.internal import *425sage: mc = MemoryChunkArguments('args', ty_mpfr)426sage: mc.pass_argument()427'c_args'428"""429return 'c_args'430431432class MemoryChunkScratch(MemoryChunkLonglivedArray):433r"""434MemoryChunkScratch is a subtype of MemoryChunkLonglivedArray435for dealing with memory chunks that are allocated in the wrapper,436but only used in the interpreter -- stacks, scratch registers, etc.437438(Currently these are only used as stacks.)439"""440441def __init__(self, name, storage_type, is_stack=False):442r"""443Initialize an instance of MemoryChunkScratch.444445Initializes the _is_stack property, as well as446the properties described in the documentation for447MemoryChunk.__init__.448449EXAMPLES::450451sage: from sage_setup.autogen.interpreters.internal import *452sage: mc = MemoryChunkScratch('stack', ty_double, is_stack=True)453sage: mc.name454'stack'455sage: mc.storage_type is ty_double456True457sage: mc._is_stack458True459"""460461super(MemoryChunkScratch, self).__init__(name, storage_type)462self._is_stack = is_stack463464def is_stack(self):465r"""466Says whether this memory chunk is a stack. This affects code467generation for instructions using this memory chunk.468469EXAMPLES::470471sage: from sage_setup.autogen.interpreters.internal import *472sage: mc = MemoryChunkScratch('stack', ty_mpfr, is_stack=True)473sage: mc.is_stack()474True475"""476return self._is_stack477478def needs_cleanup_on_error(self):479r"""480In an interpreter that can terminate prematurely (due to an481exception from calling Python code, or divide by zero, or482whatever) it will just return at the end of the current instruction,483skipping the rest of the program. Thus, it may still have484values pushed on the stack, etc.485486This method returns True if this memory chunk is modified by the487interpreter and needs some sort of cleanup when an error happens.488489EXAMPLES::490491sage: from sage_setup.autogen.interpreters.internal import *492sage: mc = MemoryChunkScratch('registers', ty_python)493sage: mc.needs_cleanup_on_error()494True495"""496return self.storage_type.python_refcounted()497498def handle_cleanup(self):499r"""500Handle the cleanup if the interpreter exits with an error.501502For scratch/stack chunks that hold Python-refcounted values,503we assume that they are filled with NULL on every entry to the504interpreter. If the interpreter exited with an error, it may505have left values in the chunk, so we need to go through506the chunk and Py_CLEAR it.507508EXAMPLES::509510sage: from sage_setup.autogen.interpreters.internal import *511sage: mc = MemoryChunkScratch('registers', ty_python)512sage: print(mc.handle_cleanup())513for i in range(self._n_registers):514Py_CLEAR(self._registers[i])515<BLANKLINE>516"""517# XXX This is a lot slower than it needs to be, because518# we don't have a "cdef int i" in scope here.519return je(ri(0, """520for i in range(self._n_{{ myself.name }}):521Py_CLEAR(self._{{ myself.name }}[i])522"""), myself=self)523524525