(***********************************************************************)
(*                                                                     *)
(*                           Objective Caml                            *)
(*                                                                     *)
(*            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: selection.ml 10295 2010-04-22 12:39:40Z xleroy $ *)

(* 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
