Chapter 4. Data and control flow

4.1. Minimal compilation
4.2. Function types
4.3. C Calling conventions
4.4. C Reference
ecl_bds_bind — Bind a special variable
ecl_bds_unwind — Undo one variable binding
ecl_setq — C equivalent of setq
ecl_symbol_value — C equivalent of symbol-value
ecl_va_arg — Accepting a variable number of arguments
ecl_nth_value, ecl_nvalues — Accessing output values
ecl_return0, ecl_return1, ... — Returning multiple values
ECL_BLOCK_BEGIN — C macro for block
ECL_CATCH_BEGIN — C macro for catch
ECL_UNWIND_PROTECT_BEGIN — C macro for unwind-protect
ANSI Dictionary — Common Lisp and C equivalence

4.1. Minimal compilation

Former versions of ECL, as well as many other lisps, used linked lists to represent code. Executing code thus meant traversing these lists and performing code transformations, such as macro expansion, every time that a statement was to be executed. The result was a slow and memory hungry interpreter.

Beginning with version 0.3, ECL was shipped with a bytecodes compiler and interpreter which circumvent the limitations of linked lists. When you enter code at the lisp prompt, or when you load a source file, ECL begins a process known as minimal compilation. Barely this process consists on parsing each form, macroexpanding it and translating it into an intermediate language made of bytecodes.

The bytecodes compiler is implemented in src/c/compiler.d. The main entry point is the lisp function si::make-lambda, which takes a name for the function and the body of the lambda lists, and produces a lisp object that can be invoked. For instance,

> (defvar fun (si::make-lambda 'f '((x) (1+ x))))
*FUN*
> (funcall fun 2)
3

ECL can only execute bytecodes. When a list is passed to EVAL it must be first compiled to bytecodes and, if the process succeeds, the resulting bytecodes are passed to the interpreter. Similarly, every time a function object is created, such as in DEFUN or DEFMACRO, the compiler processes the lambda form to produce a suitable bytecodes object.

The fact that ECL performs this eager compilation means that changes on a macro are not immediately seen in code which was already compiled. This has subtle implications. Take the following code:

> (defmacro f (a b) `(+ ,a ,b))
F
> (defun g (x y) (f x y))
G
> (g 1 2)
3
> (defmacro f (a b) `(- ,a ,b))
F
> (g 1 2)
3

The last statement always outputs 3 while in former implementations based on simple list traversal it would produce -1.