-hygienic
option?
(define-macro foo bar)
not work?
define-foreign-variable
or
define-constant
or define-inline
not seen outside of the
containing source file?
cond-expand
know which features are registered in used units?
STACKTRACE
?
cons
still trigger garbage collections?
./configure; make
fails during installation?
_C_..._toplevel
?
out of memory - heap full
when compiling
a large soure file?
define-constant
not honoured in case
contructs?
-unsafe
or unsafe declarations, it crashes during execution.
extended-bindings
or
usual-integrations
declaration or compiler option is used?
define-reader-ctor
not work in my compiled program?
-debug-level 2
and -unsafe
generate link error?
csc
abort with error message on my non-UNIX system?
match
?
formatprofile
under Windows/DOS?
csi
inside an emacs buffer under Windows, nothing happens.
define-external
?
match-error-control
during compilation?
-benchmark-mode
not imply -inline
?
match
in combination with the highlevel (syntax-case
) macro
system?
eval-when
in combination with the
highlevel macro system?
Since Scheme is a relatively simple language, a large number of implementations exist and each has its specific advantages and disadvantages. Some are fast, some provide a rich programming environment. Some are free, others are tailored to specific domains, and so on. The reasons for the existance of CHICKEN are:
A nice property of Baker's Cheney on the M.T.A. concept is that each Scheme procedure
is translated into (at least one) equivalent C function, with exactly the same calling
convention that C uses. Procedures that accept a variable number of parameters are handled
using ANSI C's stdarg
facility. The first three parameters of a compiled procedure
contain all necessary information to allow for closures and argument-count checks.
An example:
(lambda (x y z) ...)is translated (roughly) into:
C_word f(int argc, C_word closure, C_word continuation, C_word x, C_word y, C_word z) { ... }where
argc
contains the number of arguments passed, closure
contains
a pointer to the closure of the given procedure, and continuation
is
a pointer to a continuation-closure that is to be called with the result(s) of the
body of the procedure. It can be seen here that the program is transformed into
CPS (continuation passing style).
The simplest method to use for calling C functions is foreign-lambda
(see the User's manual).
If you need a more flexible access to C, then you can link to an external
procedure from Scheme with the ##core#primitive
or ##core#inline
special forms.
An example shows how this works: We add an external routine that expects
a small integer and returns that integer multiplied with itself (squared).
;;; square.scm: (declare ;; "foreign-declare": embed declarations on the C level directly into compiled code (foreign-declare "extern C_word square(int c, C_word closure, C_word cont, C_word n);") ) (define square (##core#primitive "square")) (let ((n 123)) (print n " squared is " (square n)) ) /* square.c: */ #include "chicken.h" /* "C_noret" is for the GNU compiler: it indicates that this function never returns: */ extern C_word square(int c, C_word closure, C_word cont, C_word n) C_noret; C_word square(int c, C_word closure, C_word cont, C_word n) { /* We omit all error checking here! */ int n2 = C_unfix(n), /* convert "n" from fixnum into int */ n3 = n2 * n2; C_kontinue(cont, C_fix(n3)); /* convert "n3" into fixnum and call continuation */ }That's about all.
chicken.h
contains some useful macros like C_fix
and C_unfix
to convert between Scheme fixnums and C integers and vice versa. C_kontinue
extracts the code-pointer from the continuation closure and calls it.
There exists yet another method of interfacing to C: the ##core#inline
form.
An expressions of the form (##core#inline NAME ARGUMENT1 ...)
is embedded
directly into the compiled code as a call to a standard C procedure or macro,
so to use it in the example above we could write:
;;; square.scm: (declare (foreign-declare "#define simple_square(n) C_fix(C_unfix(n) * C_unfix(n))") ) (let ((n 123)) (printf "~s squared is ~s~%" n (##core#inline simple_square n)) )Which method you use depends on the situation: the interface via
##core#primitive
is more powerful and flexible. But ##core#inline
is much easier to use and handles
macros as well. One more point: an ##core#inline
call with no arguments is translated into NAME()
,
so this can not access a C-macro because it is not valid macro syntax.
All parameters to and from routines interacting with the CHICKEN runtime-system
should be of type C_word
, which has the size of a machine word.
Needless to say, all this is only working in compiled code and not in the interpreter.
Use the foreign-callback-lambda
and define-external
forms.
For more information, see the User's manual.
Instead of linking your object module with the normal runtime system
(chicken-runtime.lib
), link it with chicken-dll-runtime.lib
.
You can use the define-entry-point
facility to interface to
the Scheme code (see the User's manual).
Instead of linking your object module with the normal runtime-system,
link it with chicken-embedded-runtime.lib
/ libembedded-chicken.a
.
This eliminates the default main
routine. Your own main
function (that you link to
the final executable) can now perform any necessary operations and use the define-entry-point
facility to interface to Scheme.
Remember that the command line (and especially runtime-parameters like
-:h...
) is not parsed, now.
CHICKEN should work with any decent C compiler and on most platforms. The platform dependent consists mostly of:
alloca
has to be available (or something equivalent). It is important
that the C stack grows downwards!
chicken.h
have to be changed.
-hygienic
option?
The high-level macro system adds some overhead to the system.
Using this options evaluates some standard macro-definitions and will
slow down the startup-times of the compiler and the interpreter.
For many situations the simple define-macro
style macro-system is
sufficient, and so the use of the extended macro system is optional.
(define-macro foo bar)
not work?Consider this code snippet:
(define (double x) (list '* x x)) (define-macro twice double)Because the macro
twice
is defined at compile time (following forms may refer
to it), functions defined at runtime (as double
in this case) are not available.
The alternative syntax of define-macro
:
(define-macro twice (lambda (x) (list '* x x)))does just exist to make porting other Scheme code easier. It is not able to assign procedures computed at runtime as macro expanders.
Macro bodies that are defined and used in a compiled source-file are
evaluated during compilation and so have no access to definitions in the
compiled file. Note also that during compile-time macros are only available in
the same source file in which they are defined. Files included via include
are considered part of the containing file.
Send e-mail to felix@call-with-current-continuation.org with some hints about the problem, like version/build of the compiler, platform, system configuration, code that causes the bug, etc.
define-foreign-variable
or
define-constant
or define-inline
not seen outside of the
containing source file?Accesses to foreign variables are translated directly into C constructs that access the variable, so the Scheme name given to that variable does only exist during compile-time. The same goes for constant- and inline-definitions: The name is only there to tell the compiler that this reference is to be replaced with the actual value.
cond-expand
know which features are registered in used units?
Each unit used via (declare (uses ...))
is registered as a feature and
so a symbol with the unit-name can be tested by cond-expand
during macro-expansion-time.
Features registered using the register-feature!
procedure are only
available during run-time of the compiled file. You can use the eval-when
form
to register features at compile time.
If you don't need eval
, you can just use the library
unit:
(declare (uses library)) (display "Hello, world!\n")(Don't forget to compile with the
-explicit-use
option)
Compiled with Visual C++ this generates an excutable of around 240 kilobytes.
It is theoretically possible to compile something without the library, but
a program would have to implement quite a lot of support code on it's own.
It works nicely with the interpreter csi
, and it should also be possible to
get it to work with compiled code. An "init"-file for the interpreter is distributed with CHICKEN.
To change the default heap size in csi
(for example, when you don't want or can not set
the -:h...
option), you have to recompile the interpreter:
% chicken $CHICKEN_HOME/src/csi.scm -quiet -output-file csi.c -prologue $CHICKEN_HOME/src/build.scm \ -prelude "(declare (uses regex unistd))" -postlude "(##csi#run)" -heap-size 64m -optimize-level 2 % gcc csi.c `chicken-config -cflags -libs -extra-libs` -o csiNow you should have a new executable
csi
, with a default heap size of 64 MB. Note that on non-UNIX
platforms the -prelude
option above has to be omitted.
STACKTRACE
?
The stack-trace file is a list of the last invoked named procedures. Since the compiler
translates into continuation-passing style, a procedure never returns and only
(tail-) calls occur. The stack-trace is a log of executed tail-calls where each
line contains the name of the called routine, the @
character and the line in which
the call (or a macro expanding into that call) occurred.
Only calls in units compiled without the -no-debug
option are traced.
build.scm
chicken.scm
support.scm
compiler.scm
batch-driver.scm
c-platform.scm
c-backend.scm
Instead of linking your object module with the normal runtime-system,
link it with chicken-gui-runtime.lib
. Also you should compile
the translated C-file with the WINDOWS_GUI
macro defined. This includes
windows.h
in the generated program. The GUI runtime displays error messages in a message box and does some rudimentary command-line
parsing.
cons
still trigger garbage collections?
Under CHICKENs implementation policy, tail recursion is achieved simply by avoiding to
return from a function call. Since the programs is CPS converted, a continuous
sequence of nested procedure calls is performed. At some stage the stack-space
has to run out and the current procedure and its parameters (including the current continuation) are stored somewhere
in the runtime system. Now a minor garbage collection occurs and rescues all live
data from the stack (the first heap generation) and moves it into the the second heap generation. Than the stack is cleared (using
a longjmp
) and execution can continue from the saved state.
With this method arbitrary recursion (in tail- or non-tail position) can happen,
provided the application doesn't run out of heap-space.
(The difference between a tail- and a non-tail call is that the tail-call has no
live data after it invokes it's continuation - and so the amount of heap-space needed stays constant)
There are a number of declaration specifiers that should be used to speed up
compiled files: declaring (standard-bindings)
is mandatory, since this enables
most optimizations. Even if some standard procedures should be redefined, you can
list untouched bindings in the declaration.
Declaring (extended-bindings)
lets the compiler chose faster versions of certain
internal library functions. This might give another speedup. You can also use the
the usual-integrations
declaration, which is identical to declaring
standard-bindings
and extended-bindings
.
Declaring (block)
tells the compiler that global procedures are not changed
outside the current compilation unit, this gives the compiler some more
opportunities for optimization.
If no floating point arithmetic is required, then declaring (number-type fixnum)
can give a big performance improvement, because the compiler can now inline
most arithmetic operations.
Declaring (unsafe)
will switch off many safety checks.
You can speed up call/cc by using the no-winding-callcc
declaration or command-line
option.
If threads are not used, you can declare (disable-interrupts)
.
The declaration (inline)
or the compiler option -inline
enables inlining
that can give some speed improvements. This depends largely on the compiled
program. It might be advisable to experiment with different inlining settings
(compiler options -inline-limit
and -inline-passes
, declaration inline-limit
).
Note that compilation times will increase substantially.
You should always use maximum optimizations settings for your C compiler.
Good GCC compiler options on Pentium (and compatible) hardware are:
-O3 -fomit-frame-pointer -fstrict-aliasing -mcpu=i586 -mpreferred-stack-boundary=2
./configure; make
fails during installation?
The distribution contains a simple makefile for the GNU C compiler, named makefile.gcc
.
This does a basic statically linked build of the system circumventing the usual configuration stuff.
Invoke the makefile with
% make -f makefile.gccIf this doesn't work also, then please contact me at felix@call-with-current-continuation.org.
If you made changes to the compiler-code in the compiler's source files, the compiler can be rebuilt with the following procedure:
build.scm
.
chicken.scm
should be compiled with the -explicit-use
option, since these are library units. You should also give the compiler a large heap with
the -:h
runtime option.
Good optimization settings for translating the sources are -optimize-level 2
.
Compiling chicken.scm
with -heap-size 16m
allows for compilation
of larger source files.
Take care! A messed up rebuild that inadvertedly overwrites an older, working version can be a very painful experience (that I have made more than once).
_C_..._toplevel
?
This message indicates that your program uses a library-unit, but that the
object-file or library was not supplied to the linker. If you have the unit
foo
, which is contained in foo.o
than you have to supply it to the
linker like this (assuming a GCC environment):
% chicken program.scm -output-file program.c % gcc program.c foo.o `chicken-config -cflags -libs` -o programThe CHICKEN runtime library (
libchicken.a
or chicken-runtime.lib
)
already contains the units library
, eval
, modules
and syntax-case
.
The extras library (libstuffed-chicken.a
or chicken-utilities.lib
)
contains the units extras
, format
, srfi-1
, srfi-4
,
srfi-13
, srfi-14
, srfi-18
, match
,
lolevel
, tinyclos
,
regex
and unistd
(if available).
out of memory - heap full
when compiling
a large soure file?
The compiler creates fairly large internal data structures while compiling a file and
this may exceed the static heap-limit. But since the compiler is just another
compiled Scheme program, we can enlarge it's heap via the -:h
runtime option.
The default heap-size is 8 megabytes. To compile a large file, compile it like this:
% chicken foo.scm -output-file foo.c -:h32m
To cut down turnaround times during development of the compiler, you can run
it in the interpreter csi
, like this:
csi $CHICKEN_HOME/src/chicken.scm -include-path $CHICKEN_HOME/src SOURCE-FILENAME OPTION1 ...
define-constant
not honoured in case
contructs?
Case expands into a cascaded if
expression, where the first item in each arm
is treated as a quoted list. So the case
macro can not infer wether
a symbol is to be treated as a constant-name (defined via define-constant
) or
a literal symbol.
By default the interpreter csi
uses the library units extras, srfi-1, srfi-4, srfi-18, lolevel,
match
and syntax-case
. On UNIX platforms the library units regex
and unistd
are additionally used.
The CHICKEN distribution contains a Scheme script that provides an easy way of adding compiled library units to
the interpreter. For more information see the User's Manual.
-unsafe
or unsafe declarations, it crashes during execution.
The compiler option -unsafe
or the declaration (declare (unsafe))
disable
certain safety-checks to improve performance, so code that would normally
trigger an error will work unexpectedly or even crash the running application.
It is advisable to develop and debug a program in safe mode (without unsafe
declarations) and use this feature only if the application works properly.
When you use R5RS highlevel macros, the low-level macro system is completely disabled and
a new macroexpansion handler is installed. In addition to this derived syntax definitions
are loaded from highlevel-macros.scm
. The compiler and the interpreter perform this when
you invoke them with the -hygienic
option. To add this functionality to your own programs,
you can exexcute:
(##syncase#install-macro-package #f #f)This performs all necessary steps for you. Make sure that
highlevel-macros.scm
is
available (in your include-path).
Procedures can be mapped directly to C functions. When parameter lists in calls to such functions don't match the function's signature, then the C compiler will signal an error. Check your source code for internal procedure calls with non-matching argument counts.
To add a compiled user pass instead of an interpreted one, create a library unit and recompile
the main unit of the compiler (in the file chicken.scm
) with an additional uses
declaration. Then link all compiler modules and your (compiled) extension to create a new version of
the compiler, like this (assuming a UNIX like environment and also assuming all sources are in the
current directory):
% cat userpass.scm ;;;; userpass.scm - My very own compiler pass (declare (unit userpass)) ;; Perhaps more user passes/extensions are added: (let ([old (user-pass)]) (user-pass (lambda (x) (let ([x2 (do-something-with x)]) (if old (old x2) x2) ) ) ) ) ... % chicken userpass.scm -output-file userpass.c -explicit-use -quiet % chicken chicken.scm -output-file chicken-extended.c -quiet -postlude "(declare (uses userpass))" % gcc -c userpass.c `chicken-config -cflags` -o userpass.o % gcc -c chicken-extended.c `chicken-config -cflags` -o chicken-extended.o % gcc chicken-extended.o support.o compiler.o batch-driver.o c-platform.o \ c-backend.o userpass.o `chicken-config -libs -extra-libs` -o chicken-extended
extended-bindings
or
usual-integrations
declaration or compiler option is used?The following extended bindings are handled specially:
gc bitwise-and bitwise-ior bitwise-xor bitwise-not add1 sub1 fx+ fx- fx* fx/ fxmod
fx= fx> fx< fx>= fx<= fixnum? fxneg fxmax fxmin arithmetic-shift signum flush-output
not-pair? null-list? print print* u8vector->bytevector s8vector->bytevector u16vector->bytevector s16vector->bytevector
u32vector->bytevector
s32vector->bytevector f32vector->bytevector f64vector->bytevector block-ref block-set! number-of-slots
first second third fourth null-pointer?
.
define-reader-ctor
not work in my compiled program?The following piece of code does not work as expected:
(eval-when (compile) (define-reader-ctor 'integer->char integer->char) ) (print #,(integer->char 33))
The problem is that the compiler reads the complete source-file before doing any processing on it, so the sharp-comma form is encountered before the reader-ctor is defined. A possible solution is to include the file containing the sharp-comma form, like this:
(eval-when (compile) (define-reader-ctor 'integer->char integer->char) ) (include "other-file")
"other-file.scm":
(print #,(integer->char 33))
-debug-level 2
and -unsafe
generate link error?debugger
library unit is not available in the unsafe runtime libraries. Compile without the
-unsafe
option instead.
csc
abort with error message on my non-UNIX system?
The csc
script uses some definitions from the unistd
library, which is only
available on systems that provide UNIX systems that have the GNU regex package installed.
The compiler instruments the source file with a lot of support code to provide things like arbitrary restarting/returning
from activation frames. This will transform calls in tail-position into non-tail calls in all situations but the most
trivial ones (do
loops and named let
). This will result in a much higher memory
usage for allocating continuation frames. Try passing the -:hXXX
option when executing the program,
with a large value for XXX
.
match
?
Even when the match
unit is not used, the macros from that package are visible in the compiler.
The reason for this is that macros can not be accessed from library units (only when explicitly evaluated in running
code). To speed up macro-expansion time, the compiler and the interpreter both already provide the compiled
match-...
macro definitions. Macros shadowed lexically are no problem, but global definitions
of variables named identically to (global) macros are useless - the macro definition shadows the global
variable.
This problem can be solved in one of three ways:
(eval-when (compile eval) (undefine-macro! 'match))
To enable the read
procedure to read symbols and identifiers case sensitive, you can set the
parameter case-sensitivity
to #t
.
When the module system is used, the use of qualified names of the form
##
PREFIX#
SYMBOL is
disabled to avoid namespace collisions. Sometimes it might desirable to use internal procedures with module code.
A solution is to provide a module definition for the required identifiers, like this:
(define-module sys (export symbol-has-toplevel-binding?)) (define-module foo (import scheme sys) (begin (write (list (symbol-has-toplevel-binding? 'abc) (symbol-has-toplevel-binding? 'newline))) (newline) ) )
formatprofile
under Windows/DOS?
Just invoke csi
with the -script
option, like this:
csi -script SCRIPTFILENAME ARGUMENTS ...
There are two reasons why code involving callbacks can crash out of know apparent reason. The first is that it is
important to use foreign-callback-lambda/foreign-callback-lambda*
for the C code that is
to call back into Scheme. If this is not done than sooner or later the available stack space will be exhausted.
The second reason is that if the C code uses a large amount of stack storage, or if Scheme-to-C-to-Scheme calls are
nested deeply, then the available nursery space on the stack will run low. To avoid this it might be advisable
to run the compiled code with a larger nursery setting, i.e. run the code with -:s...
and a larger
value than the default (for example -:s300k
), or use the -nursery
compiler option.
Note that this can decrease runtime performance on some platforms.
csi
inside an emacs buffer under Windows, nothing happens.
Invoke csi
with the -:c
runtime option. Under Windows the interpreter thinks is
is not running under control of a terminal and doesn't print the prompt and does not flush the output stream properly.
define-external
?
You can enclose the name inside |...|
to use arbitrary characters in a symbol name. But note that
the name should be legal C syntax.
match-error-control
during compilation?
Use eval-when
, like this:
(eval-when (compile) (match-error-control #:unspecified) )
There are a number of reasons for this:
-benchmark-mode
not imply -inline
?
CHICKENs inlining heuristics are currently not overly successfull, so compiling with the -inline
option gives only minor or no performance improvements, but increases compilation-time substantially.
match
in combination with the highlevel (syntax-case
)
macro system?
It is generally not possible to mix lowlevel and highlevel macros in the same compilation unit (i.e. source file).
Unfortunately the match
macro package is implemented with low-level macros, so it is not
available when interpreted or compiled with the -hygienic
option.
But it is possible to split a project into separate compilation units and compile some of those with highlevel macros
and some without, because both macro-systems expand into compatible code.
eval-when
in combination with the
highlevel macro system?
eval-when
expands into an internal special form, which effectively puts the contained body into
a non-toplevel context with the syntax-case
macro system. An ugly but working solution
is to use set!
instead of define
.
No. The debugger is exclusively for compiled code. This may seem strange, since most other Scheme systems provide debugging support for the interpreter but no (or only rudimentary) debugging facilities for compiled code, but the intention is to provide ways to debug code that interacts with foreign code, etc.