{- CAO Compiler
Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . -}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE BangPatterns #-}
{- |
Module : $Header$
Description : CAO control flow graph.
Copyright : (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
License : GPL
Maintainer : Paulo Silva
Stability : experimental
Portability : non-portable
CAO control flow graph abstractions.
-}
module Language.CAO.Analysis.CFG
( NodeId
, BasicBlock
, Connections
, LocalGraph
, CaoCFG(..)
, buildCFG
, toAST
, getDefFromBlocks
, swaps
, showCFG
, entryNode
, exitNode
, removeSsaDecl
, graphFromEdges_
) where
import Control.Monad.State
import Data.Graph
import Data.List
import Data.Map (Map)
import qualified Data.Map as Map
import Language.CAO.Analysis.Dominance
import Language.CAO.Common.Outputable
import Language.CAO.Common.SrcLoc
import Language.CAO.Common.Utils
import Language.CAO.Common.Var
import Language.CAO.Syntax
import Language.CAO.Syntax.Utils (isSimpleVDecl)
--------------------------------------------------------------------------------
type NodeId = Int
type BasicBlock = [LStmt Var]
type Connections = [NodeId]
type LocalGraph = Map NodeId (BasicBlock, Connections)
data CaoCFG = CaoCFG {
definition :: LDef Var,
blocks :: LocalGraph
}
data CFGState = CFGState {
currentId :: !NodeId,
currentNode :: BasicBlock,
graph :: LocalGraph
}
emptyState :: CFGState
emptyState = CFGState (entryNode + 1) [] $
Map.fromList [ (entryNode, ([], [])), (exitNode, ([], [])) ]
entryNode :: NodeId
entryNode = 1
exitNode :: NodeId
exitNode = 0
currentNodeId :: State CFGState NodeId
currentNodeId = gets currentId
addToCurrentNode :: LStmt Var -> State CFGState ()
addToCurrentNode stmt =
modify $ \ s -> s { currentNode = stmt : currentNode s }
endCurrentNode :: State CFGState ()
endCurrentNode = modify $ \ s -> let
cnode = currentNode s
in if null cnode
then s
else s { graph = Map.insert (currentId s) (reverse cnode, []) (graph s),
currentNode = [],
currentId = succ (currentId s) }
addEmptyNode :: State CFGState NodeId
addEmptyNode = get >>= \ s -> do
let i = currentId s
cnode = reverse $ currentNode s
put $ s { graph = Map.insert i (cnode, []) (graph s),
currentNode = [],
currentId = succ i
}
return i
-- Precondition: endCurrentNode should have been called first
addSingleNode :: LStmt Var -> State CFGState NodeId
addSingleNode stmt = do
addToCurrentNode stmt
cid <- currentNodeId
endCurrentNode
return cid
addEdges :: [NodeId] -> [NodeId] -> State CFGState ()
addEdges origin target = mapM_ (`addEdge` target) origin
-- This needs to add target nodes to the end of the list,
-- in order to ensure the correct order when translating
-- back to an AST
addEdge :: NodeId -> [NodeId] -> State CFGState ()
addEdge origin target = modify $ \ s ->
s { graph = Map.adjust (mapSnd (++ target)) origin (graph s) }
--------------------------------------------------------------------------------
-- Generation of CFG
--------------------------------------------------------------------------------
------------------------------------------ Prog --------------------------------
buildCFG :: Prog Var -> [CaoCFG]
buildCFG (Prog defs _) = map definitionCFG defs
--------------------------------------------------------------------------------
--------------------------------------- Definition -----------------------------
definitionCFG :: LDef Var -> CaoCFG
definitionCFG (L l (FunDef (Fun n args rt body)))
| [] <- filter (not . isSimpleVDecl . unLoc) body =
let st = flip execState emptyState $ do
nd <- addEmptyNode
addEdges [entryNode] [nd]
addEdges [nd] [exitNode]
in CaoCFG fd (graph st)
| otherwise =
let st = flip execState emptyState $ do
(entry, exit) <- toGraph body
addEdges [entryNode] entry
addEdges exit [exitNode]
in CaoCFG fd (graph st)
where
fd = L l $ FunDef $ Fun n args rt []
definitionCFG def
= CaoCFG def Map.empty
--------------------------------------------------------------------------------
------------------------------------------ Func --------------------------------
-- The function returns the id of the entry node of a sub-graph and the list
-- of id's of exit blocks of a sub-graph
toGraph :: [LStmt Var] -> State CFGState ([NodeId], [NodeId])
toGraph [] = do
curNode <- currentNodeId
endCurrentNode
return ([curNode], [curNode])
toGraph (L l (VDecl (ContD vn _ exs)) : xs) = do
let sv = L (getLoc vn) $ mkStoreInit $ varName $ unLoc vn
declEntry l vn sv exs
toGraph xs
-- Ignoring variable declaration
toGraph (L l (VDecl (VarD vn _ _)) : xs) = do
declEntrySSA l vn
toGraph xs
toGraph (L l (VDecl (MultiD vns _)) : xs) = do
mapM_ (declEntrySSA l) vns
toGraph xs
toGraph (L l (CDecl (ConstD cn _ _)) : xs) = do
declEntrySSA l cn
toGraph xs
toGraph (L l (CDecl (MultiConstD cns _ _)) : xs) = do
mapM_ (declEntrySSA l) cns
toGraph xs
toGraph (s@(L _ (Assign _ _)) : xs) =
addToCurrentNode s >> toGraph xs
toGraph (s@(L _ (FCallS _ _)) : xs) =
addToCurrentNode s >> toGraph xs
toGraph (s@(L _ (Ret _)) : _) = do
curNode <- currentNodeId
addToCurrentNode s
endCurrentNode
addEdges [curNode] [exitNode]
return ([curNode], []) -- There is no exit point since the node is final
toGraph (L l (Ite i t Nothing) : xs) = do
curNode <- currentNodeId
addToCurrentNode (L l $ Ite i [] Nothing)
endCurrentNode
(ifEntryNode, ifExitNodes) <- toGraph t
(nextEntryNode, nextExitNodes) <- toGraphRest xs
addEdges [curNode] (ifEntryNode ++ nextEntryNode)
addEdges ifExitNodes nextEntryNode
return ([curNode], if null xs then curNode : ifExitNodes else nextExitNodes)
toGraph (L l (Ite i t (Just e)) : xs) = do
curNode <- currentNodeId
addToCurrentNode (L l $ Ite i [] (Just []))
endCurrentNode
(ifEntryNode, ifExitNodes) <- toGraph t
(elseEntryNode, elseExitNodes) <- toGraph e
(nextEntryNode, nextExitNodes) <- toGraphRest xs
addEdges [curNode] (ifEntryNode ++ elseEntryNode)
addEdges (ifExitNodes ++ elseExitNodes) nextEntryNode
return ([curNode],
if null xs then ifExitNodes ++ elseExitNodes else nextExitNodes)
toGraph (L l (While cond wstmts) : xs) = do
curNode <- currentNodeId
endCurrentNode
whileNode <- addSingleNode (L l $ While cond [])
(bodyEntryNode, bodyExitNodes) <- toGraph wstmts
(nextEntryNode, nextExitNodes) <- toGraphRest xs
when (curNode /= whileNode) $ addEdges [curNode] [whileNode]
addEdges [whileNode] (bodyEntryNode ++ nextEntryNode)
addEdges bodyExitNodes [whileNode]
return ([curNode], if null xs then [whileNode] else nextExitNodes)
toGraph (L l (Seq iter stmts) : xs) = do
curNode <- currentNodeId
endCurrentNode
seqNode <- addSingleNode (L l $ Seq iter [])
(bodyEntryNode, bodyExitNodes) <- toGraph stmts
(nextEntryNode, nextExitNodes) <- toGraphRest xs
when (curNode /= seqNode) $ addEdges [curNode] [seqNode]
addEdges [seqNode] (bodyEntryNode ++ nextEntryNode)
addEdges bodyExitNodes [seqNode]
return ([curNode], if null xs then [seqNode] else nextExitNodes)
toGraph (s@(L _ (Nop _)) : xs) =
addToCurrentNode s >> toGraph xs
declEntrySSA :: SrcLoc -> Located Var -> State CFGState ()
declEntrySSA l vn = declEntry l vn (genLoc ssaDecl) []
declEntry :: SrcLoc -> Located Var -> Located Var -> [TLExpr Var] -> State CFGState ()
declEntry l v fc exs = addToCurrentNode $
L l $ Assign [LVVar v] [ L l $ annTyE (varType $ unLoc v) $ FunCall fc exs]
toGraphRest :: [LStmt Var] -> State CFGState ([NodeId], [NodeId])
toGraphRest [] = return ([], [])
toGraphRest xs = toGraph xs
--------------------------------------------------------------------------------
-- Back from CFG
toAST :: [CaoCFG] -> Prog Var
toAST cfg = Prog (map getDefFromBlocks cfg) Nothing
getDefFromBlocks :: CaoCFG -> LDef Var
getDefFromBlocks (CaoCFG cdef cblocks) = case unLoc cdef of
FunDef (Fun n args rt []) ->
L (getLoc cdef) $ FunDef $ Fun n args rt $ bodyFromBlocks cblocks
_ -> cdef
bodyFromBlocks :: LocalGraph -> [LStmt Var]
bodyFromBlocks blks = let
g = graphFromEdges_ blks
dt = invertMap $ genDomTree g
in bodyFromBlocks' dt blks $ head $ snd $ blks Map.! entryNode
graphFromEdges_ :: LocalGraph -> Graph
graphFromEdges_ blks = let
(g,_,_) = graphFromEdges $ map (\ (k, (_, c)) -> (k, k, c) ) $ Map.assocs blks
in g
bodyFromBlocks' :: Map Vertex [Vertex] -> LocalGraph -> NodeId -> [LStmt Var]
bodyFromBlocks' domTree blks nid =
if nid == exitNode then [] else let
nextNodes = domTree Map.! nid
(bn, cn) = blks Map.! nid
(stmts, lastStmt) = initLast bn
in if null bn then [] else
case unLoc lastStmt of
Ite i _ Nothing -> stmts ++ ( L (getLoc lastStmt)
(Ite i (fetchNextBlock (cn !! 0)) Nothing) :
fetchNext (nextNodes \\ [cn !! 0]))
Ite i _ (Just _) -> stmts ++ ( L (getLoc lastStmt)
(Ite i (fetchNextBlock (cn !! 0))
(Just $ fetchNextBlock (cn !! 1))) :
fetchNext (nextNodes \\ [cn !! 0, cn !! 1]))
While c _ -> stmts ++ ( L (getLoc lastStmt)
(While c (fetchNextBlock (cn !! 0))) :
fetchNext (nextNodes \\ [cn !! 0]))
Seq i _ -> stmts ++ ( L (getLoc lastStmt)
(Seq i (fetchNextBlock (cn !! 0) )) :
fetchNext (nextNodes \\ [cn !! 0]))
_ -> bn ++ fetchNext nextNodes
where
fetchNextBlock = bodyFromBlocks' domTree blks
fetchNext = concatMap (bodyFromBlocks' domTree blks)
--- auxiliary ---
showCFG :: [CaoCFG] -> String
showCFG cfg = "digraph cao_cfg {\n" ++ unlines (map aux cfg) ++ invisedgs ++ "\n}"
where
invisedgs
| null invisedgs' = ""
| otherwise = "edge [style = invis]\n" ++ unlines invisedgs'
invisedgs' = graphs cfg
graphs :: [CaoCFG] -> [String]
graphs [] = []
graphs [_] = []
graphs ((CaoCFG (unLoc->FunDef f1) _):c@(CaoCFG (unLoc->FunDef f2) _):rest)
= let edg = "\"0" ++ showPprIds (funId f1)
++ "\" -> \"1" ++ showPprIds (funId f2) ++ "\"\n"
in edg:(graphs (c:rest))
graphs (d@(CaoCFG (unLoc->FunDef _) _):_:rest)
= graphs (d:rest)
graphs (_:rest)
= graphs rest
aux :: CaoCFG -> String
aux (CaoCFG def bk) = case unLoc def of
FunDef f -> let fundef = showPprIds (funId f)
in unlines $
["subgraph " ++ fundef ++ " {\n"] ++ nodes fundef bk ++ cfgEdges fundef bk ++
["}"]
_ -> ""
nodes str = map (\ (k, (s, _)) -> let
sst = if k == entryNode then "Entry\\l" ++ str ++ "\\n" else if k == exitNode then "Exit\\n" else ""
in "node [label=\"" ++ sst ++ showStmts s ++ "\"]\n\"" ++ show k ++ str ++ "\" [shape=box];")
. Map.assocs
cfgEdges str = concatMap (\ (k, (_, nl)) ->
map (\b -> '"':show k ++ str ++ "\" -> \"" ++ show b ++ str ++ "\"") nl) . Map.assocs
showStmts :: PP a => [a] -> String
showStmts = concatMap ((++ "\\l") . filter (/= '\n') . showPprIds)
removeSsaDecl :: CaoCFG -> CaoCFG
removeSsaDecl cfg = cfg { blocks = Map.map filterSsaDecls (blocks cfg) }
where filterSsaDecls :: (BasicBlock, Connections) -> (BasicBlock, Connections)
filterSsaDecls (ss, n) = (filter (not . isSsaDeclStmt . unLoc) ss, n)
isSsaDeclStmt :: Stmt Var -> Bool
isSsaDeclStmt (Assign [LVVar _] [unLoc -> unTyp -> FunCall fn []]) = isSsaDecl $ unLoc fn
isSsaDeclStmt _ = False