[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / runtime / c-as-asm / StgMiniInt.lc
diff --git a/ghc/runtime/c-as-asm/StgMiniInt.lc b/ghc/runtime/c-as-asm/StgMiniInt.lc
new file mode 100644 (file)
index 0000000..2739ad7
--- /dev/null
@@ -0,0 +1,244 @@
+\section[StgMiniInt]{The ``mini-interpreter'' that drives the STG machine}
+
+% this file is part of the C-as-assembler document
+
+\begin{code}
+#include "rtsdefs.h"
+\end{code}
+
+For portable~C, there really is a mini-interpreter that repeatedly
+grabs the continuation from each code fragment and jumps to it.
+
+In portable~C, we also have a ``debugging'' version of the
+mini-interpreter which does ``hygiene-checking'' of the stacks/heap(?)
+each time it regains control.  This is deeply wonderful when the
+compiler's generating duff code and things are badly broken!
+
+For optimised~C, the mini-interpreter really {\em doesn't} do anything
+remotely interpretive.  It just jumps off into a Haskell Threaded
+World, dropping a label for \tr{_miniInterpretEnd} so we'll have a
+place to eventually come back to.
+
+A complication in optimised~C: Because we completely nuke C-stack
+activity (pushing/popping frames, moving register-windows) within the
+Haskell-threaded world, we need to ensure there is enough C-stack
+space actually present to satisfy the code that GCC generated.
+
+IMPORTANT POINT: the mini-interpreter is supposed to be {\em generic}.
+It is not only for the Haskell Threaded World---the storage manager
+may use it as well.  So: whatever threaded world is in operation has
+to handle its own register saving/restoring, and such grimy details.
+For an example, see the @startStgWorld@, @stopStgWorld@ pair of
+routines.
+
+%************************************************************************
+%*                                                                     *
+\subsection[StgMiniInt-optimised]{Mini-interpreter for ``optimised~C''}
+%*                                                                     *
+%************************************************************************
+
+Unusually, for mini-interpreters, the ``optimised~C'' case involves
+less code.
+
+\begin{code}
+#if defined(__STG_TAILJUMPS__) && defined(__GNUC__)
+
+#if i386_TARGET_ARCH || i486_TARGET_ARCH
+/* All together now: "Hack me gently, hack me dead ..." */
+P_ SP_stack[8]; /* two/three? is all that is really needed, I think (WDP) */
+I_ SP_stack_ptr = -1;
+#endif
+
+void
+miniInterpret(start_cont)
+    StgFunPtr start_cont;
+{
+    /* 
+     * MINI_INTERPRETER_SETUP _must_ save _all_ callee-saves registers, because
+     * the caller expects them to be saved if they are used, but the threaded
+     * code never saaves anything (all function prologues have been removed).
+     */
+
+    MINI_INTERPRETER_SETUP
+
+    /* 
+     * starts Haskell world by _calling_ "start_cont"
+     * 
+     * Make this a JMP_ and dead code elimination will make you unhappy.
+     *
+     * You will be even more unhappy with a registerized HP build, because
+     * the continuation passed in here is actually the address of a function
+     * ADT, and not the address where the function really begins.
+     */
+    (start_cont)();
+
+    /* 
+     * and drops a label for "miniInterpretEnd" right here, along
+     * with any cleanup that has to be done before we return.
+     *
+     * _Always_ RESUME_(miniInterpretEnd).  Never JMP_(miniInterpretEnd).
+     */
+
+    MINI_INTERPRETER_END
+
+    return;
+}
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[StgMiniInt-portable]{Mini-interpreter for ``portable~C''}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+#else /* ! (__STG_TAILJUMPS__ && __GNUC__) */
+
+#include <setjmp.h>
+/* by which we mean the Standard C Library stuff */
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection[StgMiniInt-portable-normal]{Normal mini-interpreter for ``portable~C''}
+%*                                                                     *
+%************************************************************************
+
+The static @jmp_environment@ variable allows @miniInterpret@ to
+communicate with @miniInterpretEnd@.
+
+Because @miniInterpret@ may be used recursively, we carefully
+save and restore the whole of @jmp_environment@.
+
+\begin{code}
+static jmp_buf jmp_environment;
+
+extern void bcopy PROTO((char *, char *, int)); /*ToDo: properly?*/
+
+void
+miniInterpret(start_cont)
+    StgFunPtr start_cont;
+{
+    StgFunPtr continuation = (StgFunPtr) start_cont;
+    jmp_buf save_buf;
+    bcopy((char *) jmp_environment, (char *) save_buf, sizeof(jmp_buf));       
+        /* Save jmp_environment for previous call to miniInterpret */
+    
+    if (setjmp(jmp_environment) == 0) {
+
+       while ( 1 ) {
+           /* unrolled for a little speed */
+           continuation = (StgFunPtr) (continuation)();
+           continuation = (StgFunPtr) (continuation)();
+           continuation = (StgFunPtr) (continuation)();
+           continuation = (StgFunPtr) (continuation)();
+           continuation = (StgFunPtr) (continuation)();
+       }
+    }
+
+
+    /* Restore jmp_environment for previous call */
+    bcopy((char *) save_buf, (char *) jmp_environment, sizeof(jmp_buf));
+
+    /* ToDo: restore real registers ... (see longjmp) */
+    return;
+    /*
+       Note that on returning (after miniInterpretEnd is called)
+       the values variables declared as real machine registers
+       will be undefined.
+    */
+}
+
+void miniInterpretEnd(STG_NO_ARGS)
+{
+    /* ToDo: save real register in something somewhere */
+    longjmp(jmp_environment, 1);
+}
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsubsection[StgMiniInt-portable-debugging]{Debugging mini-interpreter for ``portable~C''}
+%*                                                                     *
+%************************************************************************
+
+See comments about @jmp_environment@ in section above.
+
+The debugging mini-interpreter, which is invoked if suitable RTS flags
+are given, offers two extra ``features:''
+\begin{description}
+
+\item[Circular buffer of last @NUM_SAVED_CONTINUATIONS@ continuations:]
+These are in @savedCont@, with @savedContCtr@ pointing to where the
+last one was slotted in.
+
+Reference is frequently made to this buffer when \tr{gdb}-ing broken C
+out of the compiler!
+
+\item[Hygiene-checking:]
+
+This version of the mini-interpreter can be given a hygiene-checking
+function which will be invoked each time 'round the loop.  Again,
+given suitable RTS flags, we pass along a routine that walks over the
+stack checking for Bad Stuff.  An example might be: pointers from the
+A stack into the wrong semi-space of the heap (indicating a
+garbage-collection bug)...
+\end{description}
+
+\begin{code}
+extern I_ doSanityChks; /* ToDo: move tidily */
+
+#define          NUM_SAVED_CONTINUATIONS 32    /* For debug */
+I_       totalContCtr;
+I_       savedContCtr;
+StgFunPtr savedCont[NUM_SAVED_CONTINUATIONS];
+
+void miniInterpret_debug(start_cont, hygiene)
+    StgFunPtr start_cont;
+    void (*hygiene)();
+{
+    StgFunPtr continuation = (StgFunPtr) start_cont;
+    StgFunPtr next_continuation;
+    jmp_buf save_buf;
+    bcopy((char *) jmp_environment, (char *) save_buf, sizeof(jmp_buf));       
+        /* Save jmp_environment for previous call to miniInterpret */
+    
+    if (setjmp(jmp_environment) == 0) {
+
+       totalContCtr = 0;
+       savedContCtr = 0;
+       savedCont[0] = start_cont;
+
+       while ( 1 ) {
+           next_continuation = (StgFunPtr) (continuation)();
+
+           totalContCtr += 1;
+           savedContCtr = (savedContCtr + 1) % NUM_SAVED_CONTINUATIONS;
+           savedCont[savedContCtr] = next_continuation;
+
+           continuation = next_continuation;
+
+           /* hygiene chk can't be at start of loop, because it's the
+              first continuation-thingy that loads up the registers.
+           */
+           if (doSanityChks && hygiene) {
+               (hygiene)();
+           }
+       }
+    }
+    /* Restore jmp_environment for previous call */
+    bcopy((char *) save_buf, (char *) jmp_environment, sizeof(jmp_buf));
+
+    /* ToDo: restore real registers ... (see longjmp) */
+    return;
+    /*
+       Note that on returning (after miniInterpretEnd is called)
+       the values variables declared as real machine registers
+       will be undefined.
+    */
+}
+
+/* debugging version uses same "miniInterpretEnd" as the regular one */
+
+#endif /* ! __STG_TAILJUMPS__ */
+\end{code}