Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Annotation of /sml/trunk/src/MLRISC/ppc/c-calls/ppc-macosx.sml
ViewVC logotype

Annotation of /sml/trunk/src/MLRISC/ppc/c-calls/ppc-macosx.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1565 - (view) (download)

1 : jhr 1346 (* ppc-macosx.sml
2 :     *
3 :     * COPYRIGHT (c) 2003 John Reppy (http://www.cs.uchicago.edu/~jhr)
4 :     * All rights reserved.
5 :     *
6 :     * C function calls for the PowerPC using the MacOS X ABI.
7 :     *
8 :     * Register conventions:
9 :     *
10 :     * Register Callee-save Purpose
11 :     * -------- ----------- -------
12 :     * GPR0 no Zero
13 :     * 1 no Stack pointer
14 :     * 2 no scratch (TOC on AIX)
15 :     * 3 no arg0 and return result
16 :     * 4-10 no arg1-arg7
17 :     * 11 no scratch
18 :     * 12 no holds taget of indirect call
19 :     * 13-31 yes callee-save registers
20 :     *
21 :     * FPR0 no scratch
22 :     * 1-13 no floating-point arguments
23 :     * 14-31 yes floating-point callee-save registers
24 :     *
25 :     * V0-V1 no scratch vector registers
26 :     * 2-13 no vector argument registers
27 :     * 14-19 no scratch vector registers
28 :     * 20-31 yes callee-save vector registers
29 :     *
30 :     * LR no link register holds return address
31 :     *
32 :     * CR0-CR1 no scratch condition registers
33 :     * 2-4 yes callee-save condition registers
34 :     * 5-7 no scratch condition registers
35 :     *
36 :     * Calling convention:
37 :     *
38 :     * Return result:
39 :     * + Integer and pointer results are returned in GPR3
40 :     * + 64-bit integers (long long) returned in GPR3/GPR4
41 :     * + float/double results are returned in FPR1
42 :     * + Struct results are returned in space provided by the caller.
43 :     * The address of this space is passed to the callee as an
44 :     * implicit first argument in GPR3 and the first real argument is
45 :     * passed in GPR4.
46 :     *
47 :     * Function arguments:
48 :     * * arguments (except for floating-point values) are passed in
49 :     * registers GPR3-GPR10
50 : jhr 1438 *
51 :     * Note also that stack frames are supposed to be 16-byte aligned.
52 : jhr 1346 *)
53 :    
54 :     (* we extend the interface to support generating the stubs needed for
55 :     * dynamic linking (see "Inside MacOS X: Mach-O Runtime Architecture"
56 :     * for details.
57 :     *)
58 :     signature PPC_MACOSX_C_CALLS =
59 :     sig
60 :     include C_CALLS
61 : jhr 1438
62 :     (*
63 :     val genStub : {
64 :     name : T.rexp,
65 :     proto : CTypes.c_proto,
66 :     paramAlloc : {szb : int, align : int} -> bool,
67 :     structRet : {szb : int, align : int} -> T.rexp,
68 :     saveRestoreDedicated :
69 :     T.mlrisc list -> {save: T.stm list, restore: T.stm list},
70 :     callComment : string option,
71 :     args : c_arg list
72 :     } -> {
73 :     callseq : T.stm list,
74 :     result: T.mlrisc list
75 :     }
76 :     *)
77 :    
78 : jhr 1346 end;
79 :    
80 :     functor PPCMacOSX_CCalls (
81 :    
82 :     structure T : MLTREE
83 :    
84 : jhr 1527 ): C_CALLS = struct
85 :    
86 : jhr 1346 structure T = T
87 : jhr 1521 structure CTy = CTypes
88 : jhr 1346 structure C = PPCCells
89 :    
90 :     fun error msg = MLRiscErrorMsg.error ("PPCCompCCalls", msg)
91 :    
92 : jhr 1521 (* the location of arguments/parameters; offsets are given with respect to the
93 :     * low end of the parameter area.
94 :     *)
95 :     datatype arg_location
96 : jhr 1522 = Reg of T.ty * T.reg * T.I.machine_int option
97 :     (* integer/pointer argument in register *)
98 : jhr 1524 | FReg of T.fty * T.reg * T.I.machine_int option
99 : jhr 1522 (* floating-point argument in register *)
100 : jhr 1521 | Stk of T.ty * T.I.machine_int (* integer/pointer argument in parameter area *)
101 :     | FStk of T.fty * T.I.machine_int (* floating-point argument in parameter area *)
102 :     | Args of arg_location list
103 :    
104 : jhr 1522 val wordTy = 32
105 :     val fltTy = 32 (* MLRISC type of float *)
106 :     val dblTy = 64 (* MLRISC type of double *)
107 : jhr 1346
108 : jhr 1565 (* shorts and chars are promoted to 32-bits *)
109 :     val naturalIntSz = wordTy
110 :    
111 : jhr 1523 (* stack pointer *)
112 : jhr 1530 val spReg = T.REG(wordTy, C.GPReg 1)
113 : jhr 1523
114 : jhr 1521 (* registers used for parameter passing *)
115 :     val argGPRs = List.map C.GPReg [3, 4, 5, 6, 7, 8, 9, 10]
116 :     val argFPRs = List.map C.FPReg [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
117 : jhr 1530 val resGPR = C.GPReg 3
118 :     val resRegLoc = Reg(wordTy, resGPR, NONE)
119 : jhr 1522 val resFPR = C.FPReg 1
120 : jhr 1521
121 :     (* C callee-save registers *)
122 :     val calleeSaveRegs = List.map C.GPReg [
123 :     13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
124 :     23, 24, 25, 26, 27, 28, 29, 30, 31
125 :     ]
126 :     val calleeSaveFRegs = List.map C.FPReg [
127 :     14, 15, 16, 17, 18, 19, 20, 21, 22,
128 :     23, 24, 25, 26, 27, 28, 29, 30, 31
129 :     ]
130 :    
131 : jhr 1523 (* C caller-save registers (including argument registers) *)
132 :     val callerSaveRegs =
133 : jhr 1528 T.FPR(T.FREG(dblTy, C.FPReg 0)) ::
134 :     (List.map (fn r => T.GPR(T.REG(wordTy, C.GPReg r))) [2, 11, 12])
135 : jhr 1523
136 : jhr 1528 val linkReg = T.GPR(T.REG(wordTy, C.lr))
137 :    
138 : jhr 1525 (* the parameter area lies just above the linkage area in the caller's frame.
139 :     * The linkage area is 24 bytes, so the first parameter is at 24(sp).
140 :     *)
141 :     val paramAreaOffset = 24
142 :    
143 : jhr 1531 (* size, padding, and natural alignment for integer types. Note that the
144 :     * padding is based on the parameter-passing description on p. 35 of the
145 :     * documentation and the alignment is from p. 31.
146 : jhr 1522 *)
147 : jhr 1550 fun sizeOfInt CTy.I_char = {sz = 1, pad = 3, align = 1}
148 :     | sizeOfInt CTy.I_short = {sz = 2, pad = 2, align = 2}
149 :     | sizeOfInt CTy.I_int = {sz = 4, pad = 0, align = 4}
150 :     | sizeOfInt CTy.I_long = {sz = 4, pad = 0, align = 4}
151 :     | sizeOfInt CTy.I_long_long = {sz = 8, pad = 0, align = 8}
152 : jhr 1521
153 : jhr 1522 (* sizes of other C types *)
154 : jhr 1531 val sizeOfPtr = {sz = 4, pad = 0, align = 4}
155 : jhr 1522
156 : jhr 1550 (* align the address to the given alignment, which must be a power of 2 *)
157 :     fun alignAddr (addr, align) = let
158 :     val mask = Word.fromInt(align-1)
159 :     in
160 :     Word.toIntX(Word.andb(Word.fromInt addr + mask, Word.notb mask))
161 :     end
162 :    
163 :     (* compute the size and alignment information for a struct; tys is the list
164 : jhr 1531 * of member types. The alignment is what Apple calls the "embedding" alignment.
165 : jhr 1550 * The total size is padded to agree with the struct's alignment.
166 : jhr 1531 *)
167 : jhr 1550 fun sizeOfStruct tys = let
168 :     fun ssz [] = {sz = 0, align = 4}
169 :     | ssz (first::rest) = let
170 :     fun f ([], maxAlign, offset) =
171 :     {sz = alignAddr(offset, maxAlign), align = maxAlign}
172 :     | f (ty::tys, maxAlign, offset) = let
173 :     val {sz, align} = sizeOfTy ty
174 :     val align = Int.min(align, 4)
175 :     val offset = alignAddr(offset, align)
176 :     in
177 :     f (tys, Int.max(maxAlign, align), offset+sz)
178 :     end
179 :     val {sz, align} = sizeOfTy first
180 : jhr 1531 in
181 : jhr 1550 f (rest, align, sz)
182 : jhr 1531 end
183 : jhr 1550 in
184 :     ssz tys
185 :     end
186 :    
187 :     (* the size alignment of a union type is the maximum of the sizes and alignments of the
188 :     * members. The final size is padded to agree with the alignment.
189 :     *)
190 :     and sizeOfUnion tys = let
191 :     fun usz [] = {sz = 0, align = 4}
192 :     | usz (first::rest) = let
193 :     fun f ([], maxAlign, maxSz) =
194 :     {sz = alignAddr(maxSz, maxAlign), align = maxAlign}
195 :     | f (ty::tys, maxAlign, maxSz) = let
196 :     val {sz, align} = sizeOfTy ty
197 :     in
198 :     f (tys, Int.max(maxAlign, align), Int.max(align, maxAlign))
199 :     end
200 :     val {sz, align} = sizeOfTy first
201 : jhr 1531 in
202 : jhr 1550 f (rest, align, sz)
203 : jhr 1531 end
204 : jhr 1525 in
205 : jhr 1550 usz tys
206 : jhr 1525 end
207 : jhr 1522
208 : jhr 1550 and sizeOfTy CTy.C_void = error "unexpected void argument type"
209 :     | sizeOfTy CTy.C_float = {sz = 4, align = 4}
210 :     | sizeOfTy CTy.C_double = {sz = 8, align = 8}
211 :     | sizeOfTy CTy.C_long_double = {sz = 8, align = 8}
212 :     | sizeOfTy (CTy.C_unsigned isz) = let
213 :     val {sz, align, ...} = sizeOfInt isz
214 :     in
215 :     {sz = sz, align = align}
216 :     end
217 :     | sizeOfTy (CTy.C_signed isz) = let
218 :     val {sz, align, ...} = sizeOfInt isz
219 :     in
220 :     {sz = sz, align = align}
221 :     end
222 :     | sizeOfTy CTy.C_PTR = {sz = 4, align = 4}
223 :     | sizeOfTy (CTy.C_ARRAY(ty, n)) = let
224 :     val {sz, align} = sizeOfTy ty
225 :     in
226 :     {sz = n*sz, align = align}
227 :     end
228 :     | sizeOfTy (CTy.C_STRUCT tys) = sizeOfStruct tys
229 :     | sizeOfTy (CTy.C_UNION tys) = sizeOfUnion tys
230 : mblume 1549
231 : jhr 1521 (* compute the layout of a C call's arguments *)
232 :     fun layout {conv, retTy, paramTys} = let
233 : jhr 1550 fun gprRes isz = (case #sz(sizeOfInt isz)
234 : jhr 1522 of 8 => raise Fail "register pairs not yet supported"
235 : jhr 1528 | _ => SOME resRegLoc
236 : jhr 1521 (* end case *))
237 : jhr 1527 val (resLoc, argGPRs, structRet) = (case retTy
238 :     of CTy.C_void => (NONE, argGPRs, NONE)
239 :     | CTy.C_float => (SOME(FReg(fltTy, resFPR, NONE)), argGPRs, NONE)
240 :     | CTy.C_double => (SOME(FReg(dblTy, resFPR, NONE)), argGPRs, NONE)
241 :     | CTy.C_long_double => (SOME(FReg(dblTy, resFPR, NONE)), argGPRs, NONE)
242 :     | CTy.C_unsigned isz => (gprRes isz, argGPRs, NONE)
243 :     | CTy.C_signed isz => (gprRes isz, argGPRs, NONE)
244 : jhr 1528 | CTy.C_PTR => (SOME resRegLoc, argGPRs, NONE)
245 : jhr 1522 | CTy.C_ARRAY _ => error "array return type"
246 : jhr 1550 | CTy.C_STRUCT s => let
247 :     val sz = #sz(sizeOfStruct s)
248 :     in
249 :     (* Note that this is a place where the MacOS X and Linux ABIs differ.
250 :     * In Linux, GPR3/GPR4 are used to return composite values of 8 bytes.
251 :     *)
252 :     (SOME resRegLoc, List.tl argGPRs, SOME{szb=sz, align=4})
253 :     end
254 :     | CTy.C_UNION u => let
255 :     val sz = #sz(sizeOfUnion u)
256 :     in
257 :     (SOME resRegLoc, List.tl argGPRs, SOME{szb=sz, align=4})
258 :     end
259 : mblume 1549 (* end case *))
260 :     fun assign ([], offset, _, _, layout) = (offset, List.rev layout)
261 : jhr 1550 | assign (ty::tys, offset, availGPRs, availFPRs, layout) = (
262 : jhr 1525 case ty
263 : jhr 1533 of CTy.C_void => error "unexpected void argument type"
264 : jhr 1522 | CTy.C_float => (case (availGPRs, availFPRs)
265 : jhr 1527 of (_::gprs, fpr::fprs) =>
266 : jhr 1525 assign (tys, offset+4, gprs, fprs, FReg(fltTy, fpr, SOME offset)::layout)
267 : jhr 1522 | ([], fpr::fprs) =>
268 : jhr 1525 assign (tys, offset+4, [], fprs, FReg(fltTy, fpr, SOME offset)::layout)
269 : jhr 1522 | ([], []) =>
270 : jhr 1525 assign (tys, offset+4, [], [], FStk(fltTy, offset)::layout)
271 : jhr 1536 | _ => error "FPRs exhausted before GPRs"
272 : jhr 1522 (* end case *))
273 : jhr 1521 | CTy.C_double =>
274 : jhr 1525 assignFPR (tys, offset, availGPRs, availFPRs, layout)
275 : jhr 1521 | CTy.C_long_double =>
276 : jhr 1525 assignFPR (tys, offset, availGPRs, availFPRs, layout)
277 : jhr 1521 | CTy.C_unsigned isz =>
278 : jhr 1550 assignGPR(sizeOfInt isz, tys, offset, availGPRs, availFPRs, layout)
279 : jhr 1521 | CTy.C_signed isz =>
280 : jhr 1550 assignGPR(sizeOfInt isz, tys, offset, availGPRs, availFPRs, layout)
281 : jhr 1521 | CTy.C_PTR =>
282 : jhr 1525 assignGPR(sizeOfPtr, tys, offset, availGPRs, availFPRs, layout)
283 : jhr 1521 | CTy.C_ARRAY _ =>
284 : jhr 1525 assignGPR(sizeOfPtr, tys, offset, availGPRs, availFPRs, layout)
285 : jhr 1550 | CTy.C_STRUCT tys' =>
286 :     assignMem(sizeOfStruct tys', tys, offset, availGPRs, availFPRs, layout)
287 :     | CTy.C_UNION tys' =>
288 :     assignMem(sizeOfUnion tys', tys, offset, availGPRs, availFPRs, layout)
289 :     (* end case *))
290 : jhr 1522 (* assign a GP register and memory for an integer/pointer argument. *)
291 : jhr 1531 and assignGPR ({sz, pad, ...}, args, offset, availGPRs, availFPRs, layout) = let
292 : jhr 1522 val (loc, availGPRs) = (case (sz, availGPRs)
293 :     of (8, _) => raise Fail "register pairs not yet supported"
294 :     | (_, []) => (Stk(wordTy, offset), [])
295 :     | (_, r1::rs) => (Reg(wordTy, r1, SOME offset), rs)
296 :     (* end case *))
297 : jhr 1527 val offset = offset + IntInf.fromInt(sz + pad)
298 : jhr 1521 in
299 : jhr 1522 assign (args, offset, availGPRs, availFPRs, loc::layout)
300 : jhr 1521 end
301 : jhr 1522 (* assign a FP register and memory/GPRs for double-precision argument. *)
302 :     and assignFPR (args, offset, availGPRs, availFPRs, layout) = let
303 :     fun continue (availGPRs, availFPRs, loc) =
304 :     assign (args, offset+8, availGPRs, availFPRs, loc::layout)
305 :     fun freg fpr = FReg(dblTy, fpr, SOME offset)
306 :     in
307 :     case (availGPRs, availFPRs)
308 :     of (_::_::gprs, fpr::fprs) => continue (gprs, fprs, freg fpr)
309 :     | (_, fpr::fprs) => continue ([], fprs, freg fpr)
310 :     | ([], []) => continue ([], [], FStk(dblTy, offset))
311 : jhr 1536 | _ => error "FPRs exhausted before GPRs"
312 : jhr 1522 (* end case *)
313 :     end
314 : jhr 1550 (* assign a argument locations to pass a composite argument (struct or union) *)
315 :     and assignMem ({sz, ...}, args, offset, availGPRs, availFPRs, layout) = let
316 :     val sz = IntInf.fromInt sz
317 :     fun assignMem (relOffset, availGPRs, fields) =
318 :     if (relOffset < sz)
319 :     then let
320 :     val (loc, availGPRs) = (case availGPRs
321 :     of [] => (Stk(wordTy, offset+relOffset), [])
322 :     | r1::rs => (Reg(wordTy, r1, SOME(offset+relOffset)), rs)
323 :     (* end case *))
324 :     in
325 :     assignMem (relOffset+4, availGPRs, loc::fields)
326 :     end
327 :     else assign (args, offset+relOffset, availGPRs, availFPRs,
328 :     Args(List.rev fields) :: layout)
329 :     in
330 :     assignMem (0, availGPRs, [])
331 :     end
332 : jhr 1536 val (sz, argLocs) = assign (paramTys, 0, argGPRs, argFPRs, [])
333 : jhr 1523 in {
334 : jhr 1536 argLocs = argLocs,
335 :     argMem = {szb = IntInf.toInt sz, align = 4},
336 : jhr 1527 resLoc = resLoc,
337 : jhr 1530 structRetLoc = structRet
338 : jhr 1523 } end
339 : jhr 1521
340 : jhr 1346 datatype c_arg
341 :     = ARG of T.rexp
342 :     | FARG of T.fexp
343 :     | ARGS of c_arg list
344 :    
345 : jhr 1523 val memRg = T.Region.memory
346 :     val stkRg = T.Region.memory
347 : jhr 1346
348 : jhr 1525 (* SP-based address of parameter at given offset *)
349 : jhr 1530 fun paramAddr off =
350 :     T.ADD(wordTy, spReg, T.LI(off + IntInf.fromInt paramAreaOffset))
351 : jhr 1525
352 : jhr 1445 fun genCall {
353 :     name, proto, paramAlloc, structRet, saveRestoreDedicated,
354 :     callComment, args
355 :     } = let
356 :     val {conv, retTy, paramTys} = proto
357 : jhr 1536 val {argLocs, argMem, resLoc, structRetLoc} = layout proto
358 :     (* inform the client of the size of the parameter area *)
359 :     val _ = if not(paramAlloc argMem)
360 :     then raise Fail "parameter memory allocation not implemented yet"
361 :     else ()
362 : jhr 1523 (* generate code to assign the arguments to their locations *)
363 :     fun assignArgs ([], [], stms) = stms
364 :     | assignArgs (Reg(ty, r, _) :: locs, ARG exp :: args, stms) =
365 :     assignArgs (locs, args, T.MV(ty, r, exp) :: stms)
366 :     | assignArgs (Stk(ty, off) :: locs, ARG exp :: args, stms) =
367 : jhr 1525 assignArgs (locs, args, T.STORE(ty, paramAddr off, exp, stkRg) :: stms)
368 : jhr 1527 | assignArgs (FReg(ty, r, _) :: locs, FARG fexp :: args, stms) =
369 : jhr 1523 assignArgs (locs, args, T.FMV(ty, r, fexp) :: stms)
370 :     | assignArgs (FStk(ty, off) :: locs, FARG fexp :: args, stms) =
371 : jhr 1525 assignArgs (locs, args, T.FSTORE(ty, paramAddr off, fexp, stkRg) :: stms)
372 : jhr 1523 | assignArgs ((Args locs') :: locs, (ARGS args') :: args, stms) =
373 : jhr 1544 raise Fail "ARGS constructor is obsolete"
374 :     | assignArgs ((Args locs') :: locs, ARG exp :: args, stms) = let
375 : jhr 1545 (* MLRISC expression for address inside the source struct *)
376 : jhr 1544 fun addr 0 = T.LOAD(wordTy, exp, memRg)
377 :     | addr offset = T.LOAD(wordTy, T.ADD(wordTy, exp, T.LI offset), memRg)
378 :     fun copy ([], _, stms) = assignArgs(locs, args, stms)
379 :     | copy (Reg(ty, r, _) :: locs, offset, stms) =
380 :     copy (locs, offset+4, T.MV(ty, r, addr offset)::stms)
381 :     | copy (Stk(ty, off) :: locs, offset, stms) = let
382 :     val r = C.newReg()
383 :     in
384 :     copy (locs, offset+4,
385 :     T.STORE(ty, paramAddr off, T.REG(wordTy, r), stkRg)
386 :     :: T.MV(ty, r, addr offset) :: stms)
387 :     end
388 :     | copy _ = raise Fail "unexpected FReg/FStk/Args in location list"
389 :     in
390 :     (* copy data from memory specified by exp to locs' *)
391 :     copy (locs', 0, stms)
392 :     end
393 : jhr 1523 | assignArgs _ = error "argument/formal mismatch"
394 : jhr 1527 val argSetupCode = List.rev(assignArgs(argLocs, args, []))
395 : jhr 1523 (* convert the result location to an MLRISC expression list *)
396 :     val result = (case resLoc
397 :     of NONE => []
398 : jhr 1527 | SOME(Reg(ty, r, _)) => [T.GPR(T.REG(ty, r))]
399 :     | SOME(FReg(ty, r, _)) => [T.FPR(T.FREG(ty, r))]
400 : jhr 1523 | SOME _ => raise Fail "bogus result location"
401 :     (* end case *))
402 : jhr 1530 (* make struct return-area setup (if necessary) *)
403 :     val setupStructRet = (case structRetLoc
404 :     of NONE => []
405 :     | SOME loc => let
406 :     val structAddr = structRet loc
407 :     in
408 :     [T.MV(wordTy, resGPR, structAddr)]
409 :     end
410 :     (* end case *))
411 : jhr 1523 (* determine the registers used and defined by this call *)
412 :     val (uses, defs) = let
413 :     val locs = (case resLoc
414 :     of NONE => argLocs
415 :     | SOME loc => loc::argLocs
416 :     (* end case *))
417 :     (* get the list of registers used to pass arguments and results *)
418 :     fun addArgReg (Reg(ty, r, _)::locs, argRegs) =
419 :     addArgReg (locs, T.GPR(T.REG(ty, r))::argRegs)
420 :     | addArgReg (FReg(ty, r, _)::locs, argRegs) =
421 :     addArgReg (locs, T.FPR(T.FREG(ty, r))::argRegs)
422 :     | addArgReg ((Args locs')::locs, argRegs) =
423 :     addArgReg (locs, addArgReg(locs', argRegs))
424 :     | addArgReg (_::locs, argRegs) = addArgReg(locs, argRegs)
425 : jhr 1533 | addArgReg ([], argRegs) = argRegs
426 : jhr 1523 val argRegs = addArgReg (locs, [])
427 :     in
428 : jhr 1528 (argRegs, linkReg :: callerSaveRegs)
429 : jhr 1523 end
430 :     (* the actual call instruction *)
431 :     val callStm = T.CALL {
432 :     funct = name, targets = [],
433 :     defs = defs, uses = uses,
434 :     region = memRg, pops = 0
435 :     }
436 :     (* annotate, if necessary *)
437 :     val callStm = (case callComment
438 :     of NONE => callStm
439 :     | SOME c => T.ANNOTATION(callStm, #create MLRiscAnnotations.COMMENT c)
440 :     (* end case *))
441 : jhr 1530 (* take care of dedicated client registers *)
442 :     val {save, restore} = saveRestoreDedicated defs
443 : jhr 1445 val callseq = List.concat [
444 : jhr 1530 setupStructRet,
445 : jhr 1523 argSetupCode,
446 : jhr 1530 save,
447 :     [callStm],
448 :     restore
449 : jhr 1445 ]
450 :     in
451 :     (* check calling convention *)
452 :     case conv
453 :     of ("" | "ccall") => ()
454 :     | _ => error (concat [
455 :     "unknown calling convention \"",
456 :     String.toString conv, "\""
457 :     ])
458 :     (* end case *);
459 :     {callseq = callseq, result = result}
460 :     end
461 : jhr 1346
462 : jhr 1445 end

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0