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