-- | Function to compile core code into bytecode
module ByteCode.Compile (bcCompile) where
import ByteCode.Type
import ByteCode.Metric
import ByteCode.CompileLib
import Flags
import IntState hiding (getIntState, getFlags)
import StateMonad
import Control.Monad.State
import qualified Data.Map as Map
import Util.Extra(Pos, noPos)
import Id(Id)
import qualified Data.Set as Set
import PosCode
import StrPos
import Maybe(fromJust, isNothing, isJust)
import ForeignCode(ImpExp(..))
import TokenId
import SysDeps(unpackPS)
import NT
import Util.Extra
import Syntax(CallConv(..))
------------------------------------------------------------------------------------------------
-- compiler
------------------------------------------------------------------------------------------------
-- | Compile core code into bytecode
bcCompile :: Flags -- ^ compiler flags
-> IntState -- ^ internal compiler state generated in earlier stages
-> [(Id,PosLambda)] -- ^ list of functions to compile
-> [Id] -- ^ list of constructors to compile
-> (BCModule,IntState) -- ^ compiled bytecode and modified internal compiler state
bcCompile flags state funs cons = undefined
{- FIXME:!!!
(BCModule modu ds, cState st')
where
st = initCompileState flags state
(ds,st') = runState (compile funs cons) st
modu = getModuleId state
compile :: [(Id,PosLambda)] -> [Id] -> STCompiler [BCDecl]
compile funs cons = do cs <- mapM cCon cons
fs <- mapM cFun funs
return $ concat cs ++ concat fs
cCon :: Id -> STCompiler [BCDecl]
cCon d =
do state <- readState cState
let dataInfo = (fromJust . lookupIS state) d
cons = map mkCon (zip (constrsI dataInfo) [0..])
mkCon (c,n) = Con c noPos (arityIS state c) n
return cons
cFun :: (Id, PosLambda) -> STCompiler [BCDecl]
cFun (i, PosLambda pos fl [] [arg]
exp@(PosExpCase cpos (PosVar vpos var) [PosAltCon apoc con posargs (PosVar vpos2 var2)])) =
do only <- cOnlyCon con
numDicts <- numDictArgs i
if only && any ((var2 ==).snd) posargs then -- selector function
let no = fromJust $ lookup var2 $ (zip (map snd posargs) [0..])
selUS = UseSet 0 [] (Set.singleton (snd arg))
code = [ (NEED_HEAP 1,emptyUS), (SELECTOR_EVAL,selUS), (SELECT no,emptyUS), (RETURN,emptyUS)]
flags = [fl]
in return [ Fun i pos 1 [snd arg] (CLinear code) Map.empty False 1 numDicts flags ]
else
cFun' (i, PosLambda pos fl [] [arg] exp)
cFun (i, PosLambda pos int env args exp) =
cFun' (i, PosLambda pos int env args exp)
cFun (i, PosPrimitive pos id) = cPrimFun i i GPRIM pos
cFun (i, PosForeign pos id arity name cc Imported) = do
state <- readState cState
let (unique,state1) = uniqueIS state
arity = arityIS state i
(InfoVar un tok ex fix nt ar) = fromJust $ lookupIS state i
tok' = mkExt tok
info' = InfoVar unique tok' ex fix nt ar
name' = if name == "" then getUnqualified tok else name
state2 = addIS unique info' state1
if cc == Other "builtin" then
warning ("name of builtin = "++name++" c-name = "++name') $
return [External i pos arity name' cc nt]
else do
prim <- cPrimFun i unique GFUN pos
writeState_ $ \ s -> s { cState = state2 }
return $ prim ++ [External unique pos arity name' cc nt ]
-- | I don't know what it is *supposed* to do, but it *definately*
-- screws with 'Id's and 'Int's. Someone should sort it out. [SamB]
cPrimFun :: Id -> Id -> GType -> Pos -> STCompiler [BCDecl]
cPrimFun i call gtype pos =
do state <- readState cState
numDicts <- numDictArgs i
let arity = arityIS state i
args :: [Id]
args = map toEnum [0..arity-1] -- what is with this using 'Id's? okay so they were Ints,
-- but if I used actual 'Id's, I'd have to use toEnum so
-- many times it isn't funny... [SamB]
evals = concatMap (\i -> [ (PUSH_ARG (fromEnum i),UseSet 0 [i] (Set.singleton i)),
(EVAL,emptyUS),
(POP 1,emptyUS)]) args
code = evals ++ [ (PRIMITIVE, UseSet 0 (args) (Set.fromList args)),
(EVAL,emptyUS),
(RETURN,emptyUS) ]
consts = Map.singleton 0 (CGlobal call gtype)
return [ Fun i pos arity [] (CLinear code) consts True 1 numDicts [LamFLNone] ]
cFun' :: (Id, PosLambda) -> STCompiler [BCDecl]
cFun' (i, PosLambda pos fl env args exp) =
do let args' = map snd args
numDicts <- numDictArgs i
(state, code) <- innerMonad (cBody exp args')
let maxDepth = cMaxDepth state
consts = Map.fromList $ map (\(x,y) -> (y,x)) $ Map.toList (cConsts state)
flags = [fl]
return [ Fun i pos (length args) args' code consts False maxDepth numDicts flags ]
cBody :: PosExp -> [Id] -> STCompiler Code
cBody e args = do (cs,()) <- comp
return $ CLinear (cs [])
where
comp = bindArgs args =>>
cExpr (CMode True True True) e =>>
ins RETURN
cOnlyCon :: Id -> STCompiler Bool
cOnlyCon con = do state <- readState cState
let owner = (belongstoI . fromJust . lookupIS state) con
constrs = (constrsI . fromJust . lookupIS state) owner
return (length constrs == 1)
numDictArgs :: Id -> STCompiler Int
numDictArgs i = do state <- readState cState
let info = (fromJust . lookupIS state) i
case maybeNtI info of
Just (NewType _ _ ctxs _) -> return (length ctxs)
_ -> return 0
------------------------------------------------------------------------------------------------
-- expression compiler
------------------------------------------------------------------------------------------------
cExpr :: CMode -> PosExp -> Compiler ()
cExpr m (PosInt p i)
| isShort i = ins (PUSH_INT i) =>> whenHat m (tracePos TPRIMCON p)
| otherwise = pushConst (CInt i) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosChar p c) = ins (PUSH_CHAR c) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosFloat p f) = pushConst (CFloat f) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosDouble p f) = pushConst (CDouble f) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosInteger p i) = pushConst (CInteger i) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosString p s) = pushConst (CString s) =>> whenHat m (tracePos TPRIMCON p)
cExpr m (PosCon p c) = pushVar m p c
cExpr m (PosVar p v) =
pushVar m p v =>>
isEvaled v =>>= \ isEv ->
whenC (isStrict m && not isEv) (evaled v =>> insEval m)
cExpr m (PosExpIf p g c t f)
| isStrict m =
cExpr (cUnproject m) c =>>
newLabel =>>= \ fail ->
newLabel =>>= \ after ->
whenHat m (
addConst (CPos p) =>>= \ pi ->
if g then ins (TGUARD pi)
else ins (TIF pi)
) =>>
ins (JUMP_FALSE fail) =>>
branch (cExpr m t) =>>= \ dt ->
ins (JUMP after) =>>
ins (LABEL fail) =>>
branch (cExpr m f) =>>= \ df ->
(if isFail f then mergeDepths after "If" [dt]
else mergeDepths after "If" [dt,df]) =>>
ins (LABEL after)
cExpr m (PosExpCase p c as)
| isStrict m =
cExpr (cUnproject m) c =>>
newLabels (length as) =>>= \ las ->
newLabel =>>= \ after ->
newLabel =>>= \ def ->
whenHat m (
addConst (CPos p) =>>= \ pi ->
ins (TCASE pi)
) =>>
getIntState =>>= \ is ->
let (isInt,complete,ts) = altTags is as
tslas = zip ts las
def' = if complete then Nothing else Just def
alts = map (cAlt m after) (zip as las) in
ins (CASE isInt tslas def') =>>
mapC (\a -> branch (cAlt m after a)) (zip as las) =>>= \ das ->
whenC (not complete) (
branch (cDefault def) =>>= \ _ ->
nop
) =>>
mergeDepths after "Case" das =>>
ins (LABEL after)
cExpr m (PosExpLet _ p [] e) = cExpr m e
cExpr m (PosExpLet False p bs e) =
mapC_ (cBinding m False) (zip bs [0..]) =>>
cExpr m e =>>
ins (SLIDE (length bs))
cExpr m (PosExpLet True p bs e) =
let n = length bs in
ins (ALLOC n) =>>
mapC_ (\((i,_),n) -> bind False i n) (zip bs [0..]) =>>
mapC_ (cBinding m True) (zip bs [0..]) =>>
cExpr m e =>>
ins (SLIDE n)
cExpr m (PosExpThunk p ap [a]) = cExpr m a
cExpr m (PosExpThunk p ap [PosPrim _ SEQ _, x, y]) =
cExpr (cUnproject $ cStrict m) x =>>
ins (POP 1) =>>
cExpr m y
cExpr m (PosExpThunk p ap (f@(PosExpIf _ _ _ _ _):as)) =
let m' = cUnproject $ cStrict m in
cExpr m' f =>>
mapC_ (cExpr m') (reverse as) =>>
ins (APPLY (length as)) =>>
insEval (cStrict m)
cExpr m (PosExpThunk p ap (f:as)) =
isStrictFun f =>>= \ strict ->
let m' = cUnproject m
m'' = if strict then cStrict m' else cLazy m' in
mapC_ (cExpr m'') (reverse as) =>>
cCall m' f (length as) ap
cExpr m (PosExpApp p as) = cExpr m (PosExpThunk p False as)
cExpr m (PosExpFatBar esc e f) =
newLabel =>>= \ fail ->
newLabel =>>= \ after ->
pushFail fail =>>
branch (cExpr m e) =>>= \ de ->
popFail =>>
ins (JUMP after) =>>
ins (LABEL fail) =>>
branch (cExpr m f) =>>= \ df ->
(if esc then mergeDepths after "Escapable Fatbar" [de]
else mergeDepths after "Fatbar" [de,df]) =>>
ins (LABEL after)
cExpr m (PosExpFail) = cFail
cExpr m e =
getIntState =>>= \ is ->
error $ "cExpr: no code for '"++strPExp (strIS is) "" e ++""
-- compile a default branch
cDefault :: Label -> Compiler ()
cDefault label =
ins (LABEL label) =>>
cFail
-- compile a failure case
cFail :: Compiler ()
cFail =
getFail =>>= \ (fail,fDepth) ->
getDepth =>>= \ depth ->
let err = error $ "cExpr PosExpFail: depth = "++show depth++", fail depth = "++show fDepth
num = if depth < fDepth then err else depth - fDepth in
ins (POP num) =>>
ins (JUMP fail)
-- compile a list of alternatives paired with their labels,
-- using a specific point to jump to
-- cAlts :: [(PosAlt,Label)] -> Label -> Compiler [Int]
-- cAlts las after = mapC (\a -> branch (cAlt after a)) las
-- compile a single a alternative and label, jumping to the
-- specified place
cAlt :: CMode -> Label -> (PosAlt,Label) -> Compiler ()
cAlt m after (PosAltCon p t vs e,lab) =
ins (LABEL lab) =>>
let ids = map snd vs
n = length vs in
useIns (UNPACK n) ids Set.empty =>>
mapC_ (uncurry (bind True)) (zipWith (\(p,i) n -> (i,n)) vs [0..]) =>>
cExpr (cStrict m) e =>>
ins (SLIDE n) =>>
ins (JUMP after)
cAlt m after (PosAltInt p t b e, lab) =
ins (LABEL lab) =>>
ins (POP 1) =>>
cExpr (cStrict m) e =>>
ins (JUMP after)
-- compile a let binding and the slot it occupies, boolean indicates whether recursive or not
cBinding :: CMode -> Bool -> (PosBinding,Int) -> Compiler ()
cBinding m True ((i,PosLambda p _ _ [] e),n) =
cExpr (cLazy m) e =>>
useIns (UPDATE n) [] (Set.singleton i)
cBinding m False ((i,PosLambda p _ _ [] e),n) =
cExpr (cLazy m) e =>>
bind False i 0
-- compile a call to a function, with some number of arguments given
cCall :: CMode -> PosExp -> Int -> Bool -> Compiler ()
cCall m (PosPrim p c i) got ap =
(case c of
STRING -> simply Nothing
_ -> ifHat m (addConst (CGlobal i GFUN0) =>>= \ ii ->
addConst (CPos p) =>>= \ pi ->
ins (PUSH_CONST ii) =>>
ins (TPRIMAP pi got) =>>
simply (Just pi))
-- else
(simply Nothing)
) =>>= \ pi ->
cCallPrim c =>>
case c of
STRING -> simply ()
_ -> whenHat m (ins (TPRIMRESULT (fromJust pi)))
cCall m (PosCon p c) got ap =
addConst (CGlobal c GCON) =>>= \ ci ->
ins (MK_CON ci got) =>>
whenHat m (tracePos TCON p)
cCall m (PosVar p v) got ap =
isGlobal v =>>= \ glob ->
if glob then getIntState =>>= \ is ->
cCallGlobal m p v got (arityIS is v) ap
else pushVar m p v =>>
ins (APPLY got) =>>
whenHat m (addConst (CPos p) =>>= \ pi ->
ins (TAPPLY pi got)) =>>
insEval m
cCall m e@(PosExpThunk p _ es) got ap =
cExpr m e =>>
ins (APPLY got) =>>
whenHat m (addConst (CPos p) =>>= \ pi ->
ins (TAPPLY pi got)) =>>
insEval m
cCall m e got ap =
getIntState =>>= \ is ->
error $ "cCall: no code for '"++strPExp (strIS is) "" e++"'"
-- call a global function, comparing the number of arguments we have with
-- the number of arguments we were expecting, and thus generating the right code
cCallGlobal :: CMode -> Pos -> Id -> Int -> Int -> Bool -> Compiler ()
cCallGlobal m p v got expect ap
-- saturated or super-saturated case
| got >= expect = let extra = got - expect in
-- do a MK_AP or PUSH_CONST depending on the expected arity
(if expect > 0 then addConst (CGlobal v GFUN) =>>= \ vi ->
ins (MK_AP vi expect) =>>
whenHat m (if ap then tracePos (\pi -> TAPPLY pi (got-1)) p
else tracePos TAP p)
else addConst (CGlobal v GCAF) =>>= \ vi ->
ins (PUSH_CONST vi)
) =>>
-- apply extra arguments if needed
whenC (extra > 0) (
ins (APPLY extra) =>>
whenHat m (addConst (CPos p) =>>= \ pi ->
ins (TAPPLY pi extra))
) =>>
-- eval the result if needed
insEval m
-- partial application
| otherwise = addConst (CGlobal v GFUN) =>>= \ vi ->
ins (MK_PAP vi got) =>>
whenHat m (tracePos TAP p)
-- compile a call to a primitive function
cCallPrim :: Prim -> Compiler ()
cCallPrim (ADD op) = ins (P_ADD op)
cCallPrim (SUB op) = ins (P_SUB op)
cCallPrim (MUL op) = ins (P_MUL op)
cCallPrim (QUOT) = ins (P_DIV OpWord)
cCallPrim (REM) = ins (P_MOD OpWord)
cCallPrim (SLASH op) = ins (P_DIV op)
cCallPrim (CMP_EQ op) = ins (P_CMP_EQ op)
cCallPrim (CMP_NE op) = ins (P_CMP_NE op)
cCallPrim (CMP_LE op) = ins (P_CMP_LE op)
cCallPrim (CMP_LT op) = ins (P_CMP_LT op)
cCallPrim (CMP_GE op) = ins (P_CMP_GE op)
cCallPrim (CMP_GT op) = ins (P_CMP_GT op)
cCallPrim (NEG op) = ins (P_NEG op)
cCallPrim (ORD) = ins (P_FROM_ENUM)
cCallPrim (STRING) = ins P_STRING
cCallPrim i = error $ "cCallPrim " ++ strPrim i
-----------------------------------------------------------------------------------
-- helper functions
-----------------------------------------------------------------------------------
-- returns whether an expression should be considered strict or lazy
isStrictFun :: PosExp -> Compiler Bool
isStrictFun (PosVar _ _) = simply False
isStrictFun (PosCon _ _) = simply False
isStrictFun (PosPrim p _ _) = simply True
isStrictFun (PosExpThunk _ _ _) = simply False
isStrictFun (PosExpIf _ _ _ _ _) = simply True
isStrictFun (PosExpCase _ _ _) = simply True
isStrictFun e =
getIntState =>>= \ is ->
error $ "isStrict: no code for '"++strPExp (strIS is) "" e++"'"
-- for a list of alternatives: returns whether this is an int-case, whether it is complete or not
-- and the list of tags properly translated if necessary
altTags :: IntState -> [PosAlt] -> (Bool, Bool, [Tag])
altTags state as@(PosAltInt{} : _) =
(True, False, map (\(PosAltInt _ i _ _) -> i) as)
altTags state as@(PosAltCon _ i _ _ : _) =
(False, complete, map tag as)
where
info = fromJust $ lookupIS state i
typeInfo = fromJust $ lookupIS state (belongstoI info)
constrs = constrsI typeInfo
ncons = zip constrs [0..]
complete = length as == length constrs
tag (PosAltCon _ t _ _) = fromJust $ lookup t ncons
-- decides whether something is a failure expression
isFail :: PosExp -> Bool
isFail PosExpFail = True
isFail _ = False
-----------------------------------------------------------------------------------
-- instruction generation functions
-----------------------------------------------------------------------------------
-- issue a non instruction
nop :: Compiler ()
nop = simply ()
-- issue a full instruction
useIns :: Ins -> [Id] -> Set.Set Id -> Compiler ()
useIns i give need =
let d = imStack $ bcodeMetric i in
shiftStack d =>>
getDepth =>>= \ depth ->
let ius = (i,UseSet depth give need) in
return ((ius :) , ())
-- issue a simplified instruction
ins :: Ins -> Compiler ()
ins i = useIns i [] Set.empty
-- issue an eval instruction if needed
insEval :: CMode -> Compiler ()
insEval m = whenC (isStrict m) (ins EVAL)
-- allocate a constant item and push it on the stack
pushConst :: ConstItem -> Compiler ()
pushConst c =
addConst c =>>= \ ci ->
ins (PUSH_CONST ci)
-- returns whether the given identifier is global or not
isGlobal :: Id -> Compiler Bool
isGlobal i =
whereIs i =>>= \ w ->
let b = isNothing w in
simply b
-- push a variable on the stack
pushVar :: CMode -> Pos -> Id -> Compiler ()
pushVar m pos i =
whereIs i =>>= \ w ->
case w of
Just (Arg n) -> useIns (PUSH_ARG n) [i] (Set.singleton i) =>>
whenHat m (ins TPUSH) =>>
project m pos
Just (Stack n pm) -> useIns (PUSH n) [i] (Set.singleton i) =>>
whenHat m (
getIntState =>>= \ is ->
if (isJust (lookupIS is i)) then
let tid = tidIS is i
name = unpackPS (extractV tid) in
addConst (CVarDesc name pos) =>>= \ ci ->
ins (TPUSHVAR ci)
else
ins TPUSH
) =>>
whenC pm (project m pos)
Nothing -> getIntState =>>= \ is ->
(let ar = arityIS is i in
if isConstr $ fromJust $ lookupIS is i then
if ar == 0 then
pushConst (CGlobal i GZCON)
else
error "pushVar: pushing non zcon?"
else
if ar == 0 then
pushConst (CGlobal i GCAF)
else
pushConst (CGlobal i GFUN0)) =>>
whenHat m (ins TPUSH)
-- add code for projection, if appropriate
project :: CMode -> Pos -> Compiler ()
project m pos =
whenHat m (
whenC (isProjected m) (
addConst (CPos pos) =>>= \ pi ->
ins (TPROJECT pi)
)
)
-- conditional on hat compliation
ifHat :: CMode -> Compiler a -> Compiler a -> Compiler a
ifHat m hc oc =
getFlags =>>= \ flags ->
if sHat flags && isTraced m then hc
else oc
-- only run a compiler whenC hat is enabled
whenHat :: CMode -> Compiler () -> Compiler ()
whenHat m c = ifHat m c (simply ())
-- trace a position
tracePos :: (CRef -> Ins) -> Pos -> Compiler ()
tracePos f p = addConst (CPos p) =>>= \ pi ->
ins (f pi)
whenC :: Bool -> Compiler () -> Compiler ()
whenC c e = if c then e else nop
-}
|