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 3049 - (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 : mrainey 3049 val spReg = T.REG (32, C.esp)
66 : mrainey 3009
67 : mrainey 3049 structure CCall = CCallFn(
68 :     structure T = T
69 :     structure C = C
70 :     val wordTy = wordTy
71 :     fun offSp 0 = spReg
72 :     | offSp offset = T.ADD (32, spReg, T.LI offset))
73 :    
74 :     datatype c_arg = datatype CCall.c_arg
75 :     datatype arg_location = datatype CCall.arg_location
76 :     datatype location_kinds = datatype CCall.location_kinds
77 : mrainey 3009
78 :     structure SA = StagedAllocationFn (
79 : mrainey 3049 type reg = T.reg
80 :     datatype location_kinds = datatype location_kinds
81 : mrainey 3009 val memSize = 8)
82 :    
83 : mrainey 3049 val calleeSaveRegs = [C.eax, C.ecx, C.edx]
84 :     val callerSaveRegs = [C.ebx, C.esi, C.edi]
85 :     val calleeSaveFRegs = []
86 :     val callerSaveFRegs = []
87 :    
88 : mrainey 3010 (* calling conventions in the Staged Allocation language *)
89 : mrainey 3009 structure CCs =
90 :     struct
91 :    
92 :     (* register conventions *)
93 : mrainey 3049 val callerSaveRegs = List.map gpr calleeSaveRegs
94 :     val calleeSaveRegs = List.map gpr calleeSaveRegs
95 :     val calleeSaveFRegs = []
96 :     val callerSaveFRegs = []
97 :    
98 : mrainey 3009 (* the C calling convention requires that the FP stack be empty on function
99 :     * entry. We add the fpStk list to the defs when the fast_floating_point flag
100 :     * is set.
101 :     *)
102 :     val st0 = C.ST 0
103 :     (* the C calling convention requires that the FP stack be empty on function
104 :     * entry. We add the fpStk list to the defs when the fast_floating_point flag
105 :     * is set.
106 :     *)
107 :     val fpStk = List.tabulate(8, fn i => fpr (C.ST i))
108 :    
109 :     (* conventions for calling a C function *)
110 : mrainey 3010 val alignB = 4
111 : mrainey 3009 val cStack = SA.freshCounter()
112 : mrainey 3010 val callStages = [
113 : mrainey 3009 SA.SEQ[
114 :     SA.WIDEN (fn ty => Int.max(32, ty)),
115 :     SA.OVERFLOW {counter=cStack, blockDirection=SA.UP, maxAlign=alignB}
116 :     ]
117 :     ]
118 :    
119 :     (* conventions for returning from a C call *)
120 :     val (cInt, retInGpr) = SA.useRegs [(32, C.eax), (32, C.edx)]
121 :     val (cFloat, retInFpr) = SA.useRegs [(80, st0)]
122 :     val returnStages = [
123 :     SA.CHOICE [
124 :     (* return in general-purpose register *)
125 :     (fn (ty, k, str) => k = K_GPR,
126 :     SA.SEQ [SA.WIDEN (fn ty => Int.max(ty, 32)),
127 :     retInGpr]),
128 :     (* return in floating-point register *)
129 :     (fn (ty, k, str) => k = K_FPR,
130 :     SA.SEQ [SA.WIDEN (fn ty => 80), retInFpr])
131 :     ]
132 :     ]
133 :    
134 :     (* initial store *)
135 :     val str0 = SA.init [cInt, cFloat, cStack]
136 :    
137 : mrainey 3049 end (* CCs *)
138 : mrainey 3009
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 : mrainey 3049 else [T.MV(wordTy, C.esp, T.SUB(wordTy, spReg, T.LI(IntInf.fromInt(#szb argMem))))]
265 : mrainey 3009 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 : mrainey 3049 else [T.MV(wordTy, C.esp, T.ADD(wordTy, spReg, T.LI(IntInf.fromInt explicitArgSzB)))]
283 : mrainey 3009
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