{- 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 ViewPatterns #-} {-# LANGUAGE PatternGuards #-} {- Module : $Header$ Description : Translating back from SSA form. 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 -} module Language.CAO.Analysis.SsaBack ( fromSSA , introduceDefs , rmVars ) where import Data.List ( foldl', partition, nubBy, insertBy, minimumBy ) import Data.Map ( Map ) import qualified Data.Map as Map import Data.Set ( Set ) import qualified Data.Set as Set import Language.CAO.Analysis.CFG import Language.CAO.Syntax import Language.CAO.Syntax.Utils import Language.CAO.Type.Utils import Language.CAO.Common.Utils import Language.CAO.Common.Var import Language.CAO.Common.SrcLoc fromSSA :: CaoCFG -> CaoCFG fromSSA = introduceDefs . rmVars . coalescePhiFuncs -- . eliminateInterference -- --eliminateInterference :: CaoCFG -> CaoCFG --eliminateInterference cfg = cfg -- where liveRanges = undefined -- -- interVars :: [Set String] -- interVars = undefined -- -- insertCopies :: Set String -> CaoCFG -> CaoCFG -- insertCopies = undefined -- type Location = (NodeId, Int) -- type RLoc = Map String Location -- -- type LiveIn = Map NodeId (Set String) -- type LiveOut = Map NodeId (Set String) -- -- type PhiCong = Map String (Set String) coalescePhiFuncs :: CaoCFG -> CaoCFG coalescePhiFuncs cfg = cfg { blocks = coalesceVars phiCong stmtMap } where stmtMap :: LocalGraph stmtMap = blocks cfg phiCong :: Map Var (Var, Set Var) phiCong = Map.fold (\(stmts, _) m -> foldl' getPhis m stmts) Map.empty stmtMap getPhis :: Map Var (Var, Set Var) -> LStmt Var -> Map Var (Var, Set Var) getPhis m (unLoc -> Assign [LVVar (unLoc -> str)] [unLoc -> unTyp -> FunCall (unLoc -> fId) args]) | isPhiFun fId = fixPhiC m $ str : (Set.toList $ fvs args) getPhis m _ = m fixPhiC :: Map Var (Var, Set Var) -> [Var] -> Map Var (Var, Set Var) fixPhiC m vs = Set.fold (\v -> Map.insert v congr) m congs where congs = Set.unions $ map fGetPhiCong vs congr = (Set.findMin congs, congs) fGetPhiCong v = maybe (Set.singleton v) snd (Map.lookup v m) coalesceVars :: Map Var (Var, Set Var) -> LocalGraph -> LocalGraph coalesceVars cong = Map.map (coalesceVarsStmts rnEnv) where rnEnv :: SEnv Var Var rnEnv = Map.foldWithKey renameF emptyRN cong -- All variables in a phi congruence class are renamed to -- one "representative" element (this case, the head of the list) renameF :: Var -> (Var, Set Var) -> SEnv Var Var -> SEnv Var Var renameF v1 (v2, _) b = b +> v1 ~> v2 -- XXX: can the order of composition be changed, ie., the filter can be -- performed before the map? coalesceVarsStmts :: (SEnv Var Var) -> (BasicBlock, Connections) -> (BasicBlock, Connections) coalesceVarsStmts cong = mapFst (filter (not . isPhi . unLoc) . map (<|> cong)) where isPhi :: Stmt Var -> Bool isPhi (Assign [LVVar _] [unLoc -> unTyp -> FunCall fId _]) = isPhiFun (unLoc fId) isPhi _ = False --------------------------------------------------------- -- TODO: REFACTOR vars <---> except (almost the same def) --------------------------------------------------------- introduceDefs :: CaoCFG -> CaoCFG introduceDefs cfg = cfg { blocks = addDecls (map mkDecl $ sortDeps neededDefs) blk } --introduceDefs cfg = addDecls (map mkDecl $ sortDeps neededDefs) cfg where blk :: LocalGraph blk = blocks cfg neededDefs :: [Var] neededDefs = filter isLocal $ Set.toList $ vars `Set.difference` except vars, except, alreadyDef, args :: Set Var vars = Map.fold foldVars Set.empty blk except = alreadyDef `Set.union` args alreadyDef = Map.fold foldDecls Set.empty blk args = bvs $ definition cfg foldDecls :: (BasicBlock, Connections) -> Set Var -> Set Var foldDecls (stmts, _) s0 = s0 `Set.union` bvs stmts foldVars :: (BasicBlock, Connections) -> Set Var -> Set Var foldVars (stmts, _) s0 = s0 `Set.union` fvs stmts --addDecls :: [LStmt Var] -> CaoCFG -> CaoCFG addDecls :: [LStmt Var] -> LocalGraph -> LocalGraph addDecls lst = Map.alter fAddDecl (entryNode + 1) where fAddDecl :: Maybe (BasicBlock, Connections) -> Maybe (BasicBlock, Connections) fAddDecl = fmap (mapFst (lst ++)) mkDecl :: Var -> LStmt Var mkDecl v = genLoc $ VDecl $ VarD (genLoc v) (type2TyDecl $ varType v) Nothing sortDeps :: [Var] -> [Var] sortDeps = sortDeps' Set.empty where sortDeps' _ [] = [] sortDeps' ds vs = vs1 ++ sortDeps' (ds `Set.union` Set.fromList vs1) vs2 where (vs1, vs2) = partition noDeps vs noDeps v = Set.filter isLocal (fvs $ varType v) `Set.isSubsetOf` ds -------------------------------------------------------------------------------- rmVars :: CaoCFG -> CaoCFG rmVars cfg = CaoCFG { definition = d0, blocks = b0 } where rnMap :: SEnv Var Var rnMap = mkRenameMap $ varRange cfg b0 :: LocalGraph b0 = filterDecls $ Map.map rmAndFilter $ blocks cfg rmAndFilter :: (BasicBlock, Connections) -> (BasicBlock, Connections) rmAndFilter = mapFst (\ stmts -> filter filterAssigns $ stmts <|> rnMap) d0 :: LDef Var d0 = fmap (fmap (<|> rnMap)) (definition cfg) filterAssigns :: LStmt Var -> Bool filterAssigns (unLoc -> Assign [LVVar v] [unLoc -> unTyp -> Var v']) = unLoc v /= v' filterAssigns _ = True filterDecls :: LocalGraph -> LocalGraph filterDecls = Map.map (mapFst nubDecls) where nubDecls :: [LStmt Var] -> [LStmt Var] nubDecls = nubBy eqDecls eqDecls :: LStmt Var -> LStmt Var -> Bool eqDecls (L _ (VDecl v0)) (L _ (VDecl v1)) = eqVarDecls v0 v1 eqDecls _ _ = False -- TODO: INCOMPLETE DEFINITION (MAY CAUSE BUGS?) eqVarDecls :: VarDecl Var -> VarDecl Var -> Bool eqVarDecls (VarD v0 _ _) (VarD v1 _ _ ) = v0 == v1 eqVarDecls (MultiD v0 _ ) (MultiD v1 _ ) = v0 == v1 eqVarDecls (ContD v0 _ _) (ContD v1 _ _) = v0 == v1 eqVarDecls _ _ = False mkRenameMap :: VarRange -> SEnv Var Var mkRenameMap rng = foldl' (+>) emptyRN $ map mkM gRanges where rnglst :: [(Var, Range)] rnglst = Map.toList rng -- vars grouped same type gTypes :: [[(Var, Range)]] gTypes = groupType rnglst gRanges :: [[(Var, Range)]] gRanges = concatMap (accumRanges [] []) gTypes mkM :: [(Var, Range)] -> SEnv Var Var mkM [] = emptyRN mkM [_] = emptyRN mkM xs = let (x, _) = minimumBy cmpRng xs in foldl' (\a (b, _) -> a +> b ~> x) emptyRN (init xs) accumRanges :: [(Var, Range)] -> [(Var, Range)] -> [(Var, Range)] -> [[(Var, Range)]] accumRanges [] acc [] = [acc] accumRanges orig acc [] = acc:accumRanges [] [] orig accumRanges orig acc (x:xs) = case mutuallyDisj x acc of Just lst -> accumRanges orig lst xs Nothing -> accumRanges (x:orig) acc xs mutuallyDisj :: (Var, Range) -> [(Var, Range)] -> Maybe [(Var, Range)] mutuallyDisj l@(v, r0) rg | not (isContainer (varType v)) && all (disjoint r0 . snd) rg = Just (l:rg) | lst <- insertBy cmpRng l rg, chainsSafely lst = Just lst | otherwise = Nothing cmpRng :: (Var, Range) -> (Var, Range) -> Ordering cmpRng (_, FromTo l00 l01 _ _) (_,FromTo l10 l11 _ _) | l10 `gtLoc` l01 = LT | l00 `gtLoc` l11 = GT | otherwise = compare l00 l10 chainsSafely :: [(Var, Range)] -> Bool chainsSafely [] = True chainsSafely [_] = True chainsSafely ((_, FromTo _ l0 _ _):rest@((_, FromTo l1 _ ab1 _):_)) = l1 `gtLoc` l0 && ab1 && chainsSafely rest groupType :: [(Var, Range)] -> [[(Var, Range)]] groupType [] = [] groupType (x:rest) = let (st, r) = partition (sameType x) rest in (x:st):groupType r sameType :: (Var, Range) -> (Var, Range) -> Bool sameType (t0, _) (t1, _) = varType t0 == varType t1 disjoint :: Range -> Range -> Bool disjoint (FromTo l00 l01 _ _) (FromTo l10 l11 _ _) = l11 `gtLoc` l10 && l01 `gtLoc` l00 && (l10 `gtLoc` l01 || l00 `gtLoc` l11) gtLoc :: Location -> Location -> Bool gtLoc (n0, loc0) (n1, loc1) -- Special case when 0 | n1 == 0 = False | n0 == 0 = True -- Lexicographic order | n0 > n1 = True | n0 < n1 = False -- When n1 == n2 | otherwise = loc0 >= loc1 type Location = (NodeId, Int) data Range = FromTo { _fromL :: Location , _toL :: Location , _safeA :: Bool -- safeA is True when the variable is -- initialized completely in its -- first assignment , _safeL :: Bool -- if safeL = True, it is safe to -- consider >= instead of > to check -- disjoint live ranges, } deriving Show type VarRange = Map Var Range -- NOTE: USED ONLY FOR TESTING PURPOSES! --showVR m = Map.foldWithKey (\k a acc -> showPpr k ++ "\\\\\\" ++ show a ++ "\n" ++ acc) "" m varRange :: CaoCFG -> VarRange varRange cfg = vRange where vRange :: VarRange vRange = Map.filterWithKey (\k _ -> isLocal k && not (k `elem` seqVars)) gvRange gvRange :: VarRange gvRange = traverseCFG [entryNode + 1] [] argsRange (blocks cfg) seqVars :: [Var] seqVars = getSeqVars cfg argsRange :: VarRange argsRange = Set.fold fArgs Map.empty $ bvs $ definition cfg fArgs :: Var -> VarRange -> VarRange fArgs v m | nsVar v = Map.insert v (FromTo (entryNode, 0) (exitNode, 0) False False) m | otherwise = m getSeqVars :: CaoCFG -> [Var] getSeqVars = concatMap doGetSV . concatMap fst . Map.elems . blocks where doGetSV (unLoc -> Seq i _) = [seqVar i] doGetSV _ = [] -- NOTE: if a variable is first assigned in node 3, loc 5, but -- there is a loop back to node 2, its range should be fixed to be from node 2, -- loc 0: -- while .. -- ... x1 -- ... -- x1 := ... -- This should not be a problem, as the first x1 will never be renamed. But -- we should be careful about this. traverseCFG :: [NodeId] -> [NodeId] -> VarRange -> LocalGraph -> VarRange traverseCFG [] _ m _ = m traverseCFG (n:ns) seen m cfg | n `elem` seen = traverseCFG ns seen m cfg | otherwise = traverseCFG (ns ++ next) (n:seen) m' cfg where blk :: BasicBlock next :: Connections (blk, next) = cfg Map.! n m' :: VarRange m' = foldl' updateRanges m nStmts nStmts :: [(Location, LStmt Var)] nStmts = zip [ (n, i) | i <- [1..] ] blk updateRanges :: VarRange -> (Location, LStmt Var) -> VarRange updateRanges rng (lloc,ss@(unLoc -> Assign lvs _)) | all isSimpleLVal lvs = fixAssignRng True lvns rvns | otherwise = fixAssignRng False lvns rvns where fixAssignRng :: Bool -> [Var] -> [Var] -> VarRange fixAssignRng safe lVars rVars = let rng' = foldl' (fixLRanges safe lloc) rng lVars in foldl' (fixRanges safe lloc) rng' rVars lvns = lvnames ss rvns = lvns ++ rvnames ss updateRanges rng (lloc, ss) = foldl' (fixRanges False lloc) rng (rvnames ss) fixLRanges :: Bool -> Location -> VarRange -> Var -> VarRange fixLRanges b l = flip (Map.alter (updateDefRange b l)) fixRanges :: Bool -> Location -> VarRange -> Var -> VarRange fixRanges b l = flip (Map.alter (updateLastRange b l)) updateDefRange :: Bool -> Location -> Maybe Range -> Maybe Range updateDefRange b loc Nothing = Just (FromTo loc (exitNode, 0) b False) updateDefRange ab l@(n, loc) mr@(Just (FromTo (n0,l0) loc1 _ b)) | n0 < n || (n0 == n && l0 < loc) = mr | otherwise = Just $ FromTo l loc1 ab b updateLastRange :: Bool -> Location -> Maybe Range -> Maybe Range -- If it was not previously used as a lvalue, it should be considered -- alive along the whole CFG updateLastRange _ _ Nothing = Just (FromTo (entryNode, 0) (exitNode , 0) False False) updateLastRange b l@(n,loc) mr@(Just (FromTo loc1 (n0,l0) ab _)) | n0 > n || (n0 == n && l0 > loc) = mr | otherwise = Just $ FromTo loc1 l ab b -- TODO: REFACTOR IN Language.CAO.Syntax.Utils lvnames :: LStmt Var -> [Var] lvnames (unLoc -> Assign lvs _) = map lvname lvs lvnames _ = [] rvnames :: LStmt Var -> [Var] rvnames (unLoc -> ss@(Assign lvs _)) = Set.toList rvs where vs = fvs ss vlvs = Set.fromList $ map lvname lvs rvs = vs Set.\\ vlvs rvnames stmt = Set.toList $ fvs stmt