summaryrefslogtreecommitdiff
path: root/asmcomp/arm/selection.ml
blob: bd165898cde01569869a0c6fbf3b3bfad0802fe4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
(***********************************************************************)
(*                                                                     *)
(*                                OCaml                                *)
(*                                                                     *)
(*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         *)
(*                                                                     *)
(*  Copyright 1998 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)

(* $Id$ *)

(* Instruction selection for the ARM processor *)

open Misc
open Cmm
open Reg
open Arch
open Proc
open Mach

(* Immediate operands are 8-bit immediate values, zero-extended, and rotated
   right by 0, 2, 4, ... 30 bits.
   To avoid problems with Caml's 31-bit arithmetic,
   we check only with 8-bit values shifted left 0 to 22 bits. *)

let rec is_immed n shift =
  if shift > 22 then false
  else if n land (0xFF lsl shift) = n then true
  else is_immed n (shift + 2)

(* We have 12-bit + sign byte offsets for word accesses,
   8-bit + sign word offsets for float accesses,
   and 8-bit + sign byte offsets for bytes and shorts.
   Use lowest common denominator. *)

let is_offset n = n < 256 && n > -256

let is_intconst = function Cconst_int n -> true | _ -> false

(* Soft emulation of float comparisons *)

let float_comparison_function = function
  | Ceq -> "__eqdf2"
  | Cne -> "__nedf2"
  | Clt -> "__ltdf2"
  | Cle -> "__ledf2"
  | Cgt -> "__gtdf2"
  | Cge -> "__gedf2"

(* Instruction selection *)
class selector = object(self)

inherit Selectgen.selector_generic as super

method! regs_for tyv =
  (* Expand floats into pairs of integer registers *)
  let nty = Array.length tyv in
  let rec expand i =
    if i >= nty then [] else begin
      match tyv.(i) with
      | Float -> Int :: Int :: expand (i+1)
      | ty -> ty :: expand (i+1)
    end in
  Reg.createv (Array.of_list (expand 0))

method is_immediate n =
  n land 0xFF = n || is_immed n 2

method select_addressing = function
    Cop(Cadda, [arg; Cconst_int n]) when is_offset n ->
      (Iindexed n, arg)
  | Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) when is_offset n ->
      (Iindexed n, Cop(Cadda, [arg1; arg2]))
  | arg ->
      (Iindexed 0, arg)

method select_shift_arith op shiftop shiftrevop args =
  match args with
    [arg1; Cop(Clsl, [arg2; Cconst_int n])]
    when n > 0 && n < 32 && not(is_intconst arg2) ->
      (Ispecific(Ishiftarith(shiftop, n)), [arg1; arg2])
  | [arg1; Cop(Casr, [arg2; Cconst_int n])]
    when n > 0 && n < 32 && not(is_intconst arg2) ->
      (Ispecific(Ishiftarith(shiftop, -n)), [arg1; arg2])
  | [Cop(Clsl, [arg1; Cconst_int n]); arg2]
    when n > 0 && n < 32 && not(is_intconst arg1) ->
      (Ispecific(Ishiftarith(shiftrevop, n)), [arg2; arg1])
  | [Cop(Casr, [arg1; Cconst_int n]); arg2]
    when n > 0 && n < 32 && not(is_intconst arg1) ->
      (Ispecific(Ishiftarith(shiftrevop, -n)), [arg2; arg1])
  | _ ->
      super#select_operation op args

method! select_operation op args =
  match op with
    Cadda | Caddi ->
      begin match args with
        [arg1; Cconst_int n] when n < 0 && self#is_immediate (-n) ->
          (Iintop_imm(Isub, -n), [arg1])
      | _ ->
          self#select_shift_arith op Ishiftadd Ishiftadd args
      end
  | Csuba | Csubi ->
      begin match args with
        [arg1; Cconst_int n] when n < 0 && self#is_immediate (-n) ->
          (Iintop_imm(Iadd, -n), [arg1])
      | [Cconst_int n; arg2] when self#is_immediate n ->
          (Ispecific(Irevsubimm n), [arg2])
      | _ ->
          self#select_shift_arith op Ishiftsub Ishiftsubrev args
      end
  | Cmuli ->                    (* no multiply immediate *)
      (Iintop Imul, args)
  | Cdivi ->
      begin match args with
        [arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
          (Iintop_imm(Idiv, n), [arg1])
      | _ ->
          (Iextcall("__divsi3", false), args)
      end
  | Cmodi ->
      begin match args with
        [arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
          (Iintop_imm(Imod, n), [arg1])
      | _ ->
          (Iextcall("__modsi3", false), args)
      end
  | Ccheckbound _ ->
      begin match args with
        [Cop(Clsr, [arg1; Cconst_int n]); arg2]
        when n > 0 && n < 32 && not(is_intconst arg2) ->
          (Ispecific(Ishiftcheckbound n), [arg1; arg2])
      | _ ->
        super#select_operation op args
      end
  (* Turn floating-point operations into library function calls *)
  | Caddf -> (Iextcall("__adddf3", false), args)
  | Csubf -> (Iextcall("__subdf3", false), args)
  | Cmulf -> (Iextcall("__muldf3", false), args)
  | Cdivf -> (Iextcall("__divdf3", false), args)
  | Cfloatofint -> (Iextcall("__floatsidf", false), args)
  | Cintoffloat -> (Iextcall("__fixdfsi", false), args)
  | Ccmpf comp ->
      (Iintop_imm(Icomp(Isigned comp), 0),
       [Cop(Cextcall(float_comparison_function comp,
                     typ_int, false, Debuginfo.none),
            args)])
  (* Add coercions around loads and stores of 32-bit floats *)
  | Cload Single ->
      (Iextcall("__extendsfdf2", false), [Cop(Cload Word, args)])
  | Cstore Single ->
      begin match args with
      | [arg1; arg2] ->
          let arg2' =
            Cop(Cextcall("__truncdfsf2", typ_int, false, Debuginfo.none),
                [arg2]) in
          self#select_operation (Cstore Word) [arg1; arg2']
      | _ -> assert false
      end
  (* Other operations are regular *)
  | _ -> super#select_operation op args

method! select_condition = function
  | Cop(Ccmpf cmp, args) ->
      (Iinttest_imm(Isigned cmp, 0),
       Cop(Cextcall(float_comparison_function cmp,
                    typ_int, false, Debuginfo.none),
           args))
  | expr ->
      super#select_condition expr

(* Deal with some register irregularities:

1- In mul rd, rm, rs,  the registers rm and rd must be different.
   We deal with this by pretending that rm is also a result of the mul
   operation.

2- For Inegf and Iabsf, force arguments and results in (r0, r1);
   this simplifies code generation later.
*)

method! insert_op_debug op dbg rs rd =
  match op with
  | Iintop(Imul) ->
      self#insert_debug (Iop op) dbg rs [| rd.(0); rs.(0) |]; rd
  | Iabsf | Inegf ->
      let r = [| phys_reg 0; phys_reg 1 |] in
      self#insert_moves rs r;
      self#insert_debug (Iop op) dbg r r;
      self#insert_moves r rd;
      rd
  | _ ->
      super#insert_op_debug op dbg rs rd

end

let fundecl f = (new selector)#emit_fundecl f