[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / runtime / c-as-asm / StgMiniInt.lc
1 \section[StgMiniInt]{The ``mini-interpreter'' that drives the STG machine}
2
3 % this file is part of the C-as-assembler document
4
5 \begin{code}
6 #include "rtsdefs.h"
7 \end{code}
8
9 For portable~C, there really is a mini-interpreter that repeatedly
10 grabs the continuation from each code fragment and jumps to it.
11
12 In portable~C, we also have a ``debugging'' version of the
13 mini-interpreter which does ``hygiene-checking'' of the stacks/heap(?)
14 each time it regains control.  This is deeply wonderful when the
15 compiler's generating duff code and things are badly broken!
16
17 For optimised~C, the mini-interpreter really {\em doesn't} do anything
18 remotely interpretive.  It just jumps off into a Haskell Threaded
19 World, dropping a label for \tr{_miniInterpretEnd} so we'll have a
20 place to eventually come back to.
21
22 A complication in optimised~C: Because we completely nuke C-stack
23 activity (pushing/popping frames, moving register-windows) within the
24 Haskell-threaded world, we need to ensure there is enough C-stack
25 space actually present to satisfy the code that GCC generated.
26
27 IMPORTANT POINT: the mini-interpreter is supposed to be {\em generic}.
28 It is not only for the Haskell Threaded World---the storage manager
29 may use it as well.  So: whatever threaded world is in operation has
30 to handle its own register saving/restoring, and such grimy details.
31 For an example, see the @startStgWorld@, @stopStgWorld@ pair of
32 routines.
33
34 %************************************************************************
35 %*                                                                      *
36 \subsection[StgMiniInt-optimised]{Mini-interpreter for ``optimised~C''}
37 %*                                                                      *
38 %************************************************************************
39
40 Unusually, for mini-interpreters, the ``optimised~C'' case involves
41 less code.
42
43 \begin{code}
44 #if defined(__STG_TAILJUMPS__) && defined(__GNUC__)
45
46 #if i386_TARGET_ARCH || i486_TARGET_ARCH
47 /* All together now: "Hack me gently, hack me dead ..." */
48 P_ SP_stack[8]; /* two/three? is all that is really needed, I think (WDP) */
49 I_ SP_stack_ptr = -1;
50 #endif
51
52 void
53 miniInterpret(start_cont)
54     StgFunPtr start_cont;
55 {
56     /* 
57      * MINI_INTERPRETER_SETUP _must_ save _all_ callee-saves registers, because
58      * the caller expects them to be saved if they are used, but the threaded
59      * code never saaves anything (all function prologues have been removed).
60      */
61
62     MINI_INTERPRETER_SETUP
63
64     /* 
65      * starts Haskell world by _calling_ "start_cont"
66      * 
67      * Make this a JMP_ and dead code elimination will make you unhappy.
68      *
69      * You will be even more unhappy with a registerized HP build, because
70      * the continuation passed in here is actually the address of a function
71      * ADT, and not the address where the function really begins.
72      */
73     (start_cont)();
74
75     /* 
76      * and drops a label for "miniInterpretEnd" right here, along
77      * with any cleanup that has to be done before we return.
78      *
79      * _Always_ RESUME_(miniInterpretEnd).  Never JMP_(miniInterpretEnd).
80      */
81
82     MINI_INTERPRETER_END
83
84     return;
85 }
86 \end{code}
87
88 %************************************************************************
89 %*                                                                      *
90 \subsection[StgMiniInt-portable]{Mini-interpreter for ``portable~C''}
91 %*                                                                      *
92 %************************************************************************
93
94 \begin{code}
95 #else /* ! (__STG_TAILJUMPS__ && __GNUC__) */
96
97 #include <setjmp.h>
98 /* by which we mean the Standard C Library stuff */
99 \end{code}
100
101 %************************************************************************
102 %*                                                                      *
103 \subsubsection[StgMiniInt-portable-normal]{Normal mini-interpreter for ``portable~C''}
104 %*                                                                      *
105 %************************************************************************
106
107 The static @jmp_environment@ variable allows @miniInterpret@ to
108 communicate with @miniInterpretEnd@.
109
110 Because @miniInterpret@ may be used recursively, we carefully
111 save and restore the whole of @jmp_environment@.
112
113 \begin{code}
114 static jmp_buf jmp_environment;
115
116 extern void bcopy PROTO((char *, char *, int)); /*ToDo: properly?*/
117
118 void
119 miniInterpret(start_cont)
120     StgFunPtr start_cont;
121 {
122     StgFunPtr continuation = (StgFunPtr) start_cont;
123     jmp_buf save_buf;
124     bcopy((char *) jmp_environment, (char *) save_buf, sizeof(jmp_buf));        
125         /* Save jmp_environment for previous call to miniInterpret */
126     
127     if (setjmp(jmp_environment) == 0) {
128
129         while ( 1 ) {
130             /* unrolled for a little speed */
131             continuation = (StgFunPtr) (continuation)();
132             continuation = (StgFunPtr) (continuation)();
133             continuation = (StgFunPtr) (continuation)();
134             continuation = (StgFunPtr) (continuation)();
135             continuation = (StgFunPtr) (continuation)();
136         }
137     }
138
139
140     /* Restore jmp_environment for previous call */
141     bcopy((char *) save_buf, (char *) jmp_environment, sizeof(jmp_buf));
142
143     /* ToDo: restore real registers ... (see longjmp) */
144     return;
145     /*
146        Note that on returning (after miniInterpretEnd is called)
147        the values variables declared as real machine registers
148        will be undefined.
149     */
150 }
151
152 void miniInterpretEnd(STG_NO_ARGS)
153 {
154     /* ToDo: save real register in something somewhere */
155     longjmp(jmp_environment, 1);
156 }
157 \end{code}
158
159 %************************************************************************
160 %*                                                                      *
161 \subsubsection[StgMiniInt-portable-debugging]{Debugging mini-interpreter for ``portable~C''}
162 %*                                                                      *
163 %************************************************************************
164
165 See comments about @jmp_environment@ in section above.
166
167 The debugging mini-interpreter, which is invoked if suitable RTS flags
168 are given, offers two extra ``features:''
169 \begin{description}
170
171 \item[Circular buffer of last @NUM_SAVED_CONTINUATIONS@ continuations:]
172 These are in @savedCont@, with @savedContCtr@ pointing to where the
173 last one was slotted in.
174
175 Reference is frequently made to this buffer when \tr{gdb}-ing broken C
176 out of the compiler!
177
178 \item[Hygiene-checking:]
179
180 This version of the mini-interpreter can be given a hygiene-checking
181 function which will be invoked each time 'round the loop.  Again,
182 given suitable RTS flags, we pass along a routine that walks over the
183 stack checking for Bad Stuff.  An example might be: pointers from the
184 A stack into the wrong semi-space of the heap (indicating a
185 garbage-collection bug)...
186 \end{description}
187
188 \begin{code}
189 extern I_ doSanityChks; /* ToDo: move tidily */
190
191 #define   NUM_SAVED_CONTINUATIONS 32    /* For debug */
192 I_        totalContCtr;
193 I_        savedContCtr;
194 StgFunPtr savedCont[NUM_SAVED_CONTINUATIONS];
195
196 void miniInterpret_debug(start_cont, hygiene)
197     StgFunPtr start_cont;
198     void (*hygiene)();
199 {
200     StgFunPtr continuation = (StgFunPtr) start_cont;
201     StgFunPtr next_continuation;
202     jmp_buf save_buf;
203     bcopy((char *) jmp_environment, (char *) save_buf, sizeof(jmp_buf));        
204         /* Save jmp_environment for previous call to miniInterpret */
205     
206     if (setjmp(jmp_environment) == 0) {
207
208         totalContCtr = 0;
209         savedContCtr = 0;
210         savedCont[0] = start_cont;
211
212         while ( 1 ) {
213             next_continuation = (StgFunPtr) (continuation)();
214
215             totalContCtr += 1;
216             savedContCtr = (savedContCtr + 1) % NUM_SAVED_CONTINUATIONS;
217             savedCont[savedContCtr] = next_continuation;
218
219             continuation = next_continuation;
220
221             /* hygiene chk can't be at start of loop, because it's the
222                first continuation-thingy that loads up the registers.
223             */
224             if (doSanityChks && hygiene) {
225                 (hygiene)();
226             }
227         }
228     }
229     /* Restore jmp_environment for previous call */
230     bcopy((char *) save_buf, (char *) jmp_environment, sizeof(jmp_buf));
231
232     /* ToDo: restore real registers ... (see longjmp) */
233     return;
234     /*
235        Note that on returning (after miniInterpretEnd is called)
236        the values variables declared as real machine registers
237        will be undefined.
238     */
239 }
240
241 /* debugging version uses same "miniInterpretEnd" as the regular one */
242
243 #endif /* ! __STG_TAILJUMPS__ */
244 \end{code}