module Language.Nano.SSA.SSA (ssaTransform) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad
import qualified Data.HashMap.Strict as M
import Language.Nano.Types
import Language.Nano.Errors
import Language.Nano.Env
import Language.Nano.Typecheck.Types
import Language.Nano.SSA.SSAMonad
import Language.ECMAScript3.Syntax
import Language.ECMAScript3.Syntax.Annotations
import Language.ECMAScript3.PrettyPrint
import Language.Fixpoint.Misc
import Text.Printf (printf)
ssaTransform :: (PP t) => Nano SourcePos t -> Nano AnnSSA t
ssaTransform = either (errorstar . snd) id . execute . ssaNano
ssaNano :: (PP t) => Nano SourcePos t -> SSAM (Nano AnnSSA t)
ssaNano p@(Nano {code = Src fs})
= do addImmutables $ envMap (\_ -> ()) (specs p)
addImmutables $ envMap (\_ -> ()) (defs p)
addImmutables $ envMap (\_ -> ()) (consts p)
(_,fs') <- ssaStmts fs
anns <- getAnns
return $ p {code = Src $ (patchAnn anns <$>) <$> fs'}
patchAnn :: AnnInfo -> SourcePos -> AnnSSA
patchAnn m l = Ann l $ M.lookupDefault [] l m
ssaFun :: FunctionStatement SourcePos -> SSAM (FunctionStatement SourcePos)
ssaFun (FunctionStmt l f xs body)
= do θ <- getSsaEnv
imms <- getImmutables
addImmutables $ envMap (\_ -> ()) θ
setSsaEnv $ extSsaEnv ((returnId l) : xs) θ
(_, body') <- ssaStmts body
setSsaEnv θ
setImmutables imms
return $ FunctionStmt l f xs body'
ssaSeq :: (a -> SSAM (Bool, a)) -> [a] -> SSAM (Bool, [a])
ssaSeq f = go True
where
go False zs = return (False, zs)
go b [] = return (b , [])
go True (x:xs) = do (b , y) <- f x
(b', ys) <- go b xs
return (b', y:ys)
ssaStmts :: [Statement SourcePos] -> SSAM (Bool, [Statement SourcePos])
ssaStmts = ssaSeq ssaStmt
ssaStmt :: Statement SourcePos -> SSAM (Bool, Statement SourcePos)
ssaStmt s@(EmptyStmt _)
= return (True, s)
ssaStmt (ExprStmt l1 (AssignExpr l2 OpAssign (LVar l3 x) e))
= do (x', e') <- ssaAsgn l2 (Id l3 x) e
return (True, VarDeclStmt l1 [VarDecl l2 x' (Just e')])
ssaStmt (ExprStmt l e)
= do e' <- ssaExpr e
return (True, ExprStmt l e')
ssaStmt (BlockStmt l stmts)
= do (b, stmts') <- ssaStmts stmts
return (b, BlockStmt l stmts')
ssaStmt (IfSingleStmt l b s)
= ssaStmt (IfStmt l b s (EmptyStmt l))
ssaStmt (IfStmt l e s1 s2)
= do e' <- ssaExpr e
θ <- getSsaEnv
(θ1, s1') <- ssaWith θ ssaStmt s1
(θ2, s2') <- ssaWith θ ssaStmt s2
(θ', φ1, φ2) <- envJoin l θ1 θ2
let stmt' = IfStmt l e' (splice s1' φ1) (splice s2' φ2)
case θ' of
Just θ'' -> setSsaEnv θ'' >> return (True, stmt')
Nothing -> return (False, stmt')
ssaStmt (VarDeclStmt l ds)
= do (_, ds') <- ssaSeq ssaVarDecl ds
return (True, VarDeclStmt l ds')
ssaStmt s@(ReturnStmt _ Nothing)
= return (False, s)
ssaStmt (ReturnStmt l (Just e))
= do e' <- ssaExpr e
return (False, ReturnStmt l (Just e'))
ssaStmt s@(FunctionStmt _ _ _ _)
= (True,) <$> ssaFun s
ssaStmt s
= convertError "ssaStmt" s
splice :: Statement SourcePos -> Maybe (Statement SourcePos) -> Statement SourcePos
splice s Nothing = s
splice s (Just s') = seqStmt (getAnnotation s) s s'
seqStmt _ (BlockStmt l s) (BlockStmt _ s') = BlockStmt l (s ++ s')
seqStmt l s s' = BlockStmt l [s, s']
ssaWith :: SsaEnv -> (a -> SSAM (Bool, a)) -> a -> SSAM (Maybe SsaEnv, a)
ssaWith θ f x
= do setSsaEnv θ
(b, x') <- f x
(, x') <$> (if b then Just <$> getSsaEnv else return Nothing)
ssaExpr :: Expression SourcePos -> SSAM (Expression SourcePos)
ssaExpr e@(IntLit _ _)
= return e
ssaExpr e@(BoolLit _ _)
= return e
ssaExpr e@(VarRef l x)
= do imm <- isImmutable x
xo <- findSsaEnv x
case xo of
Just z -> return $ VarRef l z
Nothing -> if imm
then return e
else ssaError (srcPos x) $ errorUnboundId x
ssaExpr (PrefixExpr l o e)
= PrefixExpr l o <$> ssaExpr e
ssaExpr (InfixExpr l o e1 e2)
= InfixExpr l o <$> ssaExpr e1 <*> ssaExpr e2
ssaExpr (CallExpr l e es)
= CallExpr l <$> ssaExpr e <*> mapM ssaExpr es
ssaExpr e
= convertError "ssaExpr" e
ssaVarDecl :: VarDecl SourcePos -> SSAM (Bool, VarDecl SourcePos)
ssaVarDecl (VarDecl l x (Just e))
= do (x', e') <- ssaAsgn l x e
return (True, VarDecl l x' (Just e'))
ssaVarDecl z@(VarDecl l x Nothing)
= errorstar $ printf "Cannot handle ssaVarDECL %s at %s" (ppshow x) (ppshow l)
ssaAsgn :: SourcePos -> Id SourcePos -> Expression SourcePos -> SSAM (Id SourcePos, Expression SourcePos)
ssaAsgn l x e
= do e' <- ssaExpr e
x' <- updSsaEnv l x
return (x', e')
envJoin :: SourcePos -> Maybe SsaEnv -> Maybe SsaEnv
-> SSAM ( Maybe SsaEnv
, Maybe (Statement SourcePos)
, Maybe (Statement SourcePos) )
envJoin _ Nothing Nothing = return (Nothing, Nothing, Nothing)
envJoin _ Nothing (Just θ) = return (Just θ , Nothing, Nothing)
envJoin _ (Just θ) Nothing = return (Just θ , Nothing, Nothing)
envJoin l (Just θ1) (Just θ2) = envJoin' l θ1 θ2
envJoin' l θ1 θ2
= do setSsaEnv θ'
stmts <- forM phis $ phiAsgn l
θ'' <- getSsaEnv
let (s1,s2) = unzip stmts
return (Just θ'', Just $ BlockStmt l s1, Just $ BlockStmt l s2)
where
θ = envIntersectWith meet θ1 θ2
θ' = envRights θ
phis = envToList $ envLefts θ
meet = \x1 x2 -> if x1 == x2 then Right x1 else Left (x1, x2)
phiAsgn l (x, (SI x1, SI x2))
= do x' <- updSsaEnv l x
addAnn l (PhiVar x')
let s1 = mkPhiAsgn l x' x1
let s2 = mkPhiAsgn l x' x2
return $ (s1, s2)
where
mkPhiAsgn l x y = VarDeclStmt l [VarDecl l x (Just $ VarRef l y)]