-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
?
_C_toplevel
?
define-constant
not honoured in case
contructs?
require
doesn't seem to work.
-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?
match
?
csi
inside an emacs buffer under Windows, nothing happens.
define-external
?
match-error-control
during compilation?
eval-when
in combination with the
highlevel macro system?
"Error: qualified symbol syntax is not allowed"
.
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
(libchicken.lib
), link it with libchicken-dll.lib
.
You can use the define-entry-point
facility to interface to
the Scheme code (see the User's manual).
When you invoke the C compiler for your translated Scheme source program, add the C compiler option
-DC_EMBEDDED
, or pass -embedded
to the csc
driver program, so no entry-point function will be generated (main()
).
When your are finished with your startup processing, invoke:
CHICKEN_main(argc, argv, C_toplevel);where
C_toplevel
is the entry-point into the compiled Scheme code. You
should add the following declarations at the head of your code:
#include "chicken.h" extern void C_toplevel(C_word,C_word,C_word) C_noret;See the manual for more information.
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.
Macros are defined during compile time, so when a file has been compiled, the definitions are gone. An exception
to this rule are macros defined with define-macro
, which are also visible at run-time, i.e.
in eval
. To use macros defined in other files, use the include
special
form.
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
optimizer.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 choose 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
.
Some programs are very sensitive to the setting of the nursery (the first heap-generation). You
should experiment with different nursery settings (either by compiling with the -nursery
option or by using the -:s...
runtime option).
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)
.
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.
Good optimization settings for translating the sources are -optimize-level 2
.
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 libchicken.lib
)
already contains the units library
, eval
, modules
and syntax-case
, plus the internally used units profiler>
and
scheduler
.
The extras library (libstuffed-chicken.a
or libstuffed-chicken.lib
)
contains the units extras
, format
,
lolevel
, tinyclos
,
regex
, tcp
and posix
(if available).
The SRFI library (libsrfi-chicken.a
or libsrfi-chicken.lib
)
contains the units srfi-1
, srfi-4
, srfi-13
,
srfi-14
, srfi-18
, srfi-25
and
srfi-37
.
_C_toplevel
?
This means you have compiled a library unit as an application. When a unit-declaration (as in (declare (unit ...))
)
is given, then this file has a specially named toplevel entry procedure (see Q23). Just remove the declaration,
or compile this file to an object-module and link it to your application code.
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.
require
doesn't seem to work.
The extensions system (which you use when invoking require
or
require-for-syntax
looks up extension specifications and code
in the so called "registry". The registry is a directory that first has to be created
and initialized before it can be used. When installed normally, this directory is located
at /usr/local/lib/chicken
.
To create it and registry-information for the core libraries, enter
% csi -setupIf you do not have write access to this directory, run the command above as root or ask your local system-administrator.
-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.
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 optimizer.o batch-driver.o c-platform.o \ c-backend.o userpass.o `chicken-config -libs -extra-libs` -o chicken-extendedOn platforms that support it (Linux ELF, Solaris), compiled code can be loaded via
-extend
just like source files (see load
in the User's Manual).
extended-bindings
or
usual-integrations
declaration or compiler option is used?The following extended bindings are handled specially:
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? pointer->object make-record-instance
locative-ref locative-set! locative? locative->object
cpu-time error
.
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.
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) ) )
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:
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.
"Error: qualified symbol syntax is not allowed"
.
Some of the include files make references to internal names with so-called "qualified" symbols names (like
##sys#slot
. This syntax is disabled inside module definitions. Each of these include files
define own modules that export all relevant names, so include
the file outside of your
module definition and import what you need:
(include "describe") (define-module foo (import scheme describe) (begin (describe (read))) )
It seems that the Microsoft C compiler can only handle files up to a certain size, and it doesn't utilize virtual memory as well as the GNU C compiler, for example. Try closing running applications. If that fails, try to break up the Scheme code into several library units.
To write your own schedulers, you can utilize three low-level procedures:
(##sys#call-with-current-continuation PROC)
call-with-current-continuation
, but doesn't unwind
before- and after-thunks established by dynamic-wind
.
(##sys#interrupt-hook REASON STATE)
REASON
is a fixnum that
specifies the type of interrupt where 255 is the type of a timer interrupt. STATE
contains
the execution state of the current pending computation. To change the interrupt behaviour, one could
do something like this:
(set! ##sys#interrupt-hook (let ([old ##sys#interrupt-hook]) (lambda (reason state) (cond [(fx= reason 255) ...] ; do your stuff [else (old reason state)] ) ) ) )
(##sys#context-switch STATE)
STATE
. This procudure does not
return.
Consider the following piece of code:
(define k (call-with-current-continuation (lambda (k) k))) (k k)When compiled, this will loop endlessly. But when interpreted,
(k k)
will return
to the read-eval-print-loop! This happens, because the continuation captured will eventually read the
next toplevel expression from the standard-input (or an input-file if loading from a file). At the moment
k
was defined, the next expression was (k k)
. But when k
is invoked, the next expression will be whatever follows after (k k)
.
In other words, invoking a captured continuation will not rewind the file-position of the input source.
(begin ...)
expression, so all toplevel
expressions will be loaded together.