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

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


module DotNet.Compile (ilCompile) where

import DotNet.IL as IL
import Flags
import IntState hiding (getIntState)
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 as P
import StrPos
import Maybe(fromJust, isNothing, isJust)
import ForeignCode(ImpExp(..))
import TokenId
import SysDeps(PackedString, packString, unpackPS)
import NT
import Data.Char
import Data.List(intersperse)

--------------------------------------------------------------
-- state and types
--------------------------------------------------------------

-- the internal compiler state
--    flags,state         - saved external items
--    labels              - the list of free labels
--
--    env                 - where each variable can be found
--    fails               - a list of fail handlers (still used?)
--    evals               - the set of variables that we know to be evaluated

data CState = S { -- global state parameters
                 cState :: IntState,

                 -- local state parameters
                 cCurrId   :: Id,
                 cEnv      :: Map.Map Id Where,
                 cLabels   :: Label,
                 cLocals   :: Int,
                 cLocalEnv :: [TypeSignature] }

type STCompiler a = State CState a
type InsCode = [ILInstruction] -> [ILInstruction]
type Compiler a = STCompiler (InsCode,a)

{- whether we should be compiling strictly or lazily -}

data SMode = PrimStrict PrimOp | Strict | Lazy
           deriving Eq

{- where we can find a variable -}
data Where = This
           | Local Int Bool
           | Field Where TypeSignature String
           deriving Show

data BranchAfter = Return
                 | Continue
                 | ContinueTo Label
                 | Goto       Label

------------------------------------------------------------------------------------------------
-- compiler
------------------------------------------------------------------------------------------------

