llvm-analysis-0.3.0: src/LLVM/Analysis/ScalarEffects.hs
{-# LANGUAGE ViewPatterns, NoMonomorphismRestriction #-}
{-|
This analysis identifies the (memory) effects that functions have on
the scalar components of their arguments.
Only pointer parameters are interesting because only their effects can
escape the callee. Effects are currently restricted to increments and
decrements of integral types. The affected memory can be a struct
member; the effects are described in terms of abstract AccessPaths.
This is a must analysis. Effects are only reported if they *MUST*
occur (modulo non-termination style effects like calls to exit or
infinite loops).
Currently, sequential effects are not composed and nothing useful will
be reported.
-}
module LLVM.Analysis.ScalarEffects (
ScalarEffectResult,
ScalarEffect(..),
scalarEffectAnalysis
) where
import Control.DeepSeq
import Data.HashMap.Strict ( HashMap )
import qualified Data.HashMap.Strict as HM
import LLVM.Analysis
import LLVM.Analysis.AccessPath
import LLVM.Analysis.CFG
import LLVM.Analysis.Dataflow
-- | The types of effects tracked by this analysis. This can be expanded
-- as the analysis becomes more sophisticated (it could include general
-- affine relations or even relate arguments to each other).
data ScalarEffect = EffectAdd1 AbstractAccessPath
| EffectSub1 AbstractAccessPath
deriving (Eq)
instance NFData ScalarEffect where
rnf e@(EffectAdd1 ap) = ap `deepseq` e `seq` ()
rnf e@(EffectSub1 ap) = ap `deepseq` e `seq` ()
type ScalarEffectResult = HashMap Argument ScalarEffect
data ScalarInfo = SI (HashMap Argument (Maybe ScalarEffect))
| SITop
instance Eq ScalarInfo where
(SI s1) == (SI s2) = s1 == s2
SITop == SITop = True
_ == _ = False
meet :: ScalarInfo -> ScalarInfo -> ScalarInfo
meet SITop s = s
meet s SITop = s
meet (SI s1) (SI s2) = SI (HM.unionWith mergeEffect s1 s2)
where
-- | If there is an entry in both maps, it must be the same to be
-- retained.
mergeEffect e1 e2 = if e1 == e2 then e1 else Nothing
-- For each function, initialize all arguments to Nothing
scalarEffectAnalysis :: (Monad m, HasCFG funcLike, HasFunction funcLike)
=> funcLike
-> ScalarEffectResult
-> m ScalarEffectResult
scalarEffectAnalysis funcLike summ = do
let cfg = getCFG funcLike
analysis = fwdDataflowAnalysis SITop meet scalarTransfer
localRes <- dataflow cfg analysis SITop
let xi = case dataflowResult localRes of
SITop -> HM.empty
SI m -> HM.foldlWithKey' discardNothings HM.empty m
return $! HM.union xi summ
discardNothings :: HashMap Argument ScalarEffect
-> Argument
-> Maybe ScalarEffect
-> HashMap Argument ScalarEffect
discardNothings acc _ Nothing = acc
discardNothings acc a (Just e) = HM.insert a e acc
scalarTransfer :: (Monad m) => ScalarInfo -> Instruction -> m ScalarInfo
scalarTransfer si i =
case i of
AtomicRMWInst { atomicRMWOperation = AOAdd
, atomicRMWValue =
(valueContent -> ConstantC ConstantInt { constantIntValue = 1 })} ->
recordIfAffectsArgument EffectAdd1 i si
AtomicRMWInst { atomicRMWOperation = AOAdd
, atomicRMWValue =
(valueContent -> ConstantC ConstantInt { constantIntValue = -1 })} ->
recordIfAffectsArgument EffectSub1 i si
AtomicRMWInst { atomicRMWOperation = AOSub
, atomicRMWValue =
(valueContent -> ConstantC ConstantInt { constantIntValue = 1 })} ->
recordIfAffectsArgument EffectSub1 i si
AtomicRMWInst { atomicRMWOperation = AOSub
, atomicRMWValue =
(valueContent -> ConstantC ConstantInt { constantIntValue = -1 })} ->
recordIfAffectsArgument EffectAdd1 i si
StoreInst { storeAddress = sa, storeValue = sv } ->
case isNonAtomicAdd sa sv of
False ->
case isNonAtomicSub sa sv of
False -> return si
True -> recordIfAffectsArgument EffectSub1 i si
True -> recordIfAffectsArgument EffectAdd1 i si
_ -> return si
isNonAtomicSub :: (IsValue a) => Value -> a -> Bool
isNonAtomicSub sa sv =
case valueContent sv of
InstructionC AddInst {
binaryLhs = (valueContent -> ConstantC ConstantInt { constantIntValue = -1 }),
binaryRhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
InstructionC AddInst {
binaryRhs = (valueContent -> ConstantC ConstantInt { constantIntValue = -1 }),
binaryLhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
InstructionC SubInst {
binaryRhs = (valueContent -> ConstantC ConstantInt { constantIntValue = 1 }),
binaryLhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
_ -> False
isNonAtomicAdd :: (IsValue a) => Value -> a -> Bool
isNonAtomicAdd sa sv =
case valueContent sv of
InstructionC AddInst {
binaryLhs = (valueContent -> ConstantC ConstantInt { constantIntValue = 1 }),
binaryRhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
InstructionC AddInst {
binaryRhs = (valueContent -> ConstantC ConstantInt { constantIntValue = 1 }),
binaryLhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
InstructionC SubInst {
binaryRhs = (valueContent -> ConstantC ConstantInt { constantIntValue = -1 }),
binaryLhs = (valueContent -> InstructionC LoadInst { loadAddress = la }) } ->
sa == la
_ -> False
recordIfAffectsArgument :: (Monad m)
=> (AbstractAccessPath -> ScalarEffect)
-> Instruction
-> ScalarInfo
-> m ScalarInfo
recordIfAffectsArgument con i si =
case accessPath i of
Nothing -> return si
Just cap ->
case valueContent' (accessPathBaseValue cap) of
ArgumentC a ->
let e = Just $ con (abstractAccessPath cap)
in case si of
SITop -> return $! SI $ HM.insert a e HM.empty
SI m -> return $! SI $ HM.insert a e m
_ -> return si
{-# ANN module "HLint: ignore Use if" #-}