module Stg.Machine.Evaluate.ValidTransitions (
rule1_functionApp,
rule2_enterNonUpdatable,
rule3_let,
rule4_case,
rule5_constructorApp,
rule6_algebraicNormalMatch,
rule7_algebraicUnboundDefaultMatch,
rule8_algebraicBoundDefaultMatch,
rule9_primitiveLiteralEval,
rule10_primitiveLiteralApp,
rule11_primitiveNormalMatch,
rule12_primitiveBoundDefaultMatch,
rule13_primitiveUnboundDefaultMatch,
rule14_primop,
rule15_enterUpdatable,
rule16_missingReturnUpdate,
rule17_missingArgUpdate,
rule17a_missingArgUpdate,
rule1819_casePrimopShortcut,
) where
import Data.Bifunctor
import qualified Data.Foldable as F
import qualified Data.Map as M
import Data.Monoid hiding (Alt)
import qualified Data.Set as S
import Data.Stack (Stack (..), (<>>))
import qualified Data.Stack as S
import Stg.Language
import Stg.Machine.Env
import Stg.Machine.Evaluate.Common
import qualified Stg.Machine.Heap as H
import Stg.Machine.Types
import Stg.StaticAnalysis
import Stg.Util
mkDetail_UnusedLocalVariables :: [Var] -> Locals -> [InfoDetail]
mkDetail_UnusedLocalVariables usedVars locals =
[ Detail_UnusedLocalVariables usedVars locals
| let Locals localsMap = locals
used = M.fromList [ (var, ()) | var <- usedVars ]
unused = localsMap `M.difference` used
, not (M.null unused) && not (M.null localsMap) ]
rule1_functionApp :: StgState -> Maybe StgState
rule1_functionApp s@StgState
{ stgCode = Eval (AppF f xs) locals
, stgStack = stack
, stgGlobals = globals }
| Success (Addr addr) <- val locals globals (AtomVar f)
, Success xsVals <- vals locals globals xs
= let stack' = map ArgumentFrame xsVals <>> stack
in Just (s
{ stgCode = Enter addr
, stgStack = stack'
, stgInfo = Info
(StateTransition Rule1_Eval_FunctionApplication)
( Detail_FunctionApplication f xs
: mkDetail_UnusedLocalVariables (f : [ var | AtomVar var <- xs ]) locals )})
rule1_functionApp _ = Nothing
isArgFrame :: StackFrame -> Bool
isArgFrame ArgumentFrame{} = True
isArgFrame _else = False
rule2_enterNonUpdatable :: StgState -> Maybe StgState
rule2_enterNonUpdatable s@StgState
{ stgCode = Enter addr
, stgStack = stack
, stgHeap = heap }
| Just (HClosure (Closure (LambdaForm free NoUpdate bound body) freeVals))
<- H.lookup addr heap
, Just (frames, stack') <- bound `S.forEachPop` stack
, all isArgFrame frames
, args <- [ arg | ArgumentFrame arg <- frames ]
= let locals = makeLocals (freeLocals <> boundLocals)
freeLocals = zipWith Mapping free freeVals
boundLocals = zipWith Mapping bound args
in Just (s
{ stgCode = Eval body locals
, stgStack = stack'
, stgInfo = Info (StateTransition Rule2_Enter_NonUpdatableClosure)
[Detail_EnterNonUpdatable addr boundLocals] })
rule2_enterNonUpdatable _ = Nothing
liftLambdaToClosure :: Locals -> LambdaForm -> Validate NotInScope Closure
liftLambdaToClosure locals lf@(LambdaForm free _ _ _) =
case traverse (first (:[]) . localVal locals . AtomVar) free of
Success freeVals -> Success (Closure lf freeVals)
Failure notInScope -> Failure (mconcat notInScope)
rule3_let :: StgState -> Maybe StgState
rule3_let s@StgState
{ stgCode = Eval (Let rec (Binds letBinds) expr) locals
, stgHeap = heap }
= let (letVars, letLambdaForms) = unzip (M.assocs letBinds)
(newAddrs, heapWithPreallocations) =
let preallocatedObjs = map (const (Blackhole 0)) letVars
in H.allocMany preallocatedObjs heap
locals' = let varToAddr var addr = Mapping var (Addr addr)
newMappings = zipWith varToAddr letVars newAddrs
in makeLocals newMappings <> locals
localsRhs = case rec of
NonRecursive -> locals
Recursive -> locals'
in Just (case traverse (liftLambdaToClosure localsRhs) letLambdaForms of
Success closures ->
let addrToClosure addr closure = Mapping addr (HClosure closure)
heap' = H.updateMany
(zipWith addrToClosure newAddrs closures)
heapWithPreallocations
in s { stgCode = Eval expr locals'
, stgHeap = heap'
, stgInfo = Info (StateTransition (Rule3_Eval_Let rec))
[Detail_EvalLet letVars newAddrs] }
Failure notInScope ->
s { stgInfo = Info (StateError (VariablesNotInScope notInScope)) [] })
rule3_let _ = Nothing
rule4_case :: StgState -> Maybe StgState
rule4_case s@StgState
{ stgCode = Eval (Case expr alts) locals
, stgStack = stack }
= let stack' = ReturnFrame alts (removeUnusedLocals locals) :< stack
removeUnusedLocals (Locals unpackedLocals) =
let freeInBody var _val = var `S.member` freeVariables alts
in Locals (M.filterWithKey freeInBody unpackedLocals)
in Just (s
{ stgCode = Eval expr locals
, stgStack = stack'
, stgInfo = Info (StateTransition Rule4_Eval_Case)
[Detail_EvalCase] })
rule4_case _ = Nothing
rule5_constructorApp :: StgState -> Maybe StgState
rule5_constructorApp s@StgState
{ stgCode = Eval (AppC con xs) locals
, stgGlobals = globals }
| Success valsXs <- vals locals globals xs
= Just (s
{ stgCode = ReturnCon con valsXs
, stgInfo = Info
(StateTransition Rule5_Eval_AppC)
(mkDetail_UnusedLocalVariables [ var | AtomVar var <- xs ] locals) })
rule5_constructorApp _ = Nothing
rule6_algebraicNormalMatch :: StgState -> Maybe StgState
rule6_algebraicNormalMatch s@StgState
{ stgCode = ReturnCon con ws
, stgStack = ReturnFrame alts locals :< stack' }
| Success (AltMatches (AlgebraicAlt _con vars expr)) <-
lookupAlgebraicAlt alts con
, length ws == length vars
= let locals' = addLocals (zipWith Mapping vars ws) locals
in Just (s
{ stgCode = Eval expr locals'
, stgStack = stack'
, stgInfo = Info (StateTransition Rule6_ReturnCon_Match)
[Detail_ReturnCon_Match con vars] })
rule6_algebraicNormalMatch _ = Nothing
rule7_algebraicUnboundDefaultMatch :: StgState -> Maybe StgState
rule7_algebraicUnboundDefaultMatch s@StgState
{ stgCode = ReturnCon con _ws
, stgStack = ReturnFrame alts locals :< stack' }
| Success (DefaultMatches (DefaultNotBound expr)) <-
lookupAlgebraicAlt alts con
= Just (s
{ stgCode = Eval expr locals
, stgStack = stack'
, stgInfo = Info (StateTransition Rule7_ReturnCon_DefUnbound) [] })
rule7_algebraicUnboundDefaultMatch _ = Nothing
rule8_algebraicBoundDefaultMatch :: StgState -> Maybe StgState
rule8_algebraicBoundDefaultMatch s@StgState
{ stgCode = ReturnCon con ws
, stgStack = ReturnFrame alts locals :< stack'
, stgHeap = heap
, stgSteps = steps }
| Success (DefaultMatches (DefaultBound v expr)) <-
lookupAlgebraicAlt alts con
= let locals' = addLocals [Mapping v (Addr addr)] locals
(addr, heap') = H.alloc (HClosure closure) heap
closure = Closure (LambdaForm vs NoUpdate [] (AppC con (map AtomVar vs))) ws
vs = let newVar _old i = Var ("alg8_" <> show' steps <> "-" <> show' i)
in zipWith newVar ws [0::Integer ..]
in Just (s { stgCode = Eval expr locals'
, stgStack = stack'
, stgHeap = heap'
, stgInfo = Info (StateTransition Rule8_ReturnCon_DefBound)
[Detail_ReturnConDefBound v addr] })
rule8_algebraicBoundDefaultMatch _ = Nothing
rule9_primitiveLiteralEval :: StgState -> Maybe StgState
rule9_primitiveLiteralEval s@StgState { stgCode = Eval (LitE (Literal k)) _locals}
= Just (s
{ stgCode = ReturnInt k
, stgInfo = Info (StateTransition Rule9_Lit) [] })
rule9_primitiveLiteralEval _ = Nothing
rule10_primitiveLiteralApp :: StgState -> Maybe StgState
rule10_primitiveLiteralApp s@StgState { stgCode = Eval (AppF f []) locals }
| Success (PrimInt k) <- val locals mempty (AtomVar f)
= Just (s
{ stgCode = ReturnInt k
, stgInfo = Info (StateTransition Rule10_LitApp)
(mkDetail_UnusedLocalVariables [f] locals) })
rule10_primitiveLiteralApp _ = Nothing
rule11_primitiveNormalMatch :: StgState -> Maybe StgState
rule11_primitiveNormalMatch s@StgState
{ stgCode = ReturnInt k
, stgStack = ReturnFrame alts locals :< stack' }
| Success (AltMatches (PrimitiveAlt _k expr)) <-
lookupPrimitiveAlt alts (Literal k)
= Just (s
{ stgCode = Eval expr locals
, stgStack = stack'
, stgInfo = Info (StateTransition Rule11_ReturnInt_Match) [] })
rule11_primitiveNormalMatch _ = Nothing
rule12_primitiveBoundDefaultMatch :: StgState -> Maybe StgState
rule12_primitiveBoundDefaultMatch s@StgState
{ stgCode = ReturnInt k
, stgStack = ReturnFrame alts locals :< stack' }
| Success (DefaultMatches (DefaultBound v expr)) <-
lookupPrimitiveAlt alts (Literal k)
= let locals' = addLocals [Mapping v (PrimInt k)] locals
in Just ( s
{ stgCode = Eval expr locals'
, stgStack = stack'
, stgInfo = Info (StateTransition Rule12_ReturnInt_DefBound)
[Detail_ReturnIntDefBound v k] })
rule12_primitiveBoundDefaultMatch _ = Nothing
rule13_primitiveUnboundDefaultMatch :: StgState -> Maybe StgState
rule13_primitiveUnboundDefaultMatch s@StgState
{ stgCode = ReturnInt k
, stgStack = ReturnFrame alts locals :< stack' }
| Success (DefaultMatches (DefaultNotBound expr)) <-
lookupPrimitiveAlt alts (Literal k)
= Just (s
{ stgCode = Eval expr locals
, stgStack = stack'
, stgInfo = Info (StateTransition Rule13_ReturnInt_DefUnbound) [] })
rule13_primitiveUnboundDefaultMatch _ = Nothing
rule14_primop :: StgState -> Maybe StgState
rule14_primop s@StgState
{ stgCode = Eval (AppP op x y) locals }
| Success (PrimInt xVal) <- localVal locals x
, Success (PrimInt yVal) <- localVal locals y
, Success result <- applyPrimOp op xVal yVal
= Just (s
{ stgCode = ReturnInt result
, stgInfo = Info (StateTransition Rule14_Eval_AppP)
(mkDetail_UnusedLocalVariables [var | AtomVar var <- [x,y]]
locals )})
rule14_primop _ = Nothing
rule15_enterUpdatable :: StgState -> Maybe StgState
rule15_enterUpdatable s@StgState
{ stgCode = Enter addr
, stgStack = stack
, stgHeap = heap
, stgSteps = tick }
| Just (HClosure (Closure (LambdaForm free Update [] body) freeVals))
<- H.lookup addr heap
= let stack' = UpdateFrame addr :< stack
locals = makeLocals (zipWith Mapping free freeVals)
heap' = H.update (Mapping addr (Blackhole tick)) heap
in Just ( s
{ stgCode = Eval body locals
, stgStack = stack'
, stgHeap = heap'
, stgInfo = Info (StateTransition Rule15_Enter_UpdatableClosure)
[Detail_EnterUpdatable addr] })
rule15_enterUpdatable _ = Nothing
rule16_missingReturnUpdate :: StgState -> Maybe StgState
rule16_missingReturnUpdate s@StgState
{ stgCode = ReturnCon con ws
, stgStack = UpdateFrame addr :< stack'
, stgHeap = heap
, stgSteps = steps }
= let vs = let newVar _old i = Var ("upd16_" <> show' steps <> "-" <> show' i)
in zipWith newVar ws [0::Integer ..]
lf = LambdaForm vs NoUpdate [] (AppC con (map AtomVar vs))
heap' = H.update (Mapping addr (HClosure (Closure lf ws))) heap
in Just (s
{ stgCode = ReturnCon con ws
, stgStack = stack'
, stgHeap = heap'
, stgInfo = Info (StateTransition Rule16_ReturnCon_Update)
[Detail_ConUpdate con addr] })
rule16_missingReturnUpdate _ = Nothing
popArgsUntilUpdate :: Stack StackFrame -> Maybe ([StackFrame], Stack StackFrame)
popArgsUntilUpdate withArgsStack
= let (argFrames, argsPoppedStack) = S.span isArgFrame withArgsStack
in Just ( filter isArgFrame (F.toList argFrames)
, argsPoppedStack )
rule17_missingArgUpdate :: StgState -> Maybe StgState
rule17_missingArgUpdate s@StgState
{ stgCode = Enter addrEnter
, stgStack = stack
, stgHeap = heap }
| Just (HClosure (Closure (LambdaForm vs NoUpdate xs body) wsf))
<- H.lookup addrEnter heap
, Just (argFrames, UpdateFrame addrUpdate :< stack')
<- popArgsUntilUpdate stack
= let (xs1, xs2) = splitAt (length argFrames) xs
updatedClosure =
let freeVars = vs <> xs1
freeVals = wsf <> [af | ArgumentFrame af <- argFrames]
in Closure (LambdaForm freeVars NoUpdate xs2 body) freeVals
heap' = H.update (Mapping addrUpdate (HClosure updatedClosure)) heap
in Just (s
{ stgCode = Enter addrEnter
, stgStack = argFrames <>> stack'
, stgHeap = heap'
, stgInfo = Info (StateTransition Rule17_Enter_PartiallyAppliedUpdate)
[Detail_PapUpdate addrUpdate] })
rule17_missingArgUpdate _ = Nothing
rule17a_missingArgUpdate :: StgState -> Maybe StgState
rule17a_missingArgUpdate s@StgState
{ stgCode = Enter addrEnter
, stgStack = stack
, stgHeap = heap
, stgSteps = steps }
| Just (HClosure (Closure (LambdaForm _vs NoUpdate xs _body) _wsf))
<- H.lookup addrEnter heap
, Just (argFrames, UpdateFrame addrUpdate :< stack')
<- popArgsUntilUpdate stack
= let xs1 = zipWith const xs (F.toList argFrames)
f = Var ("upd17a_" <> show' steps)
fxs1 = AppF f (map AtomVar xs1)
updatedClosure =
let freeVars = f : xs1
freeVals = zipWith const
(Addr addrEnter : F.foldMap (\(ArgumentFrame v) -> [v]) argFrames)
freeVars
in Closure (LambdaForm freeVars NoUpdate [] fxs1) freeVals
heap' = H.update (Mapping addrUpdate (HClosure updatedClosure)) heap
in Just (s
{ stgCode = Enter addrEnter
, stgStack = argFrames <>> stack'
, stgHeap = heap'
, stgInfo = Info (StateTransition Rule17a_Enter_PartiallyAppliedUpdate)
[Detail_PapUpdate addrUpdate] })
rule17a_missingArgUpdate _ = Nothing
rule1819_casePrimopShortcut :: StgState -> Maybe StgState
rule1819_casePrimopShortcut s@StgState
{ stgCode = Eval (Case (AppP op x y) alts) locals }
| Success (PrimInt xVal) <- localVal locals x
, Success (PrimInt yVal) <- localVal locals y
, Success opXY <- applyPrimOp op xVal yVal
, Success altLookup <- lookupPrimitiveAlt alts (Literal opXY)
= let (locals', expr) = case altLookup of
DefaultMatches (DefaultBound pat e)
-> (addLocals [Mapping pat (PrimInt opXY)] locals, e)
DefaultMatches (DefaultNotBound e)
-> (locals, e)
AltMatches (PrimitiveAlt _opXY e)
-> (locals, e)
in Just (
s { stgCode = Eval expr locals'
, stgInfo = Info (StateTransition Rule1819_Eval_Case_Primop_Shortcut) [] })
rule1819_casePrimopShortcut _ = Nothing