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 3037 - (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 3037 fun kindOfCTy (CTy.C_float | CTy.C_double | CTy.C_long_double) = K_FPR
141 :     | kindOfCTy (CTy.C_unsigned _ | CTy.C_signed _ | CTy.C_PTR | CTy.C_ARRAY _) = K_GPR
142 : mrainey 3009
143 : mrainey 3010 (* convert a C type to slots for staged allocation *)
144 : mrainey 3009 fun cTyToSlots cTy = let
145 :     val {sz, align} = IA32CSizes.sizeOfTy cTy
146 : mrainey 3010 (* compute argument slots for the flattened C type *)
147 :     val slots = List.map (fn cTy => (sz * 8, kindOfCTy cTy, align))
148 : mrainey 3009 (CTy.flattenCTy cTy)
149 :     in
150 :     case (cTy, abi)
151 :     of (CTy.C_STRUCT _, "Mac OS X") =>
152 :     (* on Mac OS X, structs <= 8 bytes are returned in GPRs *)
153 :     if (sz <= 4)
154 :     then [(8, K_GPR, align)]
155 :     else if (sz <= 8)
156 :     then [(8, K_GPR, align), (8, K_GPR, align)]
157 : mrainey 3010 else slots
158 : mrainey 3009 | ( (CTy.C_unsigned CTy.I_long_long |
159 :     CTy.C_signed CTy.I_long_long ),
160 :     _ ) =>
161 :     (* 64-bit integers are returned in GPRs *)
162 :     [(8, K_GPR, align), (8, K_GPR, align)]
163 : mrainey 3010 | _ => slots
164 : mrainey 3009 end
165 :    
166 :     (* convert a finalized staged-allocation location into a C location *)
167 :     fun saToCLoc (ty, SA.REG(_, r), K_GPR) = CCall.C_GPR(ty, r)
168 :     | saToCLoc (ty, SA.REG(_, r), K_FPR) = CCall.C_FPR(ty, r)
169 :     | saToCLoc (ty, SA.BLOCK_OFFSET offB, _) = CCall.C_STK(ty, T.I.fromInt (32, offB))
170 :     | saToCLoc (ty, SA.NARROW(loc, ty', k), _) = saToCLoc (ty, loc, k)
171 :    
172 : mrainey 3010 val frameAlign = 8
173 :    
174 : mrainey 3009 fun layout {conv, retTy, paramTys} = let
175 :    
176 :     (* compute locations for return results *)
177 : mrainey 3010 val (resLocs, structRetLoc, str) = (case retTy
178 :     of CTy.C_void => ([], NONE, CCs.str0)
179 : mrainey 3009 | retTy => let
180 :     val {sz, align} = IA32CSizes.sizeOfTy retTy
181 :     (* compute the locations for return values using staged allocation *)
182 :     val returnStepper = SA.mkStep CCs.returnStages
183 :     (* finalize locations for the return type *)
184 :     val (str, locs) = SA.doStagedAllocation(CCs.str0, returnStepper, cTyToSlots retTy)
185 :     val nBytesAllocated = SA.find(str, CCs.cStack)
186 :     in
187 : mrainey 3010 (List.map saToCLoc locs, SOME {szb=sz, align=align}, str)
188 : mrainey 3009 end
189 :     (* end case *))
190 :    
191 :     (* compute locations for passing arguments *)
192 :     val paramStepper = SA.mkStep CCs.callStages
193 :     fun doParam (paramTy, (str, paramLocss)) = let
194 :     val (str', paramLocs) = SA.doStagedAllocation(str, paramStepper, cTyToSlots paramTy)
195 :     in
196 :     (str', (List.map saToCLoc paramLocs) :: paramLocss)
197 :     end
198 :     val (str, paramLocss) = List.foldl doParam (str, []) paramTys
199 :     val paramLocs = List.rev paramLocss
200 :    
201 : mrainey 3010 (* number of bytes allocated for the call *)
202 :     val cStkSzB = let
203 :     val n = SA.find(str, CCs.cStack)
204 :     in
205 :     if (abi = "Mac OS X")
206 :     then IA32CSizes.alignAddr(n, 16)
207 :     else n
208 :     end
209 : mrainey 3009 in
210 : mrainey 3010 {argLocs=paramLocs, argMem={szb=cStkSzB, align=4}, structRetLoc=structRetLoc, resLocs=resLocs}
211 : mrainey 3009 end (* layout *)
212 :    
213 :     (* List of registers defined by a C Call with the given return type; this list
214 :     * is the result registers plus the caller-save registers.
215 :     *)
216 :     fun definedRegs resTy = if !fast_floating_point
217 :     then let
218 :     val defs = CCs.callerSaveRegs @ CCs.fpStk
219 :     in
220 :     case resTy
221 :     of (CTy.C_unsigned(CTy.I_long_long)) => gpr C.edx :: defs
222 :     | (CTy.C_signed(CTy.I_long_long)) => gpr C.edx :: defs
223 :     | _ => defs
224 :     (* end case *)
225 :     end
226 :     else (case resTy
227 :     of (CTy.C_float) => fpr CCs.st0 :: CCs.callerSaveRegs
228 :     | (CTy.C_double) => fpr CCs.st0 :: CCs.callerSaveRegs
229 :     | (CTy.C_long_double) => fpr (CCs.st0) :: CCs.callerSaveRegs
230 :     | (CTy.C_unsigned(CTy.I_long_long)) => gpr C.edx :: CCs.callerSaveRegs
231 :     | (CTy.C_signed(CTy.I_long_long)) => gpr C.edx :: CCs.callerSaveRegs
232 :     | _ => CCs.callerSaveRegs
233 :     (* end case *))
234 :    
235 :     fun fstp (32, f) = T.EXT(ix(IX.FSTPS(f)))
236 :     | fstp (64, f) = T.EXT(ix(IX.FSTPL(f)))
237 :     | fstp (80, f) = T.EXT(ix(IX.FSTPT(f)))
238 :     | fstp (sz, f) = raise Fail ("fstp(" ^ Int.toString sz ^ ",_)")
239 :    
240 :     (* This annotation is used to indicate that a call returns a fp value
241 :     * in %st(0)
242 :     *)
243 :     val fpReturnValueInST0 = #create MLRiscAnnotations.RETURN_ARG C.ST0
244 :    
245 :     fun genCall {
246 :     name, proto, paramAlloc, structRet, saveRestoreDedicated, callComment, args
247 :     } = let
248 : mrainey 3010 val {argLocs, argMem, structRetLoc, resLocs} = layout proto
249 :    
250 :     (* for functions that return a struct/union, pass the location as an
251 :     * implicit first argument. Because the callee removes this implicit
252 :     * argument from the stack, we must also keep track of the size of the
253 :     * explicit arguments.
254 :     *)
255 :     val (args, argLocs, explicitArgSzB) = (case structRetLoc
256 :     of SOME pos =>
257 :     (ARG(structRet pos)::args, [CCall.C_STK(wordTy, T.I.fromInt (wordTy, 0))]::argLocs, #szb argMem - 4)
258 :     | NONE => (args, argLocs, #szb argMem)
259 :     (* end case *))
260 :    
261 : mrainey 3009 (* instruction to allocate space for arguments *)
262 :     val argAlloc = if ((#szb argMem = 0) orelse paramAlloc argMem)
263 :     then []
264 :     else [T.MV(wordTy, C.esp, T.SUB(wordTy, CCs.spReg, T.LI(IntInf.fromInt(#szb argMem))))]
265 :     val (copyArgs, gprUses, fprUses) = CCall.copyArgs(args, argLocs)
266 :    
267 :     (* the SVID specifies that the caller pops arguments, but the callee
268 :     * pops the arguments in a stdcall on Windows. I'm not sure what other
269 :     * differences there might be between the SVID and Windows ABIs. (JHR)
270 :     *)
271 :     val calleePops = (case #conv proto
272 :     of (""|"ccall") => false
273 :     | "stdcall" => true
274 :     | conv => raise Fail (concat [
275 :     "unknown calling convention \"", String.toString conv, "\""
276 :     ])
277 :     (* end case *))
278 :    
279 :     (* code to pop the arguments from the stack *)
280 :     val popArgs = if calleePops orelse (explicitArgSzB = 0)
281 :     then []
282 :     else [T.MV(wordTy, C.esp, T.ADD(wordTy, CCs.spReg, T.LI(IntInf.fromInt explicitArgSzB)))]
283 :    
284 :     (* code to copy the result into fresh pseudo registers *)
285 :     val (resultRegs, copyResult) = (case resLocs
286 :     of [] => ([], [])
287 :     | [CCall.C_GPR(ty, r)] => let
288 :     val resReg = C.newReg()
289 :     in
290 :     ([T.GPR(T.REG(ty, resReg))], [T.COPY(ty, [resReg], [r])])
291 :     end
292 :     | [CCall.C_FPR(ty, r)] => let
293 :     val resReg = C.newFreg()
294 :     val res = [T.FPR(T.FREG(ty, resReg))]
295 :     in
296 :     (* If we are using fast floating point mode then do NOT
297 :     * generate FSTP.
298 :     * --- Allen
299 :     *)
300 :     if !fast_floating_point
301 :     then (res, [T.FCOPY(ty, [resReg], [r])])
302 :     else (res, [fstp(ty, T.FREG(ty, resReg))])
303 :     end
304 :     | _ => raise Fail "bogus result location"
305 :     (* end case *))
306 :    
307 :     val defs = definedRegs(#retTy proto)
308 :     val { save, restore } = saveRestoreDedicated defs
309 :    
310 :     val callStm = T.CALL{
311 :     funct=name, targets=[], defs=defs, uses=[],
312 :     region = T.Region.memory,
313 :     pops = if calleePops
314 :     then Int32.fromInt(#szb argMem)
315 :     else Int32.fromInt(#szb argMem - explicitArgSzB)
316 :     }
317 :     val callStm = (case callComment
318 :     of NONE => callStm
319 :     | SOME c => T.ANNOTATION (callStm, #create MLRiscAnnotations.COMMENT c)
320 :     (* end case *))
321 :    
322 :     val callStm = if !fast_floating_point
323 :     andalso ((#retTy proto = CTy.C_float)
324 :     orelse (#retTy proto = CTy.C_double)
325 :     orelse (#retTy proto = CTy.C_long_double))
326 :     then T.ANNOTATION(callStm, fpReturnValueInST0)
327 :     else callStm
328 :    
329 :     (* assemble the call sequence *)
330 : mrainey 3010 val callSeq = argAlloc @ copyArgs @ save @ [callStm] @ restore(* @ popArgs*) @ copyResult
331 : mrainey 3009
332 :     in
333 :     {callseq=callSeq, result=resultRegs}
334 :     end
335 :    
336 :     end (* IA32SVIDFn *)

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