[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


Compiling with Hobbit

Compiling And Linking

(require 'compile)

Function: hobbit name1.scm name2.scm ...
Invokes the HOBBIT compiler to translate Scheme files `name1.scm', `name2.scm', ... to C files `name1.c' and `name1.h'.

Function: compile-file name1.scm name2.scm ...
Compiles the HOBBIT translation of name1.scm, name2.scm, ... to a dynamically linkable object file name1<object-suffix>, where <object-suffix> is the object file suffix for your computer (for instance, `.so'). name1.scm must be in the current directory; name2.scm, ... may be in other directories.

If a file named `name1.opt' exists, then its options are passed to the build invocation which compiles the c files.

cd ~/scm/
scm -rcompile -e'(compile-file "example.scm")'

Starting to read example.scm

Generic (slow) arithmetic assumed: 1.0e-3 found.

** Pass 1 completed **
** Pass 2 completed **
** Pass 3 completed **
** Pass 4 completed **
** Pass 5 completed **
** Pass 6 completed **

C source file example.c is built.
C header file example.h is built.

These top level higher order procedures are not clonable (slow):
(nonkeyword_make-promise map-streams generate-vector runge-kutta-4)
These top level procedures create non-liftable closures (slow):
(nonkeyword_make-promise damped-oscillator map-streams scale-vector elementwise runge-kutta-4 integrate-system)

; Scheme (linux) script created by SLIB/batch Sun Apr  7 22:49:49 2002
; ================ Write file with C defines
(delete-file "scmflags.h")
(call-with-output-file
  "scmflags.h"
  (lambda (fp)
    (for-each
      (lambda (string) (write-line string fp))
      '("#define IMPLINIT \"Init5e5.scm\""
        "#define BIGNUMS"
        "#define FLOATS"
        "#define ARRAYS"
        "#define DLL"))))
; ================ Compile C source files
(system "gcc -O2 -fpic -c -I/usr/local/lib/scm/ example.c")
(system "gcc -shared -o example.so example.o -lm -lc")
(delete-file "example.o")
; ================ Link C object files
(delete-file "slibcat")

Compilation finished at Sun Apr  7 22:49:50

Function: compile->executable exename name1.scm name2.scm ...
Compiles and links the HOBBIT translation of name1.scm, name2.scm, ... to a SCM executable named exename. name1.scm must be in the current directory; name2.scm, ... may be in other directories.

If a file named `exename.opt' exists, then its options are passed to the build invocation which compiles the c files.

cd ~/scm/
scm -rcompile -e'(compile->executable "exscm" "example.scm")'

Starting to read example.scm

Generic (slow) arithmetic assumed: 1.0e-3 found.

** Pass 1 completed **
** Pass 2 completed **
** Pass 3 completed **
** Pass 4 completed **
** Pass 5 completed **
** Pass 6 completed **

C source file example.c is built.
C header file example.h is built.

These top level higher order procedures are not clonable (slow):
(nonkeyword_make-promise map-streams generate-vector runge-kutta-4)
These top level procedures create non-liftable closures (slow):
(nonkeyword_make-promise damped-oscillator map-streams scale-vector elementwise runge-kutta-4 integrate-system)

; Scheme (linux) script created by SLIB/batch Sun Apr  7 22:46:31 2002
; ================ Write file with C defines
(delete-file "scmflags.h")
(call-with-output-file
  "scmflags.h"
  (lambda (fp)
    (for-each
      (lambda (string) (write-line string fp))
      '("#define IMPLINIT \"Init5e5.scm\""
        "#define COMPILED_INITS init_example();"
        "#define CCLO"
        "#define FLOATS"))))
; ================ Compile C source files
(system "gcc -O2 -c continue.c scmmain.c findexec.c script.c time.c repl.c scl.c eval.c sys.c subr.c debug.c unif.c rope.c example.c scm.c")
; ================ Link C object files
(system "gcc -rdynamic -o exscm continue.o scmmain.o findexec.o script.o time.o repl.o scl.o eval.o sys.o subr.o debug.o unif.o rope.o example.o scm.o -L/usr/local/lib/scm/ -lm -lc")

Compilation finished at Sun Apr  7 22:46:44

Note Bene: `#define CCLO' must be present in `scmfig.h'.

In order to see calls to the C compiler and linker, do

(verbose 3)

before calling these functions.

Error Detection

Error detection during compilation is minimal. In case your scheme code is syntactically incorrect, hobbit may crash with no sensible error messages or it may produce incorrect C code.

Hobbit does not insert any type-checking code into the C output it produces. Eg, if a hobbit-compiled program applies `car' to a number, the program will probably crash with no sensible error messages.

Thus it is strongly suggested to compile only throughly debugged scheme code.

Alternatively, it is possible to compile all the primitives into calls to the SCM procedures doing type-checking. Hobbit will do this if you tell it to assume that all the primitives may be redefined. Put

(define compile-all-proc-redefined #t)

anywhere in top level of your scheme code to achieve this.

Note Bene: The compiled code using

(define compile-all-proc-redefined #t)

will typically be much slower than one produced without using

(define compile-all-proc-redefined #t).

All errors caught by hobbit will generate an error message

COMPILATION ERROR:
<description of the error>

and hobbit will immediately halt compilation.

Hobbit Options

  1. Selecting the type of arithmetics. By default hobbit assumes that only immediate (ie small, up to 30 bits) integers are used. It will automatically assume general arithmetics in case it finds any non-immediate numbers like 1.2 or 10000000000000 or real-only procedures like real-sin anywhere in the source. Another way to make Hobbit assume that generic arithmetic supported by SCM (ie exact and/or inexact reals, bignums) is also used, is to put the following line somewhere in your scheme source file:
    (define compile-allnumbers t)
    
    where t is arbitrary. In that case all the arithmetic primitives in all the given source files will be assumed to be generic. This will make operations with immediate integers much slower. You can use the special immediate-integer-only forms of arithmetic procedures to recover:
    %negative?  %number?    %>      %>=     %=      %<=     %<
    %positive?  %zero?      %eqv?   %+      %-      %*      %/
    
    See section The Language Compiled.
  2. Redefinition of procedures. By default hobbit assumes that neither primitives nor compiled procedures are redefined, neither before the compiled program is initialized, during its work or later via the interpreter. Hobbit checks the compiled source and whenever some variable bar is defined as a procedure, but is later redefined, or set! is applied to bar, then hobbit assumes thas this particular variable bar is redefinable. bar may be a primitive (eg `car') or a name of a compiled procedure. Note Bene: According to the Report 4 it is NOT allowed to use scheme keywords as variables (you may redefine these as macros defined by defmacro, though):
    => and begin case cond define delay do else if lambda
    let let letrec or quasiquote quote set! unquote unquote-splicing
    
    If you want to be able to redefine some procedures, eg. `+' and `baz', then put both
    (set! + +)
    (set! baz baz)
    
    somewhere into your file. As a consequence hobbit will generate code for `+' and `baz' using the run-time values of these variables. This is generally much slower than using non-redefined `+' and `baz' (especially for `+'). If you want to be able to redefine all the procedures, both primitives (eg `car') and the compiled procedures, then put the following into the compiled file:
    (define compile-all-proc-redefined t)
    
    where t is arbitrary. If you want to be able to redefine all the compiled procedures, but not the scheme primitives, then put the following into the compiled file:
    (define compile-new-proc-redefined t)
    
    where t is arbitrary. Again, remember that redefinable procedures will be typically much slower than non-redefinable procedures.
  3. Inlined variables and procedures. You may inline top-level-defined variables and procedures. Notice that inlining is DIFFERENT for variables and procedures! NEVER inline variables or procedures which are set! or redefined anywhere in you program: this will produce wrong code.
    (define foo 100)
    
    then `foo' will be everywhere replaced by `100'. To declare some variables foo and bar to be inlined, put a following definition anywhere into your file:
    (define compile-inline-vars '(foo bar))
    
    Usually it makes sense to inline only these variables whose value is either a small integer, character or a boolean. Note Bene: Do not use this kind of inlining for inlining procedures! Use the following for procedures:
    (define (foo x) (+ x 2))
    
    then any call
    (foo something)
    
    will be replaced by
    (+ something 2)
    
    Inlining is NOT safe for variable clashes -- in other words, it is not "hygienic". Inlining is NOT safe for recursive procedures -- if the set of inlined procedures contains either immediate or mutual (foo calling bar, bar calling foo) recursion, the compiler will not terminate. To turn off full inlining (harmful for recursive funs), change the definition of the *full-inlining-flag* in the section "compiler options" to the value #f instead of #t. To declare some procedures foo and bar to be inlined, put a following definition anywhere into your file:
    (define compile-inline '(foo bar))
    
  4. Speeding up vectors: Put
    (define compile-stable-vectors '(baz foo))
    
    into your file to declare that baz and foo are vector names defined once on the top level, and set! is never applied to them (vector-set! is, of course, allowed). This speeds up vector reference to those vectors by precomputing their location.
  5. Speeding up and hiding certain global variables: Put
    (define compile-uninterned-variables '(bazvar foovar))
    
    into your file to declare that bazvar and foovar are defined on the top level and they do always have an immediate value, ie a boolean, immediate (30-bit) integer or a character. Then bazvar and foovar will NOT be accessible from the interpreter. They'll be compiled directly into static C vars and used without an extra C *-operation prefixed to other global scheme variables.
  6. Intermediate files To see the output of compiler passes, change the following definition in `hobbit.scm'.
    (define *build-intermediate-files* #f)
    
    to:
    (define *build-intermediate-files* #t)
    
  7. Name clashes It may happen that several originally different scheme variable names are represented by one and the same C variable. This will happen, for example, if you have separate variables a-1 and a_1. If such (or any other) name clashes occur you may need to change some control variables in the first sections of `hobbit.scm' (up to the section "global variable defs") or just rename some variables in your scheme program.
  8. Other options See various control variables in the first sections of `hobbit.scm' (up to section "global variable defs").

CC Optimizations

When using the C compiler to compile the C code output by hobbit, always use strong optimizations (eg. `cc -xO3' for cc on Sun, `gcc -O2' or `gcc -O3' for gcc). Hobbit does not attempt to do optimizations of the kind we anticipate from the C compiler, therefore it often makes a serious difference whether the C compiler is run with a strong optimization flag or not.

For the final and fast version of your program you may want to first recompile the whole scm (scmlit for the version scm4e2) using the `-DRECKLESS' flag suppressing error checking: the hobbit-compiled code uses some SCM primitives in the compiled files with the suffix .o, and a number of these primitives become faster when error checking is disabled by `-DRECKLESS'. Notice that hobbit never inserts error checking into the code it produces.


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]