-- | -- Module: UeMap -- Description: Sharing for UEs, based on IntMaps. -- Copyright: (c) 2013 Tom Hawkins & Lee Pike -- -- Sharing for 'UE's, based on IntMaps. The idea is to share subexpressions -- of 'UE's. module Language.Atom.UeMap ( UeElem (..) , MUV (..) , UeMap , emptyMap , Hash , typeOf , UeState , recoverUE , getUE , newUE , newUV , maybeUpdate , ueUpstream , nearestUVs , arrayIndices , isMathHCall ) where import Control.Monad.State.Strict import qualified Data.Bimap as M import Data.List (nub) import Language.Atom.Expressions hiding (typeOf) import qualified Language.Atom.Expressions as E type Hash = Int -- | Untyped variables map. data MUV = MUV Int String Const | MUVArray UA Hash | MUVExtern String Type deriving (Show, Eq, Ord) -- | Transforms a 'UV' into a 'MUV', returning the possibly updated map. newUV :: UV -> UeMap -> (MUV, UeMap) newUV u mp = case u of UV i j k -> (MUV i j k, mp) UVExtern i j -> (MUVExtern i j, mp) UVArray arr ue_ -> let (h,mp') = newUE ue_ mp in (MUVArray arr h, mp') -- | Corresponds to 'UE's --- the elements in the sharing structure. data UeElem = MUVRef !MUV | MUConst !Const | MUCast !Type !Hash | MUAdd !Hash !Hash | MUSub !Hash !Hash | MUMul !Hash !Hash | MUDiv !Hash !Hash | MUMod !Hash !Hash | MUNot !Hash | MUAnd [Hash] | MUBWNot !Hash | MUBWAnd !Hash !Hash | MUBWOr !Hash !Hash | MUBWXor !Hash !Hash | MUBWShiftL !Hash !Hash | MUBWShiftR !Hash !Hash | MUEq !Hash !Hash | MULt !Hash !Hash | MUMux !Hash !Hash !Hash | MUF2B !Hash | MUD2B !Hash | MUB2F !Hash | MUB2D !Hash -- math.h: | MUPi | MUExp !Hash | MULog !Hash | MUSqrt !Hash | MUPow !Hash !Hash | MUSin !Hash | MUAsin !Hash | MUCos !Hash | MUAcos !Hash | MUSinh !Hash | MUCosh !Hash | MUAsinh !Hash | MUAcosh !Hash | MUAtan !Hash | MUAtanh !Hash deriving (Show, Eq, Ord) typeOf :: Hash -> UeMap -> Type typeOf h mp = case getUE h mp of MUVRef (MUV _ _ a) -> E.typeOf a MUVRef (MUVArray a _) -> E.typeOf a MUVRef (MUVExtern _ t) -> t MUCast t _ -> t MUConst c -> E.typeOf c MUAdd a _ -> typeOf' a MUSub a _ -> typeOf' a MUMul a _ -> typeOf' a MUDiv a _ -> typeOf' a MUMod a _ -> typeOf' a MUNot _ -> Bool MUAnd _ -> Bool MUBWNot a -> typeOf' a MUBWAnd a _ -> typeOf' a MUBWOr a _ -> typeOf' a MUBWXor a _ -> typeOf' a MUBWShiftL a _ -> typeOf' a MUBWShiftR a _ -> typeOf' a MUEq _ _ -> Bool MULt _ _ -> Bool MUMux _ a _ -> typeOf' a MUF2B _ -> Word32 MUD2B _ -> Word64 MUB2F _ -> Float MUB2D _ -> Double -- math.h: MUPi -> Double MUExp a -> typeOf' a MULog a -> typeOf' a MUSqrt a -> typeOf' a MUPow a _ -> typeOf' a MUSin a -> typeOf' a MUAsin a -> typeOf' a MUCos a -> typeOf' a MUAcos a -> typeOf' a MUSinh a -> typeOf' a MUCosh a -> typeOf' a MUAsinh a -> typeOf' a MUAcosh a -> typeOf' a MUAtan a -> typeOf' a MUAtanh a -> typeOf' a where typeOf' h' = typeOf h' mp -- | An entry in the Map. type UeMap = (Hash, M.Bimap Int UeElem) -- | Wrapped in the State Monad. type UeState a = State UeMap a -- | Get the element associated with a 'Hash' value. It's an error if the -- element is not in the map. getUE :: Hash -> UeMap -> UeElem getUE h (_,mp) = case M.lookup h mp of Nothing -> error $ "Error looking up hash " ++ show h ++ " in the UE map\n" ++ show mp Just e -> e -- | Put a new 'UE' in the map, unless it's already in there, and return the -- hash pointing to the 'UE' and a new map. newUE :: UE -> UeMap -> (Hash, UeMap) newUE ue_ mp = runState (share ue_) mp emptyMap :: UeMap emptyMap = (0, M.empty) -- | Create the sharing map. share :: UE -> UeState Hash share e = case e of UVRef (UV i j k) -> maybeUpdate (MUVRef $ MUV i j k) UVRef (UVExtern i j) -> maybeUpdate (MUVRef $ MUVExtern i j) UVRef (UVArray arr a) -> unOp a (\x -> MUVRef (MUVArray arr x)) UConst a -> maybeUpdate (MUConst a) UCast t a -> unOp a (MUCast t) UAdd a b -> binOp (a,b) MUAdd USub a b -> binOp (a,b) MUSub UMul a b -> binOp (a,b) MUMul UDiv a b -> binOp (a,b) MUDiv UMod a b -> binOp (a,b) MUMod UNot a -> unOp a MUNot UAnd ls -> listOp ls MUAnd UBWNot a -> unOp a MUBWNot UBWAnd a b -> binOp (a,b) MUBWAnd UBWOr a b -> binOp (a,b) MUBWOr UBWXor a b -> binOp (a,b) MUBWXor UBWShiftL a b -> binOp (a,b) MUBWShiftL UBWShiftR a b -> binOp (a,b) MUBWShiftR UEq a b -> binOp (a,b) MUEq ULt a b -> binOp (a,b) MULt UMux a b c -> triOp (a,b,c) MUMux UF2B a -> unOp a MUF2B UD2B a -> unOp a MUD2B UB2F a -> unOp a MUB2F UB2D a -> unOp a MUB2D -- math.h: UPi -> maybeUpdate (MUPi) UExp a -> unOp a MUExp ULog a -> unOp a MULog USqrt a -> unOp a MUSqrt UPow a b -> binOp (a,b) MUPow USin a -> unOp a MUSin UAsin a -> unOp a MUAsin UCos a -> unOp a MUCos UAcos a -> unOp a MUAcos USinh a -> unOp a MUSinh UCosh a -> unOp a MUCosh UAsinh a -> unOp a MUAsinh UAcosh a -> unOp a MUAcosh UAtan a -> unOp a MUAtan UAtanh a -> unOp a MUAtanh -- XXX I could combine some of the following functions (unOp, binOp, etc.) to -- slightly reduce code... unOp :: UE -> (Hash -> UeElem) -> UeState Hash unOp e code = do h <- share e maybeUpdate (code h) binOp :: (UE, UE) -> (Hash -> Hash -> UeElem) -> UeState Hash binOp (e0,e1) code = do h0 <- share e0 h1 <- share e1 maybeUpdate (code h0 h1) triOp :: (UE, UE, UE) -> (Hash -> Hash -> Hash -> UeElem) -> UeState Hash triOp (e0,e1,e2) code = do h0 <- share e0 h1 <- share e1 h2 <- share e2 maybeUpdate (code h0 h1 h2) listOp :: [UE] -> ([Hash] -> UeElem) -> UeState Hash listOp es code = do hashes <- foldM (\hashes e -> do h <- share e return (h:hashes) ) [] es maybeUpdate (code hashes) -- | Lookup an element in the map, and if it's in there, do nothing, but return -- its hash value. Otherwise, update the map and return the new hash value -- for the inserted element. maybeUpdate :: UeElem -> UeState Hash maybeUpdate e = do st <- get let mp = snd st case M.lookupR e mp of Nothing -> do let hash = fst st + 1 put (hash, M.insert hash e mp) return hash Just h -> return h -- -- Lookup an elem, returning 'Nothing' if no hash exists in the map and 'Just' -- -- the hash value otherwise. -- getHash :: UeElem -> UeMap -> Maybe Hash -- getHash e mp = M.lookupR e -- ((k,e'):_) | e == e' = Just k -- getHash e (_:es) | otherwise = getHash e es -- getHash _ [] = Nothing -- | Get a 'UE' back out of the 'UeMap'. recoverUE :: UeMap -> Hash -> UE recoverUE st h = case getUE h st of MUVRef (MUV i j k) -> UVRef (UV i j k) MUVRef (MUVArray i a) -> UVRef (UVArray i (recover' a)) MUVRef (MUVExtern i j) -> UVRef (UVExtern i j) MUCast t a -> UCast t (recover' a) MUConst a -> UConst a MUAdd a b -> UAdd (recover' a) (recover' b) MUSub a b -> USub (recover' a) (recover' b) MUMul a b -> UMul (recover' a) (recover' b) MUDiv a b -> UDiv (recover' a) (recover' b) MUMod a b -> UMod (recover' a) (recover' b) MUNot a -> UNot (recover' a) MUAnd a -> UAnd $ map recover' a MUBWNot a -> UBWNot (recover' a) MUBWAnd a b -> UBWAnd (recover' a) (recover' b) MUBWOr a b -> UBWOr (recover' a) (recover' b) MUBWXor a b -> UBWXor (recover' a) (recover' b) MUBWShiftL a b -> UBWShiftL (recover' a) (recover' b) MUBWShiftR a b -> UBWShiftR (recover' a) (recover' b) MUEq a b -> UEq (recover' a) (recover' b) MULt a b -> ULt (recover' a) (recover' b) MUMux a b c -> UMux (recover' a) (recover' b) (recover' c) MUF2B a -> UF2B (recover' a) MUD2B a -> UD2B (recover' a) MUB2F a -> UB2F (recover' a) MUB2D a -> UB2D (recover' a) -- math.h: MUPi -> UPi MUExp a -> UExp (recover' a) MULog a -> ULog (recover' a) MUSqrt a -> USqrt (recover' a) MUPow a b -> UPow (recover' a) (recover' b) MUSin a -> USin (recover' a) MUAsin a -> UAsin (recover' a) MUCos a -> UCos (recover' a) MUAcos a -> UAcos (recover' a) MUSinh a -> USinh (recover' a) MUCosh a -> UCosh (recover' a) MUAsinh a -> UAsinh (recover' a) MUAcosh a -> UAcosh (recover' a) MUAtan a -> UAtan (recover' a) MUAtanh a -> UAtanh (recover' a) where recover' h' = recoverUE st h' -- | The list of Hashes to adjacent upstream of a UE. ueUpstream :: Hash -> UeMap -> [Hash] ueUpstream h t = case getUE h t of MUVRef (MUV _ _ _) -> [] MUVRef (MUVArray _ a) -> [a] MUVRef (MUVExtern _ _) -> [] MUCast _ a -> [a] MUConst _ -> [] MUAdd a b -> [a, b] MUSub a b -> [a, b] MUMul a b -> [a, b] MUDiv a b -> [a, b] MUMod a b -> [a, b] MUNot a -> [a] MUAnd a -> a MUBWNot a -> [a] MUBWAnd a b -> [a, b] MUBWOr a b -> [a, b] MUBWXor a b -> [a, b] MUBWShiftL a b -> [a, b] MUBWShiftR a b -> [a, b] MUEq a b -> [a, b] MULt a b -> [a, b] MUMux a b c -> [a, b, c] MUF2B a -> [a] MUD2B a -> [a] MUB2F a -> [a] MUB2D a -> [a] -- math.h: MUPi -> [] MUExp a -> [a] MULog a -> [a] MUSqrt a -> [a] MUPow a b -> [a, b] MUSin a -> [a] MUAsin a -> [a] MUCos a -> [a] MUAcos a -> [a] MUSinh a -> [a] MUCosh a -> [a] MUAsinh a -> [a] MUAcosh a -> [a] MUAtan a -> [a] MUAtanh a -> [a] -- | The list of all UVs that directly control the value of an expression. nearestUVs :: Hash -> UeMap -> [MUV] nearestUVs h mp = nub $ f h where f :: Hash -> [MUV] f hash = case getUE hash mp of (MUVRef u@(MUVArray _ h')) -> [u] ++ f h' (MUVRef u) -> [u] _ -> concatMap f $ ueUpstream hash mp -- | All array indexing subexpressions. arrayIndices :: Hash -> UeMap -> [(UA, Hash)] arrayIndices h mp = nub $ f h where f :: Hash -> [(UA, Hash)] f hash = case getUE hash mp of (MUVRef (MUVArray ua h')) -> (ua, h') : f h' _ -> concatMap f $ ueUpstream hash mp -- XXX can put this back after making UE map---won't be expensive. isMathHCall :: UeElem -> Bool isMathHCall fc = case fc of MUPi -> True MUExp _ -> True MULog _ -> True MUSqrt _ -> True MUPow _ _ -> True MUSin _ -> True MUAsin _ -> True MUCos _ -> True MUAcos _ -> True MUSinh _ -> True MUCosh _ -> True MUAsinh _ -> True MUAcosh _ -> True MUAtan _ -> True MUAtanh _ -> True _ -> False