3.4. The interpreter stack

The bytecodes interpreter uses a stack of its own to save and restore values from intermediate calculations. This Forth-like data stack is also used in other parts of the C kernel for various purposes, such as saving compiled code, keeping arguments to FORMAT, etc.

However, one of the most important roles of the Interpreter Stack is to keep a log of the functions which are called during the execution of bytecodes. For each function invoked, the interpreter keeps three lisp objects on the stack:

  +----------+------------------------------------------------+
  | function | lexical environment | index to previous record |
  +----------+---------------------+--------------------------+
  

The first item is the object which is funcalled. It can be a bytecodes object, a compiled function or a generic function. In the last two cases the lexical environment is just NIL. In the first case, the second item on the stack is the lexical environment on which the code is executed. Each of these records are popped out of the stack after function invocation.

Let us see how these invocation records are used for debugging.

  >(defun fact (x)                ;;;  Wrong definition of the
  (if (= x 0)                  ;;;  factorial function.
  one                      ;;;  one  should be  1.
  (* x (fact (1- x)))))
  FACT

  >(fact 3)                       ;;;  Tries  3!
  Error: The variable ONE is unbound.
  Error signalled by IF.
  Broken at IF.
  >>:b                            ;;;  Backtrace.
  Backtrace: eval > fact > if > fact > if > fact > if > fact > IF
  ;;;  Currently at the last  IF.
  >>:h                            ;;;  Help.

  Break commands:
  :q(uit)         Return to some previous break level.
  :pop            Pop to previous break level.
  :c(ontinue)     Continue execution.
  :b(acktrace)    Print backtrace.
  :f(unction)     Show current function.
  :p(revious)     Go to previous function.
  :n(ext)         Go to next function.
  :g(o)           Go to next function.
  :fs             Search forward for function.
  :bs             Search backward for function.
  :v(ariables)    Show local variables, functions, blocks, and tags.
  :l(ocal)        Return the nth local value on the stack.
  :hide           Hide function.
  :unhide         Unhide function.
  :hp             Hide package.
  :unhp           Unhide package.
  :unhide-all     Unhide all variables and packages.
  :bds            Show binding stack.
  :m(essage)      Show error message.
  :hs             Help stack.
  Top level commands:
  :cf             Compile file.
  :exit or ^D     Exit Lisp.
  :ld             Load file.
  :step           Single step form.
  :tr(ace)        Trace function.
  :untr(ace)      Untrace function.

  Help commands:
  :apropos        Apropos.
  :doc(ument)     Document.
  :h(elp) or ?    Help.  Type ":help help" for more information.

  >>:p                        ;;;  Move to the last call of  FACT.
  Broken at IF.

  >>:b
  Backtrace: eval > fact > if > fact > if > fact > if > FACT > if
  ;;;  Now at the last  FACT.
  >>:v                        ;;;  The environment at the last call
  Local variables:            ;;;  to  FACT  is recovered.
  X: 0                      ;;;  X  is the only bound variable.
  Block names: FACT.          ;;;  The block  FACT  is established.

  >>x
  0                           ;;;  The value of  x  is  0.

  >>(return-from fact 1)      ;;;  Return from the last call of
  6                           ;;;  FACT  with the value of  0.
  ;;;  The execution is resumed and
  >                           ;;;  the value  6  is returned.
  ;;;  Again at the top-level loop.