summaryrefslogtreecommitdiff
path: root/ghc/includes/COptWraps.lh
blob: baf217b6635f342110b82151eb35f469425e52a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
\section[COptWraps]{Wrappers for calls to ``STG C'' routines}

% this file is part of the C-as-assembler document

\begin{code}
#ifndef COPTWRAPS_H
#define COPTWRAPS_H
\end{code}

%************************************************************************
%*									*
\subsection[COptWraps-portable]{Wrappers for ``portable~C''}
%*									*
%************************************************************************

@STGCALL@ macros are used when we really have to be careful about saving
any caller-saves STG registers.  @SAFESTGCALL@ macros are used
when the caller has previously arranged to save/restore volatile user
registers (vanilla, float, and double STG registers), and we only have to
worry about the ``system'' registers (stack and heap pointers, @STK_STUB@,
etc.).  @STGCALL_GC@ macros are used whenever the callee is going to
need to access (and perhaps modify) some STG registers.  @ULTRASAFESTGCALL@
is available for our own routines that we are absolutely certain will not
damage any STG registers.

In short,
\begin{itemize}
\item @STGCALL@ saves/restores all caller-saves STG registers.
\item @SAFESTGCALL@ saves/restores only caller-saves STG ``system'' registers.
\item @ULTRASAFECALL@ is a simple call, without a wrapper.
\item @STGCALL_GC@ saves/restores {\em all} STG registers.
\end{itemize}
    
Several macros are provided to handle outcalls to functions requiring from
one to five arguments.  (If we could assume GCC, we could use macro varargs,
but unfortunately, we have to cater to ANSI C as well.)

\begin{code}

#define ULTRASAFESTGCALL0(t,p,f)	    f()
#define ULTRASAFESTGCALL1(t,p,f,a)	    f(a)
#define ULTRASAFESTGCALL2(t,p,f,a,b)	    f(a,b)
#define ULTRASAFESTGCALL3(t,p,f,a,b,c)	    f(a,b,c)
#define ULTRASAFESTGCALL4(t,p,f,a,b,c,d)    f(a,b,c,d)
#define ULTRASAFESTGCALL5(t,p,f,a,b,c,d,e)  f(a,b,c,d,e)

#if ! (defined(__GNUC__) && defined(__STG_GCC_REGS__))

#define STGCALL0(t,p,f)		    f()
#define STGCALL1(t,p,f,a)	    f(a)
#define STGCALL2(t,p,f,a,b)	    f(a,b)
#define STGCALL3(t,p,f,a,b,c)	    f(a,b,c)
#define STGCALL4(t,p,f,a,b,c,d)	    f(a,b,c,d)
#define STGCALL5(t,p,f,a,b,c,d,e)   f(a,b,c,d,e)

#define SAFESTGCALL0(t,p,f)	      f()
#define SAFESTGCALL1(t,p,f,a)	      f(a)
#define SAFESTGCALL2(t,p,f,a,b)	      f(a,b)
#define SAFESTGCALL3(t,p,f,a,b,c)     f(a,b,c)
#define SAFESTGCALL4(t,p,f,a,b,c,d)   f(a,b,c,d)
#define SAFESTGCALL5(t,p,f,a,b,c,d,e) f(a,b,c,d,e)

/* 
 * Generic call_GC wrappers have gone away in favor of these partially
 * evaluated versions.
 */

#define DO_GC(args)			    \
    do {SaveAllStgRegs(); PerformGC(args); RestoreAllStgRegs();} while(0)
#define DO_STACKOVERFLOW(headroom,args)	    \
    do {SaveAllStgRegs(); StackOverflow(headroom,args); RestoreAllStgRegs();} while(0)

#if defined(GRAN)

#define DO_YIELD(args)   DO_GRAN_YIELD(args)
#define DO_GRAN_YIELD(liveness)		    \
    do {SaveAllStgRegs(); Yield(liveness); RestoreAllStgRegs();} while(0)

#define DO_PERFORM_RESCHEDULE(liveness_mask,reenter)		    \
    do {SaveAllStgRegs(); PerformReschedule(liveness_mask,reenter); RestoreAllStgRegs();} while(0)

