1 (***********************************************************************)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
7 (* Copyright 1997 Institut National de Recherche en Informatique et *)
8 (* en Automatique. All rights reserved. This file is distributed *)
9 (* under the terms of the Q Public License version 1.0. *)
11 (***********************************************************************)
13 (* $Id: selection.ml 7812 2007-01-29 12:11:18Z xleroy $ *)
15 (* Instruction selection for the Intel x86 *)
24 (* Auxiliary for recognizing addressing modes *)
26 type addressing_expr =
28 | Alinear of expression
29 | Aadd of expression * expression
30 | Ascale of expression * int
31 | Ascaledadd of expression * expression * int
33 let rec select_addr exp =
37 | Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
38 let (a, n) = select_addr arg in (a, n + m)
39 | Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
40 let (a, n) = select_addr arg in (a, n - m)
41 | Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
42 let (a, n) = select_addr arg in (a, n + m)
43 | Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
44 begin match select_addr arg with
45 (Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
46 | _ -> (Alinear exp, 0)
48 | Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
49 begin match select_addr arg with
50 (Alinear e, n) -> (Ascale(e, mult), n * mult)
51 | _ -> (Alinear exp, 0)
53 | Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
54 begin match select_addr arg with
55 (Alinear e, n) -> (Ascale(e, mult), n * mult)
56 | _ -> (Alinear exp, 0)
58 | Cop((Caddi | Cadda), [arg1; arg2]) ->
59 begin match (select_addr arg1, select_addr arg2) with
60 ((Alinear e1, n1), (Alinear e2, n2)) ->
61 (Aadd(e1, e2), n1 + n2)
62 | ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
63 (Ascaledadd(e1, e2, scale), n1 + n2)
64 | ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
65 (Ascaledadd(e2, e1, scale), n1 + n2)
66 | (_, (Ascale(e2, scale), n2)) ->
67 (Ascaledadd(arg1, e2, scale), n2)
68 | ((Ascale(e1, scale), n1), _) ->
69 (Ascaledadd(arg2, e1, scale), n1)
76 (* C functions to be turned into Ifloatspecial instructions if -ffast-math *)
78 let inline_float_ops =
79 ["atan"; "atan2"; "cos"; "log"; "log10"; "sin"; "sqrt"; "tan"]
81 (* Estimate number of float temporaries needed to evaluate expression
82 (Ershov's algorithm) *)
84 let rec float_needs = function
85 Cop((Cnegf | Cabsf), [arg]) ->
87 | Cop((Caddf | Csubf | Cmulf | Cdivf), [arg1; arg2]) ->
88 let n1 = float_needs arg1 in
89 let n2 = float_needs arg2 in
90 if n1 = n2 then 1 + n1 else if n1 > n2 then n1 else n2
91 | Cop(Cextcall(fn, ty_res, alloc, dbg), args)
92 when !fast_math && List.mem fn inline_float_ops ->
94 [arg] -> float_needs arg
95 | [arg1; arg2] -> max (float_needs arg2 + 1) (float_needs arg1)
101 (* Special constraints on operand and result registers *)
103 exception Use_default
108 let tos = phys_reg 100
110 let pseudoregs_for_operation op arg res =
112 (* Two-address binary operations *)
113 Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor) ->
114 ([|res.(0); arg.(1)|], res, false)
115 (* Two-address unary operations *)
116 | Iintop_imm((Iadd|Isub|Imul|Idiv|Iand|Ior|Ixor|Ilsl|Ilsr|Iasr), _) ->
118 (* For shifts with variable shift count, second arg must be in ecx *)
119 | Iintop(Ilsl|Ilsr|Iasr) ->
120 ([|res.(0); ecx|], res, false)
121 (* For div and mod, first arg must be in eax, edx is clobbered,
122 and result is in eax or edx respectively.
123 Keep it simple, just force second argument in ecx. *)
125 ([| eax; ecx |], [| eax |], true)
127 ([| eax; ecx |], [| edx |], true)
128 (* For mod with immediate operand, arg must not be in eax.
129 Keep it simple, force it in edx. *)
130 | Iintop_imm(Imod, _) ->
131 ([| edx |], [| edx |], true)
132 (* For floating-point operations and floating-point loads,
133 the result is always left at the top of the floating-point stack *)
134 | Iconst_float _ | Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
135 | Ifloatofint | Iload((Single | Double | Double_u), _)
136 | Ispecific(Isubfrev | Idivfrev | Ifloatarithmem(_, _, _) | Ifloatspecial _) ->
137 (arg, [| tos |], false) (* don't move it immediately *)
138 (* For storing a byte, the argument must be in eax...edx.
139 (But for a short, any reg will do!)
140 Keep it simple, just force the argument to be in edx. *)
141 | Istore((Byte_unsigned | Byte_signed), addr) ->
142 let newarg = Array.copy arg in
145 (* Other instructions are regular *)
146 | _ -> raise Use_default
148 let chunk_double = function
154 (* The selector class *)
156 class selector = object (self)
158 inherit Selectgen.selector_generic as super
160 method is_immediate (n : int) = true
162 method is_simple_expr e =
164 | Cop(Cextcall(fn, _, alloc, _), args)
165 when !fast_math && List.mem fn inline_float_ops ->
166 (* inlined float ops are simple if their arguments are *)
167 List.for_all self#is_simple_expr args
169 super#is_simple_expr e
171 method select_addressing exp =
172 match select_addr exp with
174 (Ibased(s, d), Ctuple [])
177 | (Aadd(e1, e2), d) ->
178 (Iindexed2 d, Ctuple[e1; e2])
179 | (Ascale(e, scale), d) ->
180 (Iscaled(scale, d), e)
181 | (Ascaledadd(e1, e2, scale), d) ->
182 (Iindexed2scaled(scale, d), Ctuple[e1; e2])
184 method select_store addr exp =
187 (Ispecific(Istore_int(Nativeint.of_int n, addr)), Ctuple [])
189 (Ispecific(Istore_int(n, addr)), Ctuple [])
190 | Cconst_pointer n ->
191 (Ispecific(Istore_int(Nativeint.of_int n, addr)), Ctuple [])
192 | Cconst_natpointer n ->
193 (Ispecific(Istore_int(n, addr)), Ctuple [])
195 (Ispecific(Istore_symbol(s, addr)), Ctuple [])
197 super#select_store addr exp
199 method select_operation op args =
201 (* Recognize the LEA instruction *)
202 Caddi | Cadda | Csubi | Csuba ->
203 begin match self#select_addressing (Cop(op, args)) with
204 (Iindexed d, _) -> super#select_operation op args
205 | (Iindexed2 0, _) -> super#select_operation op args
206 | (addr, arg) -> (Ispecific(Ilea addr), [arg])
208 (* Recognize (x / cst) and (x % cst) only if cst is a power of 2. *)
210 begin match args with
211 [arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
212 (Iintop_imm(Idiv, n), [arg1])
213 | _ -> (Iintop Idiv, args)
216 begin match args with
217 [arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
218 (Iintop_imm(Imod, n), [arg1])
219 | _ -> (Iintop Imod, args)
221 (* Recognize float arithmetic with memory.
222 In passing, apply Ershov's algorithm to reduce stack usage *)
224 self#select_floatarith Iaddf Iaddf Ifloatadd Ifloatadd args
226 self#select_floatarith Isubf (Ispecific Isubfrev) Ifloatsub Ifloatsubrev args
228 self#select_floatarith Imulf Imulf Ifloatmul Ifloatmul args
230 self#select_floatarith Idivf (Ispecific Idivfrev) Ifloatdiv Ifloatdivrev args
231 (* Recognize store instructions *)
233 begin match args with
234 [loc; Cop(Caddi, [Cop(Cload _, [loc']); Cconst_int n])]
236 let (addr, arg) = self#select_addressing loc in
237 (Ispecific(Ioffset_loc(n, addr)), [arg])
239 super#select_operation op args
241 (* Recognize inlined floating point operations *)
242 | Cextcall(fn, ty_res, false, dbg)
243 when !fast_math && List.mem fn inline_float_ops ->
244 (Ispecific(Ifloatspecial fn), args)
246 | _ -> super#select_operation op args
248 (* Recognize float arithmetic with mem *)
250 method select_floatarith regular_op reversed_op mem_op mem_rev_op args =
252 [arg1; Cop(Cload chunk, [loc2])] ->
253 let (addr, arg2) = self#select_addressing loc2 in
254 (Ispecific(Ifloatarithmem(chunk_double chunk, mem_op, addr)),
256 | [Cop(Cload chunk, [loc1]); arg2] ->
257 let (addr, arg1) = self#select_addressing loc1 in
258 (Ispecific(Ifloatarithmem(chunk_double chunk, mem_rev_op, addr)),
261 (* Evaluate bigger subexpression first to minimize stack usage.
262 Because of right-to-left evaluation, rightmost arg is evaluated
264 if float_needs arg1 <= float_needs arg2
265 then (regular_op, [arg1; arg2])
266 else (reversed_op, [arg2; arg1])
268 fatal_error "Proc_i386: select_floatarith"
270 (* Deal with register constraints *)
272 method insert_op_debug op dbg rs rd =
274 let (rsrc, rdst, move_res) = pseudoregs_for_operation op rs rd in
275 self#insert_moves rs rsrc;
276 self#insert_debug (Iop op) dbg rsrc rdst;
277 if move_res then begin
278 self#insert_moves rdst rd;
283 super#insert_op_debug op dbg rs rd
285 method insert_op op rs rd =
286 self#insert_op_debug op Debuginfo.none rs rd
288 (* Selection of push instructions for external calls *)
290 method select_push exp =
292 Cconst_int n -> (Ispecific(Ipush_int(Nativeint.of_int n)), Ctuple [])
293 | Cconst_natint n -> (Ispecific(Ipush_int n), Ctuple [])
294 | Cconst_pointer n -> (Ispecific(Ipush_int(Nativeint.of_int n)), Ctuple [])
295 | Cconst_natpointer n -> (Ispecific(Ipush_int n), Ctuple [])
296 | Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
297 | Cop(Cload Word, [loc]) ->
298 let (addr, arg) = self#select_addressing loc in
299 (Ispecific(Ipush_load addr), arg)
300 | Cop(Cload Double_u, [loc]) ->
301 let (addr, arg) = self#select_addressing loc in
302 (Ispecific(Ipush_load_float addr), arg)
303 | _ -> (Ispecific(Ipush), exp)
305 method emit_extcall_args env args =
306 let rec size_pushes = function
308 | e :: el -> Selectgen.size_expr env e + size_pushes el in
309 let sz1 = size_pushes args in
310 let sz2 = Misc.align sz1 stack_alignment in
311 let rec emit_pushes = function
314 self#insert (Iop (Istackoffset (sz2 - sz1))) [||] [||]
317 let (op, arg) = self#select_push e in
318 match self#emit_expr env arg with
320 | Some r -> self#insert (Iop op) r [||] in
326 let fundecl f = (new selector)#emit_fundecl f