Path: blob/develop/src/sage_setup/autogen/interpreters/internal/__init__.py
7405 views
r"""1Generate interpreters for fast_callable23AUTHORS:45- Carl Witty67This file is part of the Sage support for "planned" computations;8that is, computations that are separated into a planning stage and9a plan-execution stage. Here, we generate fast interpreters for plan10executions.1112There are at least two kinds of computations that are often planned in13this fashion. First is arithmetic expression evaluation, where we14take an arbitrary user-specified arithmetic expression and compile it15into a bytecode form for fast interpretation. Second is things like16FFTs and large multiplications, where large problems are split into17multiple smaller problems... we can do the logical "splitting" for a18given size only once, producing a plan which can be reused as often as19we want for different problems of the same size. Currently only20arithmetic expression evaluation is implemented, but other kinds of21planned computations should be easy to add.2223Typically, for arithmetic expressions, we want the storage of24intermediate results to be handled automatically (on a stack); for25FFTs/multiplications/etc., the planner will keep track of intermediate26results itself.2728For arithmetic expression evaluation, we want to have lots of29interpreters (at least one, and possibly several, per30specially-handled type). Also, for any given type, we have many31possible variants of instruction encoding, etc.; some of these could32be handled with conditional compilation, but some are more33complicated. So we end up writing an interpreter generator.3435We want to share as much code as possible across all of these36interpreters, while still maintaining the freedom to make drastic37changes in the interpretation strategy (which may change the38generated code, the calling convention for the interpreter, etc.)3940To make this work, the interpreter back-end is divided into three41parts:42431. The interpreter itself, in C or C++.44452. The wrapper, which is a Cython object holding the46constants, code, etc., and which actually calls the interpreter.47483. The code generator.4950We generate parts 1 and 2. The code generator is table-driven,51and we generate the tables for the code generator.5253There are a lot of techniques for fast interpreters that we do not yet54use; hopefully at least some of these will eventually be implemented:5556- using gcc's "labels as values" extension where available5758- top-of-stack caching5960- superinstructions and/or superoperators6162- static stack caching6364- context threading/subrouting threading6566- selective inlining/dynamic superinstructions6768- automatic replication6970Interpreters may be stack-based or register-based. Recent research71suggests that register-based interpreters are better, but the72researchers are investigating interpreters for entire programming73languages, rather than interpreters for expressions. I suspect74that stack-based expression interpreters may be better. However,75we'll implement both varieties and see what's best.7677The relative costs of stack- and register-based interpreters will78depend on the costs of moving values. For complicated types (like79mpz_t), a register-based interpreter will quite likely be better,80since it will avoid moving values.8182We will NOT support any sort of storage of bytecode; instead, the83code must be re-generated from expression trees in every Sage run.84This means that we can trivially experiment with different styles of85interpreter, or even use quite different interpreters depending on86the architecture, without having to worry about forward and backward87compatibility.88"""8990#*****************************************************************************91# Copyright (C) 2009 Carl Witty <[email protected]>92# Copyright (C) 2015 Jeroen Demeyer <[email protected]>93#94# This program is free software: you can redistribute it and/or modify95# it under the terms of the GNU General Public License as published by96# the Free Software Foundation, either version 2 of the License, or97# (at your option) any later version.98# http://www.gnu.org/licenses/99#*****************************************************************************100101#####################################################################102# This module is used during the Sage build process, so it should not103# use any other Sage modules. (In particular, it MUST NOT use any104# Cython modules -- they won't be built yet!)105# Also, we have some trivial dependency tracking, where we don't106# rebuild the interpreters if this file hasn't changed; if107# interpreter configuration is split out into a separate file,108# that will have to be changed.109#####################################################################110111112import os113from os.path import getmtime114115from .generator import AUTOGEN_WARN, InterpreterGenerator116from .instructions import *117from .memory import *118from .specs.base import *119from .storage import *120from .utils import *121122# Tuple of (filename_root, extension, method) where filename_root is the123# root of the filename to be joined with "_<interpreter_name>".ext and124# method is the name of a get_ method on InterpreterGenerator that returns125# the contents of that file126_INTERPRETER_SOURCES = [127('interp', 'c', 'interpreter'),128('wrapper', 'pxd', 'pxd'),129('wrapper', 'pyx', 'wrapper')130]131132133def build_interp(interp_spec, dir):134r"""135Given an InterpreterSpec, write the C interpreter and the Cython136wrapper (generate a pyx and a pxd file).137138EXAMPLES::139140sage: from sage_setup.autogen.interpreters.internal import *141sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter142sage: testdir = tmp_dir()143sage: rdf_interp = RDFInterpreter()144sage: build_interp(rdf_interp, testdir)145sage: with open(testdir + '/interp_rdf.c') as f:146....: f.readline()147'/* Automatically generated by ... */\n'148"""149150ig = InterpreterGenerator(interp_spec)151152for filename_root, ext, method in _INTERPRETER_SOURCES:153filename = '{}_{}.{}'.format(filename_root, interp_spec.name, ext)154method = getattr(ig, 'get_{}'.format(method))155path = os.path.join(dir, filename)156write_if_changed(path, method())157158159def rebuild(dirname, force=False, interpreters=None, distribution=None):160r"""161Check whether the interpreter and wrapper sources have been written162since the last time this module was changed. If not, write them.163164INPUT:165166- ``dirname`` -- name of the target directory for the generated sources167168- ``force`` -- boolean (default ``False``); if ``True``, ignore timestamps169and regenerate the sources unconditionally170171- ``interpreters`` -- an iterable of strings, or ``None`` (the default,172which means all interpreters); which interpreters to generate173174- ``distribution`` -- a string (the distribution name such as175``"sagemath-categories"``) or ``None`` (the default, which means176the monolithic Sage library)177178EXAMPLES:179180Monolithic build::181182sage: from sage_setup.autogen.interpreters.internal import *183sage: testdir = tmp_dir()184sage: rebuild(testdir)185Generating interpreters for fast_callable in ...186-> First build of interpreters187sage: with open(testdir + '/wrapper_el.pyx') as f:188....: f.readline()189'# Automatically generated by ...\n'190"""191# This line will show up in "sage -b" (once per upgrade, not every time192# you run it).193print(f"Generating interpreters for fast_callable in {dirname}")194195if interpreters is None:196interpreters = ['CDF', 'Element', 'Python', 'RDF', 'RR', 'CC']197198from importlib import import_module199200_INTERPRETERS = [201getattr(202import_module(203".specs." + interpreter.lower(), package=__name__204),205interpreter + "Interpreter",206)207for interpreter in interpreters208]209210if distribution is None:211all_py = 'all.py'212else:213all_py = f'all__{distribution.replace("-", "_")}.py'214215try:216os.makedirs(dirname)217except OSError:218if not os.path.isdir(dirname):219raise220221# Although multiple files are generated by this function, since222# they are all generated at once it suffices to make sure if just223# one of the generated files is older than the generator sources224class NeedToRebuild(Exception):225pass226try:227if force:228raise NeedToRebuild("-> Force rebuilding interpreters")229gen_file = os.path.join(dirname, all_py)230if not os.path.isfile(gen_file):231raise NeedToRebuild("-> First build of interpreters")232233gen_timestamp = getmtime(gen_file)234src_dir = os.path.dirname(__file__)235for root, dirs, files in os.walk(src_dir):236for basename in files:237if basename.endswith(".py"):238src_file = os.path.join(root, basename)239src_timestamp = getmtime(src_file)240if src_timestamp > gen_timestamp:241raise NeedToRebuild("-> Rebuilding interpreters because {} changed".format(src_file))242except NeedToRebuild as E:243# Rebuild244print(E)245else:246return # Up-to-date247248for interp in _INTERPRETERS:249build_interp(interp(), dirname)250251with open(os.path.join(dirname, all_py), 'w') as f:252f.write("# " + AUTOGEN_WARN)253254with open(os.path.join(dirname, '__init__.py'), 'w') as f:255f.write("# " + AUTOGEN_WARN)256257258