#else

#define DO_YIELD(args)		    \
    do {SaveAllStgRegs(); Yield(args); RestoreAllStgRegs();} while(0)

#endif   /* GRAN */

\end{code}

%************************************************************************
%*									*
\subsection[COptWraps-optimised]{Wrappers in ``optimised~C''}
%*									*
%************************************************************************

We {\em expect} the call-wrappery to be boring---the defaults shown
herein will kick in--- but you never know.

For example: Don't try an @STGCALL6@ on a SPARC!  That's because you
cannot pass that many arguments to \tr{f} just by heaving them into
\tr{%o*} registers; anything else is too painful to contemplate.

\begin{code}
#else /* __GNUC__ && __STG_GCC_REGS__ */

#if !(defined(CALLER_SAVES_SYSTEM) || defined(CALLER_SAVES_USER))
#define STGCALL0(t,p,f)		  f()
#define STGCALL1(t,p,f,a)	  f(a)
#define STGCALL2(t,p,f,a,b)	  f(a,b)
#define STGCALL3(t,p,f,a,b,c)	  f(a,b,c)
#define STGCALL4(t,p,f,a,b,c,d)	  f(a,b,c,d)
#define STGCALL5(t,p,f,a,b,c,d,e) f(a,b,c,d,e)

#else

extern void callWrapper(STG_NO_ARGS);

#define STGCALL0(t,p,f)	    	    \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f);})

#define STGCALL1(t,p,f,a)   	    \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f,a);})

#define STGCALL2(t,p,f,a,b) 	    \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f,a,b);})

#define STGCALL3(t,p,f,a,b,c)	    \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f,a,b,c);})

#define STGCALL4(t,p,f,a,b,c,d)	    \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f,a,b,c,d);})

#define STGCALL5(t,p,f,a,b,c,d,e)   \
    ({t (*_w)p = (t (*)p) callWrapper; (*_w)((void *)f,a,b,c,d,e);})

#endif

#if !defined(CALLER_SAVES_SYSTEM)
#define SAFESTGCALL0(t,p,f)	      f()
#define SAFESTGCALL1(t,p,f,a)	      f(a)
#define SAFESTGCALL2(t,p,f,a,b)	      f(a,b)
#define SAFESTGCALL3(t,p,f,a,b,c)     f(a,b,c)
#define SAFESTGCALL4(t,p,f,a,b,c,d)   f(a,b,c,d)
#define SAFESTGCALL5(t,p,f,a,b,c,d,e) f(a,b,c,d,e)

#else

extern void callWrapper_safe(STG_NO_ARGS);

#define SAFESTGCALL0(t,p,f) 	    	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f);})

#define SAFESTGCALL1(t,p,f,a)	    	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f,a);})

#define SAFESTGCALL2(t,p,f,a,b)	    	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f,a,b);})

#define SAFESTGCALL3(t,p,f,a,b,c)   	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f,a,b,c);})

#define SAFESTGCALL4(t,p,f,a,b,c,d) 	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f,a,b,c,d);})

#define SAFESTGCALL5(t,p,f,a,b,c,d,e)	\
    ({t (*_w)p = (t (*)p) callWrapper_safe; (*_w)((void *)f,a,b,c,d,e);})

#endif

/* 
 * Generic call_GC wrappers have gone away in favor of these partially
 * evaluated versions.  These are only here so that we can avoid putting
 * all of the STG register save/restore code at each call site.
 */

#ifndef CALLWRAPPER_C
/* 
 * We may have funny declarations in CallWrapper_C, to avoid sliding the
 * register windows and other nastiness.
 */
void PerformGC_wrapper PROTO((W_));
void StackOverflow_wrapper PROTO((W_, W_));
void Yield_wrapper PROTO((W_));
#  ifdef GRAN
void PerformReschedule_wrapper PROTO((W_, W_));
void GranSimAllocate_wrapper PROTO((I_, P_, W_));
void GranSimUnallocate_wrapper PROTO((I_, P_, W_));
void GranSimFetch_wrapper PROTO((P_));
void GranSimExec_wrapper PROTO((W_, W_, W_, W_, W_));
#  endif
#endif

