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 /MLRISC/trunk/x86/staged-allocation/ia32-svid-fn.sml
ViewVC logotype

Annotation of /MLRISC/trunk/x86/staged-allocation/ia32-svid-fn.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3010 - (view) (download)

1 : mrainey 3009 (* ia32-svid-fn.sml
2 :     *
3 : mrainey 3010 *
4 :     * C function calls for the IA32 using the System V ABI
5 :     *
6 :     * Register conventions:
7 :     *
8 :     * %eax return value (caller save)
9 :     * %ebx global offset for PIC (callee save)
10 :     * %ecx scratch (caller save)
11 :     * %edx extra return/scratch (caller save)
12 :     * %ebp optional frame pointer (callee save)
13 :     * %esp stack pointer (callee save)
14 :     * %esi locals (callee save)
15 :     * %edi locals (callee save)
16 :     *
17 :     * %st(0) top of FP stack; FP return value
18 :     * %st(1..7) FP stack; must be empty on entry and return
19 :     *
20 :     * Calling convention:
21 :     *
22 :     * Return result:
23 :     * + Integer and pointer results are returned in %eax. Small
24 :     * integer results are not promoted.
25 :     * + 64-bit integers (long long) returned in %eax/%edx
26 :     * + Floating point results are returned in %st(0) (all types).
27 :     * + Struct results are returned in space provided by the caller.
28 :     * The address of this space is passed to the callee as an
29 :     * implicit 0th argument, and on return %eax contains this
30 :     * address. The called function is responsible for removing
31 :     * this argument from the stack using a "ret $4" instruction.
32 :     * NOTE: the MacOS X ABI returns small structs in %eax/%edx.
33 :     *
34 :     * Function arguments:
35 :     * + Arguments are pushed on the stack right to left.
36 :     * + Integral and pointer arguments take one word on the stack.
37 :     * + float arguments take one word on the stack.
38 :     * + double arguments take two words on the stack. The i386 ABI does
39 :     * not require double word alignment for these arguments.
40 :     * + long double arguments take three words on the stack.
41 :     * + struct arguments are padded out to word length.
42 :     *
43 : mrainey 3009 *)
44 :    
45 :     functor IA32SVIDFn (
46 :     structure T : MLTREE
47 :     val abi : string
48 :     val ix : (T.stm,T.rexp,T.fexp,T.ccexp) X86InstrExt.sext -> T.sext
49 :     (* Note that the fast_loating_point flag must match the one passed
50 :     * to the code generator module.
51 :     *)
52 :     val fast_floating_point : bool ref
53 :     ) (*: C_CALL*) =
54 :     struct
55 :    
56 :     structure T = T
57 :     structure C = X86Cells
58 :     structure CB = CellsBasis
59 :     structure CTy = CTypes
60 :     structure IX = X86InstrExt
61 :    
62 :     val wordTy = 32
63 :     fun gpr r = T.GPR(T.REG(32, r))
64 :     fun fpr f = T.FPR(T.FREG(80, f))
65 :    
66 :     (* kinds of locations for staged allocation *)
67 :     datatype location_kind
68 :     = K_GPR (* pass in general purpose registers *)
69 :     | K_FPR (* pass in floating-point registers *)
70 :    
71 :     (* staged allocation *)
72 :     structure SA = StagedAllocationFn (
73 :     structure T = T
74 :     structure TargetLang =
75 :     struct
76 :     datatype location_kind = datatype location_kind
77 :     end
78 :     val memSize = 8)
79 :    
80 : mrainey 3010 (* calling conventions in the Staged Allocation language *)
81 : mrainey 3009 structure CCs =
82 :     struct
83 :    
84 :     (* register conventions *)
85 :     val callerSaveRegs = List.map gpr [C.eax, C.ecx, C.edx]
86 :     val calleeSaveRegs = List.map gpr [C.ebx, C.esi, C.edi]
87 :     val spReg = T.REG (32, C.esp)
88 :     (* the C calling convention requires that the FP stack be empty on function
89 :     * entry. We add the fpStk list to the defs when the fast_floating_point flag
90 :     * is set.
91 :     *)
92 :     val st0 = C.ST 0
93 :     val calleeSaveFRegs = []
94 :     (* the C calling convention requires that the FP stack be empty on function
95 :     * entry. We add the fpStk list to the defs when the fast_floating_point flag
96 :     * is set.
97 :     *)
98 :     val fpStk = List.tabulate(8, fn i => fpr (C.ST i))
99 :    
100 :     (* conventions for calling a C function *)
101 : mrainey 3010 val alignB = 4
102 : mrainey 3009 val cStack = SA.freshCounter()
103 : mrainey 3010 val callStages = [
104 : mrainey 3009 SA.SEQ[
105 :     SA.WIDEN (fn ty => Int.max(32, ty)),
106 :     SA.OVERFLOW {counter=cStack, blockDirection=SA.UP, maxAlign=alignB}
107 :     ]
108 :     ]
109 :    
110 :     (* conventions for returning from a C call *)
111 :     val (cInt, retInGpr) = SA.useRegs [(32, C.eax), (32, C.edx)]
112 :     val (cFloat, retInFpr) = SA.useRegs [(80, st0)]
113 :     val returnStages = [
114 :     SA.CHOICE [
115 :     (* return in general-purpose register *)
116 :     (fn (ty, k, str) => k = K_GPR,
117 :     SA.SEQ [SA.WIDEN (fn ty => Int.max(ty, 32)),
118 :     retInGpr]),
119 :     (* return in floating-point register *)
120 :     (fn (ty, k, str) => k = K_FPR,
121 :     SA.SEQ [SA.WIDEN (fn ty => 80), retInFpr])
122 :     ]
123 :     ]
124 :    
125 :     (* initial store *)
126 :     val str0 = SA.init [cInt, cFloat, cStack]
127 :    
128 :     end (* CallingConventions *)
129 :    
130 :     structure CCall = CCallFn(
131 :     structure T = T
132 :     structure C = C
133 :     val wordTy = wordTy
134 :     fun offSp 0 = CCs.spReg
135 :     | offSp offset = T.ADD (32, CCs.spReg, T.LI offset))
136 :    
137 :     datatype c_arg = datatype CCall.c_arg
138 :    
139 :     (* classify a C type into its location kind (assuming that aggregates cannot be passed in registers) *)
140 : mrainey 3010 fun kindOfCTy (CTy.C_float |
141 :     CTy.C_double |
142 :     CTy.C_long_double) =
143 :     K_FPR
144 :     | kindOfCTy (CTy.C_unsigned _ |
145 :     CTy.C_signed _ |
146 :     CTy.C_PTR |
147 :     CTy.C_ARRAY _) =
148 :     K_GPR
149 : mrainey 3009
150 : mrainey 3010 (* convert a C type to slots for staged allocation *)
151 : mrainey 3009 fun cTyToSlots cTy = let
152 :     val {sz, align} = IA32CSizes.sizeOfTy cTy
153 : mrainey 3010 (* compute argument slots for the flattened C type *)
154 :     val slots = List.map (fn cTy => (sz * 8, kindOfCTy cTy, align))
155 : mrainey 3009 (CTy.flattenCTy cTy)
156 :     in
157 :     case (cTy, abi)
158 :     of (CTy.C_STRUCT _, "Mac OS X") =>
159 :     (* on Mac OS X, structs <= 8 bytes are returned in GPRs *)
160 :     if (sz <= 4)
161 :     then [(8, K_GPR, align)]
162 :     else if (sz <= 8)
163 :     then [(8, K_GPR, align), (8, K_GPR, align)]
164 : mrainey 3010 else slots
165 : mrainey 3009 | ( (CTy.C_unsigned CTy.I_long_long |
166 :     CTy.C_signed CTy.I_long_long ),
167 :     _ ) =>
168 :     (* 64-bit integers are returned in GPRs *)
169 :     [(8, K_GPR, align), (8, K_GPR, align)]
170 : mrainey 3010 | _ => slots
171 : mrainey 3009 end
172 :    
173 :     (* convert a finalized staged-allocation location into a C location *)
174 :     fun saToCLoc (ty, SA.REG(_, r), K_GPR) = CCall.C_GPR(ty, r)
175 :     | saToCLoc (ty, SA.REG(_, r), K_FPR) = CCall.C_FPR(ty, r)
176 :     | saToCLoc (ty, SA.BLOCK_OFFSET offB, _) = CCall.C_STK(ty, T.I.fromInt (32, offB))
177 :     | saToCLoc (ty, SA.NARROW(loc, ty', k), _) = saToCLoc (ty, loc, k)
178 :    
179 : mrainey 3010 val frameAlign = 8
180 :    
181 : mrainey 3009 fun layout {conv, retTy, paramTys} = let
182 :    
183 :     (* compute locations for return results *)
184 : mrainey 3010 val (resLocs, structRetLoc, str) = (case retTy
185 :     of CTy.C_void => ([], NONE, CCs.str0)
186 : mrainey 3009 | retTy => let
187 :     val {sz, align} = IA32CSizes.sizeOfTy retTy
188 :     (* compute the locations for return values using staged allocation *)
189 :     val returnStepper = SA.mkStep CCs.returnStages
190 :     (* finalize locations for the return type *)
191 :     val (str, locs) = SA.doStagedAllocation(CCs.str0, returnStepper, cTyToSlots retTy)
192 :     val nBytesAllocated = SA.find(str, CCs.cStack)
193 :     in
194 : mrainey 3010 (List.map saToCLoc locs, SOME {szb=sz, align=align}, str)
195 : mrainey 3009 end
196 :     (* end case *))
197 :    
198 :     (* compute locations for passing arguments *)
199 :     val paramStepper = SA.mkStep CCs.callStages
200 :     fun doParam (paramTy, (str, paramLocss)) = let
201 :     val (str', paramLocs) = SA.doStagedAllocation(str, paramStepper, cTyToSlots paramTy)
202 :     in
203 :     (str', (List.map saToCLoc paramLocs) :: paramLocss)
204 :     end
205 :     val (str, paramLocss) = List.foldl doParam (str, []) paramTys
206 :     val paramLocs = List.rev paramLocss
207 :    
208 : mrainey 3010 (* number of bytes allocated for the call *)
209 :     val cStkSzB = let
210 :     val n = SA.find(str, CCs.cStack)
211 :     in
212 :     if (abi = "Mac OS X")
213 :     then IA32CSizes.alignAddr(n, 16)
214 :     else n
215 :     end
216 : mrainey 3009 in
217 : mrainey 3010 {argLocs=paramLocs, argMem={szb=cStkSzB, align=4}, structRetLoc=structRetLoc, resLocs=resLocs}
218 : mrainey 3009 end (* layout *)
219 :    
220 :     (* List of registers defined by a C Call with the given return type; this list
221 :     * is the result registers plus the caller-save registers.
222 :     *)
223 :     fun definedRegs resTy = if !fast_floating_point
224 :     then let
225 :     val defs = CCs.callerSaveRegs @ CCs.fpStk
226 :     in
227 :     case resTy
228 :     of (CTy.C_unsigned(CTy.I_long_long)) => gpr C.edx :: defs
229 :     | (CTy.C_signed(CTy.I_long_long)) => gpr C.edx :: defs
230 :     | _ => defs
231 :     (* end case *)
232 :     end
233 :     else (case resTy
234 :     of (CTy.C_float) => fpr CCs.st0 :: CCs.callerSaveRegs
235 :     | (CTy.C_double) => fpr CCs.st0 :: CCs.callerSaveRegs
236 :     | (CTy.C_long_double) => fpr (CCs.st0) :: CCs.callerSaveRegs
237 :     | (CTy.C_unsigned(CTy.I_long_long)) => gpr C.edx :: CCs.callerSaveRegs
238 :     | (CTy.C_signed(CTy.I_long_long)) => gpr C.edx :: CCs.callerSaveRegs
239 :     | _ => CCs.callerSaveRegs
240 :     (* end case *))
241 :    
242 :     fun fstp (32, f) = T.EXT(ix(IX.FSTPS(f)))
243 :     | fstp (64, f) = T.EXT(ix(IX.FSTPL(f)))
244 :     | fstp (80, f) = T.EXT(ix(IX.FSTPT(f)))
245 :     | fstp (sz, f) = raise Fail ("fstp(" ^ Int.toString sz ^ ",_)")
246 :    
247 :     (* This annotation is used to indicate that a call returns a fp value
248 :     * in %st(0)
249 :     *)
250 :     val fpReturnValueInST0 = #create MLRiscAnnotations.RETURN_ARG C.ST0
251 :    
252 :     fun genCall {
253 :     name, proto, paramAlloc, structRet, saveRestoreDedicated, callComment, args
254 :     } = let
255 : mrainey 3010 val {argLocs, argMem, structRetLoc, resLocs} = layout proto
256 :    
257 :     (* for functions that return a struct/union, pass the location as an
258 :     * implicit first argument. Because the callee removes this implicit
259 :     * argument from the stack, we must also keep track of the size of the
260 :     * explicit arguments.
261 :     *)
262 :     val (args, argLocs, explicitArgSzB) = (case structRetLoc
263 :     of SOME pos =>
264 :     (ARG(structRet pos)::args, [CCall.C_STK(wordTy, T.I.fromInt (wordTy, 0))]::argLocs, #szb argMem - 4)
265 :     | NONE => (args, argLocs, #szb argMem)
266 :     (* end case *))
267 :    
268 : mrainey 3009 (* instruction to allocate space for arguments *)
269 :     val argAlloc = if ((#szb argMem = 0) orelse paramAlloc argMem)
270 :     then []
271 :     else [T.MV(wordTy, C.esp, T.SUB(wordTy, CCs.spReg, T.LI(IntInf.fromInt(#szb argMem))))]
272 :     val (copyArgs, gprUses, fprUses) = CCall.copyArgs(args, argLocs)
273 :    
274 :     (* the SVID specifies that the caller pops arguments, but the callee
275 :     * pops the arguments in a stdcall on Windows. I'm not sure what other
276 :     * differences there might be between the SVID and Windows ABIs. (JHR)
277 :     *)
278 :     val calleePops = (case #conv proto
279 :     of (""|"ccall") => false
280 :     | "stdcall" => true
281 :     | conv => raise Fail (concat [
282 :     "unknown calling convention \"", String.toString conv, "\""
283 :     ])
284 :     (* end case *))
285 :    
286 :     (* code to pop the arguments from the stack *)
287 :     val popArgs = if calleePops orelse (explicitArgSzB = 0)
288 :     then []
289 :     else [T.MV(wordTy, C.esp, T.ADD(wordTy, CCs.spReg, T.LI(IntInf.fromInt explicitArgSzB)))]
290 :    
291 :     (* code to copy the result into fresh pseudo registers *)
292 :     val (resultRegs, copyResult) = (case resLocs
293 :     of [] => ([], [])
294 :     | [CCall.C_GPR(ty, r)] => let
295 :     val resReg = C.newReg()
296 :     in
297 :     ([T.GPR(T.REG(ty, resReg))], [T.COPY(ty, [resReg], [r])])
298 :     end
299 :     | [CCall.C_FPR(ty, r)] => let
300 :     val resReg = C.newFreg()
301 :     val res = [T.FPR(T.FREG(ty, resReg))]
302 :     in
303 :     (* If we are using fast floating point mode then do NOT
304 :     * generate FSTP.
305 :     * --- Allen
306 :     *)
307 :     if !fast_floating_point
308 :     then (res, [T.FCOPY(ty, [resReg], [r])])
309 :     else (res, [fstp(ty, T.FREG(ty, resReg))])
310 :     end
311 :     | _ => raise Fail "bogus result location"
312 :     (* end case *))
313 :    
314 :     val defs = definedRegs(#retTy proto)
315 :     val { save, restore } = saveRestoreDedicated defs
316 :    
317 :     val callStm = T.CALL{
318 :     funct=name, targets=[], defs=defs, uses=[],
319 :     region = T.Region.memory,
320 :     pops = if calleePops
321 :     then Int32.fromInt(#szb argMem)
322 :     else Int32.fromInt(#szb argMem - explicitArgSzB)
323 :     }
324 :     val callStm = (case callComment
325 :     of NONE => callStm
326 :     | SOME c => T.ANNOTATION (callStm, #create MLRiscAnnotations.COMMENT c)
327 :     (* end case *))
328 :    
329 :     val callStm = if !fast_floating_point
330 :     andalso ((#retTy proto = CTy.C_float)
331 :     orelse (#retTy proto = CTy.C_double)
332 :     orelse (#retTy proto = CTy.C_long_double))
333 :     then T.ANNOTATION(callStm, fpReturnValueInST0)
334 :     else callStm
335 :    
336 :     (* assemble the call sequence *)
337 : mrainey 3010 val callSeq = argAlloc @ copyArgs @ save @ [callStm] @ restore(* @ popArgs*) @ copyResult
338 : mrainey 3009
339 :     in
340 :     {callseq=callSeq, result=resultRegs}
341 :     end
342 :    
343 :     end (* IA32SVIDFn *)

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