llvm-analysis-0.3.0: src/LLVM/Analysis/NoReturn.hs
{-# LANGUAGE NoMonomorphismRestriction #-}
-- | An analysis to identify functions that never return to their
-- caller. This only counts calls to exit, abort, or similar.
-- Notably, exceptions are not considered since the caller can catch
-- those.
--
-- The dataflow fact is "Function does not return". It starts at
-- False (top) and calls to termination functions (or the unreachable
-- instruction) move it to True.
--
-- Meet is &&. Functions are able to return as long as at least one
-- path can return.
module LLVM.Analysis.NoReturn (
noReturnAnalysis
) where
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Data.HashSet ( HashSet )
import qualified Data.HashSet as S
import LLVM.Analysis
import LLVM.Analysis.CFG
import LLVM.Analysis.Dataflow
-- | The dataflow fact; Bool is not enough. Top is "NotReturned" -
-- the function has not returned yet. Return instructions will
-- transfer to "Returned". However, we want to distinguish between
-- "Hasn't returned yet" and "Can never return" in case we see a call
-- to a function we know cannot return (but LLVM does not).
--
-- If LLVM recognizes that something cannot return, it will add an
-- unreachable instruction (from which we also return NeverReturns).
--
-- If even a single Returned value is incoming to the exit node, the
-- function can return.
data ReturnInfo = NotReturned
| Returned
| WillNeverReturn
deriving (Show, Eq)
meet :: ReturnInfo -> ReturnInfo -> ReturnInfo
meet Returned _ = Returned
meet _ Returned = Returned
meet WillNeverReturn _ = WillNeverReturn
meet _ WillNeverReturn = WillNeverReturn
meet NotReturned NotReturned = NotReturned
data AnalysisEnvironment m =
AE { externalSummary :: ExternalFunction -> m Bool
, internalSummary :: HashSet Function
}
-- | The analysis monad is just a Reader whose environment is a function
-- to test ExternalFunctions
type AnalysisMonad m = ReaderT (AnalysisEnvironment m) m
-- | The functions in the returned set are those that do not return.
--
-- Warning, this return type may become abstract at some point.
noReturnAnalysis :: (Monad m, HasCFG cfg)
=> (ExternalFunction -> m Bool)
-> cfg
-> HashSet Function
-> m (HashSet Function)
noReturnAnalysis extSummary cfgLike summ = do
let cfg = getCFG cfgLike
f = getFunction cfg
env = AE extSummary summ
analysis = fwdDataflowAnalysis NotReturned meet returnTransfer
localRes <- runReaderT (dataflow cfg analysis NotReturned) env
case dataflowResult localRes of
WillNeverReturn -> return $! S.insert f summ
NotReturned -> return $! S.insert f summ
Returned -> return summ
returnTransfer :: (Monad m) => ReturnInfo -> Instruction -> AnalysisMonad m ReturnInfo
returnTransfer ri i =
case i of
CallInst { callFunction = calledFunc } ->
dispatchCall ri calledFunc
InvokeInst { invokeFunction = calledFunc } ->
dispatchCall ri calledFunc
UnreachableInst {} -> return WillNeverReturn
ResumeInst {} -> return WillNeverReturn
RetInst {} -> return Returned
_ -> return ri
dispatchCall :: (Monad m) => ReturnInfo -> Value -> AnalysisMonad m ReturnInfo
dispatchCall ri v =
case valueContent' v of
FunctionC f -> do
intSumm <- asks internalSummary
case S.member f intSumm of
True -> return WillNeverReturn
False -> return ri
ExternalFunctionC ef -> do
extSumm <- asks externalSummary
isNoRet <- lift $ extSumm ef
case isNoRet of
True -> return WillNeverReturn
False -> return ri
_ -> return ri
{-# ANN module "HLint: ignore Use if" #-}