(*
 * Copyright (c) 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 includes definitions of data types and functions that are used
 * within the instruction scheduler. *)

open List
open Util
open P4Basics

type p4rflag = 				(* P4 FLAGS *************************)
  | P4RFlag_Zero			(*   Zero Flag			    *)

type p4rop =				(* P4 REAL OPERAND ******************)
  | P4ROp_MMXReg of p4rmmxreg		(*   MMX Register (SIMD)	    *)
  | P4ROp_IntReg of p4rintreg		(*   Integer Register		    *)
  | P4ROp_MemOp of p4memop		(*   Memory Operand		    *)
  | P4ROp_IP				(*   Instruction Pointer	    *)
  | P4ROp_Flag of p4rflag		(*   Flag (eg. zero)		    *)
  | P4ROp_MMXState			(*   Floating-Point Mode (x87/MMX)  *)
  | P4ROp_MemoryState			(*   for preserving Ld/St order     *)
  | P4ROp_Stack				(*   stack operations (push/pop)    *)

let zeroflag = P4ROp_Flag (P4RFlag_Zero)
let mmxstackcell i = P4ROp_MemOp(P4_MStackCell(P4_MMXStack, i))

module ResMap = Map.Make(struct type t = p4rop let compare = compare end)

let resmap_findE k m = try ResMap.find k m with Not_found -> []
let resmap_addE k v m = ResMap.add k (v::resmap_findE k m) m
let resmap_addE' value key = resmap_addE key value

let resmap_find k m = try Some (ResMap.find k m) with Not_found -> None


(* READING OF P4ROPS ********************************************************)

let raddrToRops = function
  | P4R_RID(base,_)           -> [P4ROp_IntReg base]
  | P4R_SID(index,_,_)        -> [P4ROp_IntReg index]
  | P4R_RISID(base,index,_,_) -> [P4ROp_IntReg base; P4ROp_IntReg index]

let intuopToSrcrops sd = function
  | P4_IPush  	    -> [P4ROp_IntReg p4rintreg_stackpointer; sd]
  | P4_IPop  	    -> [P4ROp_IntReg p4rintreg_stackpointer]
  | P4_IClear 	    -> []
  | P4_ILoadValue _ -> []
  | P4_INegate 	    -> [sd]
  | P4_IInc 	    -> [sd]
  | P4_IDec 	    -> [sd]
  | P4_IAddImm _    -> [sd]
  | P4_ISubImm _    -> [sd]
  | P4_IShlImm _    -> [sd]
  | P4_IShrImm _    -> [sd]

let intcpyuopToSrcrops = function
  | P4_ICopy	 -> []
  | P4_IMulImm _ -> []

let branchconditionToSrcrops = function
  | P4_BCond_NotZero	 -> [zeroflag]
  | P4_BCond_Zero	 -> [zeroflag]
  | P4_BCond_GreaterZero -> [zeroflag]
  | P4_BCond_EqualZero	 -> [zeroflag]

let simduopToSrcrops sd = function
  | P4_FPChs _ 	 -> [sd]
  | P4_FPMulC1 _ -> [sd]
  | P4_FPMulC2 _ -> [sd]

let simdcpyuopToSrcrops = function
  | P4_FPId -> []

let p4memopToSrcrops = function
  | P4_MConst _     -> []
  | P4_MVar _       -> []
  | P4_MFunArg _    -> [P4ROp_IntReg p4rintreg_stackpointer]
  | P4_MStackCell _ -> [P4ROp_IntReg p4rintreg_stackpointer]