ilCompile :: Flags -> IntState -> [(Id,PosLambda)] -> [Id] -> ([ILDecl],IntState)
ilCompile flags state funs cons = ([Namespace (getModuleId state) ds], cState st')
    where
    st = S state (toEnum 0) Map.empty 0 0 []
    (ds,st') = runState (compile funs cons) st


compile :: [(Id,PosLambda)] -> [Id] -> STCompiler [ILDecl]
compile funs cons = do cs <- mapM cCon cons
                       fs <- mapM cFun funs
                       return $ concat cs ++ concat fs

cCon :: Id -> STCompiler [ILDecl]
cCon d = do
  state <- readState cState
  let dataInfo = (fromJust . lookupIS state) d
  mapM (mkCon state) (constrsI dataInfo)
  where
    mkCon state c = do
      (ns,lname,class_sig) <- getIdLocalSignature c
      let arity = arityIS state c
      caf_decls <- mkCAFDecls class_sig arity
      (field_decls,args,stcode) <- mkCode class_sig 1 arity
      let class_decl =
              Class lname sigClosure (  field_decls
                                     ++ caf_decls
                                     ++ [ILClassConstr ILPublic ILInstance args []
                                                   ( LDARG 0
                                                   : CALL VoidSignature sigClosure ".ctor" []
                                                   : stcode
                                                   )]
                                     )
      return $! if null ns then class_decl else Namespace ns [class_decl]

    mkCAFDecls class_sig arity
      | arity == 0 = return [ ILClassField  ILPublic  ILStatic sigClosure "indirection"
                            , ILClassConstr ILPrivate ILStatic [] []
                                        ( NEWOBJ     class_sig []
    	                                : STSFLD     sigClosure class_sig "indirection"
    	                                : RET
    	                                : []
                                        )
                            ]
      | otherwise  = return []

    mkCode class_sig i n
      | i > n     = return ([], [], [RET])
      | otherwise = do
          (fields,args,stcode) <- mkCode class_sig (i+1) n
          let name = 'c':show i
          return ( (ILClassField ILPublic ILInstance sigClosure name)
                 : fields
                 , (ILMethodArg sigClosure name)
                 : args
                 , LDARG 0
		 : LDARG i
		 : STFLD sigClosure class_sig name
	         : stcode
                 )

cFun :: (Id, PosLambda) -> STCompiler [ILDecl]
cFun (i, PosLambda pos int env args exp) = do
  setCurrentId i
  (ns,lname,class_sig) <- getIdLocalSignature i
  let args' = map snd args
  field_decls <- mkFieldDecls class_sig args' 1
  caf_decls   <- mkCAFDecls   class_sig args'
  thunk_decls <- mkThunkDecls class_sig args'
  let class_decl = Class lname sigBaseClass (field_decls ++ caf_decls ++ thunk_decls)
  return $! if null ns then [class_decl] else [Namespace ns [class_decl]]
  where
    sigBaseClass
      | length args == 0 = sigCAFClosure
      | otherwise        = sigThunkClosure

    mkCAFDecls class_sig args
      | length args == 0 = return [ ILClassField  ILPublic  ILStatic sigClosure "indirection"
                                  , ILClassConstr ILPrivate ILStatic [] []
                                              ( NEWOBJ     class_sig []
	                                      : STSFLD     sigClosure class_sig "indirection"
	                                      : RET
	                                      : []
                                              )
                                  ]
      | otherwise        = return []

    mkThunkDecls class_sig args = do
      (ilargs,stcode) <- toArgsSTCode args 1
      (state, code)   <- innerMonad (cBody exp >>= \(cs,()) -> return (cs []))
      return [ ILClassConstr ILPublic ILInstance ilargs []
                         ( LDARG 0
                         : CALL VoidSignature sigBaseClass ".ctor" []
                         : stcode
                         )
             , ILClassMethod ILPublic ILVirtual sigClosure "Eval" [] (reverse (cLocalEnv state)) code
             ]
      where
        toArgsSTCode         [] n = return ([], [RET])
	toArgsSTCode (arg:args) n = do
	  let name = 'c':show n
	  (ilargs,stcode) <- toArgsSTCode args $! n+1
	  return ( (ILMethodArg sigClosure name)
	         : ilargs
	         , LDARG 0
	         : LDARG n
	         : STFLD sigClosure class_sig name
	         : stcode
	         )

        cBody exp =
	  newLabel =>>= \lab ->
	  ins (LDARG 0) =>>
	  (if length args == 0
	     then ins (LDSFLD sigClosure class_sig "indirection") =>>
	          ins (BEQ lab) =>>
	          ins (LDSFLD sigClosure class_sig "indirection") =>>
	          ins (TAIL) =>>
	          insEval Strict =>>
	          ins (RET) =>>
	          ins (LABEL lab) =>>
	          ins (LDSFLD sigClosure sigBlackHoleClosure "indirection") =>>
	          ins (STSFLD sigClosure class_sig "indirection")
	     else ins (DUP) =>>
	          ins (LDFLD sigClosure sigThunkClosure "indirection") =>>
	          ins (BRFALSE lab) =>>
	          ins (LDFLD sigClosure sigThunkClosure "indirection") =>>
	          ins (TAIL) =>>
	          insEval Strict =>>
	          ins (RET) =>>
	          ins (LABEL lab) =>>
	          ins (LDSFLD sigClosure sigBlackHoleClosure "indirection") =>>
	          ins (STFLD  sigClosure sigThunkClosure "indirection") =>>
	          ins (LDARG 0)) =>>
	  newLabel =>>= \fail ->
	  cExpr Strict fail Return exp =>>= \canFail ->
	  (if canFail
	     then ins (LABEL fail) =>>
                  ins (NEWOBJ sigPaternMatchException []) =>>
	          ins (THROW)
	     else nop)

    mkFieldDecls class_sig []         n = return []
    mkFieldDecls class_sig (arg:args) n = do
      let name = 'c':show n
      bindField arg This class_sig name
      fields   <- mkFieldDecls class_sig args $! n+1
      return ((ILClassField ILPrivate ILInstance sigClosure name):fields)

cFun (i, PosPrimitive pos id)        = do
  setCurrentId i
  state <- readState cState
  (ns,lname,class_sig) <- getIdLocalSignature i
  let arity = arityIS state i
      (field_decls, ilargs,stcode,evcode) = toArgsSTCode class_sig arity 1
      sigBaseClass
         | arity == 0 = sigCAFClosure
         | otherwise  = sigThunkClosure
      thunk_decls =
         [ ILClassConstr ILPublic ILInstance ilargs []
                     ( LDARG 0
                     : CALL VoidSignature sigThunkClosure ".ctor" []
                     : stcode
                     )
         , ILClassMethod ILPublic ILVirtual sigClosure "Eval" [] []
                     ( evcode ++
                     [ CALLCLASS sigClosure sigPrimitives lname (mkClosureArgs arity)
                     , RET
                     ])
         ]
      caf_decls  = mkCAFDecls class_sig arity
      class_decl = Class lname sigBaseClass (field_decls ++ caf_decls ++ thunk_decls)
  return $! if null ns then [class_decl] else [Namespace ns [class_decl]]
  where
    mkCAFDecls class_sig arity
      | arity == 0 = [ ILClassField  ILPublic  ILStatic sigClosure "indirection"
                     , ILClassConstr ILPrivate ILStatic [] []
                                 ( NEWOBJ     class_sig []
	                         : STSFLD     sigClosure class_sig "indirection"
	                         : RET
	                         : []
                                 )
                     ]
      | otherwise  = []

    toArgsSTCode class_sig arity n
      | n > arity = ([], [], [RET], [])
      | otherwise =
	  let name = 'c':show n
	      (fldecls, ilargs,stcode,evcode) = toArgsSTCode class_sig arity $! n+1
	  in ( ILClassField ILPrivate ILInstance sigClosure name
	     : fldecls
	     , ILMethodArg sigClosure name
	     : ilargs
	     , LDARG 0
	     : LDARG n
	     : STFLD sigClosure class_sig name
	     : stcode
	     , LDARG 0
	     : LDFLD sigClosure class_sig name
	     : CALLVIRT sigClosure sigClosure "Eval" []
	     : evcode
	     )

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

       writeState_ $ \ s -> s { cState = state2 }
       return $ [External unique pos arity name' cc nt ]

--------------------------------------------------------------
-- monadic plumbing
--
-- the underlying state of the compiler, recording stack depth, environment
-- etc. is monadic, ontop of this combinators are provided to plug together
-- the outputted code, which are also monad like in nature.
--
-- needless to say the internal details of how this works are complicated,
-- however, conceptually it's quite easy.
--
-- p =>> q
--
-- runs the monad for compiler p, then the one for compiler q and then
-- joins the instructions generated together.
--
-- p =>>= \ x -> q
--
-- does the same as the above but this time it's assumed p is returning something
-- besides just the code that is generated, which is then used as a local variable
-- in defining q.
--
-- for example:
--   newLabel =>>= \ j ->
--   ins (BR j)
--
-- calls the monad to generate a new internal label, and joins its code (i.e. none)
-- with the code for a BR to address provided by newLabel.
--------------------------------------------------------------

(=>>=) :: Compiler a -> (a -> Compiler b) -> Compiler b
c =>>= d = do (cs,a) <- c
              (ds,b) <- d a
              return (cs . ds, b)

(=>>) :: Compiler () -> Compiler a -> Compiler a
c =>> d = c =>>= \ () -> d

mapC :: (a -> Compiler b) -> [a] -> Compiler [b]
mapC f []     = simply []
mapC f (c:cs) = f c =>>= \ b ->
                mapC f cs =>>= \ bs ->
                simply (b:bs)

mapC_ :: (a -> Compiler ()) -> [a] -> Compiler ()
mapC_ f cs = mapC f cs =>>= \ _ -> simply ()

simply :: a -> Compiler a
simply a = return (id, a)

liftC :: STCompiler a -> Compiler a
liftC s = do a <- s
             simply a

block :: InsCode -> Compiler ()
block is = return (is, ())

--------------------------------------------------------------
-- state manipulation functions
--------------------------------------------------------------

{- bind an identifier to a stack location -}
bindField :: Id -> Where -> TypeSignature -> ILName -> Compiler ()
bindField i wh sig name = liftC $ writeState_ $ \s -> s { cEnv = Map.insert i (Field wh sig name) (cEnv s) }

{- bind an identifier to a stack location -}
bindLocal :: Maybe Id -> Bool -> TypeSignature -> Compiler Int
bindLocal mb_i isEval sig = liftC $ writeState $ \s ->
  let s' = s { cEnv = case mb_i of
                        Just i  -> Map.insert i (Local (cLocals s) isEval) (cEnv s)
                        Nothing -> cEnv s
             , cLocals   = cLocals s+1
             , cLocalEnv = sig : cLocalEnv s
             }
  in (s', cLocals s)

{- find out where an identifier is stored -}
whereIs :: Id -> Compiler (Maybe Where)
whereIs i = liftC $ readState $ \s -> Map.lookup i (cEnv s)

{- allocate a new compiler label and return it -}
newLabel :: Compiler Label
newLabel = liftC $ writeState $ \s -> let ls = cLabels s
                                      in (s{cLabels = ls+1}, ls)

-- take a compiler and compile it in its own environment,
-- saving and restoring the appropriate local state elements
-- give the depth on return.
branch :: Compiler a -> Compiler a
branch c =
  liftC get =>>= \ state ->
  let (r,state1) = runState c state in
  liftC (put state1{cEnv = cEnv state}) =>>
  return r

-- get the internal state
getIntState :: Compiler IntState
getIntState = liftC $ readState cState

------------------------------------------------------------------------------------------------
-- expression compiler
------------------------------------------------------------------------------------------------

cExpr :: SMode -> Label -> BranchAfter -> PosExp -> Compiler Bool

cExpr m      fail after (PosInt     p i) =
  ins (LDC_I4 i) =>>
  insBox m OpWord =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosChar    p c) =
  ins (LDC_I4 c) =>>
  insBox m OpWord =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosFloat   p f) =
  ins (LDC_R4 f) =>>
  insBox m OpFloat =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosDouble  p f) =
  ins (LDC_R8 f) =>>
  insBox m OpDouble =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosInteger p i) =
  ins (LDSTR (show i)) =>>
  ins (NEWOBJ sigIntegerClosure [ClassSignature "mscorlib" "System.String"]) =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosString  p s) =
  ins (LDSTR s) =>>
  ins (NEWOBJ sigStringClosure [ClassSignature "mscorlib" "System.String"]) =>>
  cBranchAfter False Lazy after

