{-# LANGUAGE CPP, MagicHash, RecordWildCards, BangPatterns #-}
{-# OPTIONS_GHC -fprof-auto-top #-}
--
--  (c) The University of Glasgow 2002-2006
--

-- | ByteCodeGen: Generate bytecode from Core
module ByteCodeGen ( UnlinkedBCO, byteCodeGen, coreExprToBCOs ) where

#include "HsVersions.h"

import ByteCodeInstr
import ByteCodeAsm
import ByteCodeTypes

import GHCi
import GHCi.FFI
import GHCi.RemoteTypes
import BasicTypes
import DynFlags
import Outputable
import Platform
import Name
import MkId
import Id
import ForeignCall
import HscTypes
import CoreUtils
import CoreSyn
import PprCore
import Literal
import PrimOp
import CoreFVs
import Type
import RepType
import Kind            ( isLiftedTypeKind )
import DataCon
import TyCon
import Util
import VarSet
import TysPrim
import ErrUtils
import Unique
import FastString
import Panic
import StgCmmLayout     ( ArgRep(..), toArgRep, argRepSizeW )
import SMRep
import Bitmap
import OrdList
import Maybes
import VarEnv

import Data.List
import Foreign
import Control.Monad
import Data.Char

import UniqSupply
import Module
import Control.Arrow ( second )

import Control.Exception
import Data.Array
import Data.ByteString (ByteString)
import Data.Map (Map)
import Data.IntMap (IntMap)
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import qualified FiniteMap as Map
import Data.Ord
#if MIN_VERSION_base(4,9,0)
import GHC.Stack.CCS
#else
import GHC.Stack as GHC.Stack.CCS
#endif

-- -----------------------------------------------------------------------------
-- Generating byte code for a complete module

byteCodeGen :: HscEnv
            -> Module
            -> CoreProgram
            -> [TyCon]
            -> Maybe ModBreaks
            -> IO CompiledByteCode
byteCodeGen hsc_env this_mod binds tycs mb_modBreaks
   = withTiming (pure dflags)
                (text "ByteCodeGen"<+>brackets (ppr this_mod))
                (const ()) $ do
        -- Split top-level binds into strings and others.
        -- See Note [generating code for top-level string literal bindings].
        let (strings, flatBinds) = splitEithers $ do
                (bndr, rhs) <- flattenBinds binds
                return $ case rhs of
                    Lit (MachStr str) -> Left (bndr, str)
                    _ -> Right (bndr, simpleFreeVars rhs)
        stringPtrs <- allocateTopStrings hsc_env strings

        us <- mkSplitUniqSupply 'y'
        (BcM_State{..}, proto_bcos) <-
           runBc hsc_env us this_mod mb_modBreaks (mkVarEnv stringPtrs) $
             mapM schemeTopBind flatBinds

        when (notNull ffis)
             (panic "ByteCodeGen.byteCodeGen: missing final emitBc?")

        dumpIfSet_dyn dflags Opt_D_dump_BCOs
           "Proto-BCOs" (vcat (intersperse (char ' ') (map ppr proto_bcos)))

        cbc <- assembleBCOs hsc_env proto_bcos tycs (map snd stringPtrs)
          (case modBreaks of
             Nothing -> Nothing
             Just mb -> Just mb{ modBreaks_breakInfo = breakInfo })

        -- Squash space leaks in the CompiledByteCode.  This is really
        -- important, because when loading a set of modules into GHCi
        -- we don't touch the CompiledByteCode until the end when we
        -- do linking.  Forcing out the thunks here reduces space
        -- usage by more than 50% when loading a large number of
        -- modules.
        evaluate (seqCompiledByteCode cbc)

        return cbc

  where dflags = hsc_dflags hsc_env

allocateTopStrings
  :: HscEnv
  -> [(Id, ByteString)]
  -> IO [(Var, RemotePtr ())]
allocateTopStrings hsc_env topStrings = do
  let !(bndrs, strings) = unzip topStrings
  ptrs <- iservCmd hsc_env $ MallocStrings strings
  return $ zip bndrs ptrs

{-
Note [generating code for top-level string literal bindings]

Here is a summary on how the byte code generator deals with top-level string
literals:

1. Top-level string literal bindings are spearted from the rest of the module.

2. The strings are allocated via iservCmd, in allocateTopStrings

3. The mapping from binders to allocated strings (topStrings) are maintained in
   BcM and used when generating code for variable references.
-}

-- -----------------------------------------------------------------------------
-- Generating byte code for an expression

-- Returns: the root BCO for this expression
coreExprToBCOs :: HscEnv
               -> Module
               -> CoreExpr
               -> IO UnlinkedBCO
coreExprToBCOs hsc_env this_mod expr
 = withTiming (pure dflags)
              (text "ByteCodeGen"<+>brackets (ppr this_mod))
              (const ()) $ do
      -- create a totally bogus name for the top-level BCO; this
      -- should be harmless, since it's never used for anything
      let invented_name  = mkSystemVarName (mkPseudoUniqueE 0) (fsLit "ExprTopLevel")
          invented_id    = Id.mkLocalId invented_name (panic "invented_id's type")

      -- the uniques are needed to generate fresh variables when we introduce new
      -- let bindings for ticked expressions
      us <- mkSplitUniqSupply 'y'
      (BcM_State _dflags _us _this_mod _final_ctr mallocd _ _ _, proto_bco)
         <- runBc hsc_env us this_mod Nothing emptyVarEnv $
              schemeTopBind (invented_id, simpleFreeVars expr)

      when (notNull mallocd)
           (panic "ByteCodeGen.coreExprToBCOs: missing final emitBc?")

      dumpIfSet_dyn dflags Opt_D_dump_BCOs "Proto-BCOs" (ppr proto_bco)

      assembleOneBCO hsc_env proto_bco
  where dflags = hsc_dflags hsc_env

-- The regular freeVars function gives more information than is useful to
-- us here. simpleFreeVars does the impedance matching.
simpleFreeVars :: CoreExpr -> AnnExpr Id DVarSet
simpleFreeVars = go . freeVars
  where
    go :: AnnExpr Id FVAnn -> AnnExpr Id DVarSet
    go (ann, e) = (freeVarsOfAnn ann, go' e)

    go' :: AnnExpr' Id FVAnn -> AnnExpr' Id DVarSet
    go' (AnnVar id)                  = AnnVar id
    go' (AnnLit lit)                 = AnnLit lit
    go' (AnnLam bndr body)           = AnnLam bndr (go body)
    go' (AnnApp fun arg)             = AnnApp (go fun) (go arg)
    go' (AnnCase scrut bndr ty alts) = AnnCase (go scrut) bndr ty (map go_alt alts)
    go' (AnnLet bind body)           = AnnLet (go_bind bind) (go body)
    go' (AnnCast expr (ann, co))     = AnnCast (go expr) (freeVarsOfAnn ann, co)
    go' (AnnTick tick body)          = AnnTick tick (go body)
    go' (AnnType ty)                 = AnnType ty
    go' (AnnCoercion co)             = AnnCoercion co

    go_alt (con, args, expr) = (con, args, go expr)

    go_bind (AnnNonRec bndr rhs) = AnnNonRec bndr (go rhs)
    go_bind (AnnRec pairs)       = AnnRec (map (second go) pairs)

-- -----------------------------------------------------------------------------
-- Compilation schema for the bytecode generator

type BCInstrList = OrdList BCInstr

type Sequel = Word -- back off to this depth before ENTER

-- Maps Ids to the offset from the stack _base_ so we don't have
-- to mess with it after each push/pop.
type BCEnv = Map Id Word -- To find vars on the stack

{-
ppBCEnv :: BCEnv -> SDoc
ppBCEnv p
   = text "begin-env"
     $$ nest 4 (vcat (map pp_one (sortBy cmp_snd (Map.toList p))))
     $$ text "end-env"
     where
        pp_one (var, offset) = int offset <> colon <+> ppr var <+> ppr (bcIdArgRep var)
        cmp_snd x y = compare (snd x) (snd y)
-}

-- Create a BCO and do a spot of peephole optimisation on the insns
-- at the same time.
mkProtoBCO
   :: DynFlags
   -> name
   -> BCInstrList
   -> Either  [AnnAlt Id DVarSet] (AnnExpr Id DVarSet)
   -> Int
   -> Word16
   -> [StgWord]
   -> Bool      -- True <=> is a return point, rather than a function
   -> [FFIInfo]
   -> ProtoBCO name
mkProtoBCO dflags nm instrs_ordlist origin arity bitmap_size bitmap is_ret ffis
   = ProtoBCO {
        protoBCOName = nm,
        protoBCOInstrs = maybe_with_stack_check,
        protoBCOBitmap = bitmap,
        protoBCOBitmapSize = bitmap_size,
        protoBCOArity = arity,
        protoBCOExpr = origin,
        protoBCOFFIs = ffis
      }
     where
        -- Overestimate the stack usage (in words) of this BCO,
        -- and if >= iNTERP_STACK_CHECK_THRESH, add an explicit
        -- stack check.  (The interpreter always does a stack check
        -- for iNTERP_STACK_CHECK_THRESH words at the start of each
        -- BCO anyway, so we only need to add an explicit one in the
        -- (hopefully rare) cases when the (overestimated) stack use
        -- exceeds iNTERP_STACK_CHECK_THRESH.
        maybe_with_stack_check
           | is_ret && stack_usage < fromIntegral (aP_STACK_SPLIM dflags) = peep_d
                -- don't do stack checks at return points,
                -- everything is aggregated up to the top BCO
                -- (which must be a function).
                -- That is, unless the stack usage is >= AP_STACK_SPLIM,
                -- see bug #1466.
           | stack_usage >= fromIntegral iNTERP_STACK_CHECK_THRESH
           = STKCHECK stack_usage : peep_d
           | otherwise
           = peep_d     -- the supposedly common case

        -- We assume that this sum doesn't wrap
        stack_usage = sum (map bciStackUse peep_d)

        -- Merge local pushes
        peep_d = peep (fromOL instrs_ordlist)

        peep (PUSH_L off1 : PUSH_L off2 : PUSH_L off3 : rest)
           = PUSH_LLL off1 (off2-1) (off3-2) : peep rest
        peep (PUSH_L off1 : PUSH_L off2 : rest)
           = PUSH_LL off1 (off2-1) : peep rest
        peep (i:rest)
           = i : peep rest
        peep []
           = []

argBits :: DynFlags -> [ArgRep] -> [Bool]
argBits _      [] = []
argBits dflags (rep : args)
  | isFollowableArg rep  = False : argBits dflags args
  | otherwise = take (argRepSizeW dflags rep) (repeat True) ++ argBits dflags args

-- -----------------------------------------------------------------------------
-- schemeTopBind

-- Compile code for the right-hand side of a top-level binding

schemeTopBind :: (Id, AnnExpr Id DVarSet) -> BcM (ProtoBCO Name)


schemeTopBind (id, rhs)
  | Just data_con <- isDataConWorkId_maybe id,
    isNullaryRepDataCon data_con = do
    dflags <- getDynFlags
        -- Special case for the worker of a nullary data con.
        -- It'll look like this:        Nil = /\a -> Nil a
        -- If we feed it into schemeR, we'll get
        --      Nil = Nil
        -- because mkConAppCode treats nullary constructor applications
        -- by just re-using the single top-level definition.  So
        -- for the worker itself, we must allocate it directly.
    -- ioToBc (putStrLn $ "top level BCO")
    emitBc (mkProtoBCO dflags (getName id) (toOL [PACK data_con 0, ENTER])
                       (Right rhs) 0 0 [{-no bitmap-}] False{-not alts-})

  | otherwise
  = schemeR [{- No free variables -}] (id, rhs)


-- -----------------------------------------------------------------------------
-- schemeR

-- Compile code for a right-hand side, to give a BCO that,
-- when executed with the free variables and arguments on top of the stack,
-- will return with a pointer to the result on top of the stack, after
-- removing the free variables and arguments.
--
-- Park the resulting BCO in the monad.  Also requires the
-- variable to which this value was bound, so as to give the
-- resulting BCO a name.

schemeR :: [Id]                 -- Free vars of the RHS, ordered as they
                                -- will appear in the thunk.  Empty for
                                -- top-level things, which have no free vars.
        -> (Id, AnnExpr Id DVarSet)
        -> BcM (ProtoBCO Name)
schemeR fvs (nm, rhs)
{-
   | trace (showSDoc (
              (char ' '
               $$ (ppr.filter (not.isTyVar).dVarSetElems.fst) rhs
               $$ pprCoreExpr (deAnnotate rhs)
               $$ char ' '
              ))) False
   = undefined
   | otherwise
-}
   = schemeR_wrk fvs nm rhs (collect rhs)

collect :: AnnExpr Id DVarSet -> ([Var], AnnExpr' Id DVarSet)
collect (_, e) = go [] e
  where
    go xs e | Just e' <- bcView e = go xs e'
    go xs (AnnLam x (_,e))
      | typePrimRep (idType x) `lengthExceeds` 1
      = multiValException
      | otherwise
      = go (x:xs) e
    go xs not_lambda = (reverse xs, not_lambda)

schemeR_wrk :: [Id] -> Id -> AnnExpr Id DVarSet -> ([Var], AnnExpr' Var DVarSet) -> BcM (ProtoBCO Name)
schemeR_wrk fvs nm original_body (args, body)
   = do
     dflags <- getDynFlags
     let
         all_args  = reverse args ++ fvs
         arity     = length all_args
         -- all_args are the args in reverse order.  We're compiling a function
         -- \fv1..fvn x1..xn -> e
         -- i.e. the fvs come first

         szsw_args = map (fromIntegral . idSizeW dflags) all_args
         szw_args  = sum szsw_args
         p_init    = Map.fromList (zip all_args (mkStackOffsets 0 szsw_args))

         -- make the arg bitmap
         bits = argBits dflags (reverse (map bcIdArgRep all_args))
         bitmap_size = genericLength bits
         bitmap = mkBitmap dflags bits
     body_code <- schemeER_wrk szw_args p_init body

     emitBc (mkProtoBCO dflags (getName nm) body_code (Right original_body)
                 arity bitmap_size bitmap False{-not alts-})

-- introduce break instructions for ticked expressions
schemeER_wrk :: Word -> BCEnv -> AnnExpr' Id DVarSet -> BcM BCInstrList
schemeER_wrk d p rhs
  | AnnTick (Breakpoint tick_no fvs) (_annot, newRhs) <- rhs
  = do  code <- schemeE (fromIntegral d) 0 p newRhs
        cc_arr <- getCCArray
        this_mod <- moduleName <$> getCurrentModule
        let idOffSets = getVarOffSets d p fvs