Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/compiler98/ByteCode/Compile.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


-- | 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

  -}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.