Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/rr.py
7405 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# https://www.gnu.org/licenses/9# ****************************************************************************10from ..instructions import (11InstrSpec,12instr_funcall_1arg_mpfr,13instr_funcall_2args_mpfr,14params_gen,15)16from ..memory import MemoryChunk, MemoryChunkConstants17from ..storage import ty_mpfr, ty_python18from ..utils import je19from ..utils import reindent_lines as ri20from .base import StackInterpreter21from .python import MemoryChunkPyConstant222324class MemoryChunkRRRetval(MemoryChunk):25r"""26A special-purpose memory chunk, for dealing with the return value27of the RR-based interpreter.28"""2930def declare_class_members(self):31r"""32Return a string giving the declarations of the class members33in a wrapper class for this memory chunk.3435EXAMPLES::3637sage: from sage_setup.autogen.interpreters.internal import *38sage: from sage_setup.autogen.interpreters.internal.specs.rr import *39sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)40sage: mc.declare_class_members()41''42"""43return ""4445def declare_call_locals(self):46r"""47Return a string to put in the __call__ method of a wrapper48class using this memory chunk, to allocate local variables.4950EXAMPLES::5152sage: from sage_setup.autogen.interpreters.internal import *53sage: from sage_setup.autogen.interpreters.internal.specs.rr import *54sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)55sage: mc.declare_call_locals()56' cdef RealNumber retval = (self.domain)()\n'57"""58return je(ri(8,59"""60cdef RealNumber {{ myself.name }} = (self.domain)()61"""), myself=self)6263def declare_parameter(self):64r"""65Return the string to use to declare the interpreter parameter66corresponding to this memory chunk.6768EXAMPLES::6970sage: from sage_setup.autogen.interpreters.internal import *71sage: from sage_setup.autogen.interpreters.internal.specs.rr import *72sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)73sage: mc.declare_parameter()74'mpfr_t retval'75"""76return '%s %s' % (self.storage_type.c_reference_type(), self.name)7778def pass_argument(self):79r"""80Return the string to pass the argument corresponding to this81memory chunk to the interpreter.8283EXAMPLES::8485sage: from sage_setup.autogen.interpreters.internal import *86sage: from sage_setup.autogen.interpreters.internal.specs.rr import *87sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)88sage: mc.pass_argument()89'retval.value'90"""91return je("""{{ myself.name }}.value""", myself=self)9293def pass_call_c_argument(self):94r"""95Return the string to pass the argument corresponding to this96memory chunk to the interpreter, for use in the call_c method.9798EXAMPLES::99100sage: from sage_setup.autogen.interpreters.internal import *101sage: from sage_setup.autogen.interpreters.internal.specs.rr import *102sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)103sage: mc.pass_call_c_argument()104'result'105"""106return "result"107108109class RRInterpreter(StackInterpreter):110r"""111A subclass of StackInterpreter, specifying an interpreter over112MPFR arbitrary-precision floating-point numbers.113"""114115name = 'rr'116117def __init__(self):118r"""119Initialize an RDFInterpreter.120121EXAMPLES::122123sage: from sage_setup.autogen.interpreters.internal import *124sage: from sage_setup.autogen.interpreters.internal.specs.rr import *125sage: interp = RRInterpreter()126sage: interp.name127'rr'128sage: interp.mc_py_constants129{MC:py_constants}130sage: interp.chunks131[{MC:args}, {MC:retval}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}, {MC:domain}]132sage: interp.pg('A[D]', 'S')133([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])134sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])135sage: instrs['add']136add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'137sage: instrs['py_call']138py_call: *->S = '\nif (!rr_py_call_h...goto error;\n}\n'139140That py_call instruction is particularly interesting, and141demonstrates a useful technique to let you use Cython code142in an interpreter. Let's look more closely::143144sage: print(instrs['py_call'].code)145if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {146goto error;147}148149This instruction makes use of the function ``rr_py_call_helper``,150which is declared in ``wrapper_rr.h``::151152sage: print(interp.c_header)153<BLANKLINE>154#include <mpfr.h>155<BLANKLINE>156157The function ``rr_py_call_helper`` is implemented in Cython::158159sage: print(interp.pyx_header)160cdef public bint rr_py_call_helper(object domain, object fn,161int n_args,162mpfr_t* args, mpfr_t retval) except 0:163py_args = []164cdef int i165cdef RealNumber rn166for i from 0 <= i < n_args:167rn = domain()168mpfr_set(rn.value, args[i], MPFR_RNDN)169py_args.append(rn)170cdef RealNumber result = domain(fn(*py_args))171mpfr_set(retval, result.value, MPFR_RNDN)172return 1173174So instructions where you need to interact with Python can175call back into Cython code fairly easily.176"""177178mc_retval = MemoryChunkRRRetval('retval', ty_mpfr)179super().__init__(ty_mpfr, mc_retval=mc_retval)180self.err_return = '0'181self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)182self.mc_domain = MemoryChunkPyConstant('domain')183self.chunks = [self.mc_args, self.mc_retval, self.mc_constants,184self.mc_py_constants,185self.mc_stack, self.mc_code, self.mc_domain]186pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,187S=self.mc_stack,188P=self.mc_py_constants)189self.pg = pg190self.c_header = ri(0,191'''192#include <mpfr.h>193''')194195self.pxd_header = ri(0,196"""197from sage.rings.real_mpfr cimport RealField_class, RealNumber198from sage.libs.mpfr cimport *199200""")201202self.pyx_header = ri(0,203"""\204cdef public bint rr_py_call_helper(object domain, object fn,205int n_args,206mpfr_t* args, mpfr_t retval) except 0:207py_args = []208cdef int i209cdef RealNumber rn210for i from 0 <= i < n_args:211rn = domain()212mpfr_set(rn.value, args[i], MPFR_RNDN)213py_args.append(rn)214cdef RealNumber result = domain(fn(*py_args))215mpfr_set(retval, result.value, MPFR_RNDN)216return 1217""")218219instrs = [220InstrSpec('load_arg', pg('A[D]', 'S'),221code='mpfr_set(o0, i0, MPFR_RNDN);'),222InstrSpec('load_const', pg('C[D]', 'S'),223code='mpfr_set(o0, i0, MPFR_RNDN);'),224InstrSpec('return', pg('S', ''),225code='mpfr_set(retval, i0, MPFR_RNDN);\nreturn 1;\n'),226InstrSpec('py_call', pg('P[D]S@D', 'S'),227uses_error_handler=True,228code=ri(0,229"""230if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {231goto error;232}233"""))234]235for (name, op) in [('add', 'mpfr_add'), ('sub', 'mpfr_sub'),236('mul', 'mpfr_mul'), ('div', 'mpfr_div'),237('pow', 'mpfr_pow')]:238instrs.append(instr_funcall_2args_mpfr(name, pg('SS', 'S'), op))239instrs.append(instr_funcall_2args_mpfr('ipow', pg('SD', 'S'), 'mpfr_pow_si'))240for name in ['neg', 'abs',241'log', 'log2', 'log10',242'exp', 'exp2', 'exp10',243'cos', 'sin', 'tan',244'sec', 'csc', 'cot',245'acos', 'asin', 'atan',246'cosh', 'sinh', 'tanh',247'sech', 'csch', 'coth',248'acosh', 'asinh', 'atanh',249'log1p', 'expm1', 'eint',250'gamma', 'lngamma',251'zeta', 'erf', 'erfc',252'j0', 'j1', 'y0', 'y1']:253instrs.append(instr_funcall_1arg_mpfr(name, pg('S', 'S'), 'mpfr_' + name))254# mpfr_ui_div constructs a temporary mpfr_t and then calls mpfr_div;255# it would probably be (slightly) faster to use a permanent copy256# of "one" (on the other hand, the constructed temporary copy is257# on the stack, so it's very likely to be in the cache).258instrs.append(InstrSpec('invert', pg('S', 'S'),259code='mpfr_ui_div(o0, 1, i0, MPFR_RNDN);'))260self.instr_descs = instrs261self._set_opcodes()262# Supported for exponents that fit in a long, so we could use263# a much wider range on a 64-bit machine. On the other hand,264# it's easier to write the code this way, and constant integer265# exponents outside this range probably aren't very common anyway.266self.ipow_range = (int(-2**31), int(2**31-1))267268269