Path: blob/develop/src/sage_setup/autogen/interpreters/internal/__init__.py
4086 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#####################################################################110111from __future__ import absolute_import, print_function112113import os114from os.path import getmtime115116from .generator import AUTOGEN_WARN, InterpreterGenerator117from .instructions import *118from .memory import *119from .specs.base import *120from .storage import *121from .utils import *122123# Tuple of (filename_root, extension, method) where filename_root is the124# root of the filename to be joined with "_<interpreter_name>".ext and125# method is the name of a get_ method on InterpreterGenerator that returns126# the contents of that file127_INTERPRETER_SOURCES = [128('interp', 'c', 'interpreter'),129('wrapper', 'pxd', 'pxd'),130('wrapper', 'pyx', 'wrapper')131]132133134def build_interp(interp_spec, dir):135r"""136Given an InterpreterSpec, write the C interpreter and the Cython137wrapper (generate a pyx and a pxd file).138139EXAMPLES::140141sage: from sage_setup.autogen.interpreters.internal import *142sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter143sage: testdir = tmp_dir()144sage: rdf_interp = RDFInterpreter()145sage: build_interp(rdf_interp, testdir)146sage: with open(testdir + '/interp_rdf.c') as f:147....: f.readline()148'/* Automatically generated by ... */\n'149"""150151ig = InterpreterGenerator(interp_spec)152153for filename_root, ext, method in _INTERPRETER_SOURCES:154filename = '{}_{}.{}'.format(filename_root, interp_spec.name, ext)155method = getattr(ig, 'get_{}'.format(method))156path = os.path.join(dir, filename)157write_if_changed(path, method())158159160def rebuild(dirname, force=False, interpreters=None, distribution=None):161r"""162Check whether the interpreter and wrapper sources have been written163since the last time this module was changed. If not, write them.164165INPUT:166167- ``dirname`` -- name of the target directory for the generated sources168169- ``force`` -- boolean (default ``False``); if ``True``, ignore timestamps170and regenerate the sources unconditionally171172- ``interpreters`` -- an iterable of strings, or ``None`` (the default,173which means all interpreters); which interpreters to generate174175- ``distribution`` -- a string (the distribution name such as176``"sagemath-categories"``) or ``None`` (the default, which means177the monolithic Sage library)178179EXAMPLES:180181Monolithic build::182183sage: from sage_setup.autogen.interpreters.internal import *184sage: testdir = tmp_dir()185sage: rebuild(testdir)186Generating interpreters for fast_callable in ...187-> First build of interpreters188sage: with open(testdir + '/wrapper_el.pyx') as f:189....: f.readline()190'# Automatically generated by ...\n'191192Modularized build::193194sage: testdir = tmp_dir()195sage: rebuild(testdir, interpreters=['Element', 'Python'],196....: distribution='sagemath-categories')197Generating interpreters for fast_callable in ...198-> First build of interpreters199sage: with open(testdir + '/all__sagemath_categories.py') as f:200....: f.readline()201'# Automatically generated by ...'202"""203# This line will show up in "sage -b" (once per upgrade, not every time204# you run it).205print(f"Generating interpreters for fast_callable in {dirname}")206207if interpreters is None:208interpreters = ['CDF', 'Element', 'Python', 'RDF', 'RR', 'CC']209210from importlib import import_module211212_INTERPRETERS = [213getattr(214import_module(215".specs." + interpreter.lower(), package=__name__216),217interpreter + "Interpreter",218)219for interpreter in interpreters220]221222if distribution is None:223all_py = 'all.py'224else:225all_py = f'all__{distribution.replace("-", "_")}.py'226227try:228os.makedirs(dirname)229except OSError:230if not os.path.isdir(dirname):231raise232233# Although multiple files are generated by this function, since234# they are all generated at once it suffices to make sure if just235# one of the generated files is older than the generator sources236class NeedToRebuild(Exception):237pass238try:239if force:240raise NeedToRebuild("-> Force rebuilding interpreters")241gen_file = os.path.join(dirname, all_py)242if not os.path.isfile(gen_file):243raise NeedToRebuild("-> First build of interpreters")244245gen_timestamp = getmtime(gen_file)246src_dir = os.path.dirname(__file__)247for root, dirs, files in os.walk(src_dir):248for basename in files:249if basename.endswith(".py"):250src_file = os.path.join(root, basename)251src_timestamp = getmtime(src_file)252if src_timestamp > gen_timestamp:253raise NeedToRebuild("-> Rebuilding interpreters because {} changed".format(src_file))254except NeedToRebuild as E:255# Rebuild256print(E)257else:258return # Up-to-date259260for interp in _INTERPRETERS:261build_interp(interp(), dirname)262263with open(os.path.join(dirname, all_py), 'w') as f:264f.write("# " + AUTOGEN_WARN)265266with open(os.path.join(dirname, '__init__.py'), 'w') as f:267f.write("# " + AUTOGEN_WARN)268269270