[project @ 1998-11-26 09:17:22 by sof]
[ghc-hetmet.git] / ghc / runtime / main / StgStartup.lhc
1 %/****************************************************************
2 %*                                                              *
3 %*   Basic Continuations required by the STG Machine runtime    *
4 %*                                                              *
5 %****************************************************************/
6
7
8 First continuation called by the mini-interpreter is
9 evaluateTopClosure.  It has to set up return and jump to the user's
10 @main@ closure.  If @errorIO@ is called, we will be back here, doing
11 the same thing for the specified continuation.
12
13 \begin{code}
14 #define MAIN_REG_MAP        /* STG world */
15 #include "rtsdefs.h"
16
17 #if 0
18 #ifdef PAR
19 #include "Statistics.h"
20 #endif
21 #endif
22
23 /* ptr to the user's "main" closure (or "errorIO" arg closure),
24    to which we hope to be linked
25 */
26 extern P_ TopClosure;
27
28 EXTFUN(stopThreadDirectReturn);
29 UNVECTBL(,vtbl_stopStgWorld,stopThreadDirectReturn)
30
31 /* Well, we have to put the ArrayOfData and ArrayOfPtrs info tables
32    somewhere...
33 */
34
35 /* Array of data -- mutable */
36 STATICFUN(ArrayOfData_entry)
37 {
38     FB_
39     /* Don't wrap the calls; we're done with STG land */
40     fflush(stdout);
41     fprintf(stderr, "Entered a primitive array (of data)---this shouldn't happen!\n");
42     abort();
43     FE_
44 }
45
46 DATA_ITBL(ArrayOfData_info,ArrayOfData_entry,UpdErr,0,INFO_OTHER_TAG,,,const,IF_,ARR_K,"DATA-ARRAY","ARRAY");
47 /* ToDo: could put a useful tag in there!!! */
48
49 /* Array of pointers -- mutable */
50 STATICFUN(ArrayOfPtrs_entry)
51 {
52     FB_
53     /* Don't wrap the calls; we're done with STG land */
54     fflush(stdout);
55     fprintf(stderr, "Entered a primitive array (of pointers)---this shouldn't happen!\n");
56     abort();
57     FE_
58 }
59
60 MUTUPLE_ITBL(ArrayOfPtrs_info,ArrayOfPtrs_entry,UpdErr,0,INFO_OTHER_TAG,,,const,IF_,ARR_K,"PTR-ARRAY(mut)","ARRAY");
61 /* ToDo: could put a useful tag in there!!! */
62
63 STATICFUN(FullSVar_entry)
64 {
65     FB_
66     /* Don't wrap the calls; we're done with STG land */
67     fflush(stdout);
68     fprintf(stderr, "Entered a full SVar---this shouldn't happen!\n");
69     abort();
70     FE_
71 }
72
73 MUTUPLE_ITBL(FullSVar_info,FullSVar_entry,UpdErr,0,INFO_OTHER_TAG,,,const,IF_,ARR_K,"FullSVar","ARRAY");
74 /* ToDo: could put a useful tag in there!!! */
75
76 STATICFUN(EmptySVar_entry)
77 {
78     FB_
79     /* Don't wrap the calls; we're done with STG land */
80     fflush(stdout);
81     fprintf(stderr, "Entered an empty SVar---this shouldn't happen!\n");
82     abort();
83     FE_
84 }
85
86 MUTUPLE_ITBL(EmptySVar_info,EmptySVar_entry,UpdErr,0,INFO_OTHER_TAG,,,const,IF_,ARR_K,"EmptySVar","ARRAY");
87 /* ToDo: could put a useful tag in there!!! */
88
89 /* Array of pointers -- immutable */
90 STATICFUN(ImMutArrayOfPtrs_entry)
91 {
92     FB_
93     /* Don't wrap the calls; we're done with STG land */
94     fflush(stdout);
95     fprintf(stderr, "Entered a primitive array (immutable, pointers)---this shouldn't happen!\n");
96     abort();
97     FE_
98 }
99
100 IMMUTUPLE_ITBL(ImMutArrayOfPtrs_info,ImMutArrayOfPtrs_entry,UpdErr,0,INFO_OTHER_TAG,,,const,IF_,ARR_K,"PTR-ARRAY(immut)","ARRAY");
101 /* ToDo: could put a useful tag in there!!! */
102
103 /* (end of Array whatnot) */
104
105 /* Question for Will: There seem to be a lot of these static things
106 now - worth putting them in a file by themselves?? [ADR] */
107
108
109 #if !defined(PAR) /* && !defined(GRAN) */
110
111 /* Ditto for Foreign Object entry point and info tables. [ADR]
112
113    BTW Will, I copied most of this blindly from above - what's with
114    this TAG stuff? And what kind of description/ type is wanted here?
115 */
116
117 STATICFUN(ForeignObj_entry)
118 {
119     FB_
120     /* Don't wrap the calls; we're done with STG land */
121     fflush(stdout);
122     fprintf(stderr, "Compiler bug: Entered a ForeignObj---this shouldn't happen!\n");
123     abort();
124     FE_
125 }
126
127 ForeignObj_ITBL(ForeignObj_info,ForeignObj_entry,UpdErr,0,INFO_OTHER_TAG,,,const,EF_,ForeignObj_K,"FOREIGN_OBJ","ForeignObj");
128
129 /* End of ForeignObj stuff */
130
131 /* Ditto for the unused Stable Pointer info table. [ADR]
132 */
133
134 void raiseError PROTO((StgStablePtr));
135 extern StgStablePtr errorHandler; /* NB: prone to magic-value-ery (WDP 95/12) */
136
137 /* Unused Stable Pointer (ie unused slot in a stable pointer table) */
138 STATICFUN(UnusedSP_entry)
139 {
140     FB_
141     (void) SAFESTGCALL1(I_,(void *, FILE *),fflush,stdout);
142     (void) SAFESTGCALL2(I_,(void *, FILE *, char *),fprintf,stderr, "Entered an unused Stable Pointer---this shouldn't happen!\n(This could be program error (using stable pointer after freeing) or compiler bug.)\n");
143
144     (void) STGCALL1(void,(void *, StgStablePtr), raiseError, errorHandler);
145     FE_
146 }
147
148 STATIC_ITBL(UnusedSP_info,UnusedSP_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,IF_,CON_K,"UNUSED_STABLE_PTR","USP");
149
150 SET_STATIC_HDR(UnusedSP_closure,UnusedSP_info,CC_SUBSUMED,,ED_RO_)
151 };
152
153 /* Entry point and Info table for Stable Pointer Table. */
154
155 STATICFUN(EmptyStablePointerTable_entry)
156 {
157     FB_
158     /* Don't wrap the calls; we're done with STG land */
159     fflush(stdout);
160     fprintf(stderr, "Entered *empty* stable pointer table---this shouldn't happen!\n");
161     abort();
162     FE_
163 }
164
165 STATICFUN(StablePointerTable_entry)
166 {
167     FB_
168     /* Don't wrap the calls; we're done with STG land */
169     fflush(stdout);
170     fprintf(stderr, "Entered the stable pointer table---this shouldn't happen!\n");
171     abort();
172     FE_
173 }
174
175 STATIC_ITBL(EmptyStablePointerTable_info,EmptyStablePointerTable_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,IF_,SPT_K,"SP_TABLE","SP_TABLE");
176 /* ToDo: could put a useful tag in there!!! */
177
178 DYN_ITBL(StablePointerTable_info,StablePointerTable_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,IF_,SPT_K,"SP_TABLE","SP_TABLE");
179 /* ToDo: could put a useful tag in there!!! */
180
181
182 /* To ease initialisation of the heap, we start with an empty stable
183    pointer table.  When we try to create the first stable pointer, the
184    overflow will trigger creation of a table of useful size.
185 */
186
187 SET_STATIC_HDR(EmptySPTable_closure,EmptyStablePointerTable_info,CC_SUBSUMED,,ED_RO_)
188 , (W_) DYN_VHS + 0 + 1 + 0  /* size = DYN_VHS + n + 1 + n with n = 0 */
189 , (W_) 0   /* number of ptrs */
190 , (W_) 0   /* top of stack */
191 };
192
193 /* End of SP stuff */
194 #endif /* !PAR */
195
196 /* Not a natural home for these, but
197    the following symbols may be referenced in
198    an object file, but never entered
199 */
200 P_ PrelGHC_void_closure = (P_) 0xbadbadbaL;
201 P_ PrelGHC_ZcCCallable_static_info = (P_) 0xbadbadbaL;
202 P_ PrelGHC_ZcCReturnable_static_info = (P_) 0xbadbadbaL;
203
204 /* the IoWorld token to start the whole thing off */
205 /* Question: this is just an amusing hex code isn't it
206    -- or does it mean something? ADR */
207 P_ realWorldZh_closure = (P_)0xbadbadbaL;
208
209 #ifndef CONCURRENT
210
211 STGFUN(startStgWorld)
212 {
213     FB_
214     /* At this point we are in the threaded-code world.
215
216        TopClosure points to a closure of type PrimIO (), which should be
217        performed (by applying it to the state of the world).
218
219        The smInfo storage-management info block is assumed to be
220        up to date, and is used to load the STG registers.
221     */
222
223     RestoreAllStgRegs();    /* inline! */
224
225     /* ------- STG registers are now valid! -------------------------*/
226
227     /* Put a suitable return address on the B stack */
228     RetReg = (StgRetAddr) UNVEC(stopThreadDirectReturn,vtbl_stopStgWorld); 
229
230     /* Put an IoWorld token on the A stack */
231     SpB -= BREL(1);
232     (P_)*SpB = (P_) realWorldZh_closure;
233
234     Node = (P_) TopClosure; /* Point to the closure for main/errorIO-arg */
235     ENT_VIA_NODE();
236     InfoPtr=(D_)(INFO_PTR(Node));
237     JMP_(ENTRY_CODE(InfoPtr));
238     FE_
239 }
240 #endif  /* ! CONCURRENT */
241
242 \end{code}
243
244 %************************************************************************
245 %*                                                                      *
246 \subsection[thread-return]{Polymorphic end-of-thread code}
247 %*                                                                      *
248 %************************************************************************
249
250 \begin{code}
251
252 /* 
253    Here's the polymorphic return for the end of a thread.
254
255    NB: For direct returns to work properly, the name of the routine must be
256    the same as the name of the vector table with vtbl_ removed and DirectReturn
257    appended.  This is all the mangler understands.
258 */
259
260 const W_
261 vtbl_stopThread[] = {
262   /* at least "MAX_VECTORED_RTN" elements (see GhcConstants.lh) */
263   (W_) stopThreadDirectReturn,
264   (W_) stopThreadDirectReturn,
265   (W_) stopThreadDirectReturn,
266   (W_) stopThreadDirectReturn,
267   (W_) stopThreadDirectReturn,
268   (W_) stopThreadDirectReturn,
269   (W_) stopThreadDirectReturn,
270   (W_) stopThreadDirectReturn
271 };
272
273 STGFUN(stopThreadDirectReturn)
274 {
275     FB_
276     /* The final exit.
277
278        The top-top-level closures (e.g., "main") are of type "IO ()".
279        When entered, they perform an IO action and return a () --
280        essentially, TagReg is set to 1.  Here, we don't need to do
281        anything with that.
282
283        We just tidy up the register stuff (real regs in *_SAVE, then 
284        *_SAVE -> smInfo locs).
285     */
286
287 #ifdef CONCURRENT
288     SET_TASK_ACTIVITY(ST_OVERHEAD);
289 #endif
290
291     SaveAllStgRegs();   /* inline! */
292
293 #ifdef CONCURRENT
294     EndThread();
295 #else
296     RESUME_(miniInterpretEnd);
297 #endif
298     FE_
299 }
300
301 \end{code}  
302
303 \begin{code}
304 I_ ErrorIO_call_count = 0;
305
306 #ifdef CONCURRENT
307 EXTFUN(EnterNodeCode);
308
309 STGFUN(ErrorIO_innards)
310     /* Assumes that "TopClosure" has been set already */
311 {
312     FB_
313     fflush(stdout);
314     fflush(stderr);
315     if (ErrorIO_call_count >= 16 /* MAGIC CONSTANT */ ) {
316         /* Don't wrap the calls; we're done with STG land */
317         fflush(stdout);
318         fprintf(stderr, "too many nested calls to `error'\n");
319         EXIT(EXIT_FAILURE);
320     }
321     ErrorIO_call_count++; /* NB: undo later if decide to let someone else handle it */
322
323     /* Unlock all global closures held by this thread! (ToDo) --JSM */
324
325     switch(TSO_TYPE(CurrentTSO)) {
326     case T_MAIN:
327         /* Re-initialize stack pointers (cf. NewThread) */
328 #ifdef PAR
329         SpB = SuB = STKO_BSTK_BOT(StkOReg) + BREL(1);
330         SuA = STKO_ASTK_BOT(StkOReg) + AREL(1);
331 #else
332         SuA = stackInfo.botA + AREL(1);
333         SuB = stackInfo.botB + BREL(1);
334         /* HWL */
335         /* 
336         SpB = SuB = STKO_BSTK_BOT(StkOReg) + BREL(1);
337         SuA = STKO_ASTK_BOT(StkOReg) + AREL(1);
338         */
339    
340 #endif
341         break;
342
343     case T_REQUIRED:
344         /* Re-initialize stack pointers (cf. NewThread) */
345         SpB = SuB = STKO_BSTK_BOT(StkOReg) + BREL(1);
346         SuA = STKO_ASTK_BOT(StkOReg) + AREL(1);
347         break;
348
349     case T_ADVISORY:
350         ErrorIO_call_count--; /* undo the damage, as someone else will deal with it */
351         /* Let the main thread eventually handle it */
352         JMP_(stopThreadDirectReturn);
353
354     case T_FAIL:
355         EXIT(EXIT_FAILURE);
356
357     default:
358         /* Don't wrap the calls; we're done with STG land */
359         fflush(stdout);
360         fprintf(stderr,"ErrorIO: %lx unknown\n", TSO_TYPE(CurrentTSO));
361         EXIT(EXIT_FAILURE);
362     }
363
364     /* Finish stack setup as if for a top-level task and enter the error node */
365
366     /* Put an IoWorld token on the B stack */
367     SpB -= BREL(1);
368     *SpB = (P_) realWorldZh_closure;
369 /*
370     SpA = SuA - AREL(1);
371     *SpA = (P_) realWorldZh_closure;
372 */
373     STKO_LINK(StkOReg) = PrelBase_Z91Z93_closure;
374     STKO_RETURN(StkOReg) = NULL;
375
376 #ifdef TICKY_TICKY
377     STKO_ADEP(StkOReg) = STKO_BDEP(StkOReg) = 0;
378 #endif
379
380     /* Go! */
381     Node = (P_) TopClosure;
382     RetReg = (StgRetAddr) UNVEC(stopThreadDirectReturn,vtbl_stopStgWorld);
383     JMP_(EnterNodeCode);
384
385     FE_
386 }
387 \end{code}
388
389 We cannot afford to call @error@ too many times
390 (e.g., \tr{error x where x = error x}), so we keep count.
391
392 \begin{code}
393 #else   /* !CONCURRENT */
394
395 StgFunPtr
396 ErrorIO_innards(STG_NO_ARGS)
397     /* Assumes that "TopClosure" has been set already */
398 {
399     FB_
400     fflush(stdout);
401     fflush(stderr);
402     if (ErrorIO_call_count >= 16 /* MAGIC CONSTANT */ ) {
403         /* Don't wrap the calls; we're done with STG land */
404         fflush(stdout);
405         fprintf(stderr, "too many nested calls to `error'\n");
406         EXIT(EXIT_FAILURE);
407     }
408     ErrorIO_call_count++;
409
410     /* Copy the heap-related registers into smInfo.  (Other registers get
411        saved in this process, but we aren't interested in them.)
412
413        Get a new stack (which re-initialises the smInfo stack stuff),
414        and start the world again.
415     */
416     /* ToDo: chk this has been handled in parallel world */
417
418     SaveAllStgRegs();   /* inline! */
419
420     if (! initStacks( &StorageMgrInfo )) {
421         /* Don't wrap the calls; we're done with STG land */
422         fflush(stdout);
423         fprintf(stderr, "initStacks failed!\n");
424         EXIT(EXIT_FAILURE);
425     }
426
427     JMP_( startStgWorld );
428     FE_
429 }
430
431 #endif  /* !CONCURRENT */
432 \end{code}  
433
434 \begin{code}
435 #if defined(PAR) || defined(GRAN) 
436
437 STATICFUN(RBH_Save_0_entry)
438 {
439   FB_
440   fprintf(stderr,"Oops, entered an RBH save\n");
441   EXIT(EXIT_FAILURE);
442   FE_
443 }
444
445 STATICFUN(RBH_Save_1_entry)
446 {
447   FB_
448   fprintf(stderr,"Oops, entered an RBH save\n");
449   EXIT(EXIT_FAILURE);
450   FE_
451 }
452
453 STATICFUN(RBH_Save_2_entry)
454 {
455   FB_
456   fprintf(stderr,"Oops, entered an RBH save\n");
457   EXIT(EXIT_FAILURE);
458   FE_
459 }
460
461 SPEC_N_ITBL(RBH_Save_0_info,RBH_Save_0_entry,UpdErr,0,INFO_OTHER_TAG,2,0,,IF_,INTERNAL_KIND,"RBH-SAVE","RBH_Save_0");
462 SPEC_N_ITBL(RBH_Save_1_info,RBH_Save_1_entry,UpdErr,0,INFO_OTHER_TAG,2,1,,IF_,INTERNAL_KIND,"RBH-SAVE","RBH_Save_1");
463 SPEC_N_ITBL(RBH_Save_2_info,RBH_Save_2_entry,UpdErr,0,INFO_OTHER_TAG,2,2,,IF_,INTERNAL_KIND,"RBH-SAVE","RBH_Save_2");
464
465 #endif /* PAR || GRAN */
466 \end{code}
467
468
469 %/****************************************************************
470 %*                                                              *
471 %*              Other Bits and Pieces                           *
472 %*                                                              *
473 %****************************************************************/
474
475 \begin{code}
476 /* If we don't need the slow entry code for a closure, we put in a
477    pointer to this in the closure's slow entry code pointer instead.
478  */
479
480 STGFUN(__std_entry_error__) {
481     FB_
482     /* Don't wrap the calls; we're done with STG land */
483     fflush(stdout);
484     fprintf(stderr, "Called non-existent slow-entry code!!!\n");
485     abort();
486     JMP_(0);
487     FE_
488 }
489
490 /* entry code */
491 STGFUN(STK_STUB_entry) {
492     FB_
493     /* Don't wrap the calls; we're done with STG land */
494     fflush(stdout);
495     fprintf(stderr, "Entered from a stubbed stack slot!\n");
496     abort();
497     JMP_(0);
498     FE_
499 }
500
501 /* info table */
502 STATIC_ITBL(STK_STUB_info,STK_STUB_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,EF_,INTERNAL_KIND,"STK_STUB","STK_STUB");
503
504 /* closure */
505 SET_STATIC_HDR(STK_STUB_closure,STK_STUB_info,CC_SUBSUMED,,EXTDATA_RO)
506   , (W_)0, (W_)0
507 };
508
509
510 ED_RO_(vtbl_seq);
511
512 /*
513 STGFUN(seqZhCode)
514 {
515     FB_
516     __label__ cont;
517     SpB[BREL(0)] = (W_) RetReg;
518     SpB[BREL(1)] = (W_) &&cont;
519     RetReg = (StgRetAddr) vtbl_seq;
520     ENT_VIA_NODE();
521     InfoPtr = (D_)(INFO_PTR(Node));
522     JMP_(ENTRY_CODE(InfoPtr));
523 cont:
524     FE_
525 }
526 */
527
528 \end{code}
529
530 %/****************************************************************
531 %*                                                              *
532 %*              Some GC info tables                           *
533 %*                                                              *
534 %****************************************************************/
535
536 These have to be in a .lhc file, so they will be reversed correctly.
537
538 \begin{code}
539 #include "../storage/SMinternal.h"
540
541 #if defined(_INFO_COPYING)
542
543 STGFUN(Caf_Evac_Upd_entry) {
544     FB_
545     /* Don't wrap the calls; we're done with STG land */
546     fflush(stdout);
547     fprintf(stderr,"Entered Caf_Evac_Upd %lx: Should never occur!\n", (W_) Node);
548     abort();
549     FE_
550 }
551
552 CAF_EVAC_UPD_ITBL(Caf_Evac_Upd_info,Caf_Evac_Upd_entry,const/*not static*/);
553
554 #if defined(GCgn)
555
556 STGFUN(Forward_Ref_New_entry) {
557     FB_
558     /* Don't wrap the calls; we're done with STG land */
559     fflush(stdout);
560     fprintf(stderr,"Entered Forward_Ref_New %lx: Should never occur!\n", (W_) Node);
561     EXIT(EXIT_FAILURE); /* abort(); */
562     FE_
563 }
564 FORWARDREF_ITBL(Forward_Ref_New_info,Forward_Ref_New_entry,const/*not static*/,_Evacuate_Old_Forward_Ref);
565
566 STGFUN(Forward_Ref_Old_entry) {
567     FB_
568     /* Don't wrap the calls; we're done with STG land */
569     fflush(stdout);
570     fprintf(stderr,"Entered Forward_Ref_Old %lx: Should never occur!\n", (W_) Node);
571     EXIT(EXIT_FAILURE); /*    abort(); */
572     FE_
573 }
574 FORWARDREF_ITBL(Forward_Ref_Old_info,Forward_Ref_Old_entry,const/*not static*/,_Evacuate_New_Forward_Ref);
575
576 STGFUN(OldRoot_Forward_Ref_entry) {
577     FB_
578     /* Don't wrap the calls; we're done with STG land */
579     fflush(stdout);
580     fprintf(stderr,"Entered OldRoot_Forward_Ref %lx: Should never occur!\n", (W_) Node);
581     EXIT(EXIT_FAILURE); /*    abort(); */
582     FE_
583 }
584 FORWARDREF_ITBL(OldRoot_Forward_Ref_info,OldRoot_Forward_Ref_entry,const/*not static*/,_Evacuate_OldRoot_Forward);
585 #else /* ! GCgn */
586
587 STGFUN(Forward_Ref_entry) {
588     FB_
589     /* Don't wrap the calls; we're done with STG land */
590     fflush(stdout);
591     fprintf(stderr,"Entered Forward_Ref %lx: Should never occur!\n", (W_) Node);
592     EXIT(EXIT_FAILURE); /*    abort(); */
593     FE_
594 }
595 FORWARDREF_ITBL(Forward_Ref_info,Forward_Ref_entry,const/*not static*/,_Evacuate_Forward_Ref);
596 #endif /* ! GCgn */
597
598 #endif /* _INFO_COPYING */
599
600 #if defined(GCgn)
601 OLDROOT_ITBL(OldRoot_info,Ind_Entry,const,EF_);
602 #endif /* GCgn */
603 \end{code}
604
605
606 %/***************************************************************
607 %*                                                              *
608 %*              Cost Centre stuff ...                           *
609 %*                                                              *
610 %****************************************************************/
611
612 For cost centres we need prelude cost centres and register routine.
613
614 N.B. ALL prelude cost centres should be declared here as none will
615      be declared when the prelude is compiled.
616
617 ToDo: Explicit cost centres in prelude for Input and Output costs.
618
619 \begin{code}
620 #if defined(PROFILING)
621
622 STGFUN(startCcRegisteringWorld)
623 {
624     FB_
625     /* 
626      * We used to push miniInterpretEnd on the register stack, but
627      * miniInterpretEnd must only be entered with the RESUME_ macro,
628      * whereas the other addresses on the register stack must only be
629      * entered with the JMP_ macro.  Now, we push NULL and test for 
630      * it explicitly at each pop.
631      */
632     PUSH_REGISTER_STACK(NULL);
633     JMP_(_regMain);
634     FE_
635 }
636
637 CC_DECLARE(CC_CAFs,  "CAFs_in_...",  "PRELUDE", "PRELUDE", CC_IS_CAF,/*not static*/);
638 CC_DECLARE(CC_DICTs, "DICTs_in_...", "PRELUDE", "PRELUDE", CC_IS_DICT,/*not static*/);
639
640 START_REGISTER_PRELUDE(_regPrel);
641 REGISTER_CC(CC_CAFs);
642 REGISTER_CC(CC_DICTs);
643 END_REGISTER_CCS()
644
645 \end{code}
646
647 We also need cost centre declarations and registering routines for other
648 built-in prelude-like modules.
649
650 ToDo: What built-in prelude-like modules exist ?
651
652 \begin{code}
653 START_REGISTER_PRELUDE(_regByteOps);    /* used in Glasgow tests only? */
654 END_REGISTER_CCS()
655
656 /* _regPrelude is above */
657
658 START_REGISTER_PRELUDE(_regPrelGHC);
659 END_REGISTER_CCS()
660
661 #endif
662 \end{code}