#define DO_GC(args)			PerformGC_wrapper(args)
#define DO_STACKOVERFLOW(headroom,args) StackOverflow_wrapper(headroom,args)

#  ifdef GRAN

#define DO_YIELD(args)   DO_GRAN_YIELD(args)
#define DO_GRAN_YIELD(liveness)			Yield_wrapper(liveness)

#define DO_PERFORMRESCHEDULE(liveness, always_reenter_node) PerformReschedule_wrapper(liveness, always_reenter_node)
#define DO_GRANSIMALLOCATE(n, node, liveness)   GranSimAllocate_wrapper(n, node, liveness)
#define DO_GRANSIMUNALLOCATE(n, node, liveness) GranSimUnallocate_wrapper(n, node, liveness)
#define DO_GRANSIMFETCH(node)                   GranSimFetch_wrapper(node)
#define DO_GRANSIMEXEC(arith,branch,load,store,floats) GranSimExec_wrapper(arith,branch,load,store,floats)

#  else

#define DO_YIELD(args)			Yield_wrapper(args)

#  endif

#endif /* __GNUC__ && __STG_GCC_REGS__ */
\end{code}

%************************************************************************
%*									*
\subsection[COptWraps-magic]{Magic assembly bits for call wrappers}
%*									*
%************************************************************************

