packages feed

crucible-0.7: test/absint/AI.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module AI (
  aiTests
  ) where

import Control.Monad ( guard, join )
import Prelude

import qualified Test.Tasty as T
import qualified Test.Tasty.HUnit as T

import qualified Data.Parameterized.Context as PU
import qualified Data.Parameterized.Map as PM
import           Data.Parameterized.Nonce

import qualified What4.FunctionName as C
import qualified What4.ProgramLoc as P

import qualified Lang.Crucible.FunctionHandle as C
import qualified Lang.Crucible.CFG.Core as C
import qualified Lang.Crucible.CFG.Expr as C
import qualified Lang.Crucible.CFG.Generator as G
import qualified Lang.Crucible.CFG.SSAConversion as SSA
import Lang.Crucible.Syntax
import Lang.Crucible.Analysis.Fixpoint hiding ( Ignore(..) )

import EvenOdd
import Max

aiTests :: T.TestTree
aiTests = T.testGroup "Abstract Interpretation" [
  runTest "eo_p1" eo_p1,
  runTest "eo_p2" eo_p2,
  runTest "eo_p3" eo_p3,
  runTest "eo_p4" eo_p4,
  runTest "max_p1" max_p1,
  runTest "max_p2" max_p2
  ]

runTest :: (C.IsSyntaxExtension ext) => String -> TestCase ext dom -> T.TestTree
runTest name tc = T.testCase name $ join (testAI tc)

