(*
 * Copyright (c) 2000-2001 Stefan Kral
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *)

(* This module declares basics data-types for virtual simd instructions
 * and defines some frequently used functions on them.
 *)

open List
open Util
open Number
open Variable
open VFpBasics


type vintreg = V_IntReg of int		(* VIRTUAL INTEGER REGISTER *********)
type vsimdreg = V_SimdReg of int	(* VIRTUAL SIMD REGISTER ************)

let next_vintreg  (V_IntReg n)  = V_IntReg (succ n)
let next_vsimdreg (V_SimdReg n) = V_SimdReg (succ n)

let makeNewVsimdreg = 
  let vsimdreg_counter = ref 1000 in		(* avoid starting with 0 *)
  fun () ->
    let retval = !vsimdreg_counter in
      incr vsimdreg_counter;
      V_SimdReg retval

let makeNewVsimdreg2 () = 
  (makeNewVsimdreg (), makeNewVsimdreg ())

let makeNewVsimdreg3 () = 
  (makeNewVsimdreg (), makeNewVsimdreg (), makeNewVsimdreg ())

let makeNewVintreg = 
  let vintreg_counter = ref 1000 in		(* avoid starting with 0 *)
  fun () ->
    let retval = !vintreg_counter in
      incr vintreg_counter;
      V_IntReg retval

let makeNewVintreg2 () = 
  (makeNewVintreg (), makeNewVintreg ())

let makeNewVintreg3 () =
  (makeNewVintreg (), makeNewVintreg (), makeNewVintreg ())

module VIntRegMap = Map.Make(struct type t = vintreg let compare = compare end)

let vintregmap_find k m = try Some(VIntRegMap.find k m) with Not_found -> None
let vintregmap_findE k m = try VIntRegMap.find k m with Not_found -> []
let vintregmap_addE k v m = VIntRegMap.add k (v::(vintregmap_findE k m)) m
let vintregmap_cutE k m = VIntRegMap.add k (tl (VIntRegMap.find k m)) m

module VSimdRegSet = Set.Make(struct type t = vsimdreg let compare = compare end)

module VSimdRegMap = Map.Make(struct type t = vsimdreg let compare = compare end)
let vsimdregmap_find k m = try Some(VSimdRegMap.find k m) with Not_found -> None

let vsimdregmap_findE k m = try VSimdRegMap.find k m with Not_found -> []
let vsimdregmap_addE k v m = VSimdRegMap.add k (v::(vsimdregmap_findE k m)) m
let vsimdregmap_addE' value key map = vsimdregmap_addE key value map
let vsimdregmap_cutE k m = VSimdRegMap.add k (tl (VSimdRegMap.find k m)) m

(****************************************************************************)

type vsimdpos = V_Lo | V_Hi | V_LoHi	(* POSITION WITHIN A SIMD QWORD *****)
type realimag = RealPart | ImagPart	(* COMPONENT OF A COMPLEX NUMBER ****)

(****************************************************************************)

type vconst =
  | V_NumberPairConst of float * float
  | V_NamedConst of string
  | V_ChsConst of vsimdpos

module VConstMap = Map.Make(struct type t = vconst let compare = compare end)

(****************************************************************************)

type vsimdinstroperand = 		 (* VIRTUAL SIMD INSTR OPERAND ******)
  | V_SimdTmp of int			 (*   Simd Temporary Variable  [id] *)
  | V_SimdDVar of vfpaccess * array * int (*   Simd DWord Var      [arr,idx] *)
  | V_SimdQVar of array * int		 (*   Simd QWord Var      [arr,idx] *)
  | V_SimdNConst of string 		 (*   Named Const	 [name_str] *)
  | V_SimdNumConst1 of float  	 	 (*   Numeric Constant	[c] 	    *)
  | V_SimdNumConst2 of float * float  	 (*   Numeric Constant	[c_lo,c_hi] *)

module VSimdInstrOperandSet = Set.Make(struct 
					 type t = vsimdinstroperand 
					 let compare = compare 
				       end)

type vsimdrawvar = 			(* RAW SIMD VARIABLE (w/o Array) ****)
  | V_SimdRawQVar of int
  | V_SimdRawDVar of vfpaccess * int

module VSimdRawVarSet = Set.Make(struct 
				   type t = vsimdrawvar 
				   let compare = compare 
				 end)

(* Returns true if assignment of a clobbers variable b.  Do not mix
 * SimdQVars and SimdDVar (-> possible undetected aliasing conflict). *)
(*
function should work, but is no longer in use.

let vsimdinstroperand_clobbers a b = match (a,b) with
  | V_SimdQVar(Output,k1),   V_SimdQVar(Input,k2) -> k1=k2
  | V_SimdDVar(p,Output,k1), V_SimdDVar(q,Input,k2) -> k1=k2 && p=q
  | _ -> false
*)

let vsimdinstroperand_is_temporary = function 
  | V_SimdTmp _ -> true 
  | _		-> false

let vsimdinstroperand_is_twiddle = function
  | V_SimdQVar(Twiddle,_)   -> true
  | V_SimdDVar(_,Twiddle,_) -> true
  | _ 			    -> false

(****************************************************************************)

type vsimdbinop =			(* SIMD BINARY OPERATION ************)
  | V_Add1
  | V_Add2
  | V_Sub1
  | V_Sub2
  | V_Mul1
  | V_Mul2
  | V_PPAcc				(*   Intraoperand PosPos Accumulate *)
  | V_NNAcc				(*   Intraoperand NegNeg Accumulate *)
  | V_NPAcc				(*   Intraoperand NegPos Accumulate *)
  | V_PNAcc				(*   Intraoperand PosNeg Accumulate *)
  | V_UnpckLo				(*   Join Lo Parts 		    *)
  | V_UnpckHi				(*   Join Hi Parts		    *)
  | V_Shuffle of int			(*   Shuffle according to Bit-Mask  *)

type vsimdunaryop =			(* SIMD UNARY OPERATION *************)
  | V_Id				(*   Copy Contents		    *)
  | V_Swap				(*   Flip Lo/Hi Part of 2-way Reg   *)
  | V_Chs of vsimdpos 			(*   Negate Part of Simd Register   *)
  | V_MulC1 of number			(*   Scalar Multiplication w/Const  *)
  | V_MulC2 of number * number	        (*   Multiply with 2-way Constant   *)

let eq_vsimdunaryop a b = match (a,b) with
  | (V_Id, V_Id) -> true
  | (V_Swap, V_Swap) -> true
  | (V_Chs p1, V_Chs p2) -> p1=p2
  | (V_MulC1 n, V_MulC1 m) -> eq_number n m
  | (V_MulC2(n,m), V_MulC2(n',m')) -> eq_number n n' && eq_number m m'
  | _ -> false


let vsimdbinopIsParallel = function	(* inter-operand parallel binary-op *)
  | V_Add2 | V_Sub2 | V_Mul2 -> true
  | V_Add1 | V_Sub1 | V_Mul1 -> false
  | V_PPAcc | V_NNAcc | V_NPAcc | V_PNAcc -> false
  | V_UnpckLo | V_UnpckHi | V_Shuffle _ -> false

let vsimdbinopIsCommutative = function
  | V_Add1 | V_Add2 -> true
  | V_Mul1 | V_Mul2 -> true
  | V_Sub1 | V_Sub2 -> false
  | V_PPAcc | V_NNAcc | V_NPAcc | V_PNAcc -> false
  | V_UnpckLo | V_UnpckHi | V_Shuffle _ -> false

let vsimdbinopToCommutativeCounterpart x = match x with
  | V_Add1 | V_Add2 -> x
  | V_Mul1 | V_Mul2 -> x
  | _ -> failwith "vsimdbinopToCommutativeCounterpart: not supported!"

(****************************************************************************)

type vsimdinstr = 			(* VIRTUAL SIMD INSTRUCTION *********)
  | V_SimdLoadQ of 			(*   Load SIMD Quadword:	    *)
	array * 			(*	= source array (s_array)    *)
	int * 				(*      = src element index (s_idx) *)
	vsimdreg			(*	= dst simd register (d_reg) *)
  | V_SimdStoreQ of 			(*   Store SIMD Quadword:	    *)
	vsimdreg *			(*	= src simd register (s_reg) *)
	array * 			(*	= dst array (d_array)	    *)
	int				(*	= dst element index (d_idx) *)
  | V_SimdLoadD of 			(*   Load Real Array Element:	    *)
	vfpaccess *			(*	= access information (s)    *)
	array * 			(*	= source array (s_array)    *)
	int * 				(*      = src element index (s_idx) *)
	vsimdpos * 			(*	= dst simd register pos     *)
	vsimdreg			(*	= dst simd register (d_reg) *)
  | V_SimdStoreD of 			(*   Store Real Array Element:	    *)
	vsimdpos * 			(*	= src simd register pos     *)
	vsimdreg *			(*	= src simd register (s_reg) *)
	vfpaccess *			(*	= access information (d)    *)
	array * 			(*	= dst array (d_array)	    *)
	int  				(*      = dst element index (d_idx) *)
  | V_SimdUnaryOp of 			(*   Do 2-Way Unary Operation:      *)
	vsimdunaryop *			(*	= operation id (unary_op)   *)
	vsimdreg *			(*	= source register (s_reg)   *)
	vsimdreg			(*	= dest register (d_reg)     *)
  | V_SimdBinOp of 			(*   Do 2-Way Binary Operation:     *)
	vsimdbinop *			(*	= operation id (binary_op)  *)
	vsimdreg * 			(*	= source reg #1 (s1_reg)    *)
	vsimdreg *			(*	= source reg #2 (s2_reg)    *)
	vsimdreg			(*	= dest register (d_reg)     *)


(* Output list does not contain any duplicates. *)
let vsimdinstrToSrcregs = function
  | V_SimdLoadD _	    -> []
  | V_SimdLoadQ _	    -> []
  | V_SimdStoreD(_,s,_,_,_) -> [s]
  | V_SimdStoreQ(s,_,_)     -> [s]
  | V_SimdBinOp(_,s1,s2,_)  -> if s1<>s2 then [s1;s2] else [s1]
  | V_SimdUnaryOp(_,s,_)    -> [s]

let vsimdinstrToDstreg = function
  | V_SimdStoreD _ 	    -> None
  | V_SimdStoreQ _          -> None
  | V_SimdLoadD(_,_,_,_,d)  -> Some d
  | V_SimdLoadQ(_,_,d)      -> Some d
  | V_SimdBinOp(_,_,_,d)    -> Some d
  | V_SimdUnaryOp(_,_,d)    -> Some d

let vsimdinstrToReads  instr = vsimdinstrToSrcregs instr
let vsimdinstrToWrites instr = optionToList (vsimdinstrToDstreg instr) 
let vsimdinstrToReadswritespair i = (vsimdinstrToReads i,vsimdinstrToWrites i)

(****************************************************************************)

let vsimdposToSwapped = function
  | V_Lo   -> V_Hi
  | V_Hi   -> V_Lo
  | V_LoHi -> V_LoHi

let vsimdposToSrcoperand = function
  | V_Lo   -> V_SimdNConst "chsl"
  | V_Hi   -> V_SimdNConst "chsh"
  | V_LoHi -> V_SimdNConst "chslh"

let vsimdunaryopToSrcoperands = function
  | V_MulC1 n     -> [V_SimdNumConst1(numberToFloat n)]
  | V_MulC2(n,m)  -> [V_SimdNumConst2(numberToFloat n, numberToFloat m)]
  | V_Chs p	  -> [vsimdposToSrcoperand p]
  | V_Id	  -> []
  | V_Swap	  -> []

let vsimdinstrToSrcoperands = function
  | V_SimdLoadD(reim,arr,el,_,_)      -> [V_SimdDVar(reim,arr,el)]
  | V_SimdLoadQ(arr,el,_) 	      -> [V_SimdQVar(arr,el)]
  | V_SimdStoreD(_,V_SimdReg s,_,_,_) -> [V_SimdTmp s]
  | V_SimdStoreQ(V_SimdReg s,_,_)     -> [V_SimdTmp s]
  | V_SimdUnaryOp(op,V_SimdReg s,_)   -> (V_SimdTmp s)::
					   (vsimdunaryopToSrcoperands op)
  | V_SimdBinOp(_,V_SimdReg s1,V_SimdReg s2,_) -> [V_SimdTmp s1; V_SimdTmp s2]


let vsimdinstrToSrcrawvars = function
  | V_SimdLoadD(access,_,idx,_,_) -> [V_SimdRawDVar(access,idx)]
  | V_SimdLoadQ(_,idx,_)	  -> [V_SimdRawQVar idx]
  | V_SimdStoreD _		  -> []
  | V_SimdStoreQ _		  -> []
  | V_SimdUnaryOp _		  -> []
  | V_SimdBinOp _		  -> []

let vsimdinstrToDstoperands = function
  | V_SimdLoadD(_,_,_,_,V_SimdReg d) -> [V_SimdTmp d]
  | V_SimdLoadQ(_,_,V_SimdReg d)     -> [V_SimdTmp d]
  | V_SimdStoreD(_,_,access,arr,el)  -> [V_SimdDVar(access,arr,el)]
  | V_SimdStoreQ(_,arr,el) 	     -> [V_SimdQVar(arr,el)]
  | V_SimdBinOp(_,_,_,V_SimdReg d)   -> [V_SimdTmp d]
  | V_SimdUnaryOp(_,_,V_SimdReg d)   -> [V_SimdTmp d]

let vsimdinstrIsLoad = function
  | V_SimdLoadD _    -> true
  | V_SimdLoadQ _    -> true
  | V_SimdStoreD _   -> false
  | V_SimdStoreQ _   -> false
  | V_SimdBinOp _    -> false
  | V_SimdUnaryOp _  -> false

let vsimdinstrIsStore = function
  | V_SimdLoadD _    -> false
  | V_SimdLoadQ _    -> false
  | V_SimdStoreD _   -> true
  | V_SimdStoreQ _   -> true
  | V_SimdBinOp _    -> false
  | V_SimdUnaryOp _  -> false
