202 lines
5.7 KiB
Haskell
202 lines
5.7 KiB
Haskell
{-# LANGUAGE OverloadedLists #-}
|
|
module Gyehoek.CPS.Stackify
|
|
( stackifyProgram
|
|
, module Gyehoek.CPS.Syntax
|
|
) where
|
|
|
|
import Gyehoek.CPS.Syntax
|
|
import Gyehoek.Stack.Syntax qualified as Stk
|
|
import Data.Sequence (Seq)
|
|
import Data.Sequence qualified as Seq
|
|
import Gyehoek.GenSym
|
|
import Effectful.Writer.Static.Shared
|
|
import Data.Foldable
|
|
import qualified Data.HashMap.Strict as H
|
|
import Data.List (elemIndex, nub)
|
|
import Data.Text qualified as T
|
|
import Gyehoek.Prelude
|
|
import Debug.Pretty.Simple
|
|
import qualified Gyehoek.Sexp as S
|
|
|
|
|
|
type Stackify = Writer Stk.Program
|
|
|
|
runStackify :: Eff (Stackify : es) a -> Eff es (a, Stk.Program)
|
|
runStackify = runWriter
|
|
|
|
live :: Free a => Env -> a -> List Name
|
|
-- TODO: free' should return an OSet lol
|
|
live g e = nub (free' e) & filter \x ->
|
|
x `elem` g.bound
|
|
-- && not (x `elem` g.contStack)
|
|
|
|
data BlockBuilder
|
|
= Code (List Stk.Instr) BlockBuilder
|
|
| Tail Stk.Tail
|
|
deriving (Show, Generic)
|
|
|
|
buildBlock :: BlockBuilder -> Stk.Block
|
|
buildBlock = go [] where
|
|
go acc (Code xs bb) = go (acc ++ xs) bb
|
|
go acc (Tail t) = Stk.MkBlock acc t
|
|
|
|
emitRoutine :: Stackify :> es => Stk.Routine -> Eff es ()
|
|
emitRoutine rt = tell [rt]
|
|
|
|
stackify
|
|
:: (GenSym :> es, Stackify :> es)
|
|
=> Env -> Exp -> Eff es BlockBuilder
|
|
|
|
stackify g (ExpLetRec [(f, AbsKappa kap)] e) = do
|
|
kap' <- stackifyKappa g kap
|
|
emitRoutine . Stk.MkRoutine (MkLabel f) . buildBlock $ kap'
|
|
stackify g e
|
|
|
|
stackify g (ExpLetRec [(f, AbsLambda lam)] e) = do
|
|
lam' <- stackifyLambda g (MkLabel f) lam
|
|
emitRoutine lam'
|
|
stackify g e
|
|
|
|
stackify g (ExpIf c t f) = do
|
|
let c' = stackifyVal g c
|
|
t' <- buildBlock <$> stackify g t
|
|
f' <- buildBlock <$> stackify g f
|
|
pure . Tail $ Stk.If c' t' f'
|
|
|
|
stackify g (ExpApply f xs ktail) = do
|
|
pure $
|
|
Code [ Stk.Push $ stackifyVal g (ValVar ktail)
|
|
, Stk.Push $ stackifyVal g f
|
|
] $
|
|
Code (pushArgs g xs) $
|
|
Tail (Stk.Call (length xs))
|
|
|
|
-- assume that `k` is the continuation on top of the stack lol.
|
|
stackify g e@(ExpContinue k xs)
|
|
| isn't (#_ValVar . only g.tail) k = pure $
|
|
Code [ Stk.Push (stackifyVal g k) ] $
|
|
Code (pushArgs g xs) $
|
|
Tail $ Stk.TailCall (length xs)
|
|
| otherwise = pure $
|
|
Code (pushArgs g xs) $
|
|
Tail (Stk.Return (length xs))
|
|
|
|
stackify g (ExpPrim (PrimCallCC withcc) cc) = do
|
|
cc' <- stackifyKappa g cc
|
|
cc_l <- gensym' @Name "cc"
|
|
reified_cc_l <- gensym' @Name "reified-cc"
|
|
emitRoutine . Stk.MkRoutine (MkLabel cc_l) . buildBlock $ cc'
|
|
stackify g $
|
|
ExpPrim PrimCaptureCC $
|
|
MkKappa [reified_cc_l] $
|
|
ExpApply withcc [ValVar reified_cc_l] cc_l
|
|
|
|
stackify g (ExpPrim p kap) = do
|
|
kap' <- stackifyKappa g kap
|
|
pure $ Code [ Stk.Prim (stackifyVal g <$> p) ] kap'
|
|
|
|
stackify _ e = error [i|unimplemented exp: #{e}|]
|
|
|
|
loadArgs :: List Name -> List Stk.Instr
|
|
loadArgs = imapOf itraversed \n x -> Stk.Load (MkReg x) n
|
|
|
|
pushArgs :: Env -> List Val -> List Stk.Instr
|
|
pushArgs g args = [ Stk.Push $ stackifyVal g x | x <- reverse args ]
|
|
|
|
-- affine
|
|
_ValName :: Traversal' Val Name
|
|
_ValName = failing #_ValVar (#_ValImm . #_ImmLabel . #_MkLabel)
|
|
|
|
stackifyKappa
|
|
:: (Stackify :> es, GenSym :> es)
|
|
=> Env -> Kappa
|
|
-> Eff es BlockBuilder
|
|
stackifyKappa g (MkKappa xs m) = do
|
|
let g' = g & #bound <>:~ xs
|
|
Code (loadArgs g'.bound)
|
|
<$> stackify g' m
|
|
|
|
stackifyLambda
|
|
:: (Stackify :> es, GenSym :> es)
|
|
=> Env -> Label -> Lambda
|
|
-> Eff es Stk.Routine
|
|
stackifyLambda g name (MkLambda xs k m) = do
|
|
m' <- stackify (g & #bound .~ xs & #tail .~ k) m
|
|
pure $
|
|
Stk.MkRoutine name . buildBlock $
|
|
Code (loadArgs xs) $
|
|
Code [Stk.Load (MkReg k) (length xs + 1)] m'
|
|
|
|
stackifyVal :: Env -> Val -> Stk.Val
|
|
stackifyVal g = \case
|
|
ValImm imm -> Stk.ValImm imm
|
|
ValVar v -> case regOf g v of
|
|
Just r -> Stk.ValReg r
|
|
Nothing -> Stk.ValLabel (MkLabel v)
|
|
v -> error [i|unimplemented val: #{v}|]
|
|
|
|
regOf :: Env -> Name -> Maybe Reg
|
|
regOf g x
|
|
| x `elem` g.bound || x == g.tail = Just . MkReg $ x
|
|
| otherwise = Nothing
|
|
|
|
|
|
|
|
data Env = MkEnv
|
|
-- | `bound` tracks the stack lifetime of bound variables.
|
|
{ bound :: List Name
|
|
-- | for each locally-bound continuation @k@, @liveness@ has an
|
|
-- entry @(k,ls)@ where @ls@ is the sequence of registers @k@
|
|
-- expects to find saved on the stack.
|
|
, liveness :: HashMap Label (List Name)
|
|
, tail :: Name
|
|
}
|
|
deriving (Show, Generic)
|
|
|
|
emptyEnv :: Env
|
|
emptyEnv = MkEnv
|
|
{ bound = mempty
|
|
, liveness = mempty
|
|
, tail = "halt"
|
|
}
|
|
|
|
|
|
|
|
stackifyProgram :: GenSym :> es => Program -> Eff es Stk.Program
|
|
stackifyProgram (MkProgram lam) = do
|
|
let g = emptyEnv
|
|
(_,p) <- runStackify $ emitRoutine =<< stackifyLambda g "start" lam
|
|
pure p
|
|
|
|
letfn :: Program
|
|
letfn = [cps|
|
|
(λ (start-ktail0)
|
|
(letrec ((lambda-body1
|
|
(λ (x lambda-tail2)
|
|
(prim (* x x) (κ (r3) (continue lambda-tail2 r3))))))
|
|
(letrec ((let-body6
|
|
(κ (square)
|
|
(letrec ((r4 (κ (x5) (continue start-ktail0 x5))))
|
|
(square 4 r4)))))
|
|
(continue let-body6 lambda-body1))))
|
|
|]
|
|
|
|
blah :: Program
|
|
blah = [cps|
|
|
(λ (ktail0)
|
|
(letrec ((fac (λ (n ktail)
|
|
(prim (zero? n)
|
|
(κ (x0)
|
|
(if x0
|
|
(continue ktail 1)
|
|
(prim (- n 1)
|
|
(κ (x1)
|
|
(letrec ((fac-k0
|
|
(κ (x2)
|
|
(prim (* n x2)
|
|
(κ (x3)
|
|
(continue ktail x3))))))
|
|
(fac x1 fac-k0))))))))))
|
|
(fac 6 halt)))
|
|
|]
|