cExpr m      fail after (PosCon     p c) =
  pushVar Lazy after c

cExpr m      fail after (PosVar     p v) =
  pushVar m after v

cExpr m      fail after (PosExpLet False p bs e) =
  mapC (\(i,PosLambda p _ _ [] e) -> bindLocal (Just i) False sigClosure =>>= \loc ->
                                     cExpr Lazy fail Continue e =>>= \canFail ->
                                     ins (STLOC loc) =>>
                                     simply canFail) bs =>>= \fs ->
  cExpr m fail after e =>>= \canFail ->
  simply (canFail || or fs)

cExpr m      fail after (PosExpLet True p bs e) =
  mapC_ (\(i,_) -> bindLocal (Just i) False sigClosure =>>= \loc ->
                   ins (NEWOBJ sigThunkClosure []) =>>
                   ins (STLOC loc)) bs =>>
  mapC (\(i,PosLambda p _ _ [] e) -> cExpr Lazy fail Continue e =>>= \canFail1 ->
                                     pushVar Lazy Continue i =>>= \canFail2 ->
                                     ins (STFLD sigClosure sigThunkClosure "indirection") =>>
                                     simply (canFail1 || canFail2)) bs =>>= \fs ->
  cExpr m fail after e =>>= \canFail ->
  simply (canFail || or fs)

