module LLVM.Analysis.PointsTo.Andersen (
Andersen,
runPointsToAnalysis,
runPointsToAnalysisWith,
andersenConstraintGraph
) where
import Control.Exception
import Control.Monad.State.Strict
import Data.GraphViz
import Data.Maybe ( fromMaybe, mapMaybe )
import Data.Typeable
import LLVM.Analysis
import LLVM.Analysis.PointsTo
import Constraints.Set.Solver
import Constraints.Set.Internal
#if defined(DEBUGCONSTRAINTS)
import Debug.Trace
#endif
data ConstraintState = ConstraintState { freshIdSrc :: !Int }
type ConstraintGen = State ConstraintState
freshVariable :: ConstraintGen SetExp
freshVariable = do
s <- get
let vid = freshIdSrc s
v = Fresh vid
put $ s { freshIdSrc = vid + 1 }
return $! setVariable v
data Constructor = Ref
| Atom !Value
deriving (Eq, Ord, Show, Typeable)
data Var = Fresh !Int
| LocationSet !Value
| LoadedLocation !Instruction
| ArgLocation !Argument
| VirtualArg !Value !Int
| VirtualFieldArg !Type !Int !Int
| RetLocation !Instruction
| GEPLocation !Value
| PhiCopy !Instruction
| FieldLoc !Type !Int
deriving (Eq, Ord, Show, Typeable)
type SetExp = SetExpression Var Constructor
data Andersen = Andersen !(SolvedSystem Var Constructor)
instance PointsToAnalysis Andersen where
mayAlias = andersenMayAlias
pointsTo = andersenPointsTo
andersenMayAlias :: Andersen -> Value -> Value -> Bool
andersenMayAlias _ _ _ = True
andersenPointsTo :: Andersen -> Value -> [Value]
andersenPointsTo (Andersen ss) v =
either fromError (map fromLocation) (leastSolution ss var)
where
var = case valueContent' v of
ArgumentC a -> ArgLocation a
InstructionC i@CallInst {} -> RetLocation i
InstructionC i@InvokeInst {} -> RetLocation i
InstructionC i@LoadInst {} -> LoadedLocation i
InstructionC i@SelectInst {} -> PhiCopy i
InstructionC i@PhiNode {} -> PhiCopy i
InstructionC GetElementPtrInst { getElementPtrValue = base } ->
GEPLocation (getTargetIfLoad base)
_ -> LocationSet v
fromError :: ConstraintError Var Constructor -> [Value]
fromError = const []
fromLocation :: SetExp -> Value
fromLocation (ConstructedTerm Ref _ [ConstructedTerm (Atom val) _ _, _, _]) = val
fromLocation se = error ("Unexpected set expression in result " ++ show se)
runPointsToAnalysis :: Module -> Andersen
runPointsToAnalysis = runPointsToAnalysisWith (const False)
runPointsToAnalysisWith :: (Value -> Bool) -> Module -> Andersen
runPointsToAnalysisWith ignore m = evalState (pta ignore m) (ConstraintState 0)
pta :: (Value -> Bool) -> Module -> ConstraintGen Andersen
pta ignore m = do
initConstraints <- foldM globalInitializerConstraints [] (moduleGlobalVariables m)
funcConstraints <- foldM functionConstraints [] (moduleDefinedFunctions m)
let is = initConstraints ++ funcConstraints
sol = either throwErr id (solveSystem is)
return $! Andersen sol
where
loadVar ldInst = setVariable (LoadedLocation ldInst)
argVar a = setVariable (ArgLocation a)
phiVar i = setVariable (PhiCopy i)
gepVar v = setVariable (GEPLocation v)
virtArgVar sa ix =
case valueContent' sa of
InstructionC GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = ixs
} ->
case fieldDescriptor base ixs of
Just (t, fldno) -> setVariable (VirtualFieldArg t fldno ix)
Nothing -> setVariable (VirtualArg sa ix)
_ -> setVariable (VirtualArg sa ix)
returnVar i = setVariable (RetLocation i)
ref = term Ref [Covariant, Covariant, Contravariant]
loc val =
let svar = fromMaybe (setVariable (LocationSet val)) (setVarFor val)
in ref [ atom (Atom val), svar, svar ]
setVarFor v =
case valueContent' v of
InstructionC i@LoadInst {} -> return $ loadVar i
InstructionC i@CallInst {} -> return $ returnVar i
InstructionC i@InvokeInst {} -> return $ returnVar i
InstructionC i@PhiNode {} -> return $ phiVar i
InstructionC i@SelectInst {} -> return $ phiVar i
InstructionC GetElementPtrInst { getElementPtrValue = base } ->
return $ gepVar (getTargetIfLoad base)
ArgumentC a -> return $ argVar a
_ -> Nothing
setExpFor v =
case valueContent' v of
InstructionC GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = ixs
} ->
case fieldDescriptor base ixs of
Nothing -> gepVar (getTargetIfLoad base)
Just (t, ix) ->
let var = setVariable (FieldLoc t ix)
in ref [ atom (Atom v), var, var ]
ConstantC ConstantValue { constantInstruction = (valueContent' ->
InstructionC GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = is
})} ->
case valueType base of
TypePointer (TypeArray _ _) _ ->
case all isConstantZero is of
True -> setVariable (LocationSet base)
False -> loc v
_ ->
case fieldDescriptor base is of
Nothing -> gepVar (getTargetIfLoad base)
Just (t, ix) ->
let var = setVariable (FieldLoc t ix)
in ref [ atom (Atom v), var, var ]
_ -> fromMaybe (loc v) (setVarFor v)
globalInitializerConstraints acc global =
case globalVariableInitializer global of
Nothing -> return acc
Just (valueContent -> ConstantC _) -> return acc
Just i -> do
f1 <- freshVariable
f2 <- freshVariable
let c1 = loc (toValue global) <=! ref [ universalSet, universalSet, f1 ]
c2 = ref [ emptySet, loc i, emptySet ] <=! ref [ universalSet, f2, emptySet ]
c3 = f2 <=! f1
return $ c1 : c2 : c3 : acc
functionConstraints acc = foldM instructionConstraints acc . functionInstructions
instructionConstraints acc i =
case i of
LoadInst { loadAddress = la }
| ignore la -> return acc
| otherwise -> do
let c = setExpFor la <=! ref [ universalSet, loadVar i, emptySet ]
acc' <- addVirtualConstraints acc (toValue i) la
return $ c : acc' `traceConstraints` ("Inst: " ++ show i, [c])
StoreInst { storeAddress = sa, storeValue = sv }
| ignore sa || ignore sv -> return acc
| otherwise -> do
f1 <- freshVariable
f2 <- freshVariable
let c1 = setExpFor sa <=! ref [ universalSet, universalSet, f1 ]
c2 = ref [ emptySet, setExpFor sv, emptySet ] <=! ref [ universalSet, f2, emptySet ]
c3 = f2 <=! f1
acc' <- addVirtualConstraints acc sa sv
return $ c1 : c2 : c3 : acc' `traceConstraints` ("Inst: " ++ show i, [c1,c2,c3])
CallInst { callFunction = (valueContent' -> FunctionC f)
, callArguments = (map fst -> args)
} -> directCallConstraints acc i f args
InvokeInst { invokeFunction = (valueContent' -> FunctionC f)
, invokeArguments = (map fst -> args)
} -> directCallConstraints acc i f args
CallInst { callFunction = (valueContent' -> ExternalFunctionC _) } ->
return acc
InvokeInst { invokeFunction = (valueContent' -> ExternalFunctionC _) } ->
return acc
CallInst { callFunction = callee, callArguments = (map fst -> args) } ->
indirectCallConstraints acc callee args
InvokeInst { invokeFunction = callee, invokeArguments = (map fst -> args) } ->
indirectCallConstraints acc callee args
SelectInst { selectTrueValue = tv, selectFalseValue = fv } ->
foldM (valueAliasingChoise i) acc [ tv, fv ]
PhiNode { phiIncomingValues = ivs } ->
foldM (valueAliasingChoise i) acc (map fst ivs)
GetElementPtrInst { getElementPtrValue = (valueContent' ->
InstructionC LoadInst { loadAddress = la })
, getElementPtrIndices = [_]
}
| ignore la || ignore (toValue i) -> return acc
| otherwise -> do
f1 <- freshVariable
f2 <- freshVariable
let c1 = loc (toValue i) <=! ref [ universalSet, universalSet, f1 ]
c2 = ref [ emptySet, setExpFor la, emptySet ] <=! ref [ universalSet, f2, emptySet ]
c3 = f2 <=! f1
acc' <- addVirtualConstraints acc (toValue i) la
return $ c1 : c2 : c3 : acc' `traceConstraints` (concat ["GEP: " ++ show i], [c1,c2,c3])
GetElementPtrInst { getElementPtrValue = base
, getElementPtrIndices = [_]
}
| ignore base || ignore (toValue i) -> return acc
| otherwise -> do
f1 <- freshVariable
f2 <- freshVariable
let c1 = loc (toValue i) <=! ref [ universalSet, universalSet, f1 ]
c2 = ref [ emptySet, loc base, emptySet ] <=! ref [ universalSet, f2, emptySet ]
c3 = f2 <=! f1
acc' <- addVirtualConstraints acc (toValue i) base
return $ c1 : c2 : c3 : acc' `traceConstraints` (concat ["GEP: " ++ show i], [c1,c2,c3])
GetElementPtrInst { getElementPtrValue = base,
getElementPtrIndices = [(valueContent -> ConstantC ConstantInt { constantIntValue = 0 })
, _
]
} ->
case valueType base of
TypePointer (TypeArray _ _) _ -> do
f1 <- freshVariable
f2 <- freshVariable
let c1 = loc (toValue i) <=! ref [ universalSet, universalSet, f1 ]
c2 = ref [ emptySet, setExpFor base, emptySet ] <=! ref [ universalSet, f2, emptySet ]
c3 = f2 <=! f1
acc' <- addVirtualConstraints acc (toValue i) base
return $ c1 : c2 : c3 : acc' `traceConstraints` (concat ["GEP: " ++ show i], [c1,c2,c3])
_ -> return acc
_ -> return acc
directCallConstraints acc i f actuals = do
let formals = functionParameters f
acc' <- foldM copyActualToFormal acc (zip actuals formals)
case valueType i of
TypePointer _ _ | not (ignore (toValue i)) -> do
let rvs = mapMaybe extractRetVal (functionExitInstructions f)
cs <- foldM (retConstraint i) [] rvs
return $ cs ++ acc'
_ -> return acc'
addVirtualConstraints acc0 dst src = do
acc1 <- addVirtualArgConstraints acc0 dst src
return acc1
addVirtualArgConstraints acc sa sv
| not (isFuncPtrType (valueType sv)) = return acc
| otherwise =
case valueContent' sv of
FunctionC f -> do
let formals = functionParameters f
foldM (constrainVirtualArg sa) acc (zip [0..] formals)
_ -> do
let nparams = functionTypeParameters (valueType sv)
foldM (virtVirtArg sa sv) acc [0..(nparams 1)]
virtVirtArg sa sv acc ix = do
let c1 = virtArgVar sa ix <=! virtArgVar sv ix
c2 = virtArgVar sv ix <=! virtArgVar sa ix
return $ c1 : c2 : acc `traceConstraints` (concat ["VirtVirt: ", show ix, "(", show sa, " -> ", show sv, ")"], [c1, c2])
constrainVirtualArg sa acc (ix, frml) = do
let c = virtArgVar sa ix <=! argVar frml
return $ c : acc `traceConstraints` (concat ["VirtArg: ", show ix, "(", show sa, ")"], [c])
indirectCallConstraints acc callee actuals = do
let addIndirectConstraint (ix, act) a =
if ignore act then a
else let c = setExpFor act <=! virtArgVar callee ix
in c : a `traceConstraints` (concat ["IndirectCall ", show ix, "(", show act, ")" ], [c])
acc' = foldr addIndirectConstraint acc (zip [0..] actuals)
return acc'
retConstraint i acc rv
| ignore rv = return acc
| otherwise = do
let c = setExpFor rv <=! setExpFor (toValue i)
acc' <- addVirtualConstraints acc (toValue i) rv
return $ c : acc' `traceConstraints` (concat [ "RetVal ", show i ], [c])
copyActualToFormal acc (act, frml)
| ignore act = return acc
| otherwise = do
let c = setExpFor act <=! argVar frml
acc' <- addVirtualConstraints acc (toValue frml) act
return $ c : acc' `traceConstraints` (concat [ "Args ", show act, " -> ", show frml ], [c])
valueAliasingChoise i acc vfrom
| ignore (toValue i) = return acc
| otherwise = do
let c = setExpFor vfrom <=! setExpFor (toValue i)
acc' <- addVirtualConstraints acc (toValue i) vfrom
return $ c : acc' `traceConstraints` (concat [ "MultCopy ", show (valueName vfrom), " -> ", show (valueName i)], [c])
fieldDescriptor :: Value -> [Value] -> Maybe (Type, Int)
fieldDescriptor base ixs =
case (valueType base, ixs) of
(_, [_]) -> Nothing
(TypePointer (TypeArray _ _) _, (valueContent' -> ConstantC ConstantInt { constantIntValue = 0 }):_) ->
Nothing
(TypePointer t _, _:rest) -> return $ walkType t rest
_ -> Nothing
walkType :: Type -> [Value] -> (Type, Int)
walkType t [] = error ("LLVM.Analysis.PointsTo.Andersen.walkType: expected non-empty index list for " ++ show t)
walkType t [(valueContent -> ConstantC ConstantInt { constantIntValue = iv })] =
(t, fromIntegral iv)
walkType t (ix:ixs) =
case t of
TypeArray _ t' -> walkType t' ixs
TypeStruct _ ts _ ->
case valueContent ix of
ConstantC ConstantInt { constantIntValue = (fromIntegral -> iv) } ->
case iv < length ts of
True -> walkType (ts !! iv) ixs
False -> error ("LLVM.Analysis.PointsTo.Andersen.walkType: index out of range " ++ show iv ++ " in " ++ show t)
_ -> error ("LLVM.Analysis.PointsTo.Andersen.walkType: non-constant index " ++ show ix ++ " in " ++ show t)
_ -> error ("LLVM.Analysis.PointsTo.Andersen.walkType: unexpected type " ++ show ix ++ " in " ++ show t)
isConstantZero :: Value -> Bool
isConstantZero v =
case valueContent' v of
ConstantC ConstantInt { constantIntValue = 0 } -> True
_ -> False
getTargetIfLoad :: Value -> Value
getTargetIfLoad v =
case valueContent' v of
InstructionC LoadInst { loadAddress = la } -> la
_ -> v
traceConstraints :: a -> (String, [Inclusion Var Constructor]) -> a
#if defined(DEBUGCONSTRAINTS)
traceConstraints a (msg, cs) = trace (msg ++ "\n" ++ (unlines $ map ((" "++) . show) cs)) a
#else
traceConstraints = const
#endif
isFuncPtrType :: Type -> Bool
isFuncPtrType t =
case t of
TypeFunction _ _ _ -> True
TypePointer t' _ -> isFuncPtrType t'
_ -> False
functionTypeParameters :: Type -> Int
functionTypeParameters t =
case t of
TypeFunction _ ts _ -> length ts
TypePointer t' _ -> functionTypeParameters t'
_ -> 1
extractRetVal :: Instruction -> Maybe Value
extractRetVal RetInst { retInstValue = rv } = rv
extractRetVal _ = Nothing
throwErr :: ConstraintError Var Constructor -> SolvedSystem Var Constructor
throwErr = throw
andersenConstraintGraph :: Andersen -> DotGraph Int
andersenConstraintGraph (Andersen s) =
let (ns, es) = solvedSystemGraphElems s
in graphElemsToDot andersenParams ns es
andersenParams :: GraphvizParams Int (SetExpression Var Constructor) ConstraintEdge () (SetExpression Var Constructor)
andersenParams = defaultParams { isDirected = True
, fmtNode = fmtAndersenNode
, fmtEdge = fmtAndersenEdge
}
fmtAndersenNode :: (a, SetExpression Var Constructor) -> [Attribute]
fmtAndersenNode (_, l) =
case l of
EmptySet -> [toLabel (show l)]
UniversalSet -> [toLabel (show l)]
SetVariable (FieldLoc t ix) ->
[toLabel ("Field_" ++ show t ++ "<" ++ show ix ++ ">")]
SetVariable (Fresh i) -> [toLabel ("F" ++ show i)]
SetVariable (PhiCopy i) -> [toLabel ("PhiCopy " ++ show i)]
SetVariable (GEPLocation i) -> [toLabel ("GEPLoc " ++ show i)]
SetVariable (VirtualArg sa ix) ->
[toLabel ("VA_" ++ show ix ++ "[" ++ show (valueName sa) ++ "]")]
SetVariable (VirtualFieldArg t fld ix) ->
[toLabel ("VAField_" ++ show ix ++ "[" ++ show t ++ ".<" ++ show fld ++ ">]")]
SetVariable (LocationSet v) ->
case valueName v of
Nothing -> [toLabel ("X_" ++ show v)]
Just vn -> [toLabel ("X_" ++ identifierAsString vn)]
SetVariable (ArgLocation a) ->
[toLabel ("AL_" ++ show (argumentName a))]
SetVariable (RetLocation i) ->
[toLabel ("RV_" ++ show (valueName (callFunction i)))]
SetVariable (LoadedLocation i) ->
case valueName i of
Nothing -> error "Loads should have names"
Just ln -> [toLabel ("LL_" ++ identifierAsString ln)]
ConstructedTerm Ref _ [ConstructedTerm (Atom v) _ _, _, _] ->
let vn = maybe (show v) identifierAsString (valueName v)
in [toLabel $ concat [ "Ref( l_", vn, ", X_", vn, ", X_", vn ]]
ConstructedTerm (Atom a) _ _ ->
[toLabel (show a)]
ConstructedTerm _ _ _ -> [toLabel (show l)]
fmtAndersenEdge :: (a, a, ConstraintEdge) -> [Attribute]
fmtAndersenEdge (_, _, lbl) =
case lbl of
Succ -> [style solid]
Pred -> [style dashed]