The CHICKEN FAQ

Questions:

  1. Why yet another Scheme implementation?
  2. How do I call a C routine from Scheme?
  3. How do I call a Scheme routine from C?
  4. How do I generate a DLL under Windows(tm)?
  5. How do I run custom startup code before the runtime-system is invoked?
  6. What do I have to do to port CHICKEN to another platform?
  7. What is the meaning of the -hygienic option?
  8. Why does (define-macro foo bar) not work?
  9. Why doesn't my fancy macro work in compiled code?
  10. What to do if I find a bug?
  11. Why are values defined with define-foreign-variable or define-constant or define-inline not seen outside of the containing source file?
  12. How does cond-expand know which features are registered in used units?
  13. How can I cut down the size of an executable?
  14. Does SLIB work with CHICKEN?
  15. How can I change the default heap-size of the interpreter?
  16. What exactly is the meaning of a STACKTRACE?
  17. How are the compiler-sources organized?
  18. How do I generate a GUI application under Windows(tm)?
  19. Why does a loop that doesn't cons still trigger garbage collections?
  20. How can I obtain faster executables?
  21. What can I do if ./configure; make fails during installation?
  22. What is to do to recompile the compiler?
  23. Why does the linker complain about a missing function _C_..._toplevel?
  24. What can I do when the compiler prints out of memory - heap full when compiling a large soure file?
  25. Can I run the compiler in the interpreter?
  26. Why are constants defined by define-constant not honoured in case contructs?
  27. How can I add compiled library units to the interpreter?
  28. When I compile a file with -unsafe or unsafe declarations, it crashes during execution.
  29. How can I use R5RS macros in expressions I evaluate at runtime?
  30. The C compiler complains about missing arguments in a function call.
  31. How can I add compiled user passes?
  32. Which non-standard procedures are treated specially when the extended-bindings or usual-integrations declaration or compiler option is used?
  33. Why does define-reader-ctor not work in my compiled program?
  34. Why does compiling with -debug-level 2 and -unsafe generate link error?
  35. Why does csc abort with error message on my non-UNIX system?
  36. Why does my program abort with an "out of memory" error when I compile with extended debug information?
  37. Why do I get a warning when I define a global variable named match?
  38. How can I enable case sensitive reading/writing in user code?
  39. How can I use qualified internal procedures and variables in combination with the module system?
  40. How do I invoke Scheme scripts like formatprofile under Windows/DOS?
  41. When I use callback functions (from Scheme to C and back to Scheme again), I get weird crashes.
  42. When I run csi inside an emacs buffer under Windows, nothing happens.
  43. How can I use uppercase characters in a variable/procedure name defined with define-external?
  44. How can I change match-error-control during compilation?
  45. Why doesn't CHICKEN support the full numeric tower?
  46. Why does -benchmark-mode not imply -inline?
  47. How can I use match in combination with the highlevel (syntax-case) macro system?
  48. How can I put toplevel definitions inside eval-when in combination with the highlevel macro system?
  49. Can I use the debugger for interpreted code?

Answers:

  1. Why yet another Scheme implementation?

    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:


  2. How do I call a C routine from Scheme?

    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.


  3. How do I call a Scheme routine from C?

    Use the foreign-callback-lambda and define-external forms. For more information, see the User's manual.


  4. How do I generate a DLL under Windows(tm)?

    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).


  5. How do I run custom startup code before the runtime-system is invoked?

    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.


  6. What do I have to do to port CHICKEN to another platform?

    CHICKEN should work with any decent C compiler and on most platforms. The platform dependent consists mostly of:


  7. What is the meaning of the -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.


  8. Why does (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.
  9. Why doesn't my fancy macro work in compiled code?

    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.


  10. What to do if I find a bug?

    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.


  11. Why are values defined with 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.


  12. How does 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.


  13. How can I cut down the size of an executable?

    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.
  14. Does SLIB work with CHICKEN?

    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.


  15. How can I change the default heap-size of the interpreter?

    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 csi
    
    Now 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.
  16. What exactly is the meaning of a 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.


  17. How are the compiler-sources organized?

    build.scm
    Tiny include-file with the current build-version and -number.
    chicken.scm
    The loader/main module. This file can be loaded into the interpreter to run the compiler as interpreted code (for testing compiler-extensions, for example). Some target-specific definitions take place here.
    support.scm
    Support code for all other parts of the compiler.
    compiler.scm
    The guts of the compiler. Translates, optimizes, and does everything but code-generation.
    batch-driver.scm
    The toplevel compiler driver which invokes the compiler-passes.
    c-platform.scm
    Parameters and rewrite-rules for the target-platform.
    c-backend.scm
    Generates C from the processed program nodes.

  18. How do I generate a GUI application under Windows(tm)?

    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.


  19. Why does a loop that doesn't 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)


  20. How can I obtain faster executables?

    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

  21. What can I do if ./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.gcc
    
    If this doesn't work also, then please contact me at felix@call-with-current-continuation.org.
  22. What is to do to recompile the compiler?

    If you made changes to the compiler-code in the compiler's source files, the compiler can be rebuilt with the following procedure:

    1. Change the version/build number in build.scm.
    2. Compile the changed units as usual. Remember that all source-files with the exception of 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.
    3. Generate a new executable by linking/compiling the generated files with the runtime-system.

    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).


  23. Why does the linker complain about a missing function _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 program
    
    The 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).
  24. What can I do when the compiler prints 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
    

  25. Can I run the compiler in the interpreter?

    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 ...
    

  26. Why are constants defined by 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.


  27. How can I add compiled library units to the interpreter?

    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.


  28. When I compile a file with -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.


  29. How can I use R5RS macros in expressions I evaluate at runtime?

    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).
  30. The C compiler complains about missing arguments in a function call.

    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.


  31. How can I add compiled user passes?

    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
    

  32. Which non-standard procedures are treated specially when the 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?.


  33. Why does 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))
    

  34. Why does compiling with -debug-level 2 and -unsafe generate link error?

    The debugger library unit is not available in the unsafe runtime libraries. Compile without the -unsafe option instead.
  35. Why does 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.


  36. Why does my program abort with an "out of memory" error when I compile with extended debug information?

    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.


  37. Why do I get a warning when I define a global variable named 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:


  38. How can I enable case sensitive reading/writing in user code?

    To enable the read procedure to read symbols and identifiers case sensitive, you can set the parameter case-sensitivity to #t.


  39. How can I use qualified internal procedures and variables in combination with the module system?

    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) ) )

  40. How do I invoke Scheme scripts like formatprofile under Windows/DOS?

    Just invoke csi with the -script option, like this:

    csi -script SCRIPTFILENAME ARGUMENTS ...

  41. When I use callback functions (from Scheme to C and back to Scheme again), I get weird crashes.

    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.


  42. When I run 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.


  43. How can I use uppercase characters in a variable/procedure name defined with 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.


  44. How can I change match-error-control during compilation?

    Use eval-when, like this:

      (eval-when (compile)
        (match-error-control #:unspecified) )
      

  45. Why doesn't CHICKEN support the full numeric tower?

    There are a number of reasons for this:


  46. Why does -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.


  47. How can I use 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.


  48. How can I put toplevel definitions inside 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.


  49. Can I use the debugger for interpreted code?

    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.