futhark-0.19.2: src/Futhark/IR/Prop/Names.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Facilities for determining which names are used in some syntactic
-- construct. The most important interface is the 'FreeIn' class and
-- its instances, but for reasons related to the Haskell type system,
-- some constructs have specialised functions.
module Futhark.IR.Prop.Names
( -- * Free names
Names,
namesIntMap,
nameIn,
oneName,
namesFromList,
namesToList,
namesIntersection,
namesIntersect,
namesSubtract,
mapNames,
-- * Class
FreeIn (..),
freeIn,
-- * Specialised Functions
freeInStmsAndRes,
-- * Bound Names
boundInBody,
boundByStm,
boundByStms,
boundByLambda,
-- * Efficient computation
FreeDec (..),
FV,
fvBind,
fvName,
fvNames,
)
where
import Control.Category
import Control.Monad.State.Strict
import Data.Foldable
import qualified Data.IntMap.Strict as IM
import qualified Data.Map.Strict as M
import Futhark.IR.Prop.Patterns
import Futhark.IR.Prop.Scope
import Futhark.IR.Syntax
import Futhark.IR.Traversals
import Futhark.Util.Pretty
import Prelude hiding (id, (.))
-- | A set of names. Note that the 'Ord' instance is a dummy that
-- treats everything as 'EQ' if '==', and otherwise 'LT'.
newtype Names = Names (IM.IntMap VName)
deriving (Eq, Show)
-- | Retrieve the data structure underlying the names representation.
namesIntMap :: Names -> IM.IntMap VName
namesIntMap (Names m) = m
instance Ord Names where
x `compare` y = if x == y then EQ else LT
instance Semigroup Names where
vs1 <> vs2 = Names $ namesIntMap vs1 <> namesIntMap vs2
instance Monoid Names where
mempty = Names mempty
instance Pretty Names where
ppr = ppr . namesToList
-- | Does the set of names contain this name?
nameIn :: VName -> Names -> Bool
nameIn v (Names vs) = baseTag v `IM.member` vs
-- | Construct a name set from a list. Slow.
namesFromList :: [VName] -> Names
namesFromList vs = Names $ IM.fromList $ zip (map baseTag vs) vs
-- | Turn a name set into a list of names. Slow.
namesToList :: Names -> [VName]
namesToList = IM.elems . namesIntMap
-- | Construct a name set from a single name.
oneName :: VName -> Names
oneName v = Names $ IM.singleton (baseTag v) v
-- | The intersection of two name sets.
namesIntersection :: Names -> Names -> Names
namesIntersection (Names vs1) (Names vs2) = Names $ IM.intersection vs1 vs2
-- | Do the two name sets intersect?
namesIntersect :: Names -> Names -> Bool
namesIntersect vs1 vs2 = not $ IM.disjoint (namesIntMap vs1) (namesIntMap vs2)
-- | Subtract the latter name set from the former.
namesSubtract :: Names -> Names -> Names
namesSubtract (Names vs1) (Names vs2) = Names $ IM.difference vs1 vs2
-- | Map over the names in a set.
mapNames :: (VName -> VName) -> Names -> Names
mapNames f vs = namesFromList $ map f $ namesToList vs
-- | A computation to build a free variable set.
newtype FV = FV {unFV :: Names}
-- Right now the variable set is just stored explicitly, without the
-- fancy functional representation that GHC uses. Turns out it's
-- faster this way.
instance Monoid FV where
mempty = FV mempty
instance Semigroup FV where
FV fv1 <> FV fv2 = FV $ fv1 <> fv2
-- | Consider a variable to be bound in the given 'FV' computation.
fvBind :: Names -> FV -> FV
fvBind vs (FV fv) = FV $ fv `namesSubtract` vs
-- | Take note of a variable reference.
fvName :: VName -> FV
fvName v = FV $ oneName v
-- | Take note of a set of variable references.
fvNames :: Names -> FV
fvNames = FV
freeWalker ::
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (Op lore)
) =>
Walker lore (State FV)
freeWalker =
identityWalker
{ walkOnSubExp = modify . (<>) . freeIn',
walkOnBody = \scope body -> do
modify $ (<>) $ freeIn' body
modify $ fvBind (namesFromList (M.keys scope)),
walkOnVName = modify . (<>) . fvName,
walkOnOp = modify . (<>) . freeIn'
}
-- | Return the set of variable names that are free in the given
-- statements and result. Filters away the names that are bound by
-- the statements.
freeInStmsAndRes ::
( FreeIn (Op lore),
FreeIn (LetDec lore),
FreeIn (LParamInfo lore),
FreeIn (FParamInfo lore),
FreeDec (BodyDec lore),
FreeDec (ExpDec lore)
) =>
Stms lore ->
Result ->
FV
freeInStmsAndRes stms res =
fvBind (boundByStms stms) $ foldMap freeIn' stms <> freeIn' res
-- | A class indicating that we can obtain free variable information
-- from values of this type.
class FreeIn a where
freeIn' :: a -> FV
freeIn' = fvNames . freeIn
-- | The free variables of some syntactic construct.
freeIn :: FreeIn a => a -> Names
freeIn = unFV . freeIn'
instance FreeIn FV where
freeIn' = id
instance FreeIn () where
freeIn' () = mempty
instance FreeIn Int where
freeIn' = const mempty
instance (FreeIn a, FreeIn b) => FreeIn (a, b) where
freeIn' (a, b) = freeIn' a <> freeIn' b
instance (FreeIn a, FreeIn b, FreeIn c) => FreeIn (a, b, c) where
freeIn' (a, b, c) = freeIn' a <> freeIn' b <> freeIn' c
instance FreeIn a => FreeIn [a] where
freeIn' = foldMap freeIn'
instance
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (RetType lore),
FreeIn (Op lore)
) =>
FreeIn (FunDef lore)
where
freeIn' (FunDef _ _ _ rettype params body) =
fvBind (namesFromList $ map paramName params) $
freeIn' rettype <> freeIn' params <> freeIn' body
instance
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (Op lore)
) =>
FreeIn (Lambda lore)
where
freeIn' (Lambda params body rettype) =
fvBind (namesFromList $ map paramName params) $
freeIn' rettype <> freeIn' params <> freeIn' body
instance
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (Op lore)
) =>
FreeIn (Body lore)
where
freeIn' (Body dec stms res) =
precomputed dec $ freeIn' dec <> freeInStmsAndRes stms res
instance
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (Op lore)
) =>
FreeIn (Exp lore)
where
freeIn' (DoLoop ctxmerge valmerge form loopbody) =
let (ctxparams, ctxinits) = unzip ctxmerge
(valparams, valinits) = unzip valmerge
bound_here =
namesFromList $
M.keys $
scopeOf form
<> scopeOfFParams (ctxparams ++ valparams)
in fvBind bound_here $
freeIn' (ctxinits ++ valinits) <> freeIn' form
<> freeIn' (ctxparams ++ valparams)
<> freeIn' loopbody
freeIn' e = execState (walkExpM freeWalker e) mempty
instance
( FreeDec (ExpDec lore),
FreeDec (BodyDec lore),
FreeIn (FParamInfo lore),
FreeIn (LParamInfo lore),
FreeIn (LetDec lore),
FreeIn (Op lore)
) =>
FreeIn (Stm lore)
where
freeIn' (Let pat (StmAux cs attrs dec) e) =
freeIn' cs <> freeIn' attrs
<> precomputed dec (freeIn' dec <> freeIn' e <> freeIn' pat)
instance FreeIn (Stm lore) => FreeIn (Stms lore) where
freeIn' = foldMap freeIn'
instance FreeIn Names where
freeIn' = fvNames
instance FreeIn Bool where
freeIn' _ = mempty
instance FreeIn a => FreeIn (Maybe a) where
freeIn' = maybe mempty freeIn'
instance FreeIn VName where
freeIn' = fvName
instance FreeIn Ident where
freeIn' = freeIn' . identType
instance FreeIn SubExp where
freeIn' (Var v) = freeIn' v
freeIn' Constant {} = mempty
instance FreeIn Space where
freeIn' (ScalarSpace d _) = freeIn' d
freeIn' DefaultSpace = mempty
freeIn' (Space _) = mempty
instance FreeIn d => FreeIn (ShapeBase d) where
freeIn' = freeIn' . shapeDims
instance FreeIn d => FreeIn (Ext d) where
freeIn' (Free x) = freeIn' x
freeIn' (Ext _) = mempty
instance FreeIn shape => FreeIn (TypeBase shape u) where
freeIn' (Array _ shape _) = freeIn' shape
freeIn' (Mem s) = freeIn' s
freeIn' (Prim _) = mempty
instance FreeIn dec => FreeIn (Param dec) where
freeIn' (Param _ dec) = freeIn' dec
instance FreeIn dec => FreeIn (PatElemT dec) where
freeIn' (PatElem _ dec) = freeIn' dec
instance FreeIn (LParamInfo lore) => FreeIn (LoopForm lore) where
freeIn' (ForLoop _ _ bound loop_vars) = freeIn' bound <> freeIn' loop_vars
freeIn' (WhileLoop cond) = freeIn' cond
instance FreeIn d => FreeIn (DimChange d) where
freeIn' = Data.Foldable.foldMap freeIn'
instance FreeIn d => FreeIn (DimIndex d) where
freeIn' = Data.Foldable.foldMap freeIn'
instance FreeIn dec => FreeIn (PatternT dec) where
freeIn' (Pattern context values) =
fvBind bound_here $ freeIn' $ context ++ values
where
bound_here = namesFromList $ map patElemName $ context ++ values
instance FreeIn Certificates where
freeIn' (Certificates cs) = freeIn' cs
instance FreeIn Attrs where
freeIn' (Attrs _) = mempty
instance FreeIn dec => FreeIn (StmAux dec) where
freeIn' (StmAux cs attrs dec) = freeIn' cs <> freeIn' attrs <> freeIn' dec
instance FreeIn a => FreeIn (IfDec a) where
freeIn' (IfDec r _) = freeIn' r
-- | Either return precomputed free names stored in the attribute, or
-- the freshly computed names. Relies on lazy evaluation to avoid the
-- work.
class FreeIn dec => FreeDec dec where
precomputed :: dec -> FV -> FV
precomputed _ = id
instance FreeDec ()
instance (FreeDec a, FreeIn b) => FreeDec (a, b) where
precomputed (a, _) = precomputed a
instance FreeDec a => FreeDec [a] where
precomputed [] = id
precomputed (a : _) = precomputed a
instance FreeDec a => FreeDec (Maybe a) where
precomputed Nothing = id
precomputed (Just a) = precomputed a
instance FreeDec Names where
precomputed _ fv = fv
-- | The names bound by the bindings immediately in a t'Body'.
boundInBody :: Body lore -> Names
boundInBody = boundByStms . bodyStms
-- | The names bound by a binding.
boundByStm :: Stm lore -> Names
boundByStm = namesFromList . patternNames . stmPattern
-- | The names bound by the bindings.
boundByStms :: Stms lore -> Names
boundByStms = foldMap boundByStm
-- | The names of the lambda parameters plus the index parameter.
boundByLambda :: Lambda lore -> [VName]
boundByLambda lam = map paramName (lambdaParams lam)