[project @ 1998-12-02 13:17:09 by simonm]
[ghc-hetmet.git] / ghc / rts / ForeignCall.c
1 /* -*- mode: hugs-c; -*- */
2 /* -----------------------------------------------------------------------------
3  * Foreign Function calls
4  *
5  * Copyright (c) 1994-1998.
6  *
7  * $RCSfile: ForeignCall.c,v $
8  * $Revision: 1.2 $
9  * $Date: 1998/12/02 13:28:21 $
10  * ---------------------------------------------------------------------------*/
11
12 #include "Rts.h"
13
14 #ifdef INTERPRETER
15
16 #include "Assembler.h" /* for CFun stuff */
17 #include "Evaluator.h"
18 #include "ForeignCall.h"
19
20 /* the assymetry here seem to come from the caller-allocates 
21  * calling convention.  But does the caller really allocate 
22  * result??
23  */
24
25 void hcall( HFunDescriptor* d, StablePtr fun, void* as, void* rs)
26 {
27 #if 0
28     /* out of date - ADR */
29     marshall(d->arg_tys,as);
30     prim_hcall(fun);
31     unmarshall(d->result_tys,rs);
32 #else
33     assert(0);
34 #endif
35 }
36
37 /* By experiment on an x86 box, we found that gcc's
38  * __builtin_apply(fun,as,size) expects *as to look like this:
39  *   as[0] = &first arg = &as[1]
40  *   as[1] = arg1
41  *   as[2] = arg2
42  *   ...
43  *
44  * on an x86, it returns a pointer to a struct containing an
45  * int/int64/ptr in its first 4-8 bytes and a float/double in the next
46  * 8 bytes.
47  *
48  * On a sparc:
49  *   as[0] = &first arg = &as[2]
50  *   as[1] = where structures should be returned
51  *   as[2] = arg1
52  *   as[3] = arg2
53  *   ...
54  *
55  * This is something of a hack - but seems to be more portable than
56  * hacking it up in assembly language which is how I did it before - ADR
57  */
58 void ccall( CFunDescriptor* d, void (*fun)(void) )
59 {
60     void *rs;
61     char* tys = d->arg_tys;
62     /* ToDo: the use of ARG_SIZE is based on the assumption that Hugs
63      * obeys the same alignment restrictions as C.
64      * But this is almost certainly wrong!
65      * We could use gcc's __va_rounded_size macro (see varargs.h) to do a
66      * better job.
67      */
68 #if i386_TARGET_ARCH
69     void *as=alloca(4 + d->arg_size);
70     StgWord* args = (StgWord*) as;
71     *(void**)(args++) = 4 + (char*)as; /* incoming args ptr */
72     for(; *tys; ++tys) {
73       args += unmarshall(*tys,args);
74     }
75     rs = __builtin_apply(fun,as,(char*)args-(char*)as-4);
76 #elif sparc_TARGET_ARCH
77     void *as=alloca(8 + d->arg_size);
78     StgWord* args = (StgWord*) as;
79     int argcount;
80     *(void**)(args++) = (char*)as; /* incoming args ptr */
81     *(void**)(args++) = 0;  /* structure value address - I think this is the address of a block of memory where structures are returned - in which case we should initialise with rs or something like that*/
82     for(; *tys; ++tys) {
83       args += unmarshall(*tys,args);
84     }
85     argcount = ((void*)args - as);
86     ASSERT(8 + d->arg_size == argcount);
87     if (argcount <= 8) {
88       argcount = 0;
89     } else {
90       argcount -= 4;
91     }
92     rs = __builtin_apply(fun,as,argcount);
93 #else
94 #error Cant do ccall for this architecture
95 #endif
96
97     /* ToDo: can't handle multiple return values at the moment
98      * - it's hard enough to get single return values working
99      */
100     if (*(d->result_tys)) {
101         char ty = *(d->result_tys);
102         ASSERT(d->result_tys[1] == '\0');
103         switch (ty) {
104         case 'F':
105         case 'D': 
106                 /* ToDo: is this right? */
107                 marshall(ty,(char*)rs+8);
108                 return;
109         default:
110                 marshall(ty,rs);
111                 return;
112         }
113     }
114 }
115
116 CFunDescriptor* mkDescriptor( char* as, char* rs ) 
117
118     /* ToDo: don't use malloc */
119     CFunDescriptor *d = malloc(sizeof(CFunDescriptor));
120     assert(d);
121     d->arg_tys = as;
122     d->arg_size = argSize(as);
123     d->result_tys = rs;
124     d->result_size = argSize(rs);
125     return d;
126 }
127
128 #endif /* INTERPRETER */