%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
%************************************************************************
%*									*
\section[SATMonad]{The Static Argument Transformation pass Monad}
%*									*
%************************************************************************

96/03: We aren't using the static-argument transformation right now.

\begin{code}
module SATMonad where

#include "HsVersions.h"

import Panic		( panic )

junk_from_SATMonad = panic "SATMonad.junk"

{- LATER: to end of file:

module SATMonad (
	SATInfo(..), updSAEnv,
	SatM(..), initSAT, emptyEnvSAT,
	returnSAT, thenSAT, thenSAT_, mapSAT, getSATInfo, newSATName,
	getArgLists, Arg(..), insSAEnv, saTransform,

	SATEnv(..), isStatic, dropStatics
    ) where

import Type		( mkTyVarTy, mkSigmaTy,
			  splitSigmaTy, splitFunTys,
			  glueTyArgs, substTy,
			  InstTyEnv(..)
			)
import MkId		( mkSysLocal )
import Id		( idType, idName, mkLocalId )
import UniqSupply
import Util

infixr 9 `thenSAT`, `thenSAT_`
\end{code}

%************************************************************************
%*									*
\subsection{Static Argument Transformation Environment}
%*									*
%************************************************************************

\begin{code}
type SATEnv = IdEnv SATInfo

type SATInfo = ([Arg Type],[Arg Id])

data Arg a = Static a | NotStatic
    deriving Eq

delOneFromSAEnv v us env
  = ((), delVarEnv env v)

updSAEnv :: Maybe (Id,SATInfo) -> SatM ()
updSAEnv Nothing
  = returnSAT ()
updSAEnv (Just (b,(tyargs,args)))
  = getSATInfo b      `thenSAT` (\ r ->
    case r of
      Nothing		   -> returnSAT ()
      Just (tyargs',args') -> delOneFromSAEnv b `thenSAT_`
			      insSAEnv b (checkArgs tyargs tyargs',
					  checkArgs args args')
    )

checkArgs as [] = notStatics (length as)
checkArgs [] as = notStatics (length as)
checkArgs (a:as) (a':as') | a == a' = a:checkArgs as as'
checkArgs (_:as) (_:as') = NotStatic:checkArgs as as'

notStatics :: Int -> [Arg a]
notStatics n = nOfThem n NotStatic

insSAEnv :: Id -> SATInfo -> SatM ()
insSAEnv b info us env
  = ((), extendVarEnv env b info)
\end{code}

%************************************************************************
%*									*
\subsection{Static Argument Transformation Monad}
%*									*
%************************************************************************

Two items of state to thread around: a UniqueSupply and a SATEnv.

\begin{code}
type SatM result
  =  UniqSupply -> SATEnv -> (result, SATEnv)

initSAT :: SatM a -> UniqSupply -> a

initSAT f us = fst (f us emptyVarEnv)

thenSAT m k us env
  = case splitUniqSupply us	of { (s1, s2) ->
    case m s1 env   	    	of { (m_result, menv) ->
    k m_result s2 menv }}

thenSAT_ m k us env
  = case splitUniqSupply us	of { (s1, s2) ->
    case m s1 env   	    	of { (_, menv) ->
    k s2 menv }}

emptyEnvSAT :: SatM ()
emptyEnvSAT us _ = ((), emptyVarEnv)

returnSAT v us env = (v, env)

mapSAT f [] 	= returnSAT []
mapSAT f (x:xs)
  = f x		`thenSAT` \ x'	->
    mapSAT f xs	`thenSAT` \ xs' ->
    returnSAT (x':xs')
\end{code}

%************************************************************************
%*									*
\subsection{Utility Functions}
%*									*
%************************************************************************

\begin{code}
getSATInfo :: Id -> SatM (Maybe SATInfo)
getSATInfo var us env
  = (lookupVarEnv env var, env)

newSATName :: Id -> Type -> SatM Id
newSATName id ty us env
  = case (getUnique us) of { unique ->
    let
	new_name = mkCompoundName SLIT("$sat") unique (idName id)
    in
    (mkLocalId new_name ty, env) }

getArgLists :: CoreExpr -> ([Arg Type],[Arg Id])
getArgLists expr
  = let
	(tvs, lambda_bounds, body) = collectBinders expr
    in
    ([ Static (mkTyVarTy tv) | tv <- tvs ],
     [ Static v		     | v <- lambda_bounds ])

dropArgs :: CoreExpr -> CoreExpr
dropArgs (Lam   _ e)	= dropArgs e
dropArgs (CoTyLam _ e)	= dropArgs e
dropArgs e		= e
\end{code}

We implement saTransform using shadowing of binders, that is
we transform
map = \f as -> case as of
		 [] -> []
		 (a':as') -> let x = f a'
				 y = map f as'
			     in x:y
to
map = \f as -> let map = \f as -> map' as
	       in let rec map' = \as -> case as of
					  [] -> []
					  (a':as') -> let x = f a'
							  y = map f as'
						      in x:y
		  in map' as

the inner map should get inlined and eliminated.
\begin{code}
saTransform :: Id -> CoreExpr -> SatM CoreBinding
saTransform binder rhs
  = getSATInfo binder `thenSAT` \ r ->
    case r of
      -- [Andre] test: do it only if we have more than one static argument.
      --Just (tyargs,args) | any isStatic args
      Just (tyargs,args) | (filter isStatic args) `lengthExceeds` 1
	-> newSATName binder (new_ty tyargs args)  `thenSAT` \ binder' ->
	   mkNewRhs binder binder' tyargs args rhs `thenSAT` \ new_rhs ->
	   trace ("SAT "++ show (length (filter isStatic args))) (
	   returnSAT (NonRec binder new_rhs)
	   )
      _ -> returnSAT (Rec [(binder, rhs)])
  where
    mkNewRhs binder binder' tyargs args rhs
      = let
	    non_static_args :: [Id]
	    non_static_args
	       = get_nsa args (snd (getArgLists rhs))
	       where
		 get_nsa :: [Arg a] -> [Arg a] -> [a]
		 get_nsa [] _ = []
		 get_nsa _ [] = []
		 get_nsa (NotStatic:args) (Static v:as) = v:get_nsa args as
		 get_nsa (_:args)	  (_:as)	=   get_nsa args as

	    local_body = foldl App (Var binder')
				[VarArg a | a <- non_static_args]

	    nonrec_rhs = origLams local_body

	    -- HACK! The following is a fake SysLocal binder with
	    -- *the same* unique as binder.
	    -- the reason for this is the following:
	    -- this binder *will* get inlined but if it happen to be
	    -- a top level binder it is never removed as dead code,
	    -- therefore we have to remove that information (of it being
	    -- top-level or exported somehow.)
	    -- A better fix is to use binder directly but with the TopLevel
	    -- tag (or Exported tag) modified.
	    fake_binder = mkSysLocal SLIT("sat")
			    (getUnique binder)
			    (idType binder)
	    rec_body = mkValLam non_static_args
			       ( Let (NonRec fake_binder nonrec_rhs)
				 {-in-} (dropArgs rhs))
	in
	returnSAT (
	    origLams (Let (Rec [(binder',rec_body)]) {-in-} local_body)
	)
      where
	origLams = origLams' rhs
		 where
		   origLams' (Lam v e)     e' = Lam   v  (origLams' e e')
		   origLams' (CoTyLam ty e)  e' = CoTyLam ty (origLams' e e')
		   origLams' _		     e' = e'

    new_ty tyargs args
      = substTy (mk_inst_tyenv tyargs tv_tmpl)
		      (mkSigmaTy tv_tmpl' dict_tys' tau_ty')
      where
	-- get type info for the local function:
	(tv_tmpl, dict_tys, tau_ty) = (splitSigmaTy . idType) binder
	(reg_arg_tys, res_type)	    = splitFunTys tau_ty

	-- now, we drop the ones that are
	-- static, that is, the ones we will not pass to the local function
	tv_tmpl'     = dropStatics tyargs tv_tmpl

	(args1, args2) = splitAtList dict_tys args
	dict_tys'    = dropStatics args1 dict_tys
	reg_arg_tys' = dropStatics args2 reg_arg_tys

	tau_ty'	     = glueTyArgs reg_arg_tys' res_type

	mk_inst_tyenv []		    _ = emptyVarEnv
	mk_inst_tyenv (Static s:args) (t:ts)  = extendVarEnv (mk_inst_tyenv args ts) t s
	mk_inst_tyenv (_:args)	    (_:ts)    = mk_inst_tyenv args ts

dropStatics [] t = t
dropStatics (Static _:args) (t:ts) = dropStatics args ts
dropStatics (_:args)	    (t:ts) = t:dropStatics args ts

isStatic :: Arg a -> Bool
isStatic NotStatic = False
isStatic _	   = True
-}
\end{code}
