Path: blob/develop/src/sage_setup/autogen/interpreters/internal/instructions.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"""Implements generic interpreter instructions and related utilities."""1213from __future__ import print_function, absolute_import1415import re1617from .storage import ty_int181920def params_gen(**chunks):21r"""22Instructions have a parameter specification that says where they get23their inputs and where their outputs go. Each parameter has24the same form: it is a triple (chunk, addr, len). The chunk says25where the parameter is read from/written to. The addr says which26value in the chunk is used. If the chunk is a stack chunk, then27addr must be null; the value will be read from/written to the top28of the stack. Otherwise, addr must be an integer, or another chunk;29if addr is another chunk, then the next value is read from that chunk30to be the address.3132The len says how many values to read/write. It can be either None33(meaning to read/write only a single value), an integer, or34another chunk; if it is a chunk, then the next value is read from that35chunk to be the len. Note that specifying len changes the types36given to the instruction, so len=None is different than len=1 even37though both mean to use a single value.3839These parameter specifications are cumbersome to write by hand, so40there's also a simple string format for them. This (curried)41function parses the simple string format and produces parameter42specifications. The params_gen function takes keyword arguments43mapping single-character names to memory chunks. The string format44uses these names. The params_gen function returns another function,45that takes two strings and returns a pair of lists of parameter46specifications.4748Each string is the concatenation of arbitrarily many specifications.49Each specification consists of an address and a length. The50address is either a single character naming a stack chunk,51or a string of the form 'A[B]' where A names a non-stack chunk52and B names the code chunk. The length is either empty, or '@n'53for a number n (meaning to use that many arguments), or '@C', where54C is the code chunk.5556EXAMPLES::5758sage: from sage_setup.autogen.interpreters.internal import *59sage: mc_stack = MemoryChunkScratch('stack', ty_double, is_stack=True)60sage: mc_args = MemoryChunkArguments('args', ty_double)61sage: mc_code = MemoryChunkConstants('code', ty_int)6263sage: pg = params_gen(D=mc_code, A=mc_args, S=mc_stack)64sage: pg('S', '')65([({MC:stack}, None, None)], [])66sage: pg('A[D]', '')67([({MC:args}, {MC:code}, None)], [])68sage: pg('S@5', '')69([({MC:stack}, None, 5)], [])70sage: pg('S@D', '')71([({MC:stack}, None, {MC:code})], [])72sage: pg('A[D]@D', '')73([({MC:args}, {MC:code}, {MC:code})], [])74sage: pg('SSS@D', 'A[D]S@D')75([({MC:stack}, None, None), ({MC:stack}, None, None), ({MC:stack}, None, {MC:code})], [({MC:args}, {MC:code}, None), ({MC:stack}, None, {MC:code})])76"""7778def make_params(s):79p = []80s = s.strip()81while s:82chunk_code = s[0]83s = s[1:]84chunk = chunks[chunk_code]85addr = None86ch_len = None87# shouldn't hardcode 'code' here88if chunk.is_stack() or chunk.name == 'code':89pass90else:91m = re.match(r'\[(?:([0-9]+)|([a-zA-Z]))\]', s)92if m.group(1):93addr = int(m.group(1))94else:95ch = chunks[m.group(2)]96assert ch.storage_type is ty_int97addr = ch98s = s[m.end():].strip()99if len(s) and s[0] == '@':100m = re.match(r'@(?:([0-9]+)|([a-zA-Z]))', s)101if m.group(1):102ch_len = int(m.group(1))103else:104ch = chunks[m.group(2)]105assert ch.storage_type is ty_int106ch_len = ch107s = s[m.end():].strip()108p.append((chunk, addr, ch_len))109return p110111def params(s_ins, s_outs):112ins = make_params(s_ins)113outs = make_params(s_outs)114return (ins, outs)115116return params117118119class InstrSpec(object):120r"""121Each instruction in an interpreter is represented as an InstrSpec.122This contains all the information that we need to generate code123to interpret the instruction; it also is used to build the tables124that fast_callable uses, so this is the nexus point between125users of the interpreter (possibly pure Python) and the126generated C interpreter.127128The underlying instructions are matched to the caller by name.129For instance, fast_callable assumes that if the interpreter has an130instruction named 'cos', then it will take a single argument,131return a single result, and implement the cos() function.132133The print representation of an instruction (which will probably134only be used when doctesting this file) consists of the name,135a simplified stack effect, and the code (truncated if it's long).136The stack effect has two parts, the input and the output, separated137by '->'; the input shows what will be popped from the stack,138the output what will be placed on the stack. Each consists of139a sequence of 'S' and '*' characters, where 'S' refers to a single140argument and '*' refers to a variable number of arguments.141142The code for an instruction is a small snippet of C code. It has143available variables 'i0', 'i1', ..., 'o0', 'o1', ...; one variable144for each input and output; its job is to assign values to the output145variables, based on the values of the input variables.146147Normally, in an interpreter that uses doubles, each of the input148and output variables will be a double. If i0 actually represents149a variable number of arguments, then it will be a pointer to150double instead, and there will be another variable n_i0 giving151the actual number of arguments.152153When instructions refer to auto-reference types, they actually154get a pointer to the data in its original location; it is155not copied into a local variable. Mostly, this makes no difference,156but there is one potential problem to be aware of. It is possible157for an output variable to point to the same object as an input158variable; in fact, this usually will happen when you're working159with the stack. If the instruction maps to a single function call,160then this is fine; the standard auto-reference implementations161(GMP, MPFR, etc.) are careful to allow having the input and output162be the same. But if the instruction maps to multiple function163calls, you may need to use a temporary variable.164165Here's an example of this issue. Suppose you want to make an166instruction that does ``out = a+b*c``. You write code like this::167168out = b*c169out = a+out170171But out will actually share the same storage as a; so the first line172modifies a, and you actually end up computing 2*(b+c). The fix173is to only write to the output once, at the very end of your174instruction.175176Instructions are also allowed to access memory chunks (other than177the stack and code) directly. They are available as C variables178with the same name as the chunk. This is useful if some type of179memory chunk doesn't fit well with the params_gen interface.180181There are additional reference-counting rules that must be182followed if your interpreter operates on Python objects; these183rules are described in the docstring of the PythonInterpreter184class.185186EXAMPLES::187188sage: from sage_setup.autogen.interpreters.internal import *189sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter190sage: pg = RDFInterpreter().pg191sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')192add: SS->S = 'o0 = i0+i1;'193"""194195def __init__(self, name, io, code=None, uses_error_handler=False,196handles_own_decref=False):197r"""198Initialize an InstrSpec.199200INPUT:201202- name -- the name of the instruction203- io -- a pair of lists of parameter specifications for I/O of the204instruction205- code -- a string containing a snippet of C code to read206from the input variables and write to the output variables207- uses_error_handler -- True if the instruction calls Python208and jumps to error: on a Python error209- handles_own_decref -- True if the instruction handles Python210objects and includes its own211reference-counting212213EXAMPLES::214215sage: from sage_setup.autogen.interpreters.internal import *216sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter217sage: pg = RDFInterpreter().pg218sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')219add: SS->S = 'o0 = i0+i1;'220sage: instr = InstrSpec('py_call', pg('P[D]S@D', 'S'), code=('This is very complicated. ' + 'blah ' * 30)); instr221py_call: *->S = 'This is very compli... blah blah blah '222sage: instr.name223'py_call'224sage: instr.inputs225[({MC:py_constants}, {MC:code}, None), ({MC:stack}, None, {MC:code})]226sage: instr.outputs227[({MC:stack}, None, None)]228sage: instr.code229'This is very complicated. blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah '230sage: instr.parameters231['py_constants', 'n_inputs']232sage: instr.n_inputs2330234sage: instr.n_outputs2351236"""237self.name = name238self.inputs = io[0]239self.outputs = io[1]240self.uses_error_handler = uses_error_handler241self.handles_own_decref = handles_own_decref242if code is not None:243self.code = code244# XXX We assume that there is only one stack245n_inputs = 0246n_outputs = 0247in_effect = ''248out_effect = ''249p = []250for (ch, addr, len) in self.inputs:251if ch.is_stack():252if len is None:253n_inputs += 1254in_effect += 'S'255elif isinstance(len, int):256n_inputs += len257in_effect += 'S%d' % len258else:259p.append('n_inputs')260in_effect += '*'261else:262p.append(ch.name)263for (ch, addr, len) in self.outputs:264if ch.is_stack():265if len is None:266n_outputs += 1267out_effect += 'S'268elif isinstance(len, int):269n_outputs += len270out_effect += 'S%d' % len271else:272p.append('n_outputs')273out_effect += '*'274else:275p.append(ch.name)276self.parameters = p277self.n_inputs = n_inputs278self.n_outputs = n_outputs279self.in_effect = in_effect280self.out_effect = out_effect281282def __repr__(self):283r"""284Produce a string representing a given instruction, consisting285of its name, a brief stack specification, and its code286(possibly abbreviated).287288EXAMPLES::289290sage: from sage_setup.autogen.interpreters.internal import *291sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter292sage: pg = RDFInterpreter().pg293sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')294add: SS->S = 'o0 = i0+i1;'295"""296rcode = repr(self.code)297if len(rcode) > 40:298rcode = rcode[:20] + '...' + rcode[-17:]299return '%s: %s->%s = %s' % \300(self.name, self.in_effect, self.out_effect, rcode)301302303# Now we have a series of helper functions that make it slightly easier304# to create instructions.305306def instr_infix(name, io, op):307r"""308A helper function for creating instructions implemented by309a single infix binary operator.310311EXAMPLES::312313sage: from sage_setup.autogen.interpreters.internal import *314sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter315sage: pg = RDFInterpreter().pg316sage: instr_infix('mul', pg('SS', 'S'), '*')317mul: SS->S = 'o0 = i0 * i1;'318"""319return InstrSpec(name, io, code='o0 = i0 %s i1;' % op)320321322def instr_funcall_2args(name, io, op):323r"""324A helper function for creating instructions implemented by325a two-argument function call.326327EXAMPLES::328329sage: from sage_setup.autogen.interpreters.internal import *330sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter331sage: pg = RDFInterpreter().pg332sage: instr_funcall_2args('atan2', pg('SS', 'S'), 'atan2')333atan2: SS->S = 'o0 = atan2(i0, i1);'334"""335return InstrSpec(name, io, code='o0 = %s(i0, i1);' % op)336337338def instr_unary(name, io, op):339r"""340A helper function for creating instructions with one input341and one output.342343EXAMPLES::344345sage: from sage_setup.autogen.interpreters.internal import *346sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter347sage: pg = RDFInterpreter().pg348sage: instr_unary('sin', pg('S','S'), 'sin(i0)')349sin: S->S = 'o0 = sin(i0);'350sage: instr_unary('neg', pg('S','S'), '-i0')351neg: S->S = 'o0 = -i0;'352"""353return InstrSpec(name, io, code='o0 = ' + op + ';')354355356def instr_funcall_2args_mpfr(name, io, op):357r"""358A helper function for creating MPFR instructions with two inputs359and one output.360361EXAMPLES::362363sage: from sage_setup.autogen.interpreters.internal import *364sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter365sage: pg = RRInterpreter().pg366sage: instr_funcall_2args_mpfr('add', pg('SS','S'), 'mpfr_add')367add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'368"""369return InstrSpec(name, io, code='%s(o0, i0, i1, MPFR_RNDN);' % op)370371372def instr_funcall_1arg_mpfr(name, io, op):373r"""374A helper function for creating MPFR instructions with one input375and one output.376377EXAMPLES::378379sage: from sage_setup.autogen.interpreters.internal import *380sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter381sage: pg = RRInterpreter().pg382sage: instr_funcall_1arg_mpfr('exp', pg('S','S'), 'mpfr_exp')383exp: S->S = 'mpfr_exp(o0, i0, MPFR_RNDN);'384"""385return InstrSpec(name, io, code='%s(o0, i0, MPFR_RNDN);' % op)386387388def instr_funcall_2args_mpc(name, io, op):389r"""390A helper function for creating MPC instructions with two inputs391and one output.392393EXAMPLES::394395sage: from sage_setup.autogen.interpreters.internal import *396sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter397sage: pg = CCInterpreter().pg398sage: instr_funcall_2args_mpc('add', pg('SS','S'), 'mpc_add')399add: SS->S = 'mpc_add(o0, i0, i1, MPC_RNDNN);'400"""401return InstrSpec(name, io, code='%s(o0, i0, i1, MPC_RNDNN);' % op)402403404def instr_funcall_1arg_mpc(name, io, op):405r"""406A helper function for creating MPC instructions with one input407and one output.408409EXAMPLES::410411sage: from sage_setup.autogen.interpreters.internal import *412sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter413sage: pg = CCInterpreter().pg414sage: instr_funcall_1arg_mpc('exp', pg('S','S'), 'mpc_exp')415exp: S->S = 'mpc_exp(o0, i0, MPC_RNDNN);'416"""417return InstrSpec(name, io, code='%s(o0, i0, MPC_RNDNN);' % op)418419420