deconstruct closures only at the bytecode level

This commit is contained in:
2026-08-30 02:12:16 -06:00
parent a09c00badd
commit 0df7280236
3 changed files with 49 additions and 43 deletions
+15 -11
View File
@@ -18,23 +18,27 @@ close = transformM \case
-- explicitly substitute recursive calls. -- explicitly substitute recursive calls.
let frees = nub $ freeWithBound' [f] lam let frees = nub $ freeWithBound' [f] lam
let m' = ifoldr let m' = ifoldr
(\n x q -> [cps|(prim (env-ref #{f} #{n}) (\n x q ->
(κ (#{x}) #{q}))|]) let n' = if x == f then 0 else n+1
in [cps|
(prim (env-ref #{n'})
(κ (#{x}) #{q}))
|])
m frees m frees
pure [cps| pure [cps|
(letrec ((#{f_code} (λ (#{f} ##{bs} #{kb}) (letrec ((#{f_code} (λ (##{bs} #{kb})
#{m'}))) #{m'})))
(prim (make-closure ($ #{f_code}) ##{frees}) (prim (make-closure #{f_code} ##{frees})
(κ (#{f}) #{e}))) (κ (#{f}) #{e})))
|] |]
ExpApply f xs ktail -> do -- ExpApply f xs ktail -> do
code <- gensym' @Name "code" -- code <- gensym' @Name "code"
pure [cps| -- pure [cps|
(prim (env-code #{f}) -- (prim (env-code #{f})
(κ (#{code}) -- (κ (#{code})
(#{code} #{f} ##{xs} #{ktail}))) -- (#{code} #{f} ##{xs} #{ktail})))
|] -- |]
e -> pure e e -> pure e
+2 -2
View File
@@ -81,7 +81,7 @@ data Prim e
| PrimZeroP e | PrimZeroP e
| PrimNewline | PrimNewline
| PrimMakeClosure { code :: e, env :: List e } | PrimMakeClosure { code :: e, env :: List e }
| PrimEnvRef e Int | PrimEnvRef Int
| PrimEnvCode e | PrimEnvCode e
| PrimCallCC e | PrimCallCC e
| PrimCaptureCC | PrimCaptureCC
@@ -167,7 +167,7 @@ primDatumIso namefn a = S.match
$ S.With (. ht1 "zero?") $ S.With (. ht1 "zero?")
$ S.With (. ht0 "newline") $ S.With (. ht0 "newline")
$ S.With (. ht1' "make-closure") $ S.With (. ht1' "make-closure")
$ S.With (. S.headTagged2 (namefn "env-ref") a S.int) $ S.With (. S.headTagged1 (namefn "env-ref") S.int)
$ S.With (. ht1 "env-code") $ S.With (. ht1 "env-code")
$ S.With (. ht1 "call/cc") $ S.With (. ht1 "call/cc")
$ S.With (. ht0 "capture/cc") $ S.With (. ht0 "capture/cc")
+32 -30
View File
@@ -45,15 +45,12 @@ newtype Frame = MkFrame { locals :: List Obj }
deriving stock (Show, Generic) deriving stock (Show, Generic)
-- affine -- affine
returnAddress :: Traversal' Frame Label returnAddress :: Traversal' Frame Obj
returnAddress = #locals . _last . #ObjImm . #ImmLabel returnAddress = #locals . _last
-- affine -- affine
activeProcedure :: Traversal' Frame Label activeProcedure :: Traversal' Frame Obj
activeProcedure = #locals . _init . _last . #ObjImm . #ImmLabel activeProcedure = #locals . _init . _last
callStack :: Traversal' Stack Label
callStack = each . activeProcedure
newtype Stack = MkStack { frames :: NonEmpty Frame } newtype Stack = MkStack { frames :: NonEmpty Frame }
deriving stock (Show, Generic) deriving stock (Show, Generic)
@@ -137,7 +134,7 @@ stepT g vm tc@(Call nargs) = do
(args,f,ret,frm) <- parseCall nargs (vm ^. activeFrame) (args,f,ret,frm) <- parseCall nargs (vm ^. activeFrame)
& expectOf [i|#{show tc}|] _Just & expectOf [i|#{show tc}|] _Just
rt <- getRoutine g f rt <- getRoutine g f
let newFrame = MkFrame $ args ++ [ObjLabel f,ObjLabel ret] let newFrame = MkFrame $ args ++ [f,ret]
pure $ vm pure $ vm
& jumpToRoutine rt & jumpToRoutine rt
& activeFrame .~ frm & activeFrame .~ frm
@@ -149,8 +146,8 @@ stepT g vm tc@(Call nargs) = do
stepT g vm tc@(Return nret) = do stepT g vm tc@(Return nret) = do
(xs,_) <- splitAtExact nret (vm ^. activeFrame . #locals) (xs,_) <- splitAtExact nret (vm ^. activeFrame . #locals)
& expectOf [i|"#{show tc}"|] _Just & expectOf [i|"#{show tc}"|] _Just
expectOf [i||] (activeFrame . returnAddress) vm >>= \case expectOf [i|no return addr|] (activeFrame . returnAddress) vm >>= \case
"halt" -> pure $ vm & #result ?~ xs ObjLabel "halt" -> pure $ vm & #result ?~ xs
ra -> do ra -> do
rt <- getRoutine g ra rt <- getRoutine g ra
vm & traverseOf #stack (fmap snd . popFrame) vm & traverseOf #stack (fmap snd . popFrame)
@@ -164,12 +161,10 @@ stepT g vm tc@(TailCall nargs) = do
(args,f,ra) <- parseTailCall nargs (vm ^. activeFrame) (args,f,ra) <- parseTailCall nargs (vm ^. activeFrame)
& expectOf [i|#{show tc}|] _Just & expectOf [i|#{show tc}|] _Just
case f of case f of
"halt" -> pure $ vm & #result ?~ args ObjLabel "halt" -> pure $ vm & #result ?~ args
_ -> do _ -> do
rt <- case g ^. #labels . at f of rt <- getRoutine g f
Nothing -> vmerror [i|undefined label #{f}|] let newFrame = MkFrame $ args ++ [f, ra]
Just x -> pure x
let newFrame = MkFrame $ args ++ [ObjLabel f, ObjLabel ra]
pure $ vm pure $ vm
& jumpToRoutine rt & jumpToRoutine rt
-- replace the active frame; don't push a new one. -- replace the active frame; don't push a new one.
@@ -201,10 +196,12 @@ stepP g vm p = traverse (evalVal g vm) p >>= \case
case env of case env of
ObjHob (HobClosure l _) -> ret1 . ObjImm . ImmLabel $ l ObjHob (HobClosure l _) -> ret1 . ObjImm . ImmLabel $ l
_ -> vmerror [i|expected closure, got #{env}|] _ -> vmerror [i|expected closure, got #{env}|]
PrimEnvRef env n -> PrimEnvRef n ->
case env of case vm ^? activeFrame . activeProcedure of
ObjHob (HobClosure _ xs) -> ret1 $ xs ^?! ix n Just (ObjHob (HobClosure {label,env})) -> ret1 case n of
_ -> vmerror [i|expected closure, got #{env}|] 0 -> ObjLabel label
n -> env ^?! ix (n-1)
x -> vmerror [i|expected closure, got #{x}|]
PrimCons x y -> ret1 $ ObjHob $ HobPair x y PrimCons x y -> ret1 $ ObjHob $ HobPair x y
PrimCar x -> case x of PrimCar x -> case x of
ObjHob (HobPair car _) -> ret1 car ObjHob (HobPair car _) -> ret1 car
@@ -237,10 +234,18 @@ jumpToRoutine rt vm = vm
& jumpToBlock rt.start & jumpToBlock rt.start
& #debug . #activeRoutine .~ rt.label & #debug . #activeRoutine .~ rt.label
getRoutine :: (HasCallStack, Jalmot :> es) => Env -> Label -> Eff es Routine getLabel :: Obj -> Maybe Label
getRoutine g l = case g ^. #labels . at l of getLabel = \case
Just rt -> pure rt ObjHob (HobClosure {label}) -> Just label
Nothing -> vmerror [i|undefined label #{l}|] ObjImm (ImmLabel label) -> Just label
x -> Nothing
getRoutine :: (HasCallStack, Jalmot :> es) => Env -> Obj -> Eff es Routine
getRoutine g f = do
l <- getLabel f & expectOf [i|no label for #{f}|] _Just
case g ^. #labels . at l of
Just rt -> pure rt
Nothing -> vmerror [i|undefined label #{l}|]
expectOf expectOf
:: (HasCallStack, Jalmot :> es) :: (HasCallStack, Jalmot :> es)
@@ -272,20 +277,17 @@ takeExact n xs = case compareLength xs n of
(EQ;GT) -> Just $ take n xs (EQ;GT) -> Just $ take n xs
LT -> Nothing LT -> Nothing
parseCall :: Int -> Frame -> Maybe (List Obj, Label, Label, Frame) parseCall :: Int -> Frame -> Maybe (List Obj, Obj, Obj, Frame)
parseCall nargs frm = do parseCall nargs frm = do
(xs,ys) <- splitAtExact (nargs+2) (frm ^. #locals) (xs,ys) <- splitAtExact (nargs+2) (frm ^. #locals)
let (xs',[f,ret]) = splitAt nargs xs let (xs',[f,ret]) = splitAt nargs xs
f' <- f ^? #ObjImm . #ImmLabel pure (xs',f,ret,MkFrame ys)
ret' <- ret ^? #ObjImm . #ImmLabel
pure (xs',f',ret',MkFrame ys)
parseTailCall :: Int -> Frame -> Maybe (List Obj, Label, Label) parseTailCall :: Int -> Frame -> Maybe (List Obj, Obj, Obj)
parseTailCall nargs frm = do parseTailCall nargs frm = do
(xs,_) <- splitAtExact (nargs+1) (frm ^. #locals) (xs,_) <- splitAtExact (nargs+1) (frm ^. #locals)
let (xs',f) = xs ^?! _Snoc let (xs',f) = xs ^?! _Snoc
f' <- f ^? #ObjImm . #ImmLabel pure (xs',f,frm ^?! returnAddress)
pure (xs',f',frm ^?! returnAddress)
initialVM :: VM initialVM :: VM
initialVM = MkVM initialVM = MkVM