Call wrappers need to be able to call arbitrary functions, regardless of
their arguments and return types.  (Okay, we actually only allow up to
five arguments, because on the SPARC it gets more complicated to handle
any more.)  The nasty bit is that the return value can be in either an
integer register or a floating point register, and we don't know which.
(We {\em don't} handle structure returns, and we don't want to.)
Still, we have to stash the result away while we restore caller-saves
STG registers, and then we have to pass the result back to our caller
in the end.

Getting this right requires three extremely @MAGIC@ macros, no doubt
chock full of assembly gook for the current platform.  These are
@MAGIC_CALL_SETUP@, which gets ready for one of these magic calls,
@MAGIC_CALL@, which performs the call and stashes away all possible
results, and @MAGIC_RETURN@, which collects all possible results back
up again.

For example, in the SPARC version, the @SETUP@ guarantees that we
have enough space to store all of our argument registers for a wee
bit, and it gives a `C' name to the register that we're going to use
for the call.  (It helps to do the call in actual `C' fashion, so that
gcc knows about register death.)  It also stashes the incoming arguments
in the space  provided.  The @MAGIC_CALL@ then reloads the argument
registers, rotated by one, so that the function to call is in \tr{%o5},
calls the function in `C' fashion, and stashes away the possible return
values (either \tr{%o0} or \tr{%f0}) on the stack.  Finally, @MAGIC_RETURN@
ensures that \tr{%o0} and \tr{%f0} are both set to the values we stashed
away.  Presumably, we then fall into a return instruction and our caller
gets whatever it's after.

%************************************************************************
%*									*
\subsubsection[alpha-magic]{Call-wrapper MAGIC for DEC Alpha}
%*									*
%************************************************************************

\begin{code}

#if defined(__GNUC__) && defined(__STG_GCC_REGS__)

#if alpha_TARGET_ARCH

#define MAGIC_CALL_SETUP	\
    long WeNeedThisSpace[7];  	\
    double AndThisSpaceToo[6];	\
    register void (*f)() __asm__("$21");\
    __asm__ volatile (		\
        "stq $16,8($30)\n"	\
	"\tstq $17,16($30)\n"	\
    	"\tstq $18,24($30)\n"	\
    	"\tstq $19,32($30)\n"	\
    	"\tstq $20,40($30)\n"	\
    	"\tstq $21,48($30)\n"	\
    	"\tstt $f16,56($30)\n"	\
    	"\tstt $f17,64($30)\n"	\
    	"\tstt $f18,72($30)\n"	\
    	"\tstt $f19,80($30)\n"	\
    	"\tstt $f20,88($30)\n"	\
    	"\tstt $f21,96($30)");

#define MAGIC_CALL		\
    __asm__ volatile (		\
	"ldq $21,8($30)\n"	\
        "\tldq $16,16($30)\n"	\
        "\tldq $17,24($30)\n"	\
        "\tldq $18,32($30)\n"	\
        "\tldq $19,40($30)\n"	\
        "\tldq $20,48($30)\n"	\
        "\tldt $f16,56($30)\n"	\
        "\tldt $f17,64($30)\n"	\
        "\tldt $f18,72($30)\n"	\
        "\tldt $f19,80($30)\n"	\
        "\tldt $f20,88($30)\n"	\
        "\tldt $f21,96($30)");\
    (*f)();   	    	    	\
    __asm__ volatile (		\
	"stq $0,8($30)\n"	\
        "\tstt $f0,16($30)");

#define MAGIC_RETURN		\
    __asm__ volatile (		\
	"ldq $0,8($30)\n"	\
        "\tldt $f0,16($30)");

#define WRAPPER_NAME(f)	  /* nothing */

/* 
   Threaded code needs to be able to grab the return address, in case we have
   an intervening context switch.
 */

#define SET_RETADDR(loc)  { register StgFunPtrFunPtr ra __asm__ ("$26"); loc = ra; }

#define WRAPPER_SETUP(f,ignore1,ignore2)  SaveAllStgContext();

#define WRAPPER_RETURN(x)   \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* __alpha */

\end{code}

%************************************************************************
%*									*
\subsubsection[hppa-magic]{Call-wrapper MAGIC for HP-PA}
%*									*
%************************************************************************

\begin{code}

#if hppa1_1_TARGET_ARCH

#define MAGIC_CALL_SETUP	    \
    long SavedIntArgRegs[4];	    \
    double SavedFltArgRegs[2];	    \
    register void (*f)() __asm__("%r28");\
    __asm__ volatile (		    \
        "copy %r26,%r28\n"	    \
	"\tstw %r25,8(0,%r3)\n"	    \
	"\tstw %r24,12(0,%r3)\n"    \
	"\tstw %r23,16(0,%r3)\n"    \
	"\tldo 40(%r3),%r19\n"	    \
        "\tfstds %fr5,-16(0,%r19)\n"\
	"\tfstds %fr7, -8(0,%r19)\n");


#define MAGIC_CALL		    \
    __asm__ volatile (		    \
	"ldw 8(0,%r3),%r26\n"	    \
	"\tldw 12(0,%r3),%r25\n"    \
	"\tldw 16(0,%r3),%r24\n"    \
        "\tldw -52(0,%r3),%r23\n"   \
        "\tldw -56(0,%r3),%r19\n"   \
	"\tstw %r19,-52(0,%r30)\n"  \
	"\tldo 40(%r3),%r19\n"	    \
        "\tfldds -16(0,%r19),%fr5\n"\
	"\tfldds -8(0,%r19),%fr7\n" \
	"\tldo -64(%r3),%r19\n"	    \
	"\tldo -64(%r30),%r20\n"    \
        "\tfldds -16(0,%r19),%fr4\n"\
        "\tfstds %fr4,-16(0,%r20)\n"\
	"\tfldds -8(0,%r19)%fr4\n"  \
	"\tfstds %fr4,-8(0,%r19)\n" \
        "\tfldds 0(0,%r19),%fr4\n"  \
        "\tfstds %fr4,0(0,%r19)\n"  \
	"\tfldds 8(0,%r19),%fr4\n"  \
	"\tfstds %fr4,8(0,%r19)\n");\
    (*f)();			    \
    __asm__ volatile (		    \
	"stw %r28,8(0,%r3)\n"	    \
        "\tfstds %fr4,16(0,%r3)");

#define MAGIC_RETURN		    \
    __asm__ volatile (		    \
        "\tfldds 16(0,%r3),%fr4"    \
	"ldw 8(0,%r3),%r28\n");

#define WRAPPER_NAME(f)	  /* nothing */

/* 
   Threaded code needs to be able to grab the return address, in case we have
   an intervening context switch.
 */

#define SET_RETADDR(loc)  __asm__ volatile ("stw %%r2, %0" : "=m" ((void *)(loc)));

#define WRAPPER_SETUP(f,ignore1,ignore2)  SaveAllStgContext();

#define WRAPPER_RETURN(x)   \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* __hppa */

\end{code}

%************************************************************************
%*									*
\subsubsection[iX86-magic]{Call-wrapper MAGIC for iX86}
%*									*
%************************************************************************

\begin{code}
#if i386_TARGET_ARCH

/* modelled loosely on SPARC stuff */

/* NB: no MAGIC_CALL_SETUP, MAGIC_CALL, or MAGIC_RETURN! */

#define WRAPPER_NAME(f) /*nothing*/

#if defined(solaris2_TARGET_OS) || defined(linux_TARGET_OS)
#define REAL_NAME(f)   #f
#else
#define REAL_NAME(f)   "_" #f
#endif

/* 
   Threaded code needs to be able to grab the return address, in case we have
   an intervening context switch.
 */

#define SET_RETADDR(loc,val) loc = val;

/* the grab-%eax-quickly HACK is here because we use a VERY SPECIAL
   calling convention on iX86 just for calling PerformGC_wrapper.
   (WDP 95/09)

   NB: mangler makes sure that __temp_{eax,esp} get loaded.
   (This is about as ugly as it can get.)
*/

#define WRAPPER_SETUP(f,ret_addr,args)			\
    __asm__ volatile (					\
    	"movl "   REAL_NAME(__temp_esp)  ",%%edx\n"	\
	"\tmovl (%%edx),%0\n"				\
	"\tmovl " REAL_NAME(__temp_eax) ",%1"		\
    	: "=r" (ret_addr), "=r" (args) );		\
    SaveAllStgContext(ret_addr);

/* Note re WRAPPER_SETUP: we have special code just for PerformGC_wrapper;
   pls see its definition.  WDP 95/09

   Also note the EXTREMELY UGLY slamming in of an "sp_offset"; the
   return address *is* on the stack, but it is hard to get there
   before GCC has moved the sp pointer... WDP 95/11
*/

#define WRAPPER_RETURN(x)   \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* iX86 */
\end{code}

%************************************************************************
%*									*
\subsubsection[m68k-magic]{Call-wrapper MAGIC for m68k}
%*									*
%************************************************************************

\begin{code}

#if m68k_TARGET_ARCH

#define MAGIC_CALL_SETUP  \
    int WeNeedThisSpace[5];   	    	\
    register void (*f)() __asm__("a0");	\
    __asm__ volatile (			\
    "movel a6@(8),a0\n"			\
    "\tmovel a6@(12),a6@(-20)\n" 	\
    "\tmovel a6@(16),a6@(-16)\n"	\
    "\tmovel a6@(20),a6@(-12)\n" 	\
    "\tmovel a6@(24),a6@(-8)\n"		\
    "\tmovel a6@(28),a6@(-4)");

#define MAGIC_CALL	\
    (*f)();   	    	\
     __asm__ volatile (	\
    "movel d0, sp@-\n"  \
    "\tmovel d1,sp@-");

#define MAGIC_RETURN	\
    __asm__ volatile (	\
    "movel sp@+,d0\n"	\
    "\tmovel sp@+,d1");

#define WRAPPER_NAME(f)	  /* nothing */

#define WRAPPER_SETUP(f,ignore1,ignore2)  SaveAllStgContext();

#define WRAPPER_RETURN(x)  \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* __mc680x0__ */

\end{code}

%************************************************************************
%*									*
\subsubsection[mips-magic]{Call-wrapper MAGIC for MIPS}
%*									*
%************************************************************************

\begin{code}
#if mipseb_TARGET_ARCH || mipsel_TARGET_ARCH

/* shift 4 arg registers down one */

#define MAGIC_CALL_SETUP  \
    register void (*f)() __asm__("$2");	\
    __asm__ volatile (			\
    "move $2,$4\n"			\
    "\tmove $4,$5\n"			\
    "\tmove $5,$6\n"			\
    "\tmove $6,$7\n"			\
    "\tlw $7,16($sp)\n"			\
    "\taddu $sp,$sp,4\n"		\
    : : : "$2" );

#define MAGIC_CALL		\
    (*f)();   	    		\
     __asm__ volatile (		\
    "subu $sp,$sp,4\n"		\
    "\ts.d $f0, -8($sp)\n"  	\
    "\tsw  $2, -12($sp)");

#define MAGIC_RETURN		\
    __asm__ volatile (		\
    "l.d $f0, -8($sp)\n"	\
    "\tlw  $2, -12($sp)");

#define WRAPPER_NAME(f)	  /* nothing */

#define WRAPPER_SETUP(f,ignore1,ignore2)  SaveAllStgContext();

#define WRAPPER_RETURN(x)  \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* mips */
\end{code}

%************************************************************************
%*									*
\subsubsection[powerpc-magic]{Call-wrapper MAGIC for PowerPC}
%*									*
%************************************************************************

\begin{code}
#if powerpc_TARGET_ARCH

/* shift 4 arg registers down one */

#define MAGIC_CALL_SETUP  \
    register void (*f)() __asm__("$2");	\
    __asm__ volatile (			\
    "move $2,$4\n"			\
    "\tmove $4,$5\n"			\
    "\tmove $5,$6\n"			\
    "\tmove $6,$7\n"			\
    "\tlw $7,16($sp)\n"			\
    "\taddu $sp,$sp,4\n"		\
    : : : "$2" );

#define MAGIC_CALL		\
    (*f)();   	    		\
     __asm__ volatile (		\
    "subu $sp,$sp,4\n"		\
    "\ts.d $f0, -8($sp)\n"  	\
    "\tsw  $2, -12($sp)");

#define MAGIC_RETURN		\
    __asm__ volatile (		\
    "l.d $f0, -8($sp)\n"	\
    "\tlw  $2, -12($sp)");

#define WRAPPER_NAME(f)	  /* nothing */

#define WRAPPER_SETUP(f,ignore1,ignore2)  SaveAllStgContext();

#define WRAPPER_RETURN(x)  \
    do {RestoreAllStgRegs(); if(x) JMP_(EnterNodeCode);} while(0);

#define SEPARATE_WRAPPER_RESTORE    /* none */

#endif /* powerpc */
\end{code}

%************************************************************************
%*									*
\subsubsection[sparc-magic]{Call-wrapper MAGIC for SPARC}
%*									*
%************************************************************************

\begin{code}
#if sparc_TARGET_ARCH

#define MAGIC_CALL_SETUP	\
    int WeNeedThisSpace[6];   	\
    register void (*f)() __asm__("%o5");\
    __asm__ volatile (		\
	"std %i0,[%fp-40]\n"	\
	"\tstd %i2,[%fp-32]\n" 	\
	"\tstd %i4,[%fp-24]");

/* Lest GCC attempt to stick something in
   the delay slot: with compile with
   -fno-delayed-branch.  A weak solution. WDP 96/07
*/
#define MAGIC_CALL		\
    __asm__ volatile (		\
        "ld [%%fp-40],%%o5\n"	\
    	"\tld [%%fp-36],%%o0\n" \
    	"\tld [%%fp-32],%%o1\n" \
    	"\tld [%%fp-28],%%o2\n" \
    	"\tld [%%fp-24],%%o3\n" \
    	"\tld [%%fp-20],%%o4"	\
        : : : "%o0", "%o1", "%o2", "%o3", "%o4", "%o5");\
    (*f)();   	    	    	\
    __asm__ volatile (		\
	"std %f0,[%fp-40]\n"	\
    	"\tstd %o0,[%fp-32]");
#if 0
/* We leave nothing to chance here; we have seen
   GCC stick "unwanted" code in the branch delay
   slot, causing mischief (WDP 96/05)
*/
/* the problem with this one: GCC has no way of
   knowing there is a "call" in there, so it
   does not do any calling-convention stuff
   (e.g., saving used regs).  WDP 96/07
*/
#define MAGIC_CALL		\
    __asm__ volatile (		\
        "ld [%%fp-40],%%o5\n"	\
    	"\tld [%%fp-36],%%o0\n" \
    	"\tld [%%fp-32],%%o1\n" \
    	"\tld [%%fp-28],%%o2\n" \
    	"\tld [%%fp-24],%%o3\n" \
    	"\tld [%%fp-20],%%o4\n"	\
	"\tcall %%o5\n"		\
	"\tnop\n"		\
	"\tstd %%f0,[%%fp-40]\n"\
    	"\tstd %%o0,[%%fp-32]"  \
	: : : "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o7", "%f0", "memory");
#endif /* 0 */

#define MAGIC_RETURN		\
    __asm__ volatile (		\
        "ldd [%fp-40],%f0\n"	\
        "\tldd [%fp-32],%i0");

/* 
   We rename the entry points for wrappers so that we can introduce a
   new entry point after the prologue.  We want to ensure that the
   register window does not slide!  However, we insert a call to
   abort() to make gcc _believe_ that the window slid.
 */

#define WRAPPER_NAME(f)	  __asm__("L" #f "_wrapper")

#ifdef solaris2_TARGET_OS
#define REAL_NAME(f)   #f
#else
#define REAL_NAME(f)   "_" #f
#endif

#define WRAPPER_SETUP(f,ignore1,ignore2)    \
    __asm__ volatile (			    \
        ".global " REAL_NAME(f) "_wrapper\n"\
        REAL_NAME(f) "_wrapper:\n"	    \
        "\tstd %o0,[%sp-24]\n"		    \
        "\tmov %o7,%i7");		    \
    SaveAllStgContext();		    \
    __asm__ volatile (			    \
	"ldd [%sp-24],%i0\n"		    \
	"\tmov %i0,%o0\n"		    \
	"\tmov %i1,%o1");
/* 
 * In the above, we want to ensure that the arguments are both in the
 * %i registers and the %o registers, with the assumption that gcc
 * will expect them now to be in one or the other.  This is a terrible
 * hack.
 */

/* 
   Threaded code needs to be able to grab the return address, in case
   we have an intervening context switch.  Note that we want the
   address of the next instruction to be executed, so we add 8 to the
   link address.
 */

#define SET_RETADDR(loc)	\
    __asm__ volatile (		\
	"add %%i7,8,%%o7\n"	\
    	"\tst %%o7,%0"		\
    	: "=m" (loc) : : "%o7");


#define WRAPPER_RETURN(x)		\
    __asm__ volatile (			\
        "call Lwrapper_restore" #x "\n" \
        "\tnop");			\
    abort();

/* 
   The sparc is a big nuisance.  We use a separate function for 
   restoring STG registers so that gcc won't try to leave anything
   (like the address of MainRegTable) in the stack frame that we
   didn't build.  We also use a leaf return in a format that allows us 
   to pass %o7 in as an argument known to gcc, in the hope that its
   value will be preserved during the reloading of STG registers.
   Note that the current gcc (2.5.6) does not use the delay slot
   here (%#), but perhaps future versions will.
 */

#if defined(CONCURRENT)
#define WRAPPER_REENTER    \
void wrapper_restore_and_reenter_node(STG_NO_ARGS)  \
{					\
     __asm__("Lwrapper_restore1:");	\
    RestoreAllStgRegs();		\
    JMP_(EnterNodeCode);		\
}
#else
#define WRAPPER_REENTER
#endif

#define SEPARATE_WRAPPER_RESTORE	\
void wrapper_restore(STG_NO_ARGS)	\
{   	    	    	    	    	\
    register void *o7 __asm__("%o7");	\
    __asm__ volatile (			\
        "Lwrapper_restore0:\n"	    	\
	"\tmov %%i7,%0" : "=r" (o7));	\
    RestoreAllStgRegs();		\
    __asm__ volatile ("jmp %0+8%#" : : "r" (o7));	\
}					\
WRAPPER_REENTER

#endif /* __sparc__ */

#endif /* __GNUC__ && __STG_GCC_REGS__ */

\end{code}

That's all, folks.
\begin{code}
#endif /* ! COPTWRAPS_H */
\end{code}