Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/cc.py
7417 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_mpc,13instr_funcall_2args_mpc,14params_gen,15)16from ..memory import MemoryChunk, MemoryChunkConstants17from ..storage import ty_mpc, ty_python18from ..utils import je19from ..utils import reindent_lines as ri20from .base import StackInterpreter21from .python import MemoryChunkPyConstant222324class MemoryChunkCCRetval(MemoryChunk):25r"""26A special-purpose memory chunk, for dealing with the return value27of the CC-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.cc import *39sage: mc = MemoryChunkCCRetval('retval', ty_mpc)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.cc import *54sage: mc = MemoryChunkCCRetval('retval', ty_mpc)55sage: mc.declare_call_locals()56' cdef ComplexNumber retval = (self.domain_element._new())\n'57"""58return je(ri(8,59"""60cdef ComplexNumber {{ myself.name }} = (self.domain_element._new())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.cc import *72sage: mc = MemoryChunkCCRetval('retval', ty_mpc)73sage: mc.declare_parameter()74'mpc_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.cc import *87sage: mc = MemoryChunkCCRetval('retval', ty_mpc)88sage: mc.pass_argument()89'(<mpc_t>(retval.__re))'90"""91return je("""(<mpc_t>({{ myself.name }}.__re))""", 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.cc import *102sage: mc = MemoryChunkCCRetval('retval', ty_mpc)103sage: mc.pass_call_c_argument()104'result'105"""106return "result"107108109class CCInterpreter(StackInterpreter):110r"""111A subclass of StackInterpreter, specifying an interpreter over112MPFR arbitrary-precision floating-point numbers.113"""114115name = 'cc'116117def __init__(self):118r"""119Initialize a CCInterpreter.120121EXAMPLES::122123sage: from sage_setup.autogen.interpreters.internal import *124sage: from sage_setup.autogen.interpreters.internal.specs.cc import *125sage: interp = CCInterpreter()126sage: interp.name127'cc'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 = 'mpc_add(o0, i0, i1, MPC_RNDNN);'137sage: instrs['py_call']138py_call: *->S = '\n if (!cc_py_call...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)145<BLANKLINE>146if (!cc_py_call_helper(domain, i0, n_i1, i1, o0)) {147goto error;148}149<BLANKLINE>150151This instruction makes use of the function cc_py_call_helper,152which is declared::153154sage: print(interp.c_header)155<BLANKLINE>156#include <mpc.h>157<BLANKLINE>158159So instructions where you need to interact with Python can160call back into Cython code fairly easily.161"""162163mc_retval = MemoryChunkCCRetval('retval', ty_mpc)164super().__init__(ty_mpc, mc_retval=mc_retval)165self.err_return = '0'166self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)167self.mc_domain = MemoryChunkPyConstant('domain')168self.chunks = [self.mc_args, self.mc_retval, self.mc_constants,169self.mc_py_constants,170self.mc_stack, self.mc_code, self.mc_domain]171pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,172S=self.mc_stack,173P=self.mc_py_constants)174self.pg = pg175self.c_header = ri(0,176'''177#include <mpc.h>178''')179180self.pxd_header = ri(0,181"""182from sage.rings.real_mpfr cimport RealNumber183from sage.libs.mpfr cimport *184from sage.rings.complex_mpfr cimport ComplexNumber185from sage.libs.mpc cimport *186""")187188self.pyx_header = ri(0,189"""\190# distutils: libraries = mpfr mpc gmp191192cdef public bint cc_py_call_helper(object domain, object fn,193int n_args,194mpc_t* args, mpc_t retval) except 0:195py_args = []196cdef int i197cdef ComplexNumber ZERO=domain.zero()198cdef ComplexNumber cn199for i from 0 <= i < n_args:200cn = ZERO._new()201mpfr_set(cn.__re, mpc_realref(args[i]), MPFR_RNDN)202mpfr_set(cn.__im, mpc_imagref(args[i]), MPFR_RNDN)203py_args.append(cn)204cdef ComplexNumber result = domain(fn(*py_args))205mpc_set_fr_fr(retval, result.__re,result.__im, MPC_RNDNN)206return 1207""")208209instrs = [210InstrSpec('load_arg', pg('A[D]', 'S'),211code='mpc_set(o0, i0, MPC_RNDNN);'),212InstrSpec('load_const', pg('C[D]', 'S'),213code='mpc_set(o0, i0, MPC_RNDNN);'),214InstrSpec('return', pg('S', ''),215code='mpc_set(retval, i0, MPC_RNDNN);\nreturn 1;\n'),216InstrSpec('py_call', pg('P[D]S@D', 'S'),217uses_error_handler=True,218code="""219if (!cc_py_call_helper(domain, i0, n_i1, i1, o0)) {220goto error;221}222""")223]224for (name, op) in [('add', 'mpc_add'), ('sub', 'mpc_sub'),225('mul', 'mpc_mul'), ('div', 'mpc_div'),226('pow', 'mpc_pow')]:227instrs.append(instr_funcall_2args_mpc(name, pg('SS', 'S'), op))228instrs.append(instr_funcall_2args_mpc('ipow', pg('SD', 'S'), 'mpc_pow_si'))229for name in ['neg',230'log', 'log10',231'exp',232'cos', 'sin', 'tan',233'acos', 'asin', 'atan',234'cosh', 'sinh', 'tanh',235'acosh', 'asinh', 'atanh']:236instrs.append(instr_funcall_1arg_mpc(name, pg('S', 'S'), 'mpc_' + name))237# mpc_ui_div constructs a temporary mpc_t and then calls mpc_div;238# it would probably be (slightly) faster to use a permanent copy239# of "one" (on the other hand, the constructed temporary copy is240# on the stack, so it's very likely to be in the cache).241instrs.append(InstrSpec('invert', pg('S', 'S'),242code='mpc_ui_div(o0, 1, i0, MPC_RNDNN);'))243self.instr_descs = instrs244self._set_opcodes()245# Supported for exponents that fit in a long, so we could use246# a much wider range on a 64-bit machine. On the other hand,247# it's easier to write the code this way, and constant integer248# exponents outside this range probably aren't very common anyway.249self.ipow_range = (int(-2**31), int(2**31-1))250251252