module LLVM.Analysis.AccessPath (
AccessPath(..),
AbstractAccessPath(..),
AccessType(..),
AccessPathError,
accessPath,
abstractAccessPath,
appendAccessPath,
followAccessPath,
reduceAccessPath,
externalizeAccessPath
) where
import Control.DeepSeq
import Control.Exception
import Control.Failure hiding ( failure )
import qualified Control.Failure as F
import Data.Hashable
import Data.List ( foldl' )
import Data.Typeable
import LLVM.Analysis
data AccessPathError = NoPathError Value
| NotMemoryInstruction Instruction
| CannotFollowPath AbstractAccessPath Value
| BaseTypeMismatch Type Type
| NonConstantInPath AbstractAccessPath Value
deriving (Typeable, Show)
instance Exception AccessPathError
data AbstractAccessPath =
AbstractAccessPath { abstractAccessPathBaseType :: Type
, abstractAccessPathEndType :: Type
, abstractAccessPathComponents :: [AccessType]
}
deriving (Show, Eq, Ord)
instance Hashable AbstractAccessPath where
hashWithSalt s (AbstractAccessPath bt et cs) =
s `hashWithSalt` bt `hashWithSalt` et `hashWithSalt` cs
appendAccessPath :: AbstractAccessPath
-> AbstractAccessPath
-> Maybe AbstractAccessPath
appendAccessPath (AbstractAccessPath bt1 et1 cs1) (AbstractAccessPath bt2 et2 cs2) =
case et1 == bt2 of
True -> Just $ AbstractAccessPath bt1 et2 (cs1 ++ cs2)
False -> Nothing
reduceAccessPath :: AbstractAccessPath -> Maybe AbstractAccessPath
reduceAccessPath (AbstractAccessPath (TypePointer t _) et (AccessDeref:cs)) =
return $! AbstractAccessPath t et cs
reduceAccessPath (AbstractAccessPath (TypeStruct _ ts _) et (AccessField fldNo:cs)) =
case fldNo < length ts of
True -> return $! AbstractAccessPath (ts !! fldNo) et cs
False -> Nothing
reduceAccessPath (AbstractAccessPath (TypeArray _ t) et (AccessArray:cs)) =
return $! AbstractAccessPath t et cs
reduceAccessPath _ = Nothing
instance NFData AbstractAccessPath where
rnf a@(AbstractAccessPath _ _ ts) = ts `deepseq` a `seq` ()
data AccessPath =
AccessPath { accessPathBaseValue :: Value
, accessPathEndValue :: Value
, accessPathComponents :: [AccessType]
}
deriving (Show, Eq, Ord)
instance NFData AccessPath where
rnf a@(AccessPath _ _ ts) = ts `deepseq` a `seq` ()
instance Hashable AccessPath where
hashWithSalt s (AccessPath bv ev cs) =
s `hashWithSalt` bv `hashWithSalt` ev `hashWithSalt` cs
data AccessType = AccessField !Int
| AccessArray
| AccessDeref
deriving (Read, Show, Eq, Ord)
instance NFData AccessType where
rnf a@(AccessField i) = i `seq` a `seq` ()
rnf _ = ()
instance Hashable AccessType where
hashWithSalt s (AccessField ix) =
s `hashWithSalt` (1 :: Int) `hashWithSalt` ix
hashWithSalt s AccessArray = s `hashWithSalt` (26 :: Int)
hashWithSalt s AccessDeref = s `hashWithSalt` (300 :: Int)
followAccessPath :: (Failure AccessPathError m) => AbstractAccessPath -> Value -> m Value
followAccessPath aap@(AbstractAccessPath bt _ components) val =
case derefPointerType bt /= valueType val of
True -> F.failure (BaseTypeMismatch bt (valueType val))
False -> walk components val
where
walk [] v = return v
walk (AccessField ix : rest) v =
case valueContent' v of
ConstantC ConstantStruct { constantStructValues = vs } ->
case ix < length vs of
False -> error $ concat [ "LLVM.Analysis.AccessPath.followAccessPath.walk: "
," Invalid access path: ", show aap, " / ", show val
]
True -> walk rest (vs !! ix)
_ -> F.failure (NonConstantInPath aap val)
walk _ _ = F.failure (CannotFollowPath aap val)
abstractAccessPath :: AccessPath -> AbstractAccessPath
abstractAccessPath (AccessPath v v0 p) =
AbstractAccessPath (valueType v) (valueType v0) p
accessPath :: (Failure AccessPathError m) => Instruction -> m AccessPath
accessPath i = do
cpath <- case i of
LoadInst { loadAddress = la } ->
return $! go (AccessPath la (toValue i) []) la
StoreInst { storeAddress = sa, storeValue = sv } ->
return $! go (AccessPath sa sv []) sa
AtomicCmpXchgInst { atomicCmpXchgPointer = p } ->
return $! go (AccessPath p p []) p
AtomicRMWInst { atomicRMWPointer = p } ->
return $! go (AccessPath p p []) p
GetElementPtrInst {} ->
return $! go (AccessPath (toValue i) (toValue i) []) (toValue i)
_ -> F.failure (NotMemoryInstruction i)
return $! addBaseDeref cpath
where
addBaseDeref p =
p { accessPathComponents = AccessDeref : accessPathComponents p }
go p v =
case valueContent' v of
InstructionC GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = ixs
} ->
let p' = p { accessPathBaseValue = base
, accessPathComponents =
gepIndexFold base ixs ++ accessPathComponents p
}
in go p' base
ConstantC ConstantValue { constantInstruction =
GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = ixs
} } ->
let p' = p { accessPathBaseValue = base
, accessPathComponents =
gepIndexFold base ixs ++ accessPathComponents p
}
in go p' base
InstructionC LoadInst { loadAddress = la } ->
let p' = p { accessPathBaseValue = la
, accessPathComponents =
AccessDeref : accessPathComponents p
}
in go p' la
_ -> p { accessPathBaseValue = v }
externalizeAccessPath :: AbstractAccessPath -> Maybe (String, [AccessType])
externalizeAccessPath accPath = do
baseName <- structTypeToName (stripPointerTypes bt)
return (baseName, abstractAccessPathComponents accPath)
where
bt = abstractAccessPathBaseType accPath
derefPointerType :: Type -> Type
derefPointerType (TypePointer p _) = p
derefPointerType t = error ("LLVM.Analysis.AccessPath.derefPointerType: Type is not a pointer type: " ++ show t)
gepIndexFold :: Value -> [Value] -> [AccessType]
gepIndexFold base indices@(ptrIx : ixs) =
let TypePointer baseType _ = valueType base
in case valueContent ptrIx of
ConstantC ConstantInt { constantIntValue = 0 } ->
snd $ foldl' walkGep (baseType, []) ixs
_ ->
snd $ foldl' walkGep (baseType, [AccessArray]) ixs
where
walkGep (ty, acc) ix =
case ty of
TypePointer ty' _ -> (ty', AccessArray : acc)
TypeArray _ ty' -> (ty', AccessArray : acc)
TypeStruct _ ts _ ->
case valueContent ix of
ConstantC ConstantInt { constantIntValue = fldNo } ->
let fieldNumber = fromIntegral fldNo
in (ts !! fieldNumber, AccessField fieldNumber : acc)
_ -> error ("LLVM.Analysis.AccessPath.gepIndexFold.walkGep: Invalid non-constant GEP index for struct: " ++ show ty)
_ -> error ("LLVM.Analysis.AccessPath.gepIndexFold.walkGep: Unexpected type in GEP: " ++ show ty)
gepIndexFold v [] =
error ("LLVM.Analysis.AccessPath.gepIndexFold: GEP instruction/base with empty index list: " ++ show v)