language-boogie-0.1: Language/Boogie/BasicBlocks.hs
-- | Basic block transformation for imperative Boogie code
module Language.Boogie.BasicBlocks (toBasicBlocks, startLabel) where
import Language.Boogie.AST
import Language.Boogie.Util
import Language.Boogie.Position
import Data.Map (Map, (!))
import qualified Data.Map as M
import Control.Monad.State
import Control.Applicative
-- | Transform procedure body into a sequence of basic blocks.
-- A basic block starts with a label and contains no jump, if or while statements,
-- except for the last statement, which can be a goto or return.
toBasicBlocks :: Block -> [BasicBlock]
toBasicBlocks body = let
tbs = evalState (concat <$> (mapM (transform M.empty) (map node body))) 0
-- By the properties of transform, tbs' is a sequence of basic blocks
tbs' = attach startLabel (tbs ++ [justBareStatement Return])
-- Append a labeled statement to a sequence of basic blocks
-- (the first labeled statement cannot have empty label)
append :: [BasicBlock] -> BareLStatement -> [BasicBlock]
append bbs ([l], Pos _ Skip) = (l, []) : bbs
append bbs ([l], s) = (l, [s]) : bbs
append ((l, ss) : bbs) ([], s) = (l, ss ++ [s]) : bbs
in
-- First flatten control flow with transform, and then convert to basic blocks
reverse (foldl append [] tbs')
-- | Label of the first block in a procedure
startLabel = "00_start"
-- | Attach a label to the first statement (with an empty label) in a non-empty list of labeled statements
attach :: Id -> [BareLStatement] -> [BareLStatement]
attach l (([], stmts) : lsts) = ([l], stmts) : lsts
-- | LStatement with no label (no source position, generated)
justBareStatement s = ([], gen s)
-- | LStatement with no label (with a source position, derived from a source statement)
justStatement pos s = ([], Pos pos s)
-- | LStatement with no statement
justLabel l = ([l], gen Skip)
-- | Special label value that denoted the innermost loop (used for break)
innermost = "innermost"
-- | genFreshLabel kind i: returns a label of kind with id i and the id for the next label
genFreshLabel :: String -> Int -> (String, Int)
genFreshLabel kind i = (show i ++ "_" ++ kind, i + 1)
-- | transform m statement: transform statement into a sequence of basic blocks;
-- m is a map from statement labels to labels of their exit points (used for break)
transform :: Map Id Id -> BareLStatement -> State Int [BareLStatement]
transform m (l:lbs, Pos p Skip) = do
t <- transform m (lbs, Pos p Skip)
return $ (justBareStatement $ Goto [l]) : attach l t
transform m (l:lbs, stmt) = do
lDone <- state $ genFreshLabel "done"
t <- transform (M.insert l lDone m) (lbs, stmt)
return $ [justBareStatement $ Goto [l]] ++ attach l t ++ [justBareStatement $ Goto [lDone], justLabel lDone]
transform m ([], Pos p stmt) = case stmt of
Goto lbs -> do
lUnreach <- state $ genFreshLabel "unreachable"
return $ [justStatement p (Goto lbs), justLabel lUnreach]
Break (Just l) -> do
lUnreach <- state $ genFreshLabel "unreachable"
return $ [justStatement p (Goto [m ! l]), justLabel lUnreach]
Break Nothing -> do
lUnreach <- state $ genFreshLabel "unreachable"
return $ [justStatement p (Goto [m ! innermost]), justLabel lUnreach]
Return -> do
lUnreach <- state $ genFreshLabel "unreachable"
return $ [justStatement p Return, justLabel lUnreach]
If cond thenBlock Nothing -> transform m (justStatement p (If cond thenBlock (Just [])))
If we thenBlock (Just elseBlock) -> do
lThen <- state $ genFreshLabel "then"
lElse <- state $ genFreshLabel "else"
lDone <- state $ genFreshLabel "done"
t1 <- transBlock m thenBlock
t2 <- transBlock m elseBlock
case we of
Wildcard -> return $
[justBareStatement $ Goto [lThen, lElse]] ++
attach lThen (t1 ++ [justBareStatement $ Goto [lDone]]) ++
attach lElse (t2 ++ [justBareStatement $ Goto [lDone]]) ++
[justLabel lDone]
Expr e -> return $
[justBareStatement $ Goto [lThen, lElse]] ++
[([lThen], assume e)] ++ t1 ++ [justBareStatement $ Goto [lDone]] ++
[([lElse], assume (enot e))] ++ t2 ++ [justBareStatement $ Goto [lDone]] ++
[justLabel lDone]
While Wildcard invs body -> do
lHead <- state $ genFreshLabel "head"
lBody <- state $ genFreshLabel "body"
lDone <- state $ genFreshLabel "done"
t <- transBlock (M.insert innermost lDone m) body
return $
[justBareStatement $ Goto [lHead]] ++
attach lHead (map checkInvariant invs ++ [justBareStatement $ Goto [lBody, lDone]]) ++
attach lBody (t ++ [justBareStatement $ Goto [lHead]]) ++
[justLabel lDone]
While (Expr e) invs body -> do
lHead <- state $ genFreshLabel "head"
lBody <- state $ genFreshLabel "body"
lGDone <- state $ genFreshLabel "guarded_done"
lDone <- state $ genFreshLabel "done"
t <- transBlock (M.insert innermost lDone m) body
return $
[justBareStatement $ Goto [lHead]] ++
attach lHead (map checkInvariant invs ++ [justBareStatement $ Goto [lBody, lGDone]]) ++
[([lBody], assume e)] ++ t ++ [justBareStatement $ Goto [lHead]] ++
[([lGDone], assume (enot e))] ++ [justBareStatement $ Goto [lDone]] ++
[justLabel lDone]
_ -> return [justStatement p stmt]
where
transBlock m b = concat <$> mapM (transform m) (map node b)
checkInvariant inv = justStatement (position (specExpr inv)) (Predicate inv)