Copyright © 1998, 2001 by Ian Horswill, see comment within this file.
Girl compiler interface
Calling the GRL compiler
The GRL compiler works by:
- Expanding calls to signal procedures until the program is reduced
to a network of primitive signals (sources, transducer calls, and primitive
calls)
- Inferring the data type of each signal.
- Generating for each primitive signal a code fragment that will compute the
value of the signal from the value of its inputs. For reasons not
worth explaining, this is misnamed reification.
- Pasting the code fragments together into a big infinite loop
- Optimizing the loop using partial evaluation
- Translating the loop into the desired target language using a back-end
code generator
The generated code can either be run inside of scheme48 itself or written as
a text file for input to a C or BASIC compiler. After compilation, GRL
code requires a small amount of run-time support for things like checking
the system clock.
Until back-end code generation, the code is represented in a subset of Scheme
called the intermediate language. Intermediate code is non-recursive and doesn't
perform any run-time type checking or storage allocation. That is to say it might
as well be C. The back-end code generators are largely just
pretty-printers that know, for example to print (set! x 4) as x=4.
The GRL compiler is split between several interlocking Scheme packages:
GRL-top-level exports the compile and compile-to-file
procedures that compile signals to scheme code.
GRL-compiler exports various internal functions useful
for advanced programming or for debugging the compiler.
scheme48-run-time provides the run-time support for
running GRL code inside scheme48 itself.
- The various back-ends are contained in separate packages. Each
exports a set of procedures that compile signals to the back end's target
language.
Compilation procedures are called with a list of roots. Roots can be
signals, lists of signals, lists of lists of signals, etc. They're called roots
because the compiler performs a depth-first search of the graph of all signals, starting
from the roots. Any signal that is encountered in the process of the walk is emitted
as intermediate code. Other signals are ignored. Therefore, a
library can define thousands of signals, but only those signals needed for the
computation of the roots will actually appear in the compiled code.
Package GRL-top-level
The GRL-top-level package provides access to a set of procedures that allow you
compile intermediate code from a list of roots. Since they generate intermediate
code, they are mostly useful for examining the code being generated by the compiler.
When compiling code for real, you will most likely use a back end tailored for your
specific robot or target language.
- (compile roots ...)
- Compiles roots to Scheme intermediate code and displays it on the screen or in
the output file specified by the :output-file compiler
option.
- (compile-to-file file-name roots ...)
- Compiles roots to Scheme intermediate code and writes it to output-file.
- (compile-and-run roots ...)
- Compiles roots to Scheme intermediate code and evals the
code. This lets you do limited testing on your desktop, or to run the GRL
compiler directly on your robot if it supports Scheme48. This is only
useful if you have opened
scheme48-run-time or some other
package that provides the GRL run-time support.
Package structure of the compiler
Package GRL-compiler
This package is mostly intended for programmers developing new back ends.
However, some of the options will also be useful for powerusers trying to tweak the code
generated by the compiler. The only procedure exported by the package that should be
called by normal users is compiler-options. It
takes a list of keywords and values, a la Common Lisp.
User-level compiler options
- :clock-period
- :clock-frequncy
- Specifies target frequency of main command loop.
- :force-expression
- Prevents inlining of some or all signals. If #t, then no signals are inlined.
If 'roots, then only the signals specfied in the call the compiler will be
prevented from being inlined.
- :target-procedure-name
- The name of the procedure that will contain the main while loop.
- :temp-name-prefix
- A string to be prepended to every temporary variable generated by the compiler.
Used to prevent name collisions when two different programs generated by the compiler are
to be run in parallel.
Mostly used for compiler debugging
- :show-raw-intermediate-code
- Compiler pretty-prints Scheme intermediate code before partial evaluation.
- :show-partially-evaluated-code
- Compiler pretty-prints Scheme intermediate code after partial evaluation.
- :show-expanded-signals
- Compiles displays list of all signals encountered during its inital walk of the signal
graph.
- :show-sorted-signals
- Compiles displays final sorted list of all primitive signals for which code is to be
generated.
Internal compiler options
- :code-printer
- The procedure called to print the intermediate code
- :output-file
- The file to write the code to. #f means write it to the screen.
- :typed-declarations
- Whether to add type information to variable declarations in the intermediate code
- :global-variables
- Forces all variables to be global
- :generate-debug-info
- Whether extra debugging information should be added to the object code
- :generate-while-loop
- Whether the code should be wrapped in a while loop
- :never-inline-conditionals
- Prevents if and nth calls from ever being inlined
- :generate-lets
- If false, lets will be changed into applications of lambdas.
- :flatten-conditionals
- Prevents if from being placed in an subexpressions.
- :expand-cond
:expand-case
:expand-when-and-unless
- Whether these should be translated into ifs.
- :report-cycles
- Whether the compiler should tell you about cycles in the signal graph