REDUCE

15.5 Differences between Compiled and Interpreted Code

In the process of compilation, many functions are Open-Coded, and hence cannot be redefined or traced in the compiled code. Such functions are noted to be Open-Coded in the manual. The call on the function is replaced by a sequence of instructions which make up the body of the function. This trick gives faster execution, but it requires more space. A call on a macro is another instance of open coding. A PSL program usually consists of a large number of relatively small functions. Calling a function requires manipulation of a parameter stack for variable binding and a control stack for return information. The time spent inside a function may be small compared to the time spent maintaining the stacks. Some commonly used PSL functions which are open-coded are and, or, apply, idapply, codeapply, return, go, cons, cond, case, prog, progn, and prog2.

Unless variables are declared, or detected, to be fluid or global they are compiled as local variables. This causes their names to disappear, and so they are not visible on the binding stack. In addition these variables will not be available to functions called in the dynamic scope of the function containing their binding.

The compiler attempts to do the following conversions

(function (lambda ...)) - >

the lambda expression is compiled into a gensymed name. If the functional form of one of the mapping functions (map, mapc, maplist, mapcar, mapcon, and mapcan) is explicitly tagged function then the call is open coded.

(setq na va nb vb ...) - >

(setq na va), (setq nb vb) ...

(nth sequence n) - >

(car sequence) if n = 1,

(cadr sequence) if n = 2,

(caddr sequence) if n = 3,

(cadddr sequence) if n = 4

(pnth sequence n) - >

(identity sequence) if n = 1,

(cdr sequence) if n = 2,

(cddr sequence) if n = 3,

(cdddr sequence) if n = 4,

(cddddr sequence) if n = 5

The following transformations are made when the list function is applied to less than six arguments. When there are more that five arguments the compiler uses the cons expansion for additional arguments.

(list a) - >

(ncons a)

(list a b) - >

(list2 a b)

(list a b c) - >

(list3 a b c)

(list a b c d) - >

(list4 a b c d)

(list a b c d e) - >

(list5 a b c d e)

(apply (function foo) ...) - >

(foo ...)

(assoc ...) - >

(atsoc ...)

If the last clause within a cond expression is not of the

form (t ...) then the compiler will add the additinal clause (t nil).

(difference n 1) - >

(sub1 n)

(equal ...) - >

(eq ...)

(geq ...) - >

(not (lessp ...))

(intern (compress ...)) - >

(implode ...)

(intern (gensym ...)) - >

(interngensym ...)

(leq ...) - >

(not (greaterp ...))

(lessp n 0) - >

(minusp n)

(member ...) - >

(memq ...)

(neq ...) - >

(not (equal ...))

(not ...) - >

(null ...)

(plus2 n 1) - >

(add1 n)

(null (eq ...)) - >

(neq ...)

(null (atom ...)) - >

(pairp ...)

Prior to the application of some functions, such as car and cdr, there is no verification that the types of the arguments are correct.

Since compiled calls on macros, fexprs, and nexprs are different from the default exprs, these functions must be defined, or declared, before compiling the code that uses them. While fexprs and nexprs may be subsequently redefined, as new functions of the same type, macros are executed by the compiler to get the replacement form, which is then compiled. The interpreter would pick up the most recent definition of any function, and so functions can switch type as well as body.

Constants which appear in code that is to be compiled may be collapsed. For example, the following code references two distinct strings which happen to contain the same characters. The compiled code will only reference one string.

  (de fubar ()  
    (let ((this "foo")  
          (that "foo"))  
    (eq this that))  
 
1 lisp> (foo)  
nil  
2 lisp> (compile '(foo))  
nil  
3 lisp> (foo)  
t

As noted below, the switches *r2i and *nolinke may cause function calls to be replaced by jumps. This means that the backtrace of compiled functions may differ from that of interpreted functions. In addition a sequence of interpreted function calls could consume stack space while compiled function calls might not.