cExpr m      fail after (PosExpThunk p ap [a]) =
  cExpr m fail after a

cExpr m      fail after (PosExpThunk p ap [PosPrim _ SEQ _, x, y]) =
  cExpr Strict fail Continue x =>>= \canFail1 ->
  ins POP =>>
  cExpr m fail after y =>>= \canFail2 ->
  simply (canFail1 || canFail2)

cExpr m      fail after (PosExpThunk p ap (f@(PosExpIf _ _ _ _ _):as)) =
  cExpr Lazy fail Continue f =>>= \canFail ->
  mapC (cExpr Strict fail Continue) as =>>= \fs ->
  ins (CALLVIRT sigClosure sigClosure "Apply" (mkClosureArgs (length as))) =>>
  cBranchAfter (canFail || or fs) Strict after

cExpr m      fail after (PosExpThunk p ap (f:as)) =
  cCall m fail after f as

cExpr m      fail after (PosExpApp p as) =
  cExpr m fail after (PosExpThunk p False as)

cExpr m      fail after (PosExpFatBar _ e PosExpFail)=
  branch (cExpr m fail after e)

cExpr m      fail after (PosExpFatBar esc e f) =
  newLabel =>>= \ fail' ->
  cBranch after $ \after1 after2 ->
    branch (cExpr m fail' after1 e) =>>= \canFail ->
    (if canFail
       then ins (LABEL fail') =>>
            ins (POP) =>>
            branch (cExpr m fail after2 f)
       else simply False)

cExpr m      fail after (PosExpFail) =
  ins (BR fail) =>>
  simply True

cExpr Strict fail after (PosExpIf p g c t f) =
  newLabel =>>= \l1 ->
  cBranch after $ \after1 after2 ->
    cExpr (PrimStrict OpWord) fail Continue c =>>= \canFail1 ->
    ins (BRFALSE l1) =>>
    branch (cExpr Strict fail after1 t) =>>= \canFail2 ->
    ins (LABEL l1) =>>
    branch (cExpr Strict fail after2 f) =>>= \canFail3 ->
    simply (canFail1 || canFail2 || canFail3)

cExpr Strict fail after (PosExpCase p c as) =
  cExpr (if isIntCase as then PrimStrict OpWord else Strict) fail Continue c =>>= \canFail ->
  cBranch after (cCase as) =>>
  simply True
  where
    cCase []         after1 after2 = nop
    cCase (alt:alts) after1 after2 =
      (if null alts
         then simply fail
         else newLabel) =>>= \lab ->
      branch (
        ins (DUP) =>>
        (case alt of
           PosAltInt _ i _  e -> ins (LDC_I4 i) =>>
                                 ins (BNE lab)  =>>
                                 simply e
	   PosAltCon p t vs e -> liftC (getIdSignature t) =>>= \sig ->
                                 bindLocal (getExprVar c) True sig =>>= \loc ->
                                 mapC_ (\(vs,n) -> bindField (snd vs) (Local loc True) sig ('c':show n)) (zip vs [1..]) =>>
                                 ins (ISINST sig)  =>>
                                 ins (STLOC loc)   =>>
                                 ins (LDLOC loc)   =>>
                                 ins (BRFALSE lab) =>>
                                 simply e) =>>= \e ->
        ins (POP) =>>
        (if null alts
           then cExpr Strict lab after2  e =>>= \canFail ->
                nop
           else cExpr Strict lab after1 e =>>= \canFail ->
                ins (LABEL lab))) =>>
      cCase alts after1 after2

cExpr m      fail after e =
  getIntState =>>= \ is ->
  error $ "cExpr: no code for '"++strPExp (strIS is) "" e ++"'"


{- compile a call to a function, with some number of arguments given -}
cCall :: SMode -> Label -> BranchAfter -> PosExp -> [PosExp] -> Compiler Bool
cCall m fail after (PosPrim p c i) as =
  case c of
    P.ADD op    -> cPrimOp (ins IL.ADD) op
    P.SUB op    -> cPrimOp (ins IL.SUB) op
    P.MUL op    -> cPrimOp (ins IL.MUL) op
    P.QUOT      -> cPrimOp (ins IL.DIV) OpWord
    P.REM       -> cPrimOp (ins IL.REM) OpWord
    P.SLASH op  -> cPrimOp (ins IL.DIV) op
    P.CMP_EQ op -> cPrimOp (ins IL.CEQ) op
    P.CMP_NE op -> cPrimOp (ins IL.CEQ =>> ins IL.NOT) op
    P.CMP_LE op -> cPrimOp (ins IL.CGT =>> ins IL.NOT) op
    P.CMP_LT op -> cPrimOp (ins IL.CLT) op
    P.CMP_GE op -> cPrimOp (ins IL.CLT =>> ins IL.NOT) op
    P.CMP_GT op -> cPrimOp (ins IL.CGT) op
    P.NEG op    -> cPrimOp (ins IL.NEG) op
    P.ORD       -> mapC (cExpr Strict fail Continue) as =>>= \fs ->
                   ins (CALL sigClosure sigClosure "FromEnum" (mkClosureArgs 1)) =>>
                   cBranchAfter (or fs) Lazy after
    P.STRING    -> mapC (cExpr Strict fail Continue) as =>>= \fs ->
                   cBranchAfter (or fs) m after
  where
    cPrimOp f op =
      mapC (cExpr (PrimStrict op) fail Continue) as =>>= \fs ->
      f =>> insBox m op =>>
      cBranchAfter (or fs) Lazy after

cCall m fail after (PosCon p c) as =
  mapC (cExpr Lazy fail Continue) as =>>= \fs ->
  liftC (getIdSignature c) =>>= \sig ->
  ins (NEWOBJ sig (mkClosureArgs (length as))) =>>
  cBranchAfter (or fs) Lazy after

cCall m fail after (PosVar p v) as =
  isGlobal v =>>= \ glob ->
  (if glob then getIntState =>>= \ is ->
                let got    = length as
                    expect = arityIS is v
                    extra  = got - expect
                    (expected_as, extra_as) = splitAt expect as
                in
                  -- saturated or super-saturated case
                  liftC (getIdSignature v) =>>= \sig ->
                  if got >= expect
                    then mapC (cExpr Lazy fail Continue) expected_as =>>= \fs ->
                         (if expect > 0 then ins (NEWOBJ sig (mkClosureArgs expect))
                                        else ins (LDSFLD sigClosure sig "indirection")
                         ) =>>
                         mapC (cExpr Lazy fail Continue) extra_as =>>= \fs ->
                         -- apply extra arguments if needed
                         (if extra > 0 then ins (CALLVIRT sigClosure sigClosure "Apply" (mkClosureArgs extra))
                                       else nop
                         ) =>>
                         -- eval the result if needed
                         cBranchAfter (or fs) m after
                    else ins (LDTOKEN_METHOD VoidSignature sig ".ctor" (mkClosureArgs expect)) =>>
                         mapC (cExpr Lazy fail Continue) as =>>= \fs ->
                         ins (NEWOBJ sigPAPClosure (ValueSignature "mscorlib" "System.RuntimeMethodHandle" : mkClosureArgs got)) =>>
                         cBranchAfter (or fs) Lazy after
           else pushVar Lazy Continue v =>>= \canFail ->
                mapC (cExpr Lazy fail Continue) as =>>= \fs ->
                ins (CALLVIRT sigClosure sigClosure "Apply" (mkClosureArgs (length as))) =>>
                cBranchAfter (canFail || or fs) m after)

cCall m fail after f@(PosExpThunk p ap es) as =
  cExpr Lazy fail Continue f =>>= \canFail ->
  mapC (cExpr Lazy fail Continue) as =>>= \fs ->
  ins (CALLVIRT sigClosure sigClosure "Apply" (mkClosureArgs (length as))) =>>
  cBranchAfter (canFail || or fs) m after

cCall m fail after e got =
  getIntState =>>= \ is ->
  error $ "cCall: no code for '"++strPExp (strIS is) "" e++"'"


cBranchAfter canFail m (Return      ) = getIntState =>>= \ is ->
                                        liftC getCurrentId =>>= \i ->
                                        let ar = arityIS is i in
                                        (if ar == 0
                                           then liftC (getIdSignature i) =>>= \sig ->
                                                ins (STSFLD sigClosure sig "indirection") =>>
                                                ins (LDSFLD sigClosure sig "indirection")
                                           else ins (STFLD sigClosure sigThunkClosure "indirection") =>>
                                                ins (LDARG 0) =>>
                                                ins (LDFLD sigClosure sigThunkClosure "indirection")) =>>
                                        (if m /= Lazy
                                           then ins (TAIL)
                                           else nop) =>>
                                        insEval m =>> ins (RET)  =>> simply canFail
cBranchAfter canFail m (Continue    ) = insEval m =>>                simply canFail
cBranchAfter canFail m (ContinueTo l) = insEval m =>>                simply canFail
cBranchAfter canFail m (Goto       l) = insEval m =>> ins (BR l) =>> simply canFail

cBranch (Continue    ) f = newLabel =>>= \l ->
                           f (Goto l) (ContinueTo l) =>>= \r ->
                           ins (LABEL l) =>>
                           simply r
cBranch (ContinueTo l) f = f (Goto l) (ContinueTo l)
cBranch after          f = f after    after

-----------------------------------------------------------------------------------
-- helper functions
-----------------------------------------------------------------------------------

{- for a list of alternatives: returns whether this is an int-case -}
isIntCase :: [PosAlt] -> Bool
isIntCase as@(PosAltInt {} : _) = True
isIntCase as@(PosAltCon {} : _) = False

getExprVar (PosCon _ c) = Just c
getExprVar (PosVar _ v) = Just v
getExprVar _            = Nothing

-----------------------------------------------------------------------------------
-- instruction generation functions
-----------------------------------------------------------------------------------

{- issue a non instruction -}
nop :: Compiler ()
nop = simply ()

{- issue a full instruction -}
ins :: ILInstruction -> Compiler ()
ins i = return ((i :) , ())

{- issue an eval instruction if needed -}
insEval :: SMode -> Compiler ()
insEval (PrimStrict OpWord)   = ins (CALL Int32Signature  sigClosure "EvalInt" [])
insEval (PrimStrict OpDouble) = ins (CALL DoubleSignature sigClosure "EvalFloat" [])
insEval (PrimStrict OpFloat)  = ins (CALL FloatSignature  sigClosure "EvalDouble" [])
insEval Strict                = ins (CALLVIRT sigClosure  sigClosure "Eval" [])
insEval Lazy                  = nop

insBox (PrimStrict op1) op = nop
insBox _                op =
  case op of
    OpWord   -> ins (NEWOBJ sigIntClosure    [Int32Signature])
    OpDouble -> ins (NEWOBJ sigDoubleClosure [DoubleSignature])
    OpFloat  -> ins (NEWOBJ sigFloatClosure  [FloatSignature])

{- 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 :: SMode -> BranchAfter -> Id -> Compiler Bool
pushVar m after i =
  whereIs i =>>= \mb_wh ->
  case mb_wh of
    Just wh -> pushWhere m after wh
    Nothing -> getIntState =>>= \ is ->
               liftC (getIdSignature i) =>>= \sig ->
               let ar = arityIS is i in
               if ar == 0
                 then ins (LDSFLD sigClosure sig "indirection") =>>
                      cBranchAfter False m after
                 else ins (LDTOKEN_METHOD VoidSignature sig ".ctor" (mkClosureArgs ar)) =>>
	              ins (NEWOBJ sigPAPClosure [ValueSignature "mscorlib" "System.RuntimeMethodHandle"]) =>>
	              cBranchAfter False Lazy after
  where
    pushWhere m after (This)           = ins (LDARG 0) =>>
                                         cBranchAfter False Lazy after
    pushWhere m after (Local n True)   = ins (LDLOC n) =>>
                                         cBranchAfter False Lazy after
    pushWhere m after (Local n False)  = ins (LDLOC n) =>>
                                         cBranchAfter False m after
    pushWhere m after (Field wh sig n) = pushWhere m Continue wh =>>= \canFail ->
                                         ins (LDFLD sigClosure sig n) =>>
                                         cBranchAfter canFail m after

getIdSignature :: Id -> STCompiler TypeSignature
getIdSignature v = do
  state <- readState cState
  case lookupIS state v of
    Just info -> let ns = splitNS info

                     name = case ns of
                       [n] -> (getModuleId state ++ "." ++ n)
                       ns  -> (concat (intersperse "." ns))

                     package
                       | tidI info == name_System_IO_Handle = "Haskell.Runtime"
                       | otherwise                          = ""

                 in return (ClassSignature package name)
    Nothing   -> localVarError

name_System_IO_Handle = Qualified (packString "System.IO") (packString "Handle")

getIdLocalSignature :: Id -> STCompiler (String, ILName, TypeSignature)
getIdLocalSignature v = do
  state <- readState cState
  case lookupIS state v of
    Just info -> let ns    = splitNS info
                     modid = getModuleId state
                 in case ns of
                      [n]    -> return ("", n, ClassSignature "" (getModuleId state ++ "." ++ n))
                      (n:ns) -> if n == modid
                                  then let ns_name = concat (intersperse "." (init ns))
                                           name    = concat (intersperse "." (n:ns))
                                       in return (ns_name, last ns, ClassSignature "" name)
                                  else localVarError
    Nothing   -> localVarError

setCurrentId :: Id -> STCompiler ()
setCurrentId i = writeState_ $ \s -> s { cCurrId = i }

getCurrentId :: STCompiler Id
getCurrentId = readState cCurrId

localVarError = error "Not a locally defined name"

splitNS :: Info -> [String]
splitNS info = split (tidI info)
  where
    split (TupleId n)          = [unpack rpsPrelude, 'Z':show n]
    split (Visible n)          = [encodeName n]
    split (Qualified  m n)     = [unpack m, encodeName n]
    split (Qualified2 m c t)   = [unpack m] ++ split c ++ split t
    split (Qualified3 m c t i) = [unpack m] ++ split c ++ split t ++ split i

    encodeName :: PackedString -> String
    encodeName ps =
      case encode (unpackPS ps) base of
        []     -> []
        (c:cs) -> toUpper c : cs
      where
        base | isConstr info = "Closure"
             | otherwise     = "Thunk"

        encode       [] xs = xs
        encode ('-':cs) xs = encode cs ('Z':'a':xs)
        encode ('+':cs) xs = encode cs ('Z':'b':xs)
        encode ('*':cs) xs = encode cs ('Z':'c':xs)
        encode ('=':cs) xs = encode cs ('Z':'d':xs)
        encode ('>':cs) xs = encode cs ('Z':'e':xs)
        encode ('<':cs) xs = encode cs ('Z':'f':xs)
        encode ('[':cs) xs = encode cs ('Z':'g':xs)
        encode (']':cs) xs = encode cs ('Z':'h':xs)
        encode ('.':cs) xs = encode cs ('Z':'i':xs)
        encode (':':cs) xs = encode cs ('Z':'j':xs)
        encode ('&':cs) xs = encode cs ('Z':'k':xs)
        encode ('/':cs) xs = encode cs ('Z':'l':xs)
        encode ('\'':cs)xs = encode cs ('Z':'m':xs)
        encode ('|':cs) xs = encode cs ('Z':'n':xs)
        encode ('$':cs) xs = encode cs ('Z':'o':xs)
        encode ('!':cs) xs = encode cs ('Z':'p':xs)
        encode ('^':cs) xs = encode cs ('Z':'q':xs)
        encode ('%':cs) xs = encode cs ('Z':'r':xs)
        encode ('Z':cs) xs = encode cs ('Z':'z':xs)
        encode (  c:cs) xs = encode cs (      c:xs)

    unpack = reverse . unpackPS

mkClosureArgs n = replicate n sigClosure

sigClosure          = ClassSignature "Haskell.Runtime" "Haskell.Runtime.Closure"
sigStringClosure    = ClassSignature "" "Haskell.Runtime.StringClosure"
sigIntClosure       = ClassSignature "Haskell.Runtime" "Haskell.Runtime.IntClosure"
sigFloatClosure     = ClassSignature "Haskell.Runtime" "Haskell.Runtime.FloatClosure"
sigDoubleClosure    = ClassSignature "Haskell.Runtime" "Haskell.Runtime.DoubleClosure"
sigIntegerClosure   = ClassSignature "Haskell.Runtime" "Haskell.Runtime.IntegerClosure"
sigThunkClosure     = ClassSignature "Haskell.Runtime" "Haskell.Runtime.ThunkClosure"
sigPAPClosure       = ClassSignature "Haskell.Runtime" "Haskell.Runtime.PAPClosure"
sigCAFClosure       = ClassSignature "Haskell.Runtime" "Haskell.Runtime.CAFClosure"
sigBlackHoleClosure = ClassSignature "Haskell.Runtime" "Haskell.Runtime.BlackHoleClosure"
sigPrimitives       = ClassSignature "Haskell.Runtime" "Haskell.Runtime.Primitives"
sigPaternMatchException = ClassSignature "Haskell.Runtime" "Haskell.Runtime.PatternMatchException"

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.