testAI :: (C.IsSyntaxExtension ext) => TestCase ext dom -> IO T.Assertion
testAI TC { tcHandle = hdl
          , tcDef = def
          , tcGlobals = g
          , tcAssignment = a0
          , tcCheck = check
          , tcDom = dom
          , tcInterp = interp
          } = do
  fh <- hdl
  sng <- newIONonceGenerator
  (G.SomeCFG cfg, _) <- G.defineFunction P.InternalPos sng fh def
  case SSA.toSSA cfg of
    C.SomeCFG cfg' -> do
      let (assignment', rabs) = forwardFixpoint dom interp cfg' g a0
          mWorklist = do
            -- If we aren't widening, also compute the same
            -- approximation using the worklist-based iteration
            -- strategy.  The result should be the same.
            guard (isWTOIter (domIter dom))
            let dom' = dom { domIter = Worklist }
            return $ forwardFixpoint dom' interp cfg' g a0
      return (check cfg' assignment' rabs mWorklist)

data TestCase ext dom =
  forall init ret t .
  TC { tcDef :: G.FunctionDef ext t init ret IO
     , tcHandle :: IO (C.FnHandle init ret)
     , tcDom :: Domain dom
     , tcInterp :: Interpretation ext dom
     , tcAssignment :: PU.Assignment dom init
     , tcGlobals :: PM.MapF C.GlobalVar dom
     , tcCheck :: forall blocks tp
                . C.CFG ext blocks init ret
               -> PU.Assignment (PointAbstraction blocks dom) blocks
               -> dom tp
               -> Maybe (PU.Assignment (PointAbstraction blocks dom) blocks, dom tp)
               -> T.Assertion
     }

genHandle :: IO (C.FnHandle (C.EmptyCtx C.::> C.IntegerType) C.IntegerType)
genHandle = C.withHandleAllocator $ \ha -> C.mkHandle ha C.startFunctionName

type EvenOdd' = Pointed EvenOdd
type Max' = Pointed Max

eo_p1 :: TestCase EOExt EvenOdd'
eo_p1 = TC { tcDef = \ia -> (Ignore, gen ia)
           , tcHandle = genHandle
           , tcAssignment = PU.empty PU.:> Pointed Even
           , tcGlobals = PM.empty
           , tcCheck = check
           , tcDom = evenOddDom
           , tcInterp = evenOddInterp
           }
  where
    check _cfg _assignment rabs mWorklist = do
      T.assertEqual "retVal" Top rabs
      case mWorklist of
        Nothing -> T.assertFailure "Expected worklist result"
        Just (_, rabs') -> T.assertEqual "WL Result" rabs rabs'

    gen initialAssignment = do
      r0 <- G.newReg (intLitReg 0)
      let x = initialAssignment PU.! PU.baseIndex
      let c = app (atom x `C.IntLt` litExpr 5)
      G.ifte_ c (then_ r0) (else_ r0)
      rval <- G.readReg r0
      G.returnFromFunction rval

    then_ r0 = do
      G.assignReg r0 (litExpr (negate 5))

    else_ r0 = do
      G.assignReg r0 (litExpr 10)

eo_p2 :: TestCase EOExt EvenOdd'
eo_p2 = TC { tcDef = \ia -> (Ignore, gen ia)
           , tcHandle = genHandle
           , tcAssignment = PU.empty PU.:> Pointed Even
           , tcGlobals = PM.empty
           , tcCheck = check
           , tcDom = evenOddDom
           , tcInterp = evenOddInterp
           }
  where
    check _cfg _assignment rabs mWorklist = do
      T.assertEqual "retVal" (Pointed Even) rabs
      case mWorklist of
        Nothing -> T.assertFailure "Expected worklist result"
        Just (_, rabs') -> do
          T.assertEqual "WL Result" rabs rabs'

    gen initialAssignment = do
      r0 <- G.newReg (intLitReg 0)
      let x = initialAssignment PU.! PU.baseIndex
      let c = app (atom x `C.IntLt` litExpr 5)
      G.ifte_ c (then_ r0) (else_ r0)
      rval <- G.readReg r0
      G.returnFromFunction rval

    then_ r0 = do
      G.assignReg r0 (litExpr 6)

    else_ r0 = do
      G.assignReg r0 (litExpr 10)

eo_p3 :: TestCase EOExt EvenOdd'
eo_p3 = TC { tcDef = \ia -> (Ignore, gen ia)
           , tcHandle = genHandle
           , tcAssignment = PU.empty PU.:> Pointed Even
           , tcGlobals = PM.empty
           , tcCheck = check
           , tcDom = evenOddDom
           , tcInterp = evenOddInterp
           }
  where
    check _cfg _assignment rabs mWorklist = do
      T.assertEqual "retVal" (Pointed Even) rabs
      case mWorklist of
        Nothing -> T.assertFailure "Expected worklist result"
        Just (_, rabs') -> T.assertEqual "WL Result" rabs rabs'

    gen initialAssignment = do
      r0 <- G.newReg (intLitReg 0)
      r1 <- G.newReg (intLitReg 0)
      let x = initialAssignment PU.! PU.baseIndex
      let c = app (atom x `C.IntLt` litExpr 5)
      G.ifte_ c (then_ r0 r1) (else_ r0 r1)
      rval <- G.readReg r1
      G.returnFromFunction rval

    then_ r0 r1 = do
      v <- G.readReg r0
      G.assignReg r1 (app (v `C.IntAdd` litExpr 2))

    else_ r0 r1 = do
      v <- G.readReg r0
      G.assignReg r1 (app (v `C.IntAdd` litExpr 10))

eo_p4 :: TestCase EOExt EvenOdd'
eo_p4 = TC { tcDef = \ia -> (Ignore, gen ia)
           , tcHandle = genHandle
           , tcAssignment = PU.empty PU.:> Pointed Even
           , tcGlobals = PM.empty
           , tcCheck = check
           , tcDom = evenOddDom
           , tcInterp = evenOddInterp
           }
  where
    check _cfg _assignment rabs mWorklist = do
      T.assertEqual "retVal" (Pointed Odd) rabs
      case mWorklist of
        Nothing -> T.assertFailure "Expected worklist result"
        Just (_, rabs') -> T.assertEqual "WL Result" rabs rabs'

    gen initialAssignment = do
      r0 <- G.newReg (intLitReg 0)
      r1 <- G.newReg (intLitReg 0)
      let x = initialAssignment PU.! PU.baseIndex
      let c = app (atom x `C.IntLt` litExpr 5)
      G.ifte_ c (then_ r0 r1) (else_ r0 r1)
      rval <- G.readReg r1
      G.returnFromFunction rval

    then_ r0 r1 = do
      v <- G.readReg r0
      G.assignReg r1 (app (v `C.IntAdd` litExpr 3))

    else_ r0 r1 = do
      v <- G.readReg r0
      G.assignReg r1 (app (v `C.IntAdd` litExpr 11))

max_p1 :: TestCase SyntaxExt Max'
max_p1 = TC { tcDef = \ia -> (Ignore, gen ia)
            , tcHandle = genHandle
            , tcAssignment = PU.empty PU.:> Pointed (Max 5)
            , tcGlobals = PM.empty
            , tcCheck = check
            , tcDom = maxDom
            , tcInterp = maxInterp
            }
  where
    check _cfg _assignment rabs _ =
      T.assertEqual "retVal" (Pointed (Max 11)) rabs

    gen initialAssignment = do
      let x = initialAssignment PU.! PU.baseIndex
      let c = app (atom x `C.IntLt` litExpr 5)
      r0 <- G.newReg (atom x)
      G.ifte_ c (then_ r0) (else_ r0)
      rval <- G.readReg r0
      G.returnFromFunction rval

    then_ r0 = do
      v <- G.readReg r0
      G.assignReg r0 (app (v `C.IntAdd` litExpr 5))

    else_ r0 = do
      v <- G.readReg r0
      G.assignReg r0 (app (v `C.IntAdd` litExpr 6))

max_p2 :: TestCase SyntaxExt Max'
max_p2 = TC { tcDef = \ia -> (Ignore, gen ia)
            , tcHandle = genHandle
            , tcAssignment = PU.empty PU.:> Pointed (Max 5)
            , tcGlobals = PM.empty
            , tcCheck = check
            , tcDom = maxDom
            , tcInterp = maxInterp
            }
  where
    check _cfg _assignment rabs _ = do
      T.assertEqual "retVal" Top rabs

    gen initialAssignment = do
      let x = initialAssignment PU.! PU.baseIndex
      r0 <- G.newReg (atom x)
      G.while (P.InternalPos, test r0) (P.InternalPos, body r0)
      rval <- G.readReg r0
      G.returnFromFunction rval

    test r0 = do
      v <- G.readReg r0
      return (app (v `C.IntLt` litExpr 100))

    body r0 = do
      v <- G.readReg r0
      G.assignReg r0 (app (v `C.IntAdd` litExpr 1))


intLitReg :: C.IsSyntaxExtension exp => Integer -> G.Expr exp s C.IntegerType
intLitReg i = litExpr i

atom :: G.Atom s tp -> G.Expr exp s tp
atom = G.AtomExpr

data Ignore i = Ignore

isWTOIter :: IterationStrategy dom -> Bool
isWTOIter WTO = True
isWTOIter _ = False