2 /* -----------------------------------------------------------------------------
3 * $Id: ForeignCall.c,v 1.6 1999/10/19 11:01:26 sewardj Exp $
5 * (c) The GHC Team 1994-1999.
7 * Foreign Function calls
9 * ---------------------------------------------------------------------------*/
15 #include "Assembler.h" /* for CFun stuff */
16 #include "Evaluator.h"
17 #include "ForeignCall.h"
20 /* --------------------------------------------------------------------------
21 * Calling out to C: a simple, universal calling API
22 * ------------------------------------------------------------------------*/
24 /* The universal call-C API supplies a single function:
26 void universal_call_c ( int n_args,
33 args points to the start of a block of memory containing the
34 arguments. This block is an array of 8-byte entities,
35 containing (n_args+1) slots. The zeroth slot is where the
36 return result goes. Slots [1 .. n_args] contain the arguments,
37 presented left-to-right.
39 Arguments are stored in the host's byte ordering inside
40 the slots. Only 4 or 8 byte entities are allowed.
41 4-byte entities are stored in the half-slot with lower
44 For example, a 32-bit value 0xAABBCCDD would be stored, on
49 whereas on a big-endian would expect
53 Clients do not need to fill in the zero bytes; they are there
54 only for illustration.
56 argstr is a simplified argument descriptor string. argstr
57 has one character for each (notional) argument slot of
58 args. That means the first byte of argstr describes the
59 return type. args should be allocated by the caller to hold
60 as many slots as implied by argstr.
62 argstr always specifies a return type. If the function to
63 be called returns no result, you must specify a bogus
64 return type in argstr[0]; a 32-bit int seems like a good bet.
66 Characters in argstr specify the result and argument types:
73 Pointers should travel as integral entities. At the moment
74 there are no descriptors for entities smaller than 32 bits
75 since AFAIK all calling conventions expand smaller entities
76 to 32 bits anyway. Users of this routine need to handle
77 packing/unpacking of 16 and 8 bit quantities themselves.
79 If the preconditions are not met, behaviour of
80 universal_call_c is entirely undefined.
85 The function specified by fun is called with arguments
86 in args as specified by argstr. The result of the call
87 is placed in the first 8 bytes of args, again as specified
88 by the first byte of argstr. Calling and returning is to
89 be done using the correct calling convention for the
92 It's clear that implementations of universal_call_c will
93 have to be handwritten assembly. The above design is intended
94 to make that assembly as simple as possible, at the expense
95 of a small amount of complication for the API's user.
98 /* ToDo: move these to the Right Place */
99 extern StgInt PopTaggedInt ( void ) ;
100 extern StgDouble PopTaggedDouble ( void ) ;
101 extern StgFloat PopTaggedFloat ( void ) ;
102 extern StgChar PopTaggedChar ( void ) ;
103 extern StgAddr PopTaggedAddr ( void ) ;
105 extern void PushTaggedInt ( StgInt );
106 extern void PushTaggedDouble ( StgDouble );
107 extern void PushTaggedFloat ( StgFloat );
108 extern void PushTaggedChar ( StgChar );
109 extern void PushTaggedAddr ( StgAddr );
111 extern void PushPtr ( StgPtr );
112 extern StgPtr PopPtr ( void );
115 /* --------------------------------------------------------------------------
116 * Move args/results between STG stack and the above API's arg block
117 * Returns 0 on success
118 * 1 if too many args/results or non-handled type
119 * 2 if config error on this platform
120 * Tries to automatically handle 32-vs-64 bit differences.
121 * ------------------------------------------------------------------------*/
123 int ccall ( CFunDescriptor* d, void (*fun)(void), StgBCO** bco )
130 if (sizeof(int) != 4 || sizeof(double) != 8 || sizeof(float) != 4
131 || (sizeof(void*) != 4 && sizeof(void*) != 8))
134 if (d->num_args > 30 || d->num_results > 1)
135 return 1; /* unlikely, but ... */
137 //fprintf ( stderr, "ccall: `%s' %d -> `%s' %d\n",
138 // d-> arg_tys, d->num_args, d->result_tys, d->num_results );
140 p = (unsigned int*) &arg_vec[1];
141 for (i = 0; i < d->num_args; i++) {
142 switch (d->arg_tys[i]) {
144 int j = (int)PopTaggedChar();
150 int j = PopTaggedInt();
156 void* a = PopTaggedAddr();
157 if (sizeof(void*) == 4) {
158 *(void**)p = a; p++; *p++ = 0;
168 float f = PopTaggedFloat();
169 *(float*)p = f; p++; *p++ = 0;
174 double d = PopTaggedDouble();
175 *(double*)p = d; p+=2;
184 if (d->num_results == 0) {
187 switch (d->result_tys[0]) {
188 case CHAR_REP: case INT_REP:
189 argd_vec[0] = 'i'; break;
191 argd_vec[0] = (sizeof(void*)==4) ? 'i' : 'I'; break;
193 argd_vec[0] = 'f'; break;
195 argd_vec[0] = 'F'; break;
201 PushPtr((StgPtr)(*bco));
204 //fprintf(stderr, " argc=%d arg_vec=%p argd_vec=%p `%s' fun=%p\n",
205 // d->num_args, arg_vec, argd_vec, argd_vec, fun );
207 universal_call_c_x86_linux (
208 d->num_args, (void*)arg_vec, argd_vec, fun );
210 *bco=(StgBCO*)PopPtr();
212 if (d->num_results > 0) {
213 p = (unsigned int*) &arg_vec[0];
214 switch (d->result_tys[0]) {
216 PushTaggedChar ( (StgChar) p[0]);
219 PushTaggedInt ( ((StgInt*)p) [0] );
222 if (sizeof(void*) == 4)
223 PushTaggedAddr ( ((StgAddr*)p) [0] );
225 PushTaggedAddr ( ((StgAddr*)p) [0] );
228 PushTaggedFloat ( ((StgFloat*)p) [0] );
231 PushTaggedDouble ( ((StgDouble*)p) [0] );
243 CFunDescriptor* mkDescriptor( char* as, char* rs )
245 /* ToDo: don't use malloc */
246 CFunDescriptor *d = malloc(sizeof(CFunDescriptor));
247 if (d == NULL) return d;
250 d->num_args = strlen(as);
251 d->num_results = strlen(rs);
255 #endif /* INTERPRETER */