Path: blob/develop/src/sage_setup/autogen/interpreters/internal/instructions.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# http://www.gnu.org/licenses/9#*****************************************************************************1011"""Implements generic interpreter instructions and related utilities."""121314import re1516from .storage import ty_int171819def params_gen(**chunks):20r"""21Instructions have a parameter specification that says where they get22their inputs and where their outputs go. Each parameter has23the same form: it is a triple (chunk, addr, len). The chunk says24where the parameter is read from/written to. The addr says which25value in the chunk is used. If the chunk is a stack chunk, then26addr must be null; the value will be read from/written to the top27of the stack. Otherwise, addr must be an integer, or another chunk;28if addr is another chunk, then the next value is read from that chunk29to be the address.3031The len says how many values to read/write. It can be either None32(meaning to read/write only a single value), an integer, or33another chunk; if it is a chunk, then the next value is read from that34chunk to be the len. Note that specifying len changes the types35given to the instruction, so len=None is different than len=1 even36though both mean to use a single value.3738These parameter specifications are cumbersome to write by hand, so39there's also a simple string format for them. This (curried)40function parses the simple string format and produces parameter41specifications. The params_gen function takes keyword arguments42mapping single-character names to memory chunks. The string format43uses these names. The params_gen function returns another function,44that takes two strings and returns a pair of lists of parameter45specifications.4647Each string is the concatenation of arbitrarily many specifications.48Each specification consists of an address and a length. The49address is either a single character naming a stack chunk,50or a string of the form 'A[B]' where A names a non-stack chunk51and B names the code chunk. The length is either empty, or '@n'52for a number n (meaning to use that many arguments), or '@C', where53C is the code chunk.5455EXAMPLES::5657sage: from sage_setup.autogen.interpreters.internal import *58sage: mc_stack = MemoryChunkScratch('stack', ty_double, is_stack=True)59sage: mc_args = MemoryChunkArguments('args', ty_double)60sage: mc_code = MemoryChunkConstants('code', ty_int)6162sage: pg = params_gen(D=mc_code, A=mc_args, S=mc_stack)63sage: pg('S', '')64([({MC:stack}, None, None)], [])65sage: pg('A[D]', '')66([({MC:args}, {MC:code}, None)], [])67sage: pg('S@5', '')68([({MC:stack}, None, 5)], [])69sage: pg('S@D', '')70([({MC:stack}, None, {MC:code})], [])71sage: pg('A[D]@D', '')72([({MC:args}, {MC:code}, {MC:code})], [])73sage: pg('SSS@D', 'A[D]S@D')74([({MC:stack}, None, None), ({MC:stack}, None, None), ({MC:stack}, None, {MC:code})], [({MC:args}, {MC:code}, None), ({MC:stack}, None, {MC:code})])75"""7677def make_params(s):78p = []79s = s.strip()80while s:81chunk_code = s[0]82s = s[1:]83chunk = chunks[chunk_code]84addr = None85ch_len = None86# shouldn't hardcode 'code' here87if chunk.is_stack() or chunk.name == 'code':88pass89else:90m = re.match(r'\[(?:([0-9]+)|([a-zA-Z]))\]', s)91if m.group(1):92addr = int(m.group(1))93else:94ch = chunks[m.group(2)]95assert ch.storage_type is ty_int96addr = ch97s = s[m.end():].strip()98if len(s) and s[0] == '@':99m = re.match(r'@(?:([0-9]+)|([a-zA-Z]))', s)100if m.group(1):101ch_len = int(m.group(1))102else:103ch = chunks[m.group(2)]104assert ch.storage_type is ty_int105ch_len = ch106s = s[m.end():].strip()107p.append((chunk, addr, ch_len))108return p109110def params(s_ins, s_outs):111ins = make_params(s_ins)112outs = make_params(s_outs)113return (ins, outs)114115return params116117118class InstrSpec:119r"""120Each instruction in an interpreter is represented as an InstrSpec.121This contains all the information that we need to generate code122to interpret the instruction; it also is used to build the tables123that fast_callable uses, so this is the nexus point between124users of the interpreter (possibly pure Python) and the125generated C interpreter.126127The underlying instructions are matched to the caller by name.128For instance, fast_callable assumes that if the interpreter has an129instruction named 'cos', then it will take a single argument,130return a single result, and implement the cos() function.131132The print representation of an instruction (which will probably133only be used when doctesting this file) consists of the name,134a simplified stack effect, and the code (truncated if it's long).135The stack effect has two parts, the input and the output, separated136by '->'; the input shows what will be popped from the stack,137the output what will be placed on the stack. Each consists of138a sequence of 'S' and '*' characters, where 'S' refers to a single139argument and '*' refers to a variable number of arguments.140141The code for an instruction is a small snippet of C code. It has142available variables 'i0', 'i1', ..., 'o0', 'o1', ...; one variable143for each input and output; its job is to assign values to the output144variables, based on the values of the input variables.145146Normally, in an interpreter that uses doubles, each of the input147and output variables will be a double. If i0 actually represents148a variable number of arguments, then it will be a pointer to149double instead, and there will be another variable n_i0 giving150the actual number of arguments.151152When instructions refer to auto-reference types, they actually153get a pointer to the data in its original location; it is154not copied into a local variable. Mostly, this makes no difference,155but there is one potential problem to be aware of. It is possible156for an output variable to point to the same object as an input157variable; in fact, this usually will happen when you're working158with the stack. If the instruction maps to a single function call,159then this is fine; the standard auto-reference implementations160(GMP, MPFR, etc.) are careful to allow having the input and output161be the same. But if the instruction maps to multiple function162calls, you may need to use a temporary variable.163164Here's an example of this issue. Suppose you want to make an165instruction that does ``out = a+b*c``. You write code like this::166167out = b*c168out = a+out169170But out will actually share the same storage as a; so the first line171modifies a, and you actually end up computing 2*(b+c). The fix172is to only write to the output once, at the very end of your173instruction.174175Instructions are also allowed to access memory chunks (other than176the stack and code) directly. They are available as C variables177with the same name as the chunk. This is useful if some type of178memory chunk doesn't fit well with the params_gen interface.179180There are additional reference-counting rules that must be181followed if your interpreter operates on Python objects; these182rules are described in the docstring of the PythonInterpreter183class.184185EXAMPLES::186187sage: from sage_setup.autogen.interpreters.internal import *188sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter189sage: pg = RDFInterpreter().pg190sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')191add: SS->S = 'o0 = i0+i1;'192"""193194def __init__(self, name, io, code=None, uses_error_handler=False,195handles_own_decref=False):196r"""197Initialize an InstrSpec.198199INPUT:200201- name -- the name of the instruction202- io -- a pair of lists of parameter specifications for I/O of the203instruction204- code -- a string containing a snippet of C code to read205from the input variables and write to the output variables206- uses_error_handler -- True if the instruction calls Python207and jumps to error: on a Python error208- handles_own_decref -- True if the instruction handles Python209objects and includes its own210reference-counting211212EXAMPLES::213214sage: from sage_setup.autogen.interpreters.internal import *215sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter216sage: pg = RDFInterpreter().pg217sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')218add: SS->S = 'o0 = i0+i1;'219sage: instr = InstrSpec('py_call', pg('P[D]S@D', 'S'), code=('This is very complicated. ' + 'blah ' * 30)); instr220py_call: *->S = 'This is very compli... blah blah blah '221sage: instr.name222'py_call'223sage: instr.inputs224[({MC:py_constants}, {MC:code}, None), ({MC:stack}, None, {MC:code})]225sage: instr.outputs226[({MC:stack}, None, None)]227sage: instr.code228'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 '229sage: instr.parameters230['py_constants', 'n_inputs']231sage: instr.n_inputs2320233sage: instr.n_outputs2341235"""236self.name = name237self.inputs = io[0]238self.outputs = io[1]239self.uses_error_handler = uses_error_handler240self.handles_own_decref = handles_own_decref241if code is not None:242self.code = code243# XXX We assume that there is only one stack244n_inputs = 0245n_outputs = 0246in_effect = ''247out_effect = ''248p = []249for (ch, addr, len) in self.inputs:250if ch.is_stack():251if len is None:252n_inputs += 1253in_effect += 'S'254elif isinstance(len, int):255n_inputs += len256in_effect += 'S%d' % len257else:258p.append('n_inputs')259in_effect += '*'260else:261p.append(ch.name)262for (ch, addr, len) in self.outputs:263if ch.is_stack():264if len is None:265n_outputs += 1266out_effect += 'S'267elif isinstance(len, int):268n_outputs += len269out_effect += 'S%d' % len270else:271p.append('n_outputs')272out_effect += '*'273else:274p.append(ch.name)275self.parameters = p276self.n_inputs = n_inputs277self.n_outputs = n_outputs278self.in_effect = in_effect279self.out_effect = out_effect280281def __repr__(self):282r"""283Produce a string representing a given instruction, consisting284of its name, a brief stack specification, and its code285(possibly abbreviated).286287EXAMPLES::288289sage: from sage_setup.autogen.interpreters.internal import *290sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter291sage: pg = RDFInterpreter().pg292sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;')293add: SS->S = 'o0 = i0+i1;'294"""295rcode = repr(self.code)296if len(rcode) > 40:297rcode = rcode[:20] + '...' + rcode[-17:]298return '%s: %s->%s = %s' % \299(self.name, self.in_effect, self.out_effect, rcode)300301302# Now we have a series of helper functions that make it slightly easier303# to create instructions.304305def instr_infix(name, io, op):306r"""307A helper function for creating instructions implemented by308a single infix binary operator.309310EXAMPLES::311312sage: from sage_setup.autogen.interpreters.internal import *313sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter314sage: pg = RDFInterpreter().pg315sage: instr_infix('mul', pg('SS', 'S'), '*')316mul: SS->S = 'o0 = i0 * i1;'317"""318return InstrSpec(name, io, code='o0 = i0 %s i1;' % op)319320321def instr_funcall_2args(name, io, op):322r"""323A helper function for creating instructions implemented by324a two-argument function call.325326EXAMPLES::327328sage: from sage_setup.autogen.interpreters.internal import *329sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter330sage: pg = RDFInterpreter().pg331sage: instr_funcall_2args('atan2', pg('SS', 'S'), 'atan2')332atan2: SS->S = 'o0 = atan2(i0, i1);'333"""334return InstrSpec(name, io, code='o0 = %s(i0, i1);' % op)335336337def instr_unary(name, io, op):338r"""339A helper function for creating instructions with one input340and one output.341342EXAMPLES::343344sage: from sage_setup.autogen.interpreters.internal import *345sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter346sage: pg = RDFInterpreter().pg347sage: instr_unary('sin', pg('S','S'), 'sin(i0)')348sin: S->S = 'o0 = sin(i0);'349sage: instr_unary('neg', pg('S','S'), '-i0')350neg: S->S = 'o0 = -i0;'351"""352return InstrSpec(name, io, code='o0 = ' + op + ';')353354355def instr_funcall_2args_mpfr(name, io, op):356r"""357A helper function for creating MPFR instructions with two inputs358and one output.359360EXAMPLES::361362sage: from sage_setup.autogen.interpreters.internal import *363sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter364sage: pg = RRInterpreter().pg365sage: instr_funcall_2args_mpfr('add', pg('SS','S'), 'mpfr_add')366add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'367"""368return InstrSpec(name, io, code='%s(o0, i0, i1, MPFR_RNDN);' % op)369370371def instr_funcall_1arg_mpfr(name, io, op):372r"""373A helper function for creating MPFR instructions with one input374and one output.375376EXAMPLES::377378sage: from sage_setup.autogen.interpreters.internal import *379sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter380sage: pg = RRInterpreter().pg381sage: instr_funcall_1arg_mpfr('exp', pg('S','S'), 'mpfr_exp')382exp: S->S = 'mpfr_exp(o0, i0, MPFR_RNDN);'383"""384return InstrSpec(name, io, code='%s(o0, i0, MPFR_RNDN);' % op)385386387def instr_funcall_2args_mpc(name, io, op):388r"""389A helper function for creating MPC instructions with two inputs390and one output.391392EXAMPLES::393394sage: from sage_setup.autogen.interpreters.internal import *395sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter396sage: pg = CCInterpreter().pg397sage: instr_funcall_2args_mpc('add', pg('SS','S'), 'mpc_add')398add: SS->S = 'mpc_add(o0, i0, i1, MPC_RNDNN);'399"""400return InstrSpec(name, io, code='%s(o0, i0, i1, MPC_RNDNN);' % op)401402403def instr_funcall_1arg_mpc(name, io, op):404r"""405A helper function for creating MPC instructions with one input406and one output.407408EXAMPLES::409410sage: from sage_setup.autogen.interpreters.internal import *411sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter412sage: pg = CCInterpreter().pg413sage: instr_funcall_1arg_mpc('exp', pg('S','S'), 'mpc_exp')414exp: S->S = 'mpc_exp(o0, i0, MPC_RNDNN);'415"""416return InstrSpec(name, io, code='%s(o0, i0, MPC_RNDNN);' % op)417418419