[project @ 1996-01-08 20:28:12 by partain]
[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 #ifndef PAR
110
111 /* Ditto for Malloc Pointer 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(MallocPtr_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 Malloc Pointer---this shouldn't happen!\n");
123     abort();
124     FE_
125 }
126
127 MallocPtr_ITBL(MallocPtr_info,MallocPtr_entry,UpdErr,0,INFO_OTHER_TAG,,,const,EF_,MallocPtr_K,"MALLOC PTR","MallocPtr");
128
129 /* End of MallocPtr stuff */
130
131 /* Ditto for the unused Stable Pointer info table. [ADR]
132 */
133
134 extern void raiseError PROTO((StgStablePtr));
135 extern StgStablePtr errorHandler;
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_static_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_static_info,CC_SUBSUMED,,ED_RO_)
151 };
152
153 /* Entry point and Info table for Stable Pointer Table. */
154
155 STATICFUN(StablePointerTable_entry)
156 {
157     FB_
158     /* Don't wrap the calls; we're done with STG land */
159     fflush(stdout);
160     fprintf(stderr, "Entered the stable pointer table---this shouldn't happen!\n");
161     abort();
162     FE_
163 }
164
165 STATIC_ITBL(EmptyStablePointerTable_static_info,StablePointerTable_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,IF_,SPT_K,"SP_TABLE","SP_TABLE");
166 /* ToDo: could put a useful tag in there!!! */
167
168 DYN_ITBL(StablePointerTable_info,StablePointerTable_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,IF_,SPT_K,"SP_TABLE","SP_TABLE");
169 /* ToDo: could put a useful tag in there!!! */
170
171
172 /* To ease initialisation of the heap, we start with an empty stable
173    pointer table.  When we try to create the first stable pointer, the
174    overflow will trigger creation of a table of useful size.
175 */
176
177 SET_STATIC_HDR(EmptySPTable_closure,EmptyStablePointerTable_static_info,CC_SUBSUMED,,ED_RO_)
178 , (W_) DYN_VHS + 0 + 1 + 0  /* size = DYN_VHS + n + 1 + n with n = 0 */
179 , (W_) 0   /* number of ptrs */
180 , (W_) 0   /* top of stack */
181 };
182
183 /* End of SP stuff */
184 #endif /* !PAR */
185
186
187 /* the IoWorld token to start the whole thing off */
188 /* Question: this is just an amusing hex code isn't it
189    -- or does it mean something? ADR */
190 P_ realWorldZh_closure = (P_) 0xbadbadbaL;
191
192 SET_STATIC_HDR(WorldStateToken_closure,SZh_static_info,CC_SUBSUMED/*harmless*/,,ED_RO_)
193 , (W_) 0xbadbadbaL
194 };
195
196 #ifndef CONCURRENT
197
198 STGFUN(startStgWorld)
199 {
200     FB_
201     /* At this point we are in the threaded-code world.
202
203        TopClosure points to a closure of type PrimIO (), which should be
204        performed (by applying it to the state of the world).
205
206        The smInfo storage-management info block is assumed to be
207        up to date, and is used to load the STG registers.
208     */
209
210 #if defined (DO_SPAT_PROFILING)
211     SET_ACTIVITY(ACT_REDN); /* init: do this first, so we count the restore insns  */
212 #endif
213
214     RestoreAllStgRegs();    /* inline! */
215
216     /* ------- STG registers are now valid! -------------------------*/
217
218     /* Put a suitable return address on the B stack */
219     RetReg = (StgRetAddr) UNVEC(stopThreadDirectReturn,vtbl_stopStgWorld); 
220
221     /* Put an IoWorld token on the A stack */
222     SpA -= AREL(1);
223     *SpA = (P_) WorldStateToken_closure;
224
225     Node = (P_) TopClosure; /* Point to the closure for main/errorIO-arg */
226     ENT_VIA_NODE();
227     InfoPtr=(D_)(INFO_PTR(Node));
228     JMP_(ENTRY_CODE(InfoPtr));
229     FE_
230 }
231 #endif  /* ! CONCURRENT */
232
233 \end{code}
234
235 %************************************************************************
236 %*                                                                      *
237 \subsection[thread-return]{Polymorphic end-of-thread code}
238 %*                                                                      *
239 %************************************************************************
240
241 \begin{code}
242
243 /* 
244    Here's the polymorphic return for the end of a thread.
245
246    NB: For direct returns to work properly, the name of the routine must be
247    the same as the name of the vector table with vtbl_ removed and DirectReturn
248    appended.  This is all the mangler understands.
249 */
250
251 const W_
252 vtbl_stopThread[] = {
253   /* at least "MAX_VECTORED_RTN" elements (see GhcConstants.lh) */
254   (W_) stopThreadDirectReturn,
255   (W_) stopThreadDirectReturn,
256   (W_) stopThreadDirectReturn,
257   (W_) stopThreadDirectReturn,
258   (W_) stopThreadDirectReturn,
259   (W_) stopThreadDirectReturn,
260   (W_) stopThreadDirectReturn,
261   (W_) stopThreadDirectReturn
262 };
263
264 STGFUN(stopThreadDirectReturn)
265 {
266     FB_
267     /* The final exit.
268
269        The top-top-level closures (e.g., "main") are of type "IO ()".
270        When entered, they perform an IO action and return a () --
271        essentially, TagReg is set to 1.  Here, we don't need to do
272        anything with that.
273
274        We just tidy up the register stuff (real regs in *_SAVE, then 
275        *_SAVE -> smInfo locs).
276     */
277
278 #ifdef CONCURRENT
279     SET_TASK_ACTIVITY(ST_OVERHEAD);
280 #endif
281
282     SaveAllStgRegs();   /* inline! */
283
284 #ifdef CONCURRENT
285     EndThread();
286 #else
287     RESUME_(miniInterpretEnd);
288 #endif
289     FE_
290 }
291
292 \end{code}  
293
294 \begin{code}
295 I_ ErrorIO_call_count = 0;
296
297 #ifdef CONCURRENT
298 EXTFUN(EnterNodeCode);
299
300 STGFUN(ErrorIO_innards)
301     /* Assumes that "TopClosure" has been set already */
302 {
303     FB_
304     if (ErrorIO_call_count >= 16 /* MAGIC CONSTANT */ ) {
305         /* Don't wrap the calls; we're done with STG land */
306         fflush(stdout);
307         fprintf(stderr, "too many nested calls to `error'\n");
308         EXIT(EXIT_FAILURE);
309     }
310     ErrorIO_call_count++; /* NB: undo later if decide to let someone else handle it */
311
312     /* Unlock all global closures held by this thread! (ToDo) --JSM */
313
314     switch(TSO_TYPE(CurrentTSO)) {
315     case T_MAIN:
316         /* Re-initialize stack pointers (cf. NewThread) */
317 #ifdef PAR
318         SpB = SuB = STKO_BSTK_BOT(StkOReg) + BREL(1);
319         SuA = STKO_ASTK_BOT(StkOReg) + AREL(1);
320 #else
321         SuA = stackInfo.botA + AREL(1);
322         SuB = stackInfo.botB + BREL(1);
323 #endif
324         break;
325
326     case T_REQUIRED:
327         /* Re-initialize stack pointers (cf. NewThread) */
328         SpB = SuB = STKO_BSTK_BOT(StkOReg) + BREL(1);
329         SuA = STKO_ASTK_BOT(StkOReg) + AREL(1);
330         break;
331
332     case T_ADVISORY:
333         ErrorIO_call_count--; /* undo the damage, as someone else will deal with it */
334         /* Let the main thread eventually handle it */
335         JMP_(stopThreadDirectReturn);
336
337     case T_FAIL:
338         EXIT(EXIT_FAILURE);
339
340     default:
341         /* Don't wrap the calls; we're done with STG land */
342         fflush(stdout);
343         fprintf(stderr,"ErrorIO: %x unknown\n", TSO_TYPE(CurrentTSO));
344         EXIT(EXIT_FAILURE);
345     }
346
347     /* Finish stack setup as if for a top-level task and enter the error node */
348
349     SpA = SuA - AREL(1);
350
351     *SpA = (P_) WorldStateToken_closure;
352
353     STKO_LINK(StkOReg) = Nil_closure;
354     STKO_RETURN(StkOReg) = NULL;
355
356 #ifdef DO_REDN_COUNTING
357     STKO_ADEP(StkOReg) = STKO_BDEP(StkOReg) = 0;
358 #endif
359
360     /* Go! */
361     Node = (P_) TopClosure;
362     RetReg = (StgRetAddr) UNVEC(stopThreadDirectReturn,vtbl_stopStgWorld);
363     JMP_(EnterNodeCode);
364
365     FE_
366 }
367 \end{code}
368
369 We cannot afford to call @error@ too many times
370 (e.g., \tr{error x where x = error x}), so we keep count.
371
372 \begin{code}
373 #else   /* !CONCURRENT */
374
375 StgFunPtr
376 ErrorIO_innards(STG_NO_ARGS)
377     /* Assumes that "TopClosure" has been set already */
378 {
379     FB_
380     if (ErrorIO_call_count >= 16 /* MAGIC CONSTANT */ ) {
381         /* Don't wrap the calls; we're done with STG land */
382         fflush(stdout);
383         fprintf(stderr, "too many nested calls to `error'\n");
384         EXIT(EXIT_FAILURE);
385     }
386     ErrorIO_call_count++;
387
388     /* Copy the heap-related registers into smInfo.  (Other registers get
389        saved in this process, but we aren't interested in them.)
390
391        Get a new stack (which re-initialises the smInfo stack stuff),
392        and start the world again.
393     */
394     /* ToDo: chk this has been handled in parallel world */
395
396     SaveAllStgRegs();   /* inline! */
397
398     if ( initStacks( &StorageMgrInfo ) != 0) {
399         /* Don't wrap the calls; we're done with STG land */
400         fflush(stdout);
401         fprintf(stderr, "initStacks failed!\n");
402         EXIT(EXIT_FAILURE);
403     }
404
405     JMP_( startStgWorld );
406     FE_
407 }
408
409 #endif  /* !CONCURRENT */
410 \end{code}  
411
412 \begin{code}
413 #ifdef PAR
414
415 STATICFUN(RBH_Save_0_entry)
416 {
417   FB_
418   fprintf(stderr,"Oops, entered an RBH save\n");
419   EXIT(EXIT_FAILURE);
420   FE_
421 }
422
423 STATICFUN(RBH_Save_1_entry)
424 {
425   FB_
426   fprintf(stderr,"Oops, entered an RBH save\n");
427   EXIT(EXIT_FAILURE);
428   FE_
429 }
430
431 STATICFUN(RBH_Save_2_entry)
432 {
433   FB_
434   fprintf(stderr,"Oops, entered an RBH save\n");
435   EXIT(EXIT_FAILURE);
436   FE_
437 }
438
439 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");
440 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");
441 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");
442
443 #endif /* PAR */
444 \end{code}
445
446
447 %/****************************************************************
448 %*                                                              *
449 %*              Other Bits and Pieces                           *
450 %*                                                              *
451 %****************************************************************/
452
453 \begin{code}
454 /* If we don't need the slow entry code for a closure, we put in a
455    pointer to this in the closure's slow entry code pointer instead.
456  */
457
458 STGFUN(__std_entry_error__) {
459     FB_
460     /* Don't wrap the calls; we're done with STG land */
461     fflush(stdout);
462     fprintf(stderr, "Called non-existent slow-entry code!!!\n");
463     abort();
464     JMP_(0);
465     FE_
466 }
467
468 /* entry code */
469 STGFUN(STK_STUB_entry) {
470     FB_
471     /* Don't wrap the calls; we're done with STG land */
472     fflush(stdout);
473     fprintf(stderr, "Entered from a stubbed stack slot!\n");
474     abort();
475     JMP_(0);
476     FE_
477 }
478
479 /* info table */
480 STATIC_ITBL(STK_STUB_static_info,STK_STUB_entry,UpdErr,0,INFO_OTHER_TAG,0,0,const,EF_,INTERNAL_KIND,"STK_STUB","STK_STUB");
481
482 /* closure */
483 SET_STATIC_HDR(STK_STUB_closure,STK_STUB_static_info,CC_SUBSUMED,,EXTDATA_RO)
484   , (W_)0, (W_)0
485 };
486 \end{code}
487
488 \begin{code}
489 #ifdef GRAN
490
491 STGFUN(Event_Queue_entry) {
492     FB_
493     /* Don't wrap the calls; we're done with STG land */
494     fflush(stdout);
495     fprintf(stderr, "Entered from an event queue!\n");
496     abort();
497     JMP_(0);
498     FE_
499 }
500
501 GEN_N_ITBL(Event_Queue_info,Event_Queue_entry,UpdErr,0,INFO_OTHER_TAG,5,2,const,EF_,INTERNAL_KIND,"EventQ","EventQ");
502
503 #endif /* GRAN */
504 \end{code}
505
506
507
508 %/****************************************************************
509 %*                                                              *
510 %*              Some GC info tables                           *
511 %*                                                              *
512 %****************************************************************/
513
514 These have to be in a .lhc file, so they will be reversed correctly.
515
516 \begin{code}
517 #include "../storage/SMinternal.h"
518
519 #if defined(_INFO_COPYING)
520
521 STGFUN(Caf_Evac_Upd_entry) {
522     FB_
523     /* Don't wrap the calls; we're done with STG land */
524     fflush(stdout);
525     fprintf(stderr,"Entered Caf_Evac_Upd %lx: Should never occur!\n", (W_) Node);
526     abort();
527     FE_
528 }
529
530 CAF_EVAC_UPD_ITBL(Caf_Evac_Upd_info,Caf_Evac_Upd_entry,const/*not static*/);
531
532 #if defined(GCgn)
533
534 STGFUN(Forward_Ref_New_entry) {
535     FB_
536     /* Don't wrap the calls; we're done with STG land */
537     fflush(stdout);
538     fprintf(stderr,"Entered Forward_Ref_New %lx: Should never occur!\n", (W_) Node);
539     abort();
540     FE_
541 }
542 FORWARDREF_ITBL(Forward_Ref_New_info,Forward_Ref_New_entry,const/*not static*/,_Evacuate_Old_Forward_Ref);
543
544 STGFUN(Forward_Ref_Old_entry) {
545     FB_
546     /* Don't wrap the calls; we're done with STG land */
547     fflush(stdout);
548     fprintf(stderr,"Entered Forward_Ref_Old %lx: Should never occur!\n", (W_) Node);
549     abort();
550     FE_
551 }
552 FORWARDREF_ITBL(Forward_Ref_Old_info,Forward_Ref_Old_entry,const/*not static*/,_Evacuate_New_Forward_Ref);
553
554 STGFUN(OldRoot_Forward_Ref_entry) {
555     FB_
556     /* Don't wrap the calls; we're done with STG land */
557     fflush(stdout);
558     fprintf(stderr,"Entered OldRoot_Forward_Ref %lx: Should never occur!\n", (W_) Node);
559     abort();
560     FE_
561 }
562 FORWARDREF_ITBL(OldRoot_Forward_Ref_info,OldRoot_Forward_Ref_entry,const/*not static*/,_Evacuate_OldRoot_Forward);
563 #else /* ! GCgn */
564
565 STGFUN(Forward_Ref_entry) {
566     FB_
567     /* Don't wrap the calls; we're done with STG land */
568     fflush(stdout);
569     fprintf(stderr,"Entered Forward_Ref %lx: Should never occur!\n", (W_) Node);
570     abort();
571     FE_
572 }
573 FORWARDREF_ITBL(Forward_Ref_info,Forward_Ref_entry,const/*not static*/,_Evacuate_Forward_Ref);
574 #endif /* ! GCgn */
575
576 #endif /* _INFO_COPYING */
577
578 #if defined(GCgn)
579 OLDROOT_ITBL(OldRoot_info,Ind_Entry,const,EF_);
580 #endif /* GCgn */
581 \end{code}
582
583
584 %/***************************************************************
585 %*                                                              *
586 %*              Cost Centre stuff ...                           *
587 %*                                                              *
588 %****************************************************************/
589
590 For cost centres we need prelude cost centres and register routine.
591
592 N.B. ALL prelude cost centres should be declared here as none will
593      be declared when the prelude is compiled.
594
595 ToDo: Explicit cost centres in prelude for Input and Output costs.
596
597 \begin{code}
598 #if defined(USE_COST_CENTRES)
599
600 STGFUN(startCcRegisteringWorld)
601 {
602     FB_
603     /* 
604      * We used to push miniInterpretEnd on the register stack, but
605      * miniInterpretEnd must only be entered with the RESUME_ macro,
606      * whereas the other addresses on the register stack must only be
607      * entered with the JMP_ macro.  Now, we push NULL and test for 
608      * it explicitly at each pop.
609      */
610     PUSH_REGISTER_STACK(NULL);
611     JMP_(_regMain);
612     FE_
613 }
614
615 CC_DECLARE(CC_CAFs,  "CAFs_in_...",  "PRELUDE", "PRELUDE", CC_IS_CAF,/*not static*/);
616 CC_DECLARE(CC_DICTs, "DICTs_in_...", "PRELUDE", "PRELUDE", CC_IS_DICT,/*not static*/);
617
618 START_REGISTER_PRELUDE(_regPrelude);
619 REGISTER_CC(CC_CAFs);
620 REGISTER_CC(CC_DICTs);
621 END_REGISTER_CCS()
622 \end{code}
623
624 We also need cost centre declarations and registering routines for other
625 built-in prelude-like modules.
626
627 ToDo: What built-in prelude-like modules exist ?
628
629 \begin{code}
630 START_REGISTER_PRELUDE(_regByteOps);    /* used in Glasgow tests only? */
631 END_REGISTER_CCS()
632
633 /* _regPrelude is above */
634
635 START_REGISTER_PRELUDE(_regPreludeArray);
636 END_REGISTER_CCS()
637
638 START_REGISTER_PRELUDE(_regPreludeCore);
639 END_REGISTER_CCS()
640
641 START_REGISTER_PRELUDE(_regPreludeDialogueIO);
642 END_REGISTER_CCS()
643
644 START_REGISTER_PRELUDE(_regPreludeGlaMisc);
645 END_REGISTER_CCS()
646
647 START_REGISTER_PRELUDE(_regPreludeGlaST);
648 END_REGISTER_CCS()
649
650 START_REGISTER_PRELUDE(_regPreludeIOError);
651 END_REGISTER_CCS()
652
653 START_REGISTER_PRELUDE(_regPreludePS);
654 END_REGISTER_CCS()
655
656 START_REGISTER_PRELUDE(_regPreludePrimIO);
657 END_REGISTER_CCS()
658
659 START_REGISTER_PRELUDE(_regPreludeStdIO);
660 END_REGISTER_CCS()
661 #endif
662 \end{code}