Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/cc.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# https://www.gnu.org/licenses/9# ****************************************************************************10from .base import StackInterpreter11from .python import MemoryChunkPyConstant12from ..instructions import (params_gen, instr_funcall_1arg_mpc,13instr_funcall_2args_mpc, InstrSpec)14from ..memory import MemoryChunk, MemoryChunkConstants15from ..storage import ty_mpc, ty_python16from ..utils import je, reindent_lines as ri171819class MemoryChunkCCRetval(MemoryChunk):20r"""21A special-purpose memory chunk, for dealing with the return value22of the CC-based interpreter.23"""2425def declare_class_members(self):26r"""27Return a string giving the declarations of the class members28in a wrapper class for this memory chunk.2930EXAMPLES::3132sage: from sage_setup.autogen.interpreters.internal import *33sage: from sage_setup.autogen.interpreters.internal.specs.cc import *34sage: mc = MemoryChunkCCRetval('retval', ty_mpc)35sage: mc.declare_class_members()36''37"""38return ""3940def declare_call_locals(self):41r"""42Return a string to put in the __call__ method of a wrapper43class using this memory chunk, to allocate local variables.4445EXAMPLES::4647sage: from sage_setup.autogen.interpreters.internal import *48sage: from sage_setup.autogen.interpreters.internal.specs.cc import *49sage: mc = MemoryChunkCCRetval('retval', ty_mpc)50sage: mc.declare_call_locals()51' cdef ComplexNumber retval = (self.domain_element._new())\n'52"""53return je(ri(8,54"""55cdef ComplexNumber {{ myself.name }} = (self.domain_element._new())56"""), myself=self)5758def declare_parameter(self):59r"""60Return the string to use to declare the interpreter parameter61corresponding to this memory chunk.6263EXAMPLES::6465sage: from sage_setup.autogen.interpreters.internal import *66sage: from sage_setup.autogen.interpreters.internal.specs.cc import *67sage: mc = MemoryChunkCCRetval('retval', ty_mpc)68sage: mc.declare_parameter()69'mpc_t retval'70"""71return '%s %s' % (self.storage_type.c_reference_type(), self.name)7273def pass_argument(self):74r"""75Return the string to pass the argument corresponding to this76memory chunk to the interpreter.7778EXAMPLES::7980sage: from sage_setup.autogen.interpreters.internal import *81sage: from sage_setup.autogen.interpreters.internal.specs.cc import *82sage: mc = MemoryChunkCCRetval('retval', ty_mpc)83sage: mc.pass_argument()84'(<mpc_t>(retval.__re))'85"""86return je("""(<mpc_t>({{ myself.name }}.__re))""", myself=self)8788def pass_call_c_argument(self):89r"""90Return the string to pass the argument corresponding to this91memory chunk to the interpreter, for use in the call_c method.9293EXAMPLES::9495sage: from sage_setup.autogen.interpreters.internal import *96sage: from sage_setup.autogen.interpreters.internal.specs.cc import *97sage: mc = MemoryChunkCCRetval('retval', ty_mpc)98sage: mc.pass_call_c_argument()99'result'100"""101return "result"102103104class CCInterpreter(StackInterpreter):105r"""106A subclass of StackInterpreter, specifying an interpreter over107MPFR arbitrary-precision floating-point numbers.108"""109110name = 'cc'111112def __init__(self):113r"""114Initialize a CCInterpreter.115116EXAMPLES::117118sage: from sage_setup.autogen.interpreters.internal import *119sage: from sage_setup.autogen.interpreters.internal.specs.cc import *120sage: interp = CCInterpreter()121sage: interp.name122'cc'123sage: interp.mc_py_constants124{MC:py_constants}125sage: interp.chunks126[{MC:args}, {MC:retval}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}, {MC:domain}]127sage: interp.pg('A[D]', 'S')128([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])129sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])130sage: instrs['add']131add: SS->S = 'mpc_add(o0, i0, i1, MPC_RNDNN);'132sage: instrs['py_call']133py_call: *->S = '\n if (!cc_py_call...goto error;\n}\n'134135That py_call instruction is particularly interesting, and136demonstrates a useful technique to let you use Cython code137in an interpreter. Let's look more closely::138139sage: print(instrs['py_call'].code)140<BLANKLINE>141if (!cc_py_call_helper(domain, i0, n_i1, i1, o0)) {142goto error;143}144<BLANKLINE>145146This instruction makes use of the function cc_py_call_helper,147which is declared::148149sage: print(interp.c_header)150<BLANKLINE>151#include <mpc.h>152<BLANKLINE>153154So instructions where you need to interact with Python can155call back into Cython code fairly easily.156"""157158mc_retval = MemoryChunkCCRetval('retval', ty_mpc)159super(CCInterpreter, self).__init__(ty_mpc, mc_retval=mc_retval)160self.err_return = '0'161self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)162self.mc_domain = MemoryChunkPyConstant('domain')163self.chunks = [self.mc_args, self.mc_retval, self.mc_constants,164self.mc_py_constants,165self.mc_stack, self.mc_code, self.mc_domain]166pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,167S=self.mc_stack,168P=self.mc_py_constants)169self.pg = pg170self.c_header = ri(0,171'''172#include <mpc.h>173''')174175self.pxd_header = ri(0,176"""177from sage.rings.real_mpfr cimport RealNumber178from sage.libs.mpfr cimport *179from sage.rings.complex_mpfr cimport ComplexNumber180from sage.libs.mpc cimport *181""")182183self.pyx_header = ri(0,184"""\185# distutils: libraries = mpfr mpc gmp186187cdef public bint cc_py_call_helper(object domain, object fn,188int n_args,189mpc_t* args, mpc_t retval) except 0:190py_args = []191cdef int i192cdef ComplexNumber ZERO=domain.zero()193cdef ComplexNumber cn194for i from 0 <= i < n_args:195cn = ZERO._new()196mpfr_set(cn.__re, mpc_realref(args[i]), MPFR_RNDN)197mpfr_set(cn.__im, mpc_imagref(args[i]), MPFR_RNDN)198py_args.append(cn)199cdef ComplexNumber result = domain(fn(*py_args))200mpc_set_fr_fr(retval, result.__re,result.__im, MPC_RNDNN)201return 1202""")203204instrs = [205InstrSpec('load_arg', pg('A[D]', 'S'),206code='mpc_set(o0, i0, MPC_RNDNN);'),207InstrSpec('load_const', pg('C[D]', 'S'),208code='mpc_set(o0, i0, MPC_RNDNN);'),209InstrSpec('return', pg('S', ''),210code='mpc_set(retval, i0, MPC_RNDNN);\nreturn 1;\n'),211InstrSpec('py_call', pg('P[D]S@D', 'S'),212uses_error_handler=True,213code="""214if (!cc_py_call_helper(domain, i0, n_i1, i1, o0)) {215goto error;216}217""")218]219for (name, op) in [('add', 'mpc_add'), ('sub', 'mpc_sub'),220('mul', 'mpc_mul'), ('div', 'mpc_div'),221('pow', 'mpc_pow')]:222instrs.append(instr_funcall_2args_mpc(name, pg('SS', 'S'), op))223instrs.append(instr_funcall_2args_mpc('ipow', pg('SD', 'S'), 'mpc_pow_si'))224for name in ['neg',225'log', 'log10',226'exp',227'cos', 'sin', 'tan',228'acos', 'asin', 'atan',229'cosh', 'sinh', 'tanh',230'acosh', 'asinh', 'atanh']:231instrs.append(instr_funcall_1arg_mpc(name, pg('S', 'S'), 'mpc_' + name))232# mpc_ui_div constructs a temporary mpc_t and then calls mpc_div;233# it would probably be (slightly) faster to use a permanent copy234# of "one" (on the other hand, the constructed temporary copy is235# on the stack, so it's very likely to be in the cache).236instrs.append(InstrSpec('invert', pg('S', 'S'),237code='mpc_ui_div(o0, 1, i0, MPC_RNDNN);'))238self.instr_descs = instrs239self._set_opcodes()240# Supported for exponents that fit in a long, so we could use241# a much wider range on a 64-bit machine. On the other hand,242# it's easier to write the code this way, and constant integer243# exponents outside this range probably aren't very common anyway.244self.ipow_range = (int(-2**31), int(2**31-1))245246247