let rinstrToSrcrops' = function
  | P4R_IntLoadMem(s,_)		  -> p4memopToSrcrops s @ [P4ROp_MemOp s]
  | P4R_IntStoreMem(s,d)	  -> p4memopToSrcrops d @ [P4ROp_IntReg s] 
  | P4R_IntLoadEA(s,_)		  -> raddrToRops s
  | P4R_IntUnaryOp(op,sd)	  -> intuopToSrcrops (P4ROp_IntReg sd) op
  | P4R_IntUnaryOpMem(op,sd)	  -> p4memopToSrcrops sd @ 
				       intuopToSrcrops (P4ROp_MemOp sd) op
  | P4R_IntCpyUnaryOp(op,s,_)	  -> (P4ROp_IntReg s)::(intcpyuopToSrcrops op)
  | P4R_IntBinOp(_,s,sd)	  -> [P4ROp_IntReg s; P4ROp_IntReg sd]
  | P4R_IntBinOpMem(_,s,sd)	  -> p4memopToSrcrops s @ 
				       [P4ROp_MemOp s; P4ROp_IntReg sd]
  | P4R_CondBranch(cond,_)	  -> branchconditionToSrcrops cond
  | P4R_SimdLoad(_,s,_) 	  -> P4ROp_MemoryState::(raddrToRops s)
  | P4R_SimdLoad1(s,_,_) 	  -> P4ROp_MemoryState::(raddrToRops s)
  | P4R_SimdStore(s,_,d) 	  -> P4ROp_MemoryState::
					(P4ROp_MMXReg s)::(raddrToRops d)
  | P4R_SimdStore1(_,s,d) 	  -> P4ROp_MemoryState::
					(P4ROp_MMXReg s)::(raddrToRops d)

  | P4R_SimdSpill(s,_) 		  -> [P4ROp_MMXReg s; 
				      P4ROp_IntReg p4rintreg_stackpointer]

  | P4R_SimdCpyUnaryOpMem(op,s,_) -> p4memopToSrcrops s @ 
				       ((P4ROp_MemOp s)::(simdcpyuopToSrcrops op))
  | P4R_SimdUnaryOp(op,sd) 	  -> simduopToSrcrops (P4ROp_MMXReg sd) op
  | P4R_SimdCpyUnaryOp(op,s,_)    -> (P4ROp_MMXReg s)::(simdcpyuopToSrcrops op)

  | P4R_SimdBinOpMem(_,s,sd)	  -> p4memopToSrcrops s @ 
				       [P4ROp_MemOp s; P4ROp_MMXReg sd]
  | P4R_SimdBinOp(_,s,sd) 	  -> [P4ROp_MMXReg s; P4ROp_MMXReg sd]
  | P4R_Label _			  -> []
  | P4R_Jump _			  -> []
  | P4R_Ret			  -> []
  | P4R_SimdLoadStoreBarrier	  -> [P4ROp_MemoryState]
  | P4R_SimdPromiseCellSize _     -> []

(* WRITING OF P4ROPS ********************************************************)

let intuopToDstrops d = function
  | P4_IPush  	    -> [(P4ROp_IntReg p4rintreg_stackpointer,1)]
  | P4_IPop  	    -> [(P4ROp_IntReg p4rintreg_stackpointer,1); (d,1)]
  | P4_IClear 	    -> [(d,1); (zeroflag, 1)]
  | P4_IInc 	    -> [(d,1); (zeroflag, 1)]
  | P4_IDec 	    -> [(d,1); (zeroflag, 1)]
  | P4_IAddImm _    -> [(d,1); (zeroflag, 1)]
  | P4_ISubImm _    -> [(d,1); (zeroflag, 1)]
  | P4_IShlImm _    -> [(d,1); (zeroflag, 3)]
  | P4_IShrImm _    -> [(d,1); (zeroflag, 3)]
  | P4_INegate 	    -> [(d,1); (zeroflag, 1)]
  | P4_ILoadValue _ -> [(d,1)]

let intcpyuopToDstrops d = function
  | P4_ICopy	 -> [(d,1)]
  | P4_IMulImm _ -> [(d,7)]

let simduopToLatency = function
  | P4_FPChs _   -> 3
  | P4_FPMulC1 _ -> 3
  | P4_FPMulC2 _ -> 3

let simdbopToLatency = function
  | P4_FPAdd1  -> 2
  | P4_FPAdd2  -> 2
  | P4_FPSub1  -> 2
  | P4_FPSub2  -> 2
  | P4_FPMul1  -> 3
  | P4_FPMul2  -> 3
  | P4_UnpckLo -> 2
  | P4_UnpckHi -> 3
  | P4_Shuffle _ -> 3


(* EXPORTED FUNCTIONS *******************************************************)

let p4rinstrToSrcp4rops instr =
  let reads = rinstrToSrcrops' instr in
    P4ROp_IP::(if p4rinstrIsMMX instr then P4ROp_MMXState::reads else reads)

let p4rinstrToDstp4rops = function
  | P4R_Ret			  -> [(P4ROp_IP, 4)]
  | P4R_Label _			  -> [(P4ROp_IP, 2); (P4ROp_MemoryState, 0)]
  | P4R_Jump d			  -> [(P4ROp_IP, 4)]
  | P4R_IntLoadMem(_,d)		  -> [(P4ROp_IntReg d, 4)]
  | P4R_IntStoreMem(_,d)	  -> [(P4ROp_MemOp d, 4)]
  | P4R_IntLoadEA(_,d)		  -> [(P4ROp_IntReg d, 4)]
  | P4R_IntUnaryOp(op,sd)	  -> intuopToDstrops (P4ROp_IntReg sd) op
  | P4R_IntUnaryOpMem(op,sd)	  -> intuopToDstrops (P4ROp_MemOp sd) op
  | P4R_IntCpyUnaryOp(op,_,d)	  -> intcpyuopToDstrops (P4ROp_IntReg d) op
  | P4R_IntBinOp(_,_,sd)	  -> [(P4ROp_IntReg sd, 1); (zeroflag, 1)]
  | P4R_IntBinOpMem(_,_,sd)	  -> [(P4ROp_IntReg sd, 4); (zeroflag, 1)]
  | P4R_CondBranch(_,d)		  -> [(P4ROp_IP, 4)]
  | P4R_SimdLoad(_,_,d)	  	  -> [(P4ROp_MMXReg d, 6)]
  | P4R_SimdLoad1(_,_,d)	  -> [(P4ROp_MMXReg d, 6)]
  | P4R_SimdStore _		  -> []
  | P4R_SimdStore1 _		  -> []
  | P4R_SimdSpill(_,d)		  -> [(mmxstackcell d, 4)]
  | P4R_SimdUnaryOp(op,sd) 	  -> [(P4ROp_MMXReg sd, simduopToLatency op)]
  | P4R_SimdCpyUnaryOp(op,_,d)    -> [(P4ROp_MMXReg d, 2)]
  | P4R_SimdCpyUnaryOpMem(op,_,d) -> [(P4ROp_MMXReg d, 4)]
  | P4R_SimdBinOp(op,_,sd)	  -> [(P4ROp_MMXReg sd, simdbopToLatency op)]
  | P4R_SimdBinOpMem(op,_,sd)	  -> [(P4ROp_MMXReg sd, simdbopToLatency op+3)]
  | P4R_SimdLoadStoreBarrier	  -> [(P4ROp_MemoryState, 0)]
  | P4R_SimdPromiseCellSize _     -> [(P4ROp_IP, 0)]

let p4rinstrToMaxlatency instr = max_list (map snd (p4rinstrToDstp4rops instr))


(* returns true, if instruction x cannot roll over instruction y. *)
let p4rinstrCannotRollOverP4rinstr x y =
  let (xR, xW) = (p4rinstrToSrcp4rops x, map fst (p4rinstrToDstp4rops x))
  and (yR, yW) = (p4rinstrToSrcp4rops y, map fst (p4rinstrToDstp4rops y)) in
    (* RAW *) lists_overlap xW yR ||
    (* WAW *) lists_overlap xW yW ||
    (* WAR *) lists_overlap xR yW

