summaryrefslogtreecommitdiff
path: root/compiler/ghci
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/ghci')
-rw-r--r--compiler/ghci/ByteCodeAsm.hs93
-rw-r--r--compiler/ghci/ByteCodeGen.hs175
-rw-r--r--compiler/ghci/ByteCodeInstr.hs55
-rw-r--r--compiler/ghci/ByteCodeItbls.hs437
-rw-r--r--compiler/ghci/ByteCodeLink.hs284
-rw-r--r--compiler/ghci/ByteCodeTypes.hs90
-rw-r--r--compiler/ghci/Debugger.hs8
-rw-r--r--compiler/ghci/DebuggerUtils.hs2
-rw-r--r--compiler/ghci/GHCi.hs499
-rw-r--r--compiler/ghci/Linker.hs544
-rw-r--r--compiler/ghci/ObjLink.hs142
-rw-r--r--compiler/ghci/RtClosureInspect.hs14
12 files changed, 1168 insertions, 1175 deletions
diff --git a/compiler/ghci/ByteCodeAsm.hs b/compiler/ghci/ByteCodeAsm.hs
index c69cede7f3..875de879cb 100644
--- a/compiler/ghci/ByteCodeAsm.hs
+++ b/compiler/ghci/ByteCodeAsm.hs
@@ -8,8 +8,7 @@
module ByteCodeAsm (
assembleBCOs, assembleBCO,
- CompiledByteCode(..),
- UnlinkedBCO(..), BCOPtr(..), BCONPtr(..), bcoFreeNames,
+ bcoFreeNames,
SizedSeq, sizeSS, ssElts,
iNTERP_STACK_CHECK_THRESH
) where
@@ -18,12 +17,13 @@ module ByteCodeAsm (
import ByteCodeInstr
import ByteCodeItbls
+import ByteCodeTypes
+import HscTypes
import Name
import NameSet
import Literal
import TyCon
-import PrimOp
import FastString
import StgCmmLayout ( ArgRep(..) )
import SMRep
@@ -32,6 +32,9 @@ import Outputable
import Platform
import Util
+-- From iserv
+import SizedSeq
+
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative (Applicative(..))
#endif
@@ -47,6 +50,7 @@ import Data.Array.Base ( UArray(..) )
import Data.Array.Unsafe( castSTUArray )
+import qualified Data.ByteString as B
import Foreign
import Data.Char ( ord )
import Data.List
@@ -54,44 +58,12 @@ import Data.Map (Map)
import Data.Maybe (fromMaybe)
import qualified Data.Map as Map
-import GHC.Base ( ByteArray#, MutableByteArray#, RealWorld )
-
-- -----------------------------------------------------------------------------
-- Unlinked BCOs
-- CompiledByteCode represents the result of byte-code
-- compiling a bunch of functions and data types
-data CompiledByteCode
- = ByteCode [UnlinkedBCO] -- Bunch of interpretable bindings
- ItblEnv -- A mapping from DataCons to their itbls
-
-instance Outputable CompiledByteCode where
- ppr (ByteCode bcos _) = ppr bcos
-
-
-data UnlinkedBCO
- = UnlinkedBCO {
- unlinkedBCOName :: Name,
- unlinkedBCOArity :: Int,
- unlinkedBCOInstrs :: ByteArray#, -- insns
- unlinkedBCOBitmap :: ByteArray#, -- bitmap
- unlinkedBCOLits :: (SizedSeq BCONPtr), -- non-ptrs
- unlinkedBCOPtrs :: (SizedSeq BCOPtr) -- ptrs
- }
-
-data BCOPtr
- = BCOPtrName Name
- | BCOPtrPrimOp PrimOp
- | BCOPtrBCO UnlinkedBCO
- | BCOPtrBreakInfo BreakInfo
- | BCOPtrArray (MutableByteArray# RealWorld)
-
-data BCONPtr
- = BCONPtrWord Word
- | BCONPtrLbl FastString
- | BCONPtrItbl Name
-
-- | Finds external references. Remember to remove the names
-- defined by this group of BCOs themselves
bcoFreeNames :: UnlinkedBCO -> NameSet
@@ -105,12 +77,6 @@ bcoFreeNames bco
map bco_refs [ bco | BCOPtrBCO bco <- ssElts ptrs ]
)
-instance Outputable UnlinkedBCO where
- ppr (UnlinkedBCO nm _arity _insns _bitmap lits ptrs)
- = sep [text "BCO", ppr nm, text "with",
- ppr (sizeSS lits), text "lits",
- ppr (sizeSS ptrs), text "ptrs" ]
-
-- -----------------------------------------------------------------------------
-- The bytecode assembler
@@ -122,11 +88,11 @@ instance Outputable UnlinkedBCO where
-- bytecode address in this BCO.
-- Top level assembler fn.
-assembleBCOs :: DynFlags -> [ProtoBCO Name] -> [TyCon] -> IO CompiledByteCode
-assembleBCOs dflags proto_bcos tycons
- = do itblenv <- mkITbls dflags tycons
- bcos <- mapM (assembleBCO dflags) proto_bcos
- return (ByteCode bcos itblenv)
+assembleBCOs :: HscEnv -> [ProtoBCO Name] -> [TyCon] -> IO CompiledByteCode
+assembleBCOs hsc_env proto_bcos tycons = do
+ itblenv <- mkITbls hsc_env tycons
+ bcos <- mapM (assembleBCO (hsc_dflags hsc_env)) proto_bcos
+ return (ByteCode bcos itblenv (concat (map protoBCOFFIs proto_bcos)))
assembleBCO :: DynFlags -> ProtoBCO Name -> IO UnlinkedBCO
assembleBCO dflags (ProtoBCO nm instrs bitmap bsize arity _origin _malloced) = do
@@ -161,15 +127,9 @@ assembleBCO dflags (ProtoBCO nm instrs bitmap bsize arity _origin _malloced) = d
ASSERT(n_insns == sizeSS final_insns) return ()
let asm_insns = ssElts final_insns
- barr a = case a of UArray _lo _hi _n b -> b
-
- insns_arr = Array.listArray (0, n_insns - 1) asm_insns
- !insns_barr = barr insns_arr
-
+ insns_arr = Array.listArray (0, fromIntegral n_insns - 1) asm_insns
bitmap_arr = mkBitmapArray bsize bitmap
- !bitmap_barr = barr bitmap_arr
-
- ul_bco = UnlinkedBCO nm arity insns_barr bitmap_barr final_lits final_ptrs
+ ul_bco = UnlinkedBCO nm arity insns_arr bitmap_arr final_lits final_ptrs
-- 8 Aug 01: Finalisers aren't safe when attached to non-primitive
-- objects, since they might get run too early. Disable this until
@@ -191,23 +151,6 @@ type AsmState = (SizedSeq Word16,
SizedSeq BCONPtr,
SizedSeq BCOPtr)
-data SizedSeq a = SizedSeq !Word [a]
-emptySS :: SizedSeq a
-emptySS = SizedSeq 0 []
-
-addToSS :: SizedSeq a -> a -> SizedSeq a
-addToSS (SizedSeq n r_xs) x = SizedSeq (n+1) (x:r_xs)
-
-addListToSS :: SizedSeq a -> [a] -> SizedSeq a
-addListToSS (SizedSeq n r_xs) xs
- = SizedSeq (n + genericLength xs) (reverse xs ++ r_xs)
-
-ssElts :: SizedSeq a -> [a]
-ssElts (SizedSeq _ r_xs) = reverse r_xs
-
-sizeSS :: SizedSeq a -> Word
-sizeSS (SizedSeq n _) = n
-
data Operand
= Op Word
| SmallOp Word16
@@ -365,9 +308,7 @@ assembleI dflags i = case i of
-> do let ul_bco = assembleBCO dflags proto
p <- ioptr (liftM BCOPtrBCO ul_bco)
emit (push_alts pk) [Op p]
- PUSH_UBX (Left lit) nws -> do np <- literal lit
- emit bci_PUSH_UBX [Op np, SmallOp nws]
- PUSH_UBX (Right aa) nws -> do np <- addr aa
+ PUSH_UBX lit nws -> do np <- literal lit
emit bci_PUSH_UBX [Op np, SmallOp nws]
PUSH_APPLY_N -> emit bci_PUSH_APPLY_N []
@@ -437,7 +378,9 @@ assembleI dflags i = case i of
literal (MachChar c) = int (ord c)
literal (MachInt64 ii) = int64 (fromIntegral ii)
literal (MachWord64 ii) = int64 (fromIntegral ii)
- literal other = pprPanic "ByteCodeAsm.literal" (ppr other)
+ literal (MachStr bs) = lit [BCONPtrStr (bs `B.snoc` 0)]
+ -- MachStr requires a zero-terminator when emitted
+ literal LitInteger{} = panic "ByteCodeAsm.literal: LitInteger"
litlabel fs = lit [BCONPtrLbl fs]
addr = words . mkLitPtr
diff --git a/compiler/ghci/ByteCodeGen.hs b/compiler/ghci/ByteCodeGen.hs
index f331214892..f74b4c439a 100644
--- a/compiler/ghci/ByteCodeGen.hs
+++ b/compiler/ghci/ByteCodeGen.hs
@@ -9,11 +9,13 @@ module ByteCodeGen ( UnlinkedBCO, byteCodeGen, coreExprToBCOs ) where
#include "HsVersions.h"
import ByteCodeInstr
-import ByteCodeItbls
import ByteCodeAsm
-import ByteCodeLink
-import LibFFI
+import ByteCodeTypes
+import GHCi
+import GHCi.FFI
+import GHCi.RemoteTypes
+import BasicTypes
import DynFlags
import Outputable
import Platform
@@ -45,7 +47,6 @@ import OrdList
import Data.List
import Foreign
-import Foreign.C
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative (Applicative(..))
@@ -59,8 +60,6 @@ import Data.Maybe
import Module
import Control.Arrow ( second )
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Unsafe as BS
import Data.Map (Map)
import qualified Data.Map as Map
import qualified FiniteMap as Map
@@ -69,42 +68,43 @@ import Data.Ord
-- -----------------------------------------------------------------------------
-- Generating byte code for a complete module
-byteCodeGen :: DynFlags
+byteCodeGen :: HscEnv
-> Module
-> CoreProgram
-> [TyCon]
-> ModBreaks
-> IO CompiledByteCode
-byteCodeGen dflags this_mod binds tycs modBreaks
- = do showPass dflags "ByteCodeGen"
+byteCodeGen hsc_env this_mod binds tycs modBreaks
+ = do let dflags = hsc_dflags hsc_env
+ showPass dflags "ByteCodeGen"
let flatBinds = [ (bndr, simpleFreeVars rhs)
| (bndr, rhs) <- flattenBinds binds]
us <- mkSplitUniqSupply 'y'
- (BcM_State _dflags _us _this_mod _final_ctr mallocd _, proto_bcos)
- <- runBc dflags us this_mod modBreaks (mapM schemeTopBind flatBinds)
+ (BcM_State _hsc_env _us _this_mod _final_ctr ffis _, proto_bcos)
+ <- runBc hsc_env us this_mod modBreaks (mapM schemeTopBind flatBinds)
- when (notNull mallocd)
+ 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)))
- assembleBCOs dflags proto_bcos tycs
- where
+ assembleBCOs hsc_env proto_bcos tycs
-- -----------------------------------------------------------------------------
-- Generating byte code for an expression
-- Returns: (the root BCO for this expression,
-- a list of auxilary BCOs resulting from compiling closures)
-coreExprToBCOs :: DynFlags
+coreExprToBCOs :: HscEnv
-> Module
-> CoreExpr
-> IO UnlinkedBCO
-coreExprToBCOs dflags this_mod expr
- = do showPass dflags "ByteCodeGen"
+coreExprToBCOs hsc_env this_mod expr
+ = do let dflags = hsc_dflags hsc_env
+ showPass dflags "ByteCodeGen"
-- create a totally bogus name for the top-level BCO; this
-- should be harmless, since it's never used for anything
@@ -115,7 +115,7 @@ coreExprToBCOs dflags this_mod expr
-- let bindings for ticked expressions
us <- mkSplitUniqSupply 'y'
(BcM_State _dflags _us _this_mod _final_ctr mallocd _ , proto_bco)
- <- runBc dflags us this_mod emptyModBreaks $
+ <- runBc hsc_env us this_mod emptyModBreaks $
schemeTopBind (invented_id, simpleFreeVars expr)
when (notNull mallocd)
@@ -184,9 +184,9 @@ mkProtoBCO
-> Word16
-> [StgWord]
-> Bool -- True <=> is a return point, rather than a function
- -> [BcPtr]
+ -> [FFIInfo]
-> ProtoBCO name
-mkProtoBCO dflags nm instrs_ordlist origin arity bitmap_size bitmap is_ret mallocd_blocks
+mkProtoBCO dflags nm instrs_ordlist origin arity bitmap_size bitmap is_ret ffis
= ProtoBCO {
protoBCOName = nm,
protoBCOInstrs = maybe_with_stack_check,
@@ -194,7 +194,7 @@ mkProtoBCO dflags nm instrs_ordlist origin arity bitmap_size bitmap is_ret mallo
protoBCOBitmapSize = bitmap_size,
protoBCOArity = arity,
protoBCOExpr = origin,
- protoBCOPtrs = mallocd_blocks
+ protoBCOFFIs = ffis
}
where
-- Overestimate the stack usage (in words) of this BCO,
@@ -1042,27 +1042,23 @@ generateCCall d0 s p (CCallSpec target cconv safety) fn args_r_to_l
void marshall_code ( StgWord* ptr_to_top_of_stack )
-}
-- resolve static address
- get_target_info = do
+ maybe_static_target =
case target of
- DynamicTarget
- -> return (False, panic "ByteCodeGen.generateCCall(dyn)")
-
+ DynamicTarget -> Nothing
StaticTarget _ _ _ False ->
- panic "generateCCall: unexpected FFI value import"
- StaticTarget _ target _ True
- -> do res <- ioToBc (lookupStaticPtr stdcall_adj_target)
- return (True, res)
+ panic "generateCCall: unexpected FFI value import"
+ StaticTarget _ target _ True ->
+ Just (MachLabel target mb_size IsFunction)
where
- stdcall_adj_target
+ mb_size
| OSMinGW32 <- platformOS (targetPlatform dflags)
, StdCallConv <- cconv
- = let size = fromIntegral a_reps_sizeW * wORD_SIZE dflags in
- mkFastString (unpackFS target ++ '@':show size)
+ = Just (fromIntegral a_reps_sizeW * wORD_SIZE dflags)
| otherwise
- = target
+ = Nothing
- (is_static, static_target_addr) <- get_target_info
let
+ is_static = isJust maybe_static_target
-- Get the arg reps, zapping the leading Addr# in the dynamic case
a_reps -- | trace (showSDoc (ppr a_reps_pushed_RAW)) False = error "???"
@@ -1073,8 +1069,8 @@ generateCCall d0 s p (CCallSpec target cconv safety) fn args_r_to_l
-- push the Addr#
(push_Addr, d_after_Addr)
- | is_static
- = (toOL [PUSH_UBX (Right static_target_addr) addr_sizeW],
+ | Just machlabel <- maybe_static_target
+ = (toOL [PUSH_UBX machlabel addr_sizeW],
d_after_args + fromIntegral addr_sizeW)
| otherwise -- is already on the stack
= (nilOL, d_after_args)
@@ -1086,7 +1082,7 @@ generateCCall d0 s p (CCallSpec target cconv safety) fn args_r_to_l
r_lit = mkDummyLiteral r_rep
push_r = (if returns_void
then nilOL
- else unitOL (PUSH_UBX (Left r_lit) r_sizeW))
+ else unitOL (PUSH_UBX r_lit r_sizeW))
-- generate the marshalling code we're going to call
@@ -1096,16 +1092,26 @@ generateCCall d0 s p (CCallSpec target cconv safety) fn args_r_to_l
-- is. See comment in Interpreter.c with the CCALL instruction.
stk_offset = trunc16 $ d_after_r - s
+ conv = case cconv of
+ CCallConv -> FFICCall
+ StdCallConv -> FFIStdCall
+ _ -> panic "ByteCodeGen: unexpected calling convention"
+
-- the only difference in libffi mode is that we prepare a cif
-- describing the call type by calling libffi, and we attach the
-- address of this to the CCALL instruction.
- token <- ioToBc $ prepForeignCall dflags cconv a_reps r_rep
- let addr_of_marshaller = castPtrToFunPtr token
- recordItblMallocBc (ItblPtr (castFunPtrToPtr addr_of_marshaller))
+
+ let ffires = primRepToFFIType dflags r_rep
+ ffiargs = map (primRepToFFIType dflags) a_reps
+ hsc_env <- getHscEnv
+ rp <- ioToBc $ iservCmd hsc_env (PrepFFI conv ffiargs ffires)
+ let token = fromRemotePtr rp
+ recordFFIBc token
+
let
-- do the call
- do_call = unitOL (CCALL stk_offset (castFunPtrToPtr addr_of_marshaller)
+ do_call = unitOL (CCALL stk_offset token
(fromIntegral (fromEnum (playInterruptible safety))))
-- slide and return
wrapup = mkSLIDE r_sizeW (d_after_r - fromIntegral r_sizeW - s)
@@ -1116,6 +1122,24 @@ generateCCall d0 s p (CCallSpec target cconv safety) fn args_r_to_l
push_Addr `appOL` push_r `appOL` do_call `appOL` wrapup
)
+primRepToFFIType :: DynFlags -> PrimRep -> FFIType
+primRepToFFIType dflags r
+ = case r of
+ VoidRep -> FFIVoid
+ IntRep -> signed_word
+ WordRep -> unsigned_word
+ Int64Rep -> FFISInt64
+ Word64Rep -> FFIUInt64
+ AddrRep -> FFIPointer
+ FloatRep -> FFIFloat
+ DoubleRep -> FFIDouble
+ _ -> panic "primRepToFFIType"
+ where
+ (signed_word, unsigned_word)
+ | wORD_SIZE dflags == 4 = (FFISInt32, FFIUInt32)
+ | wORD_SIZE dflags == 8 = (FFISInt64, FFIUInt64)
+ | otherwise = panic "primTyDescChar"
+
-- Make a dummy literal, to be used as a placeholder for FFI return
-- values on the stack.
mkDummyLiteral :: PrimRep -> Literal
@@ -1240,7 +1264,7 @@ implement_tagToId d s p arg names
steps = map (mkStep label_exit) infos
return (push_arg
- `appOL` unitOL (PUSH_UBX (Left MachNullAddr) 1)
+ `appOL` unitOL (PUSH_UBX MachNullAddr 1)
-- Push bogus word (see Note [Implementing tagToEnum#])
`appOL` concatOL steps
`appOL` toOL [ LABEL label_fail, CASEFAIL,
@@ -1319,7 +1343,7 @@ pushAtom _ _ (AnnLit lit) = do
dflags <- getDynFlags
let code rep
= let size_host_words = fromIntegral (argRepSizeW dflags rep)
- in return (unitOL (PUSH_UBX (Left lit) size_host_words),
+ in return (unitOL (PUSH_UBX lit size_host_words),
size_host_words)
case lit of
@@ -1332,42 +1356,16 @@ pushAtom _ _ (AnnLit lit) = do
MachDouble _ -> code D
MachChar _ -> code N
MachNullAddr -> code N
- MachStr s -> pushStr s
+ MachStr _ -> code N
-- No LitInteger's should be left by the time this is called.
-- CorePrep should have converted them all to a real core
-- representation.
LitInteger {} -> panic "pushAtom: LitInteger"
- where
- pushStr s
- = let getMallocvilleAddr
- =
- -- we could grab the Ptr from the ForeignPtr,
- -- but then we have no way to control its lifetime.
- -- In reality it'll probably stay alive long enoungh
- -- by virtue of the global FastString table, but
- -- to be on the safe side we copy the string into
- -- a malloc'd area of memory.
- do let n = BS.length s
- ptr <- ioToBc (mallocBytes (n+1))
- recordMallocBc ptr
- ioToBc (
- BS.unsafeUseAsCString s $ \p -> do
- memcpy ptr p (fromIntegral n)
- pokeByteOff ptr n (fromIntegral (ord '\0') :: Word8)
- return ptr
- )
- in do
- addr <- getMallocvilleAddr
- -- Get the addr on the stack, untaggedly
- return (unitOL (PUSH_UBX (Right addr) 1), 1)
pushAtom _ _ expr
= pprPanic "ByteCodeGen.pushAtom"
(pprCoreExpr (deAnnotate (undefined, expr)))
-foreign import ccall unsafe "memcpy"
- memcpy :: Ptr a -> Ptr b -> CSize -> IO ()
-
-- -----------------------------------------------------------------------------
-- Given a bunch of alts code and their discrs, do the donkey work
@@ -1627,15 +1625,13 @@ typeArgRep = toArgRep . typePrimRep
-- -----------------------------------------------------------------------------
-- The bytecode generator's monad
-type BcPtr = Either ItblPtr (Ptr ())
-
data BcM_State
= BcM_State
- { bcm_dflags :: DynFlags
- , uniqSupply :: UniqSupply -- for generating fresh variable names
- , thisModule :: Module -- current module (for breakpoints)
- , nextlabel :: Word16 -- for generating local labels
- , malloced :: [BcPtr] -- thunks malloced for current BCO
+ { bcm_hsc_env :: HscEnv
+ , uniqSupply :: UniqSupply -- for generating fresh variable names
+ , thisModule :: Module -- current module (for breakpoints)
+ , nextlabel :: Word16 -- for generating local labels
+ , ffis :: [FFIInfo] -- ffi info blocks, to free later
-- Should be free()d when it is GCd
, breakArray :: BreakArray -- array of breakpoint flags
}
@@ -1647,10 +1643,10 @@ ioToBc io = BcM $ \st -> do
x <- io
return (st, x)
-runBc :: DynFlags -> UniqSupply -> Module -> ModBreaks -> BcM r
+runBc :: HscEnv -> UniqSupply -> Module -> ModBreaks -> BcM r
-> IO (BcM_State, r)
-runBc dflags us this_mod modBreaks (BcM m)
- = m (BcM_State dflags us this_mod 0 [] breakArray)
+runBc hsc_env us this_mod modBreaks (BcM m)
+ = m (BcM_State hsc_env us this_mod 0 [] breakArray)
where
breakArray = modBreaks_flags modBreaks
@@ -1684,19 +1680,18 @@ instance Monad BcM where
return = pure
instance HasDynFlags BcM where
- getDynFlags = BcM $ \st -> return (st, bcm_dflags st)
+ getDynFlags = BcM $ \st -> return (st, hsc_dflags (bcm_hsc_env st))
-emitBc :: ([BcPtr] -> ProtoBCO Name) -> BcM (ProtoBCO Name)
-emitBc bco
- = BcM $ \st -> return (st{malloced=[]}, bco (malloced st))
+getHscEnv :: BcM HscEnv
+getHscEnv = BcM $ \st -> return (st, bcm_hsc_env st)
-recordMallocBc :: Ptr a -> BcM ()
-recordMallocBc a
- = BcM $ \st -> return (st{malloced = Right (castPtr a) : malloced st}, ())
+emitBc :: ([FFIInfo] -> ProtoBCO Name) -> BcM (ProtoBCO Name)
+emitBc bco
+ = BcM $ \st -> return (st{ffis=[]}, bco (ffis st))
-recordItblMallocBc :: ItblPtr -> BcM ()
-recordItblMallocBc a
- = BcM $ \st -> return (st{malloced = Left a : malloced st}, ())
+recordFFIBc :: Ptr () -> BcM ()
+recordFFIBc a
+ = BcM $ \st -> return (st{ffis = FFIInfo a : ffis st}, ())
getLabelBc :: BcM Word16
getLabelBc
diff --git a/compiler/ghci/ByteCodeInstr.hs b/compiler/ghci/ByteCodeInstr.hs
index 2de4941aa6..4f2b82ba27 100644
--- a/compiler/ghci/ByteCodeInstr.hs
+++ b/compiler/ghci/ByteCodeInstr.hs
@@ -6,17 +6,15 @@
-- | ByteCodeInstrs: Bytecode instruction definitions
module ByteCodeInstr (
- BCInstr(..), ProtoBCO(..), bciStackUse, BreakInfo (..)
+ BCInstr(..), ProtoBCO(..), bciStackUse,
) where
#include "HsVersions.h"
#include "../includes/MachDeps.h"
-import ByteCodeItbls ( ItblPtr )
-
+import ByteCodeTypes
import StgCmmLayout ( ArgRep(..) )
import PprCore
-import Type
import Outputable
import FastString
import Name
@@ -28,7 +26,6 @@ import VarSet
import PrimOp
import SMRep
-import Module (Module)
import GHC.Exts
import Data.Word
@@ -46,7 +43,7 @@ data ProtoBCO a
-- what the BCO came from
protoBCOExpr :: Either [AnnAlt Id DVarSet] (AnnExpr Id DVarSet),
-- malloc'd pointers
- protoBCOPtrs :: [Either ItblPtr (Ptr ())]
+ protoBCOFFIs :: [FFIInfo]
}
type LocalLabel = Word16
@@ -70,7 +67,7 @@ data BCInstr
| PUSH_ALTS_UNLIFTED (ProtoBCO Name) ArgRep
-- Pushing literals
- | PUSH_UBX (Either Literal (Ptr ())) Word16
+ | PUSH_UBX Literal Word16
-- push this int/float/double/addr, on the stack. Word16
-- is # of words to copy from literal pool. Eitherness reflects
-- the difficulty of dealing with MachAddr here, mostly due to
@@ -144,28 +141,13 @@ data BCInstr
-- Breakpoints
| BRK_FUN (MutableByteArray# RealWorld) Word16 BreakInfo
-data BreakInfo
- = BreakInfo
- { breakInfo_module :: Module
- , breakInfo_number :: {-# UNPACK #-} !Int
- , breakInfo_vars :: [(Id,Word16)]
- , breakInfo_resty :: Type
- }
-
-instance Outputable BreakInfo where
- ppr info = text "BreakInfo" <+>
- parens (ppr (breakInfo_module info) <+>
- ppr (breakInfo_number info) <+>
- ppr (breakInfo_vars info) <+>
- ppr (breakInfo_resty info))
-
-- -----------------------------------------------------------------------------
-- Printing bytecode instructions
instance Outputable a => Outputable (ProtoBCO a) where
- ppr (ProtoBCO name instrs bitmap bsize arity origin malloced)
+ ppr (ProtoBCO name instrs bitmap bsize arity origin ffis)
= (text "ProtoBCO" <+> ppr name <> char '#' <> int arity
- <+> text (show malloced) <> colon)
+ <+> text (show ffis) <> colon)
$$ nest 3 (case origin of
Left alts -> vcat (zipWith (<+>) (char '{' : repeat (char ';'))
(map (pprCoreAltShort.deAnnAlt) alts)) <+> char '}'
@@ -210,19 +192,18 @@ instance Outputable BCInstr where
ppr (PUSH_ALTS bco) = hang (text "PUSH_ALTS") 2 (ppr bco)
ppr (PUSH_ALTS_UNLIFTED bco pk) = hang (text "PUSH_ALTS_UNLIFTED" <+> ppr pk) 2 (ppr bco)
- ppr (PUSH_UBX (Left lit) nw) = text "PUSH_UBX" <+> parens (ppr nw) <+> ppr lit
- ppr (PUSH_UBX (Right aa) nw) = text "PUSH_UBX" <+> parens (ppr nw) <+> text (show aa)
- ppr PUSH_APPLY_N = text "PUSH_APPLY_N"
- ppr PUSH_APPLY_V = text "PUSH_APPLY_V"
- ppr PUSH_APPLY_F = text "PUSH_APPLY_F"
- ppr PUSH_APPLY_D = text "PUSH_APPLY_D"
- ppr PUSH_APPLY_L = text "PUSH_APPLY_L"
- ppr PUSH_APPLY_P = text "PUSH_APPLY_P"
- ppr PUSH_APPLY_PP = text "PUSH_APPLY_PP"
- ppr PUSH_APPLY_PPP = text "PUSH_APPLY_PPP"
- ppr PUSH_APPLY_PPPP = text "PUSH_APPLY_PPPP"
- ppr PUSH_APPLY_PPPPP = text "PUSH_APPLY_PPPPP"
- ppr PUSH_APPLY_PPPPPP = text "PUSH_APPLY_PPPPPP"
+ ppr (PUSH_UBX lit nw) = text "PUSH_UBX" <+> parens (ppr nw) <+> ppr lit
+ ppr PUSH_APPLY_N = text "PUSH_APPLY_N"
+ ppr PUSH_APPLY_V = text "PUSH_APPLY_V"
+ ppr PUSH_APPLY_F = text "PUSH_APPLY_F"
+ ppr PUSH_APPLY_D = text "PUSH_APPLY_D"
+ ppr PUSH_APPLY_L = text "PUSH_APPLY_L"
+ ppr PUSH_APPLY_P = text "PUSH_APPLY_P"
+ ppr PUSH_APPLY_PP = text "PUSH_APPLY_PP"
+ ppr PUSH_APPLY_PPP = text "PUSH_APPLY_PPP"
+ ppr PUSH_APPLY_PPPP = text "PUSH_APPLY_PPPP"
+ ppr PUSH_APPLY_PPPPP = text "PUSH_APPLY_PPPPP"
+ ppr PUSH_APPLY_PPPPPP = text "PUSH_APPLY_PPPPPP"
ppr (SLIDE n d) = text "SLIDE " <+> ppr n <+> ppr d
ppr (ALLOC_AP sz) = text "ALLOC_AP " <+> ppr sz
diff --git a/compiler/ghci/ByteCodeItbls.hs b/compiler/ghci/ByteCodeItbls.hs
index 01420f5e34..5a3e6d3e1a 100644
--- a/compiler/ghci/ByteCodeItbls.hs
+++ b/compiler/ghci/ByteCodeItbls.hs
@@ -5,416 +5,69 @@
--
-- | ByteCodeItbls: Generate infotables for interpreter-made bytecodes
-module ByteCodeItbls ( ItblEnv, ItblPtr(..), itblCode, mkITbls, peekItbl
- , StgInfoTable(..)
- ) where
+module ByteCodeItbls ( mkITbls ) where
#include "HsVersions.h"
+import ByteCodeTypes
+import GHCi
+import GHCi.RemoteTypes
import DynFlags
-import Panic
-import Platform
+import HscTypes
import Name ( Name, getName )
import NameEnv
import DataCon ( DataCon, dataConRepArgTys, dataConIdentity )
import TyCon ( TyCon, tyConFamilySize, isDataTyCon, tyConDataCons )
import Type ( flattenRepType, repType, typePrimRep )
import StgCmmLayout ( mkVirtHeapOffsets )
-import CmmInfo ( conInfoTableSizeB, profInfoTableSizeW )
import Util
-
-import Control.Monad
-import Control.Monad.Trans.Class
-import Control.Monad.Trans.State.Strict
-import Data.Maybe
-import Foreign
-import Foreign.C
-
-import GHC.Exts ( Int(I#), addr2Int# )
-import GHC.Ptr ( FunPtr(..) )
+import Panic
{-
Manufacturing of info tables for DataCons
-}
-newtype ItblPtr = ItblPtr (Ptr ()) deriving Show
-
-itblCode :: DynFlags -> ItblPtr -> Ptr ()
-itblCode dflags (ItblPtr ptr)
- | ghciTablesNextToCode = castPtr ptr `plusPtr` conInfoTableSizeB dflags
- | otherwise = castPtr ptr
-
-type ItblEnv = NameEnv (Name, ItblPtr)
- -- We need the Name in the range so we know which
- -- elements to filter out when unloading a module
+-- Make info tables for the data decls in this module
+mkITbls :: HscEnv -> [TyCon] -> IO ItblEnv
+mkITbls hsc_env tcs =
+ foldr plusNameEnv emptyNameEnv <$>
+ mapM (mkITbl hsc_env) (filter isDataTyCon tcs)
+ where
+ mkITbl :: HscEnv -> TyCon -> IO ItblEnv
+ mkITbl hsc_env tc
+ | dcs `lengthIs` n -- paranoia; this is an assertion.
+ = make_constr_itbls hsc_env dcs
+ where
+ dcs = tyConDataCons tc
+ n = tyConFamilySize tc
+ mkITbl _ _ = panic "mkITbl"
mkItblEnv :: [(Name,ItblPtr)] -> ItblEnv
mkItblEnv pairs = mkNameEnv [(n, (n,p)) | (n,p) <- pairs]
-
--- Make info tables for the data decls in this module
-mkITbls :: DynFlags -> [TyCon] -> IO ItblEnv
-mkITbls _ [] = return emptyNameEnv
-mkITbls dflags (tc:tcs) = do itbls <- mkITbl dflags tc
- itbls2 <- mkITbls dflags tcs
- return (itbls `plusNameEnv` itbls2)
-
-mkITbl :: DynFlags -> TyCon -> IO ItblEnv
-mkITbl dflags tc
- | not (isDataTyCon tc)
- = return emptyNameEnv
- | dcs `lengthIs` n -- paranoia; this is an assertion.
- = make_constr_itbls dflags dcs
- where
- dcs = tyConDataCons tc
- n = tyConFamilySize tc
-
-mkITbl _ _ = error "Unmatched patter in mkITbl: assertion failed!"
-
-#include "../includes/rts/storage/ClosureTypes.h"
-cONSTR :: Int -- Defined in ClosureTypes.h
-cONSTR = CONSTR
-
-- Assumes constructors are numbered from zero, not one
-make_constr_itbls :: DynFlags -> [DataCon] -> IO ItblEnv
-make_constr_itbls dflags cons
- = do is <- mapM mk_dirret_itbl (zip cons [0..])
- return (mkItblEnv is)
- where
- mk_dirret_itbl (dcon, conNo)
- = mk_itbl dcon conNo stg_interp_constr_entry
-
- mk_itbl :: DataCon -> Int -> EntryFunPtr -> IO (Name,ItblPtr)
- mk_itbl dcon conNo entry_addr = do
- let rep_args = [ (typePrimRep rep_arg,rep_arg) | arg <- dataConRepArgTys dcon, rep_arg <- flattenRepType (repType arg) ]
- (tot_wds, ptr_wds, _) = mkVirtHeapOffsets dflags False{-not a THUNK-} rep_args
-
- ptrs' = ptr_wds
- nptrs' = tot_wds - ptr_wds
- nptrs_really
- | ptrs' + nptrs' >= mIN_PAYLOAD_SIZE dflags = nptrs'
- | otherwise = mIN_PAYLOAD_SIZE dflags - ptrs'
- code' = mkJumpToAddr dflags entry_addr
- itbl = StgInfoTable {
- entry = if ghciTablesNextToCode
- then Nothing
- else Just entry_addr,
- ptrs = fromIntegral ptrs',
- nptrs = fromIntegral nptrs_really,
- tipe = fromIntegral cONSTR,
- srtlen = fromIntegral conNo,
- code = if ghciTablesNextToCode
- then Just code'
- else Nothing
- }
-
- -- Make a piece of code to jump to "entry_label".
- -- This is the only arch-dependent bit.
- addrCon <- newExecConItbl dflags itbl (dataConIdentity dcon)
- --putStrLn ("SIZE of itbl is " ++ show (sizeOf itbl))
- --putStrLn ("# ptrs of itbl is " ++ show ptrs)
- --putStrLn ("# nptrs of itbl is " ++ show nptrs_really)
- return (getName dcon, ItblPtr (castFunPtrToPtr addrCon))
-
-
--- Make code which causes a jump to the given address. This is the
--- only arch-dependent bit of the itbl story.
-
--- For sparc_TARGET_ARCH, i386_TARGET_ARCH, etc.
-#include "nativeGen/NCG.h"
-
-type ItblCodes = Either [Word8] [Word32]
-
-funPtrToInt :: FunPtr a -> Int
-funPtrToInt (FunPtr a#) = I# (addr2Int# a#)
-
-mkJumpToAddr :: DynFlags -> EntryFunPtr -> ItblCodes
-mkJumpToAddr dflags a = case platformArch (targetPlatform dflags) of
- ArchSPARC ->
- -- After some consideration, we'll try this, where
- -- 0x55555555 stands in for the address to jump to.
- -- According to includes/rts/MachRegs.h, %g3 is very
- -- likely indeed to be baggable.
- --
- -- 0000 07155555 sethi %hi(0x55555555), %g3
- -- 0004 8610E155 or %g3, %lo(0x55555555), %g3
- -- 0008 81C0C000 jmp %g3
- -- 000c 01000000 nop
-
- let w32 = fromIntegral (funPtrToInt a)
-
- hi22, lo10 :: Word32 -> Word32
- lo10 x = x .&. 0x3FF
- hi22 x = (x `shiftR` 10) .&. 0x3FFFF
-
- in Right [ 0x07000000 .|. (hi22 w32),
- 0x8610E000 .|. (lo10 w32),
- 0x81C0C000,
- 0x01000000 ]
-
- ArchPPC ->
- -- We'll use r12, for no particular reason.
- -- 0xDEADBEEF stands for the address:
- -- 3D80DEAD lis r12,0xDEAD
- -- 618CBEEF ori r12,r12,0xBEEF
- -- 7D8903A6 mtctr r12
- -- 4E800420 bctr
-
- let w32 = fromIntegral (funPtrToInt a)
- hi16 x = (x `shiftR` 16) .&. 0xFFFF
- lo16 x = x .&. 0xFFFF
- in Right [ 0x3D800000 .|. hi16 w32,
- 0x618C0000 .|. lo16 w32,
- 0x7D8903A6, 0x4E800420 ]
-
- ArchX86 ->
- -- Let the address to jump to be 0xWWXXYYZZ.
- -- Generate movl $0xWWXXYYZZ,%eax ; jmp *%eax
- -- which is
- -- B8 ZZ YY XX WW FF E0
-
- let w32 = fromIntegral (funPtrToInt a) :: Word32
- insnBytes :: [Word8]
- insnBytes
- = [0xB8, byte0 w32, byte1 w32,
- byte2 w32, byte3 w32,
- 0xFF, 0xE0]
- in
- Left insnBytes
-
- ArchX86_64 ->
- -- Generates:
- -- jmpq *.L1(%rip)
- -- .align 8
- -- .L1:
- -- .quad <addr>
- --
- -- which looks like:
- -- 8: ff 25 02 00 00 00 jmpq *0x2(%rip) # 10 <f+0x10>
- -- with addr at 10.
- --
- -- We need a full 64-bit pointer (we can't assume the info table is
- -- allocated in low memory). Assuming the info pointer is aligned to
- -- an 8-byte boundary, the addr will also be aligned.
-
- let w64 = fromIntegral (funPtrToInt a) :: Word64
- insnBytes :: [Word8]
- insnBytes
- = [0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- byte0 w64, byte1 w64, byte2 w64, byte3 w64,
- byte4 w64, byte5 w64, byte6 w64, byte7 w64]
- in
- Left insnBytes
-
- ArchAlpha ->
- let w64 = fromIntegral (funPtrToInt a) :: Word64
- in Right [ 0xc3800000 -- br at, .+4
- , 0xa79c000c -- ldq at, 12(at)
- , 0x6bfc0000 -- jmp (at) # with zero hint -- oh well
- , 0x47ff041f -- nop
- , fromIntegral (w64 .&. 0x0000FFFF)
- , fromIntegral ((w64 `shiftR` 32) .&. 0x0000FFFF) ]
-
- ArchARM { } ->
- -- Generates Arm sequence,
- -- ldr r1, [pc, #0]
- -- bx r1
- --
- -- which looks like:
- -- 00000000 <.addr-0x8>:
- -- 0: 00109fe5 ldr r1, [pc] ; 8 <.addr>
- -- 4: 11ff2fe1 bx r1
- let w32 = fromIntegral (funPtrToInt a) :: Word32
- in Left [ 0x00, 0x10, 0x9f, 0xe5
- , 0x11, 0xff, 0x2f, 0xe1
- , byte0 w32, byte1 w32, byte2 w32, byte3 w32]
-
- arch ->
- panic ("mkJumpToAddr not defined for " ++ show arch)
-
-byte0 :: (Integral w) => w -> Word8
-byte0 w = fromIntegral w
-
-byte1, byte2, byte3, byte4, byte5, byte6, byte7
- :: (Integral w, Bits w) => w -> Word8
-byte1 w = fromIntegral (w `shiftR` 8)
-byte2 w = fromIntegral (w `shiftR` 16)
-byte3 w = fromIntegral (w `shiftR` 24)
-byte4 w = fromIntegral (w `shiftR` 32)
-byte5 w = fromIntegral (w `shiftR` 40)
-byte6 w = fromIntegral (w `shiftR` 48)
-byte7 w = fromIntegral (w `shiftR` 56)
-
--- entry point for direct returns for created constr itbls
-foreign import ccall "&stg_interp_constr_entry"
- stg_interp_constr_entry :: EntryFunPtr
-
-
-
-
--- Ultra-minimalist version specially for constructors
-#if SIZEOF_VOID_P == 8
-type HalfWord = Word32
-type FullWord = Word64
-#else
-type HalfWord = Word16
-type FullWord = Word32
-#endif
-
-data StgConInfoTable = StgConInfoTable {
- conDesc :: Ptr Word8,
- infoTable :: StgInfoTable
-}
-
-sizeOfConItbl :: DynFlags -> StgConInfoTable -> Int
-sizeOfConItbl dflags conInfoTable
- = sum [ fieldSz conDesc conInfoTable
- , sizeOfItbl dflags (infoTable conInfoTable) ]
-
-pokeConItbl :: DynFlags -> Ptr StgConInfoTable -> Ptr StgConInfoTable
- -> StgConInfoTable
- -> IO ()
-pokeConItbl dflags wr_ptr ex_ptr itbl
- = flip evalStateT (castPtr wr_ptr) $ do
- when ghciTablesNextToCode $ do
- let con_desc = conDesc itbl `minusPtr`
- (ex_ptr `plusPtr` conInfoTableSizeB dflags)
- store (fromIntegral con_desc :: Word32)
- when (wORD_SIZE dflags == 8) $
- store (fromIntegral con_desc :: Word32)
- store' (sizeOfItbl dflags) (pokeItbl dflags) (infoTable itbl)
- unless ghciTablesNextToCode $ store (conDesc itbl)
-
-type EntryFunPtr = FunPtr (Ptr () -> IO (Ptr ()))
-
-data StgInfoTable = StgInfoTable {
- entry :: Maybe EntryFunPtr, -- Just <=> not ghciTablesNextToCode
- ptrs :: HalfWord,
- nptrs :: HalfWord,
- tipe :: HalfWord,
- srtlen :: HalfWord,
- code :: Maybe ItblCodes -- Just <=> ghciTablesNextToCode
- }
-
-sizeOfItbl :: DynFlags -> StgInfoTable -> Int
-sizeOfItbl dflags itbl
- = sum
- [
- if ghciTablesNextToCode then 0 else fieldSz (fromJust . entry) itbl,
- fieldSz ptrs itbl,
- fieldSz nptrs itbl,
- fieldSz tipe itbl,
- fieldSz srtlen itbl,
- if ghciTablesNextToCode then case mkJumpToAddr dflags undefined of
- Left xs -> sizeOf (head xs) * length xs
- Right xs -> sizeOf (head xs) * length xs
- else 0
- ]
- + if rtsIsProfiled then profInfoTableSizeW * wORD_SIZE dflags
- else 0
-
-pokeItbl :: DynFlags -> Ptr StgInfoTable -> StgInfoTable -> IO ()
-pokeItbl _ a0 itbl
- = flip evalStateT (castPtr a0)
- $ do
- case entry itbl of
- Nothing -> return ()
- Just e -> store e
- when rtsIsProfiled $ do
- store (0 :: FullWord)
- store (0 :: FullWord)
- store (ptrs itbl)
- store (nptrs itbl)
- store (tipe itbl)
- store (srtlen itbl)
- case code itbl of
- Nothing -> return ()
- Just (Left xs) -> mapM_ store xs
- Just (Right xs) -> mapM_ store xs
-
-peekItbl :: DynFlags -> Ptr StgInfoTable -> IO StgInfoTable
-peekItbl dflags a0
- = flip evalStateT (castPtr a0)
- $ do
- entry' <- if ghciTablesNextToCode
- then return Nothing
- else liftM Just load
- when rtsIsProfiled $ do
- (_ :: Ptr FullWord) <- advance
- (_ :: Ptr FullWord) <- advance
- return ()
- ptrs' <- load
- nptrs' <- load
- tipe' <- load
- srtlen' <- load
- code' <- if ghciTablesNextToCode
- then liftM Just $ case mkJumpToAddr dflags undefined of
- Left xs ->
- liftM Left $ sequence (replicate (length xs) load)
- Right xs ->
- liftM Right $ sequence (replicate (length xs) load)
- else return Nothing
- return
- StgInfoTable {
- entry = entry',
- ptrs = ptrs',
- nptrs = nptrs',
- tipe = tipe',
- srtlen = srtlen'
- ,code = code'
- }
-
-fieldSz :: Storable b => (a -> b) -> a -> Int
-fieldSz sel x = sizeOf (sel x)
-
-type PtrIO = StateT (Ptr Word8) IO
-
-advance :: Storable a => PtrIO (Ptr a)
-advance = advance' sizeOf
-
-advance' :: (a -> Int) -> PtrIO (Ptr a)
-advance' fSizeOf = state adv
- where adv addr = case castPtr addr of
- addrCast ->
- (addrCast,
- addr `plusPtr` sizeOfPointee fSizeOf addrCast)
-
-sizeOfPointee :: (a -> Int) -> Ptr a -> Int
-sizeOfPointee fSizeOf addr = fSizeOf (typeHack addr)
- where typeHack = undefined :: Ptr a -> a
-
-store :: Storable a => a -> PtrIO ()
-store = store' sizeOf poke
-
-store' :: (a -> Int) -> (Ptr a -> a -> IO ()) -> a -> PtrIO ()
-store' fSizeOf fPoke x = do addr <- advance' fSizeOf
- lift (fPoke addr x)
-
-load :: Storable a => PtrIO a
-load = do addr <- advance
- lift (peek addr)
-
-newExecConItbl :: DynFlags -> StgInfoTable -> [Word8] -> IO (FunPtr ())
-newExecConItbl dflags obj con_desc
- = alloca $ \pcode -> do
- let lcon_desc = length con_desc + 1{- null terminator -}
- dummy_cinfo = StgConInfoTable { conDesc = nullPtr, infoTable = obj }
- sz = fromIntegral (sizeOfConItbl dflags dummy_cinfo)
- -- Note: we need to allocate the conDesc string next to the info
- -- table, because on a 64-bit platform we reference this string
- -- with a 32-bit offset relative to the info table, so if we
- -- allocated the string separately it might be out of range.
- wr_ptr <- _allocateExec (sz + fromIntegral lcon_desc) pcode
- ex_ptr <- peek pcode
- let cinfo = StgConInfoTable { conDesc = ex_ptr `plusPtr` fromIntegral sz
- , infoTable = obj }
- pokeConItbl dflags wr_ptr ex_ptr cinfo
- pokeArray0 0 (castPtr wr_ptr `plusPtr` fromIntegral sz) con_desc
- _flushExec sz ex_ptr -- Cache flush (if needed)
- return (castPtrToFunPtr ex_ptr)
-
-foreign import ccall unsafe "allocateExec"
- _allocateExec :: CUInt -> Ptr (Ptr a) -> IO (Ptr a)
-
-foreign import ccall unsafe "flushExec"
- _flushExec :: CUInt -> Ptr a -> IO ()
+make_constr_itbls :: HscEnv -> [DataCon] -> IO ItblEnv
+make_constr_itbls hsc_env cons =
+ mkItblEnv <$> mapM (uncurry mk_itbl) (zip cons [0..])
+ where
+ dflags = hsc_dflags hsc_env
+
+ mk_itbl :: DataCon -> Int -> IO (Name,ItblPtr)
+ mk_itbl dcon conNo = do
+ let rep_args = [ (typePrimRep rep_arg,rep_arg)
+ | arg <- dataConRepArgTys dcon
+ , rep_arg <- flattenRepType (repType arg) ]
+
+ (tot_wds, ptr_wds, _) =
+ mkVirtHeapOffsets dflags False{-not a THUNK-} rep_args
+
+ ptrs' = ptr_wds
+ nptrs' = tot_wds - ptr_wds
+ nptrs_really
+ | ptrs' + nptrs' >= mIN_PAYLOAD_SIZE dflags = nptrs'
+ | otherwise = mIN_PAYLOAD_SIZE dflags - ptrs'
+
+ descr = dataConIdentity dcon
+
+ r <- iservCmd hsc_env (MkConInfoTable ptrs' nptrs_really conNo descr)
+ return (getName dcon, ItblPtr (fromRemotePtr r))
diff --git a/compiler/ghci/ByteCodeLink.hs b/compiler/ghci/ByteCodeLink.hs
index b977f370d3..aa92ecc610 100644
--- a/compiler/ghci/ByteCodeLink.hs
+++ b/compiler/ghci/ByteCodeLink.hs
@@ -12,18 +12,21 @@
-- | ByteCodeLink: Bytecode assembler and linker
module ByteCodeLink (
ClosureEnv, emptyClosureEnv, extendClosureEnv,
- linkBCO, lookupStaticPtr, lookupName
- ,lookupIE
+ linkBCO, lookupStaticPtr,
+ lookupIE,
+ nameToCLabel, linkFail
) where
#include "HsVersions.h"
-import ByteCodeItbls
-import ByteCodeAsm
-import ObjLink
+import GHCi.RemoteTypes
+import GHCi.ResolvedBCO
+import GHCi.InfoTable
+import SizedSeq
-import DynFlags
-import BasicTypes
+import GHCi
+import ByteCodeTypes
+import HscTypes
import Name
import NameEnv
import PrimOp
@@ -34,27 +37,21 @@ import Outputable
import Util
-- Standard libraries
-
-import Data.Array.Base
-
-import Control.Monad
-import Control.Monad.ST ( stToIO )
-
-import GHC.Arr ( Array(..), STArray(..) )
+import Data.Array.Unboxed
+import Foreign.Ptr
import GHC.IO ( IO(..) )
import GHC.Exts
-import GHC.Ptr ( castPtr )
{-
Linking interpretables into something we can run
-}
-type ClosureEnv = NameEnv (Name, HValue)
+type ClosureEnv = NameEnv (Name, ForeignHValue)
emptyClosureEnv :: ClosureEnv
emptyClosureEnv = emptyNameEnv
-extendClosureEnv :: ClosureEnv -> [(Name,HValue)] -> ClosureEnv
+extendClosureEnv :: ClosureEnv -> [(Name,ForeignHValue)] -> ClosureEnv
extendClosureEnv cl_env pairs
= extendNameEnvList cl_env [ (n, (n,v)) | (n,v) <- pairs]
@@ -62,173 +59,86 @@ extendClosureEnv cl_env pairs
Linking interpretables into something we can run
-}
-{-
-data BCO# = BCO# ByteArray# -- instrs :: Array Word16#
- ByteArray# -- literals :: Array Word32#
- PtrArray# -- ptrs :: Array HValue
- ByteArray# -- itbls :: Array Addr#
--}
-
-linkBCO :: DynFlags -> ItblEnv -> ClosureEnv -> UnlinkedBCO -> IO HValue
-linkBCO dflags ie ce ul_bco
- = do BCO bco# <- linkBCO' dflags ie ce ul_bco
- -- SDM: Why do we need mkApUpd0 here? I *think* it's because
- -- otherwise top-level interpreted CAFs don't get updated
- -- after evaluation. A top-level BCO will evaluate itself and
- -- return its value when entered, but it won't update itself.
- -- Wrapping the BCO in an AP_UPD thunk will take care of the
- -- update for us.
- --
- -- Update: the above is true, but now we also have extra invariants:
- -- (a) An AP thunk *must* point directly to a BCO
- -- (b) A zero-arity BCO *must* be wrapped in an AP thunk
- -- (c) An AP is always fully saturated, so we *can't* wrap
- -- non-zero arity BCOs in an AP thunk.
- --
- if (unlinkedBCOArity ul_bco > 0)
- then return (HValue (unsafeCoerce# bco#))
- else case mkApUpd0# bco# of { (# final_bco #) -> return (HValue final_bco) }
-
-
-linkBCO' :: DynFlags -> ItblEnv -> ClosureEnv -> UnlinkedBCO -> IO BCO
-linkBCO' dflags ie ce (UnlinkedBCO _ arity insns_barr bitmap literalsSS ptrsSS)
- -- Raises an IO exception on failure
- = do let literals = ssElts literalsSS
- ptrs = ssElts ptrsSS
-
- linked_literals <- mapM (lookupLiteral dflags ie) literals
-
- let n_literals = sizeSS literalsSS
- n_ptrs = sizeSS ptrsSS
-
- ptrs_arr <- mkPtrsArray dflags ie ce n_ptrs ptrs
-
- let
- !ptrs_parr = case ptrs_arr of Array _lo _hi _n parr -> parr
-
- litRange
- | n_literals > 0 = (0, fromIntegral n_literals - 1)
- | otherwise = (1, 0)
- literals_arr :: UArray Word Word
- literals_arr = listArray litRange linked_literals
- !literals_barr = case literals_arr of UArray _lo _hi _n barr -> barr
-
- !(I# arity#) = arity
-
- newBCO insns_barr literals_barr ptrs_parr arity# bitmap
-
-
--- we recursively link any sub-BCOs while making the ptrs array
-mkPtrsArray :: DynFlags -> ItblEnv -> ClosureEnv -> Word -> [BCOPtr] -> IO (Array Word HValue)
-mkPtrsArray dflags ie ce n_ptrs ptrs = do
- let ptrRange = if n_ptrs > 0 then (0, n_ptrs-1) else (1, 0)
- marr <- newArray_ ptrRange
- let
- fill (BCOPtrName n) i = do
- ptr <- lookupName ce n
- unsafeWrite marr i ptr
- fill (BCOPtrPrimOp op) i = do
- ptr <- lookupPrimOp op
- unsafeWrite marr i ptr
- fill (BCOPtrBCO ul_bco) i = do
- BCO bco# <- linkBCO' dflags ie ce ul_bco
- writeArrayBCO marr i bco#
- fill (BCOPtrBreakInfo brkInfo) i =
- unsafeWrite marr i (HValue (unsafeCoerce# brkInfo))
- fill (BCOPtrArray brkArray) i =
- unsafeWrite marr i (HValue (unsafeCoerce# brkArray))
- zipWithM_ fill ptrs [0..]
- unsafeFreeze marr
-
-newtype IOArray i e = IOArray (STArray RealWorld i e)
-
-instance MArray IOArray e IO where
- getBounds (IOArray marr) = stToIO $ getBounds marr
- getNumElements (IOArray marr) = stToIO $ getNumElements marr
- newArray lu init = stToIO $ do
- marr <- newArray lu init; return (IOArray marr)
- newArray_ lu = stToIO $ do
- marr <- newArray_ lu; return (IOArray marr)
- unsafeRead (IOArray marr) i = stToIO (unsafeRead marr i)
- unsafeWrite (IOArray marr) i e = stToIO (unsafeWrite marr i e)
-
--- XXX HACK: we should really have a new writeArray# primop that takes a BCO#.
-writeArrayBCO :: IOArray Word a -> Int -> BCO# -> IO ()
-writeArrayBCO (IOArray (STArray _ _ _ marr#)) (I# i#) bco# = IO $ \s# ->
- case (unsafeCoerce# writeArray#) marr# i# bco# s# of { s# ->
- (# s#, () #) }
-
-{-
-writeArrayMBA :: IOArray Int a -> Int -> MutableByteArray# a -> IO ()
-writeArrayMBA (IOArray (STArray _ _ marr#)) (I# i#) mba# = IO $ \s# ->
- case (unsafeCoerce# writeArray#) marr# i# bco# s# of { s# ->
- (# s#, () #) }
--}
-
-data BCO = BCO BCO#
-
-newBCO :: ByteArray# -> ByteArray# -> Array# a -> Int# -> ByteArray# -> IO BCO
-newBCO instrs lits ptrs arity bitmap
- = IO $ \s -> case newBCO# instrs lits ptrs arity bitmap s of
- (# s1, bco #) -> (# s1, BCO bco #)
-
-
-lookupLiteral :: DynFlags -> ItblEnv -> BCONPtr -> IO Word
-lookupLiteral _ _ (BCONPtrWord lit) = return lit
-lookupLiteral _ _ (BCONPtrLbl sym) = do Ptr a# <- lookupStaticPtr sym
- return (W# (int2Word# (addr2Int# a#)))
-lookupLiteral dflags ie (BCONPtrItbl nm) = do Ptr a# <- lookupIE dflags ie nm
- return (W# (int2Word# (addr2Int# a#)))
-
-lookupStaticPtr :: FastString -> IO (Ptr ())
-lookupStaticPtr addr_of_label_string
- = do let label_to_find = unpackFS addr_of_label_string
- m <- lookupSymbol label_to_find
- case m of
- Just ptr -> return ptr
- Nothing -> linkFail "ByteCodeLink: can't find label"
- label_to_find
-
-lookupPrimOp :: PrimOp -> IO HValue
-lookupPrimOp primop
- = do let sym_to_find = primopToCLabel primop "closure"
- m <- lookupSymbol sym_to_find
- case m of
- Just (Ptr addr) -> case addrToAny# addr of
- (# a #) -> return (HValue a)
- Nothing -> linkFail "ByteCodeLink.lookupCE(primop)" sym_to_find
-
-lookupName :: ClosureEnv -> Name -> IO HValue
-lookupName ce nm
- = case lookupNameEnv ce nm of
- Just (_,aa) -> return aa
- Nothing
- -> ASSERT2(isExternalName nm, ppr nm)
- do let sym_to_find = nameToCLabel nm "closure"
- m <- lookupSymbol sym_to_find
- case m of
- Just (Ptr addr) -> case addrToAny# addr of
- (# a #) -> return (HValue a)
- Nothing -> linkFail "ByteCodeLink.lookupCE" sym_to_find
-
-lookupIE :: DynFlags -> ItblEnv -> Name -> IO (Ptr a)
-lookupIE dflags ie con_nm
- = case lookupNameEnv ie con_nm of
- Just (_, a) -> return (castPtr (itblCode dflags a))
- Nothing
- -> do -- try looking up in the object files.
- let sym_to_find1 = nameToCLabel con_nm "con_info"
- m <- lookupSymbol sym_to_find1
- case m of
- Just addr -> return addr
- Nothing
- -> do -- perhaps a nullary constructor?
- let sym_to_find2 = nameToCLabel con_nm "static_info"
- n <- lookupSymbol sym_to_find2
- case n of
- Just addr -> return addr
- Nothing -> linkFail "ByteCodeLink.lookupIE"
- (sym_to_find1 ++ " or " ++ sym_to_find2)
+linkBCO
+ :: HscEnv -> ItblEnv -> ClosureEnv -> NameEnv Int -> UnlinkedBCO
+ -> IO ResolvedBCO
+linkBCO hsc_env ie ce bco_ix
+ (UnlinkedBCO _ arity insns bitmap lits0 ptrs0) = do
+ lits <- mapM (lookupLiteral hsc_env ie) (ssElts lits0)
+ ptrs <- mapM (resolvePtr hsc_env ie ce bco_ix) (ssElts ptrs0)
+ return (ResolvedBCO arity insns bitmap
+ (listArray (0, fromIntegral (sizeSS lits0)-1) lits)
+ (addListToSS emptySS ptrs))
+
+lookupLiteral :: HscEnv -> ItblEnv -> BCONPtr -> IO Word
+lookupLiteral _ _ (BCONPtrWord lit) = return lit
+lookupLiteral hsc_env _ (BCONPtrLbl sym) = do
+ Ptr a# <- lookupStaticPtr hsc_env sym
+ return (W# (int2Word# (addr2Int# a#)))
+lookupLiteral hsc_env ie (BCONPtrItbl nm) = do
+ Ptr a# <- lookupIE hsc_env ie nm
+ return (W# (int2Word# (addr2Int# a#)))
+lookupLiteral hsc_env _ (BCONPtrStr bs) = do
+ fromIntegral . ptrToWordPtr <$> mallocData hsc_env bs
+
+lookupStaticPtr :: HscEnv -> FastString -> IO (Ptr ())
+lookupStaticPtr hsc_env addr_of_label_string = do
+ m <- lookupSymbol hsc_env addr_of_label_string
+ case m of
+ Just ptr -> return ptr
+ Nothing -> linkFail "ByteCodeLink: can't find label"
+ (unpackFS addr_of_label_string)
+
+lookupIE :: HscEnv -> ItblEnv -> Name -> IO (Ptr a)
+lookupIE hsc_env ie con_nm =
+ case lookupNameEnv ie con_nm of
+ Just (_, ItblPtr a) -> return (castPtr (conInfoPtr a))
+ Nothing -> do -- try looking up in the object files.
+ let sym_to_find1 = nameToCLabel con_nm "con_info"
+ m <- lookupSymbol hsc_env sym_to_find1
+ case m of
+ Just addr -> return (castPtr addr)
+ Nothing
+ -> do -- perhaps a nullary constructor?
+ let sym_to_find2 = nameToCLabel con_nm "static_info"
+ n <- lookupSymbol hsc_env sym_to_find2
+ case n of
+ Just addr -> return (castPtr addr)
+ Nothing -> linkFail "ByteCodeLink.lookupIE"
+ (unpackFS sym_to_find1 ++ " or " ++
+ unpackFS sym_to_find2)
+
+lookupPrimOp :: HscEnv -> PrimOp -> IO RemotePtr
+lookupPrimOp hsc_env primop = do
+ let sym_to_find = primopToCLabel primop "closure"
+ m <- lookupSymbol hsc_env (mkFastString sym_to_find)
+ case m of
+ Just p -> return (toRemotePtr p)
+ Nothing -> linkFail "ByteCodeLink.lookupCE(primop)" sym_to_find
+
+resolvePtr
+ :: HscEnv -> ItblEnv -> ClosureEnv -> NameEnv Int -> BCOPtr
+ -> IO ResolvedBCOPtr
+resolvePtr hsc_env _ie ce bco_ix (BCOPtrName nm)
+ | Just ix <- lookupNameEnv bco_ix nm =
+ return (ResolvedBCORef ix) -- ref to another BCO in this group
+ | Just (_, rhv) <- lookupNameEnv ce nm =
+ return (ResolvedBCOPtr (unsafeForeignHValueToHValueRef rhv))
+ | otherwise =
+ ASSERT2(isExternalName nm, ppr nm)
+ do let sym_to_find = nameToCLabel nm "closure"
+ m <- lookupSymbol hsc_env sym_to_find
+ case m of
+ Just p -> return (ResolvedBCOStaticPtr (toRemotePtr p))
+ Nothing -> linkFail "ByteCodeLink.lookupCE" (unpackFS sym_to_find)
+resolvePtr hsc_env _ _ _ (BCOPtrPrimOp op) =
+ ResolvedBCOStaticPtr <$> lookupPrimOp hsc_env op
+resolvePtr hsc_env ie ce bco_ix (BCOPtrBCO bco) =
+ ResolvedBCOPtrBCO <$> linkBCO hsc_env ie ce bco_ix bco
+resolvePtr _ _ _ _ (BCOPtrBreakInfo break_info) =
+ return (ResolvedBCOPtrLocal (unsafeCoerce# break_info))
+resolvePtr _ _ _ _ (BCOPtrArray break_array) =
+ return (ResolvedBCOPtrLocal (unsafeCoerce# break_array))
linkFail :: String -> String -> IO a
linkFail who what
@@ -246,8 +156,9 @@ linkFail who what
])
-nameToCLabel :: Name -> String -> String
-nameToCLabel n suffix = label where
+nameToCLabel :: Name -> String -> FastString
+nameToCLabel n suffix = mkFastString label
+ where
encodeZ = zString . zEncodeFS
(Module pkgKey modName) = ASSERT( isExternalName n ) nameModule n
packagePart = encodeZ (unitIdFS pkgKey)
@@ -268,4 +179,3 @@ primopToCLabel primop suffix = concat
, zString (zEncodeFS (occNameFS (primOpOcc primop)))
, '_':suffix
]
-
diff --git a/compiler/ghci/ByteCodeTypes.hs b/compiler/ghci/ByteCodeTypes.hs
new file mode 100644
index 0000000000..0a8dd304b6
--- /dev/null
+++ b/compiler/ghci/ByteCodeTypes.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE MagicHash #-}
+--
+-- (c) The University of Glasgow 2002-2006
+--
+
+-- | Bytecode assembler types
+module ByteCodeTypes
+ ( CompiledByteCode(..), FFIInfo(..)
+ , UnlinkedBCO(..), BCOPtr(..), BCONPtr(..)
+ , ItblEnv, ItblPtr(..)
+ , BreakInfo(..)
+ ) where
+
+import FastString
+import Id
+import Module
+import Name
+import NameEnv
+import Outputable
+import PrimOp
+import SizedSeq
+import Type
+
+import Foreign
+import Data.Array.Base ( UArray(..) )
+import Data.ByteString (ByteString)
+import GHC.Exts
+
+
+data CompiledByteCode
+ = ByteCode [UnlinkedBCO] -- Bunch of interpretable bindings
+ ItblEnv -- A mapping from DataCons to their itbls
+ [FFIInfo] -- ffi blocks we allocated
+ -- ToDo: we're not tracking strings that we malloc'd
+
+newtype FFIInfo = FFIInfo (Ptr ())
+ deriving Show
+
+instance Outputable CompiledByteCode where
+ ppr (ByteCode bcos _ _) = ppr bcos
+
+type ItblEnv = NameEnv (Name, ItblPtr)
+ -- We need the Name in the range so we know which
+ -- elements to filter out when unloading a module
+
+newtype ItblPtr = ItblPtr (Ptr ()) deriving Show
+
+data UnlinkedBCO
+ = UnlinkedBCO {
+ unlinkedBCOName :: Name,
+ unlinkedBCOArity :: Int,
+ unlinkedBCOInstrs :: UArray Int Word16, -- insns
+ unlinkedBCOBitmap :: UArray Int Word, -- bitmap
+ unlinkedBCOLits :: (SizedSeq BCONPtr), -- non-ptrs
+ unlinkedBCOPtrs :: (SizedSeq BCOPtr) -- ptrs
+ }
+
+data BCOPtr
+ = BCOPtrName Name
+ | BCOPtrPrimOp PrimOp
+ | BCOPtrBCO UnlinkedBCO
+ | BCOPtrBreakInfo BreakInfo
+ | BCOPtrArray (MutableByteArray# RealWorld)
+
+data BCONPtr
+ = BCONPtrWord Word
+ | BCONPtrLbl FastString
+ | BCONPtrItbl Name
+ | BCONPtrStr ByteString
+
+data BreakInfo
+ = BreakInfo
+ { breakInfo_module :: Module
+ , breakInfo_number :: {-# UNPACK #-} !Int
+ , breakInfo_vars :: [(Id,Word16)]
+ , breakInfo_resty :: Type
+ }
+
+instance Outputable UnlinkedBCO where
+ ppr (UnlinkedBCO nm _arity _insns _bitmap lits ptrs)
+ = sep [text "BCO", ppr nm, text "with",
+ ppr (sizeSS lits), text "lits",
+ ppr (sizeSS ptrs), text "ptrs" ]
+
+instance Outputable BreakInfo where
+ ppr info = text "BreakInfo" <+>
+ parens (ppr (breakInfo_module info) <+>
+ ppr (breakInfo_number info) <+>
+ ppr (breakInfo_vars info) <+>
+ ppr (breakInfo_resty info))
diff --git a/compiler/ghci/Debugger.hs b/compiler/ghci/Debugger.hs
index 2b9e732c4b..5c6a02d3ff 100644
--- a/compiler/ghci/Debugger.hs
+++ b/compiler/ghci/Debugger.hs
@@ -17,6 +17,8 @@ module Debugger (pprintClosureCommand, showTerm, pprTypeAndContents) where
import Linker
import RtClosureInspect
+import GHCi
+import GHCi.RemoteTypes
import GhcMonad
import HscTypes
import Id
@@ -117,7 +119,8 @@ bindSuspensions t = do
let ids = [ mkVanillaGlobal name ty
| (name,ty) <- zip names tys]
new_ic = extendInteractiveContextWithIds ictxt ids
- liftIO $ extendLinkEnv (zip names hvals)
+ fhvs <- liftIO $ mapM (mkFinalizedHValue hsc_env <=< mkHValueRef) hvals
+ liftIO $ extendLinkEnv (zip names fhvs)
modifySession $ \_ -> hsc_env {hsc_IC = new_ic }
return t'
where
@@ -170,7 +173,8 @@ showTerm term = do
let noop_log _ _ _ _ _ = return ()
expr = "show " ++ showPpr dflags bname
_ <- GHC.setSessionDynFlags dflags{log_action=noop_log}
- txt_ <- withExtendedLinkEnv [(bname, val)]
+ fhv <- liftIO $ mkFinalizedHValue hsc_env =<< mkHValueRef val
+ txt_ <- withExtendedLinkEnv [(bname, fhv)]
(GHC.compileExpr expr)
let myprec = 10 -- application precedence. TODO Infix constructors
let txt = unsafeCoerce# txt_ :: [a]
diff --git a/compiler/ghci/DebuggerUtils.hs b/compiler/ghci/DebuggerUtils.hs
index d1ff9134ec..096b809c26 100644
--- a/compiler/ghci/DebuggerUtils.hs
+++ b/compiler/ghci/DebuggerUtils.hs
@@ -4,8 +4,8 @@ module DebuggerUtils (
dataConInfoPtrToName,
) where
+import GHCi.InfoTable
import CmmInfo ( stdInfoTableSizeB )
-import ByteCodeItbls
import DynFlags
import FastString
import TcRnTypes
diff --git a/compiler/ghci/GHCi.hs b/compiler/ghci/GHCi.hs
new file mode 100644
index 0000000000..d9c26c1d47
--- /dev/null
+++ b/compiler/ghci/GHCi.hs
@@ -0,0 +1,499 @@
+{-# LANGUAGE RecordWildCards, ScopedTypeVariables, BangPatterns, CPP #-}
+
+--
+-- | Interacting with the interpreter, whether it is running on an
+-- external process or in the current process.
+--
+module GHCi
+ ( -- * High-level interface to the interpreter
+ evalStmt, EvalStatus(..), EvalResult(..), EvalExpr(..)
+ , resumeStmt
+ , abandonStmt
+ , evalIO
+ , evalString
+ , evalStringToIOString
+ , mallocData
+
+ -- * The object-code linker
+ , initObjLinker
+ , lookupSymbol
+ , lookupClosure
+ , loadDLL
+ , loadArchive
+ , loadObj
+ , unloadObj
+ , addLibrarySearchPath
+ , removeLibrarySearchPath
+ , resolveObjs
+ , findSystemLibrary
+
+ -- * Lower-level API using messages
+ , iservCmd, Message(..), withIServ, stopIServ
+ , iservCall, readIServ, writeIServ
+ , purgeLookupSymbolCache
+ , freeHValueRefs
+ , mkFinalizedHValue
+ , wormhole, wormholeRef
+ , mkEvalOpts
+ , fromEvalResult
+ ) where
+
+import GHCi.Message
+import GHCi.Run
+import GHCi.RemoteTypes
+import HscTypes
+import UniqFM
+import Panic
+import DynFlags
+#ifndef mingw32_HOST_OS
+import ErrUtils
+import Outputable
+#endif
+import Exception
+import BasicTypes
+import FastString
+
+import Control.Concurrent
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Binary
+import Data.ByteString (ByteString)
+import Data.IORef
+import Foreign
+import System.Exit
+#ifndef mingw32_HOST_OS
+import Data.Maybe
+import System.Posix as Posix
+#endif
+import System.Process
+
+{- Note [Remote GHCi]
+
+When the flag -fexternal-interpreter is given to GHC, interpreted code
+is run in a separate process called iserv, and we communicate with the
+external process over a pipe using Binary-encoded messages.
+
+Motivation
+~~~~~~~~~~
+
+When the interpreted code is running in a separate process, it can
+use a different "way", e.g. profiled or dynamic. This means
+
+- compiling Template Haskell code with -prof does not require
+ building the code without -prof first
+
+- when GHC itself is profiled, it can interpret unprofiled code,
+ and the same applies to dynamic linking.
+
+- An unprofiled GHCi can load and run profiled code, which means it
+ can use the stack-trace functionality provided by profiling without
+ taking the performance hit on the compiler that profiling would
+ entail.
+
+For other reasons see RemoteGHCi on the wiki.
+
+Implementation Overview
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The main pieces are:
+
+- libraries/ghci, containing:
+ - types for talking about remote values (GHCi.RemoteTypes)
+ - the message protocol (GHCi.Message),
+ - implementation of the messages (GHCi.Run)
+ - implementation of Template Haskell (GHCi.TH)
+ - a few other things needed to run interpreted code
+
+- top-level iserv directory, containing the codefor the external
+ server. This is a fairly simple wrapper, most of the functionality
+ is provided by modules in libraries/ghci.
+
+- This module (GHCi) which provides the interface to the server used
+ by the rest of GHC.
+
+GHC works with and without -fexternal-interpreter. With the flag, all
+interpreted code is run by the iserv binary. Without the flag,
+interpreted code is run in the same process as GHC.
+
+Things that do not work with -fexternal-interpreter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynCompileExpr cannot work, because we have no way to run code of an
+unknown type in the remote process. This API fails with an error
+message if it is used with -fexternal-interpreter.
+-}
+
+-- | Run a command in the interpreter's context. With
+-- @-fexternal-interpreter@, the command is serialized and sent to an
+-- external iserv process, and the response is deserialized (hence the
+-- @Binary@ constraint). With @-fno-external-interpreter@ we execute
+-- the command directly here.
+iservCmd :: Binary a => HscEnv -> Message a -> IO a
+iservCmd hsc_env@HscEnv{..} msg
+ | gopt Opt_ExternalInterpreter hsc_dflags =
+ withIServ hsc_env $ \iserv ->
+ uninterruptibleMask_ $ do -- Note [uninterruptibleMask_]
+ iservCall iserv msg
+ | otherwise = -- Just run it directly
+ run msg
+
+
+-- Note [uninterruptibleMask_ and iservCmd]
+--
+-- If we receive an async exception, such as ^C, while communicating
+-- with the iserv process then we will be out-of-sync and not be able
+-- to recoever. Thus we use uninterruptibleMask_ during
+-- communication. A ^C will be delivered to the iserv process (because
+-- signals get sent to the whole process group) which will interrupt
+-- the running computation and return an EvalException result.
+
+-- | Grab a lock on the 'IServ' and do something with it.
+-- Overloaded because this is used from TcM as well as IO.
+withIServ
+ :: (MonadIO m, ExceptionMonad m)
+ => HscEnv -> (IServ -> m a) -> m a
+withIServ HscEnv{..} action =
+ gmask $ \restore -> do
+ m <- liftIO $ takeMVar hsc_iserv
+ -- start the iserv process if we haven't done so yet
+ iserv <- maybe (liftIO $ startIServ hsc_dflags) return m
+ `gonException` (liftIO $ putMVar hsc_iserv Nothing)
+ -- free any ForeignHValues that have been garbage collected.
+ let iserv' = iserv{ iservPendingFrees = [] }
+ a <- (do
+ liftIO $ when (not (null (iservPendingFrees iserv))) $
+ iservCall iserv (FreeHValueRefs (iservPendingFrees iserv))
+ -- run the inner action
+ restore $ action iserv)
+ `gonException` (liftIO $ putMVar hsc_iserv (Just iserv'))
+ liftIO $ putMVar hsc_iserv (Just iserv')
+ return a
+
+
+-- -----------------------------------------------------------------------------
+-- Wrappers around messages
+
+-- | Execute an action of type @IO [a]@, returning 'ForeignHValue's for
+-- each of the results.
+evalStmt
+ :: HscEnv -> Bool -> EvalExpr ForeignHValue -> IO (EvalStatus [ForeignHValue])
+evalStmt hsc_env step foreign_expr = do
+ let dflags = hsc_dflags hsc_env
+ status <- withExpr foreign_expr $ \expr ->
+ iservCmd hsc_env (EvalStmt (mkEvalOpts dflags step) expr)
+ handleEvalStatus hsc_env status
+ where
+ withExpr :: EvalExpr ForeignHValue -> (EvalExpr HValueRef -> IO a) -> IO a
+ withExpr (EvalThis fhv) cont =
+ withForeignHValue fhv $ \hvref -> cont (EvalThis hvref)
+ withExpr (EvalApp fl fr) cont =
+ withExpr fl $ \fl' ->
+ withExpr fr $ \fr' ->
+ cont (EvalApp fl' fr')
+
+resumeStmt :: HscEnv -> Bool -> ForeignHValue -> IO (EvalStatus [ForeignHValue])
+resumeStmt hsc_env step resume_ctxt = do
+ let dflags = hsc_dflags hsc_env
+ status <- withForeignHValue resume_ctxt $ \rhv ->
+ iservCmd hsc_env (ResumeStmt (mkEvalOpts dflags step) rhv)
+ handleEvalStatus hsc_env status
+
+abandonStmt :: HscEnv -> ForeignHValue -> IO ()
+abandonStmt hsc_env resume_ctxt = do
+ withForeignHValue resume_ctxt $ \rhv ->
+ iservCmd hsc_env (AbandonStmt rhv)
+
+handleEvalStatus
+ :: HscEnv -> EvalStatus [HValueRef] -> IO (EvalStatus [ForeignHValue])
+handleEvalStatus hsc_env status =
+ case status of
+ EvalBreak a b c d -> return (EvalBreak a b c d)
+ EvalComplete alloc res ->
+ EvalComplete alloc <$> addFinalizer res
+ where
+ addFinalizer (EvalException e) = return (EvalException e)
+ addFinalizer (EvalSuccess rs) = do
+ EvalSuccess <$> mapM (mkFinalizedHValue hsc_env) rs
+
+-- | Execute an action of type @IO ()@
+evalIO :: HscEnv -> ForeignHValue -> IO ()
+evalIO hsc_env fhv = do
+ liftIO $ withForeignHValue fhv $ \fhv ->
+ iservCmd hsc_env (EvalIO fhv) >>= fromEvalResult
+
+-- | Execute an action of type @IO String@
+evalString :: HscEnv -> ForeignHValue -> IO String
+evalString hsc_env fhv = do
+ liftIO $ withForeignHValue fhv $ \fhv ->
+ iservCmd hsc_env (EvalString fhv) >>= fromEvalResult
+
+-- | Execute an action of type @String -> IO String@
+evalStringToIOString :: HscEnv -> ForeignHValue -> String -> IO String
+evalStringToIOString hsc_env fhv str = do
+ liftIO $ withForeignHValue fhv $ \fhv ->
+ iservCmd hsc_env (EvalStringToString fhv str) >>= fromEvalResult
+
+
+-- | Allocate and store the given bytes in memory, returning a pointer
+-- to the memory in the remote process.
+mallocData :: HscEnv -> ByteString -> IO (Ptr ())
+mallocData hsc_env bs = fromRemotePtr <$> iservCmd hsc_env (MallocData bs)
+
+
+-- -----------------------------------------------------------------------------
+-- Interface to the object-code linker
+
+initObjLinker :: HscEnv -> IO ()
+initObjLinker hsc_env = iservCmd hsc_env InitLinker
+
+lookupSymbol :: HscEnv -> FastString -> IO (Maybe (Ptr ()))
+lookupSymbol hsc_env@HscEnv{..} str
+ | gopt Opt_ExternalInterpreter hsc_dflags =
+ -- Profiling of GHCi showed a lot of time and allocation spent
+ -- making cross-process LookupSymbol calls, so I added a GHC-side
+ -- cache which sped things up quite a lot. We have to be careful
+ -- to purge this cache when unloading code though.
+ withIServ hsc_env $ \iserv@IServ{..} -> do
+ cache <- readIORef iservLookupSymbolCache
+ case lookupUFM cache str of
+ Just p -> return (Just p)
+ Nothing -> do
+ m <- uninterruptibleMask_ $
+ iservCall iserv (LookupSymbol (unpackFS str))
+ case m of
+ Nothing -> return Nothing
+ Just r -> do
+ let p = fromRemotePtr r
+ writeIORef iservLookupSymbolCache $! addToUFM cache str p
+ return (Just p)
+ | otherwise =
+ fmap fromRemotePtr <$> run (LookupSymbol (unpackFS str))
+
+lookupClosure :: HscEnv -> String -> IO (Maybe HValueRef)
+lookupClosure hsc_env str =
+ iservCmd hsc_env (LookupClosure str)
+
+purgeLookupSymbolCache :: HscEnv -> IO ()
+purgeLookupSymbolCache hsc_env@HscEnv{..} =
+ when (gopt Opt_ExternalInterpreter hsc_dflags) $
+ withIServ hsc_env $ \IServ{..} ->
+ writeIORef iservLookupSymbolCache emptyUFM
+
+
+-- | loadDLL loads a dynamic library using the OS's native linker
+-- (i.e. dlopen() on Unix, LoadLibrary() on Windows). It takes either
+-- an absolute pathname to the file, or a relative filename
+-- (e.g. "libfoo.so" or "foo.dll"). In the latter case, loadDLL
+-- searches the standard locations for the appropriate library.
+--
+-- Returns:
+--
+-- Nothing => success
+-- Just err_msg => failure
+loadDLL :: HscEnv -> String -> IO (Maybe String)
+loadDLL hsc_env str = iservCmd hsc_env (LoadDLL str)
+
+loadArchive :: HscEnv -> String -> IO ()
+loadArchive hsc_env str = iservCmd hsc_env (LoadArchive str)
+
+loadObj :: HscEnv -> String -> IO ()
+loadObj hsc_env str = iservCmd hsc_env (LoadObj str)
+
+unloadObj :: HscEnv -> String -> IO ()
+unloadObj hsc_env str = iservCmd hsc_env (UnloadObj str)
+
+addLibrarySearchPath :: HscEnv -> String -> IO (Ptr ())
+addLibrarySearchPath hsc_env str =
+ fromRemotePtr <$> iservCmd hsc_env (AddLibrarySearchPath str)
+
+removeLibrarySearchPath :: HscEnv -> Ptr () -> IO Bool
+removeLibrarySearchPath hsc_env p =
+ iservCmd hsc_env (RemoveLibrarySearchPath (toRemotePtr p))
+
+resolveObjs :: HscEnv -> IO SuccessFlag
+resolveObjs hsc_env = successIf <$> iservCmd hsc_env ResolveObjs
+
+findSystemLibrary :: HscEnv -> String -> IO (Maybe String)
+findSystemLibrary hsc_env str = iservCmd hsc_env (FindSystemLibrary str)
+
+
+-- -----------------------------------------------------------------------------
+-- Raw calls and messages
+
+-- | Send a 'Message' and receive the response from the iserv process
+iservCall :: Binary a => IServ -> Message a -> IO a
+iservCall iserv@IServ{..} msg =
+ remoteCall iservPipe msg
+ `catch` \(e :: SomeException) -> handleIServFailure iserv e
+
+-- | Read a value from the iserv process
+readIServ :: IServ -> Get a -> IO a
+readIServ iserv@IServ{..} get =
+ readPipe iservPipe get
+ `catch` \(e :: SomeException) -> handleIServFailure iserv e
+
+-- | Send a value to the iserv process
+writeIServ :: IServ -> Put -> IO ()
+writeIServ iserv@IServ{..} put =
+ writePipe iservPipe put
+ `catch` \(e :: SomeException) -> handleIServFailure iserv e
+
+handleIServFailure :: IServ -> SomeException -> IO a
+handleIServFailure IServ{..} e = do
+ ex <- getProcessExitCode iservProcess
+ case ex of
+ Just (ExitFailure n) ->
+ throw (InstallationError ("ghc-iserv terminated (" ++ show n ++ ")"))
+ _ -> do
+ terminateProcess iservProcess
+ _ <- waitForProcess iservProcess
+ throw e
+
+-- -----------------------------------------------------------------------------
+-- Starting and stopping the iserv process
+
+startIServ :: DynFlags -> IO IServ
+#ifdef mingw32_HOST_OS
+startIServ _ = panic "startIServ"
+ -- should not be called, because we disable -fexternal-interpreter on Windows.
+ -- (see DynFlags.makeDynFlagsConsistent)
+#else
+startIServ dflags = do
+ let flavour
+ | WayProf `elem` ways dflags = "-prof"
+ | WayDyn `elem` ways dflags = "-dyn"
+ | otherwise = ""
+ prog = pgm_i dflags ++ flavour
+ opts = getOpts dflags opt_i
+ debugTraceMsg dflags 3 $ text "Starting " <> text prog
+ (rfd1, wfd1) <- Posix.createPipe -- we read on rfd1
+ (rfd2, wfd2) <- Posix.createPipe -- we write on wfd2
+ setFdOption rfd1 CloseOnExec True
+ setFdOption wfd2 CloseOnExec True
+ let args = show wfd1 : show rfd2 : opts
+ (_, _, _, ph) <- createProcess (proc prog args)
+ closeFd wfd1
+ closeFd rfd2
+ rh <- fdToHandle rfd1
+ wh <- fdToHandle wfd2
+ lo_ref <- newIORef Nothing
+ cache_ref <- newIORef emptyUFM
+ return $ IServ
+ { iservPipe = Pipe { pipeRead = rh
+ , pipeWrite = wh
+ , pipeLeftovers = lo_ref }
+ , iservProcess = ph
+ , iservLookupSymbolCache = cache_ref
+ , iservPendingFrees = []
+ }
+#endif
+
+stopIServ :: HscEnv -> IO ()
+#ifdef mingw32_HOST_OS
+stopIServ _ = return ()
+#else
+stopIServ HscEnv{..} =
+ gmask $ \_restore -> do
+ m <- takeMVar hsc_iserv
+ maybe (return ()) stop m
+ putMVar hsc_iserv Nothing
+ where
+ stop iserv = do
+ ex <- getProcessExitCode (iservProcess iserv)
+ if isJust ex
+ then return ()
+ else iservCall iserv Shutdown
+#endif
+
+-- -----------------------------------------------------------------------------
+{- Note [External GHCi pointers]
+
+We have the following ways to reference things in GHCi:
+
+HValue
+------
+
+HValue is a direct reference to an value in the local heap. Obviously
+we cannot use this to refer to things in the external process.
+
+
+HValueRef
+---------
+
+HValueRef is a StablePtr to a heap-resident value. When
+-fexternal-interpreter is used, this value resides in the external
+process's heap. HValueRefs are mostly used to send pointers in
+messages between GHC and iserv.
+
+An HValueRef must be explicitly freed when no longer required, using
+freeHValueRefs, or by attaching a finalizer with mkForeignHValue.
+
+To get from an HValueRef to an HValue you can use 'wormholeRef', which
+fails with an error message if -fexternal-interpreter is in use.
+
+ForeignHValue
+-------------
+
+A ForeignHValue is an HValueRef with a finalizer that will free the
+'HValueRef' when it is gargabe collected. We mostly use ForeignHValue
+on the GHC side.
+
+The finalizer adds the HValueRef to the iservPendingFrees list in the
+IServ record. The next call to iservCmd will free any HValueRefs in
+the list. It was done this way rather than calling iservCmd directly,
+because I didn't want to have arbitrary threads calling iservCmd. In
+principle it would probably be ok, but it seems less hairy this way.
+-}
+
+-- | Creates a 'ForeignHValue' that will automatically release the
+-- 'HValueRef' when it is no longer referenced.
+mkFinalizedHValue :: HscEnv -> HValueRef -> IO ForeignHValue
+mkFinalizedHValue HscEnv{..} hvref = mkForeignHValue hvref free
+ where
+ !external = gopt Opt_ExternalInterpreter hsc_dflags
+
+ free :: IO ()
+ free
+ | not external = freeHValueRef hvref
+ | otherwise =
+ modifyMVar_ hsc_iserv $ \mb_iserv ->
+ case mb_iserv of
+ Nothing -> return Nothing -- already shut down
+ Just iserv@IServ{..} ->
+ return (Just iserv{iservPendingFrees = hvref : iservPendingFrees})
+
+freeHValueRefs :: HscEnv -> [HValueRef] -> IO ()
+freeHValueRefs _ [] = return ()
+freeHValueRefs hsc_env refs = iservCmd hsc_env (FreeHValueRefs refs)
+
+-- | Convert a 'ForeignHValue' to an 'HValue' directly. This only works
+-- when the interpreter is running in the same process as the compiler,
+-- so it fails when @-fexternal-interpreter@ is on.
+wormhole :: DynFlags -> ForeignHValue -> IO HValue
+wormhole dflags r = wormholeRef dflags (unsafeForeignHValueToHValueRef r)
+
+-- | Convert an 'HValueRef' to an 'HValue' directly. This only works
+-- when the interpreter is running in the same process as the compiler,
+-- so it fails when @-fexternal-interpreter@ is on.
+wormholeRef :: DynFlags -> HValueRef -> IO HValue
+wormholeRef dflags r
+ | gopt Opt_ExternalInterpreter dflags
+ = throwIO (InstallationError
+ "this operation requires -fno-external-interpreter")
+ | otherwise
+ = localHValueRef r
+
+-- -----------------------------------------------------------------------------
+-- Misc utils
+
+mkEvalOpts :: DynFlags -> Bool -> EvalOpts
+mkEvalOpts dflags step =
+ EvalOpts
+ { useSandboxThread = gopt Opt_GhciSandbox dflags
+ , singleStep = step
+ , breakOnException = gopt Opt_BreakOnException dflags
+ , breakOnError = gopt Opt_BreakOnError dflags }
+
+fromEvalResult :: EvalResult a -> IO a
+fromEvalResult (EvalException e) = throwIO (fromSerializableException e)
+fromEvalResult (EvalSuccess a) = return a
diff --git a/compiler/ghci/Linker.hs b/compiler/ghci/Linker.hs
index 7c10fae331..11936c7c75 100644
--- a/compiler/ghci/Linker.hs
+++ b/compiler/ghci/Linker.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, NondecreasingIndentation #-}
+{-# LANGUAGE CPP, NondecreasingIndentation, TupleSections #-}
{-# OPTIONS_GHC -fno-cse #-}
--
-- (c) The University of Glasgow 2002-2006
@@ -24,11 +24,12 @@ module Linker ( getHValue, showLinkerState,
#include "HsVersions.h"
+import GHCi
+import GHCi.RemoteTypes
import LoadIface
-import ObjLink
import ByteCodeLink
-import ByteCodeItbls
import ByteCodeAsm
+import ByteCodeTypes
import TcRnMonad
import Packages
import DriverPhases
@@ -63,7 +64,6 @@ import Data.Maybe
import Control.Concurrent.MVar
import System.FilePath
-import System.IO
import System.Directory
import Exception
@@ -147,35 +147,46 @@ extendLoadedPkgs pkgs =
modifyPLS_ $ \s ->
return s{ pkgs_loaded = pkgs ++ pkgs_loaded s }
-extendLinkEnv :: [(Name,HValue)] -> IO ()
--- Automatically discards shadowed bindings
+extendLinkEnv :: [(Name,ForeignHValue)] -> IO ()
extendLinkEnv new_bindings =
- modifyPLS_ $ \pls ->
- let new_closure_env = extendClosureEnv (closure_env pls) new_bindings
- in return pls{ closure_env = new_closure_env }
+ modifyPLS_ $ \pls -> do
+ let ce = closure_env pls
+ let new_ce = extendClosureEnv ce new_bindings
+ return pls{ closure_env = new_ce }
deleteFromLinkEnv :: [Name] -> IO ()
deleteFromLinkEnv to_remove =
- modifyPLS_ $ \pls ->
- let new_closure_env = delListFromNameEnv (closure_env pls) to_remove
- in return pls{ closure_env = new_closure_env }
+ modifyPLS_ $ \pls -> do
+ let ce = closure_env pls
+ let new_ce = delListFromNameEnv ce to_remove
+ return pls{ closure_env = new_ce }
-- | Get the 'HValue' associated with the given name.
--
-- May cause loading the module that contains the name.
--
-- Throws a 'ProgramError' if loading fails or the name cannot be found.
-getHValue :: HscEnv -> Name -> IO HValue
+getHValue :: HscEnv -> Name -> IO ForeignHValue
getHValue hsc_env name = do
- initDynLinker (hsc_dflags hsc_env)
+ initDynLinker hsc_env
pls <- modifyPLS $ \pls -> do
if (isExternalName name) then do
- (pls', ok) <- linkDependencies hsc_env pls noSrcSpan [nameModule name]
+ (pls', ok) <- linkDependencies hsc_env pls noSrcSpan
+ [nameModule name]
if (failed ok) then throwGhcExceptionIO (ProgramError "")
else return (pls', pls')
else
return (pls, pls)
- lookupName (closure_env pls) name
+ case lookupNameEnv (closure_env pls) name of
+ Just (_,aa) -> return aa
+ Nothing
+ -> ASSERT2(isExternalName name, ppr name)
+ do let sym_to_find = nameToCLabel name "closure"
+ m <- lookupClosure hsc_env (unpackFS sym_to_find)
+ case m of
+ Just hvref -> mkFinalizedHValue hsc_env hvref
+ Nothing -> linkFail "ByteCodeLink.lookupCE"
+ (unpackFS sym_to_find)
linkDependencies :: HscEnv -> PersistentLinkerState
-> SrcSpan -> [Module]
@@ -195,14 +206,14 @@ linkDependencies hsc_env pls span needed_mods = do
maybe_normal_osuf span needed_mods
-- Link the packages and modules required
- pls1 <- linkPackages' dflags pkgs pls
- linkModules dflags pls1 lnks
+ pls1 <- linkPackages' hsc_env pkgs pls
+ linkModules hsc_env pls1 lnks
-- | Temporarily extend the linker state.
withExtendedLinkEnv :: (ExceptionMonad m) =>
- [(Name,HValue)] -> m a -> m a
+ [(Name,ForeignHValue)] -> m a -> m a
withExtendedLinkEnv new_env action
= gbracket (liftIO $ extendLinkEnv new_env)
(\_ -> reset_old_env)
@@ -219,19 +230,6 @@ withExtendedLinkEnv new_env action
new = delListFromNameEnv cur (map fst new_env)
in return pls{ closure_env = new }
--- filterNameMap removes from the environment all entries except
--- those for a given set of modules;
--- Note that this removes all *local* (i.e. non-isExternal) names too
--- (these are the temporary bindings from the command line).
--- Used to filter both the ClosureEnv and ItblEnv
-
-filterNameMap :: [Module] -> NameEnv (Name, a) -> NameEnv (Name, a)
-filterNameMap mods env
- = filterNameEnv keep_elt env
- where
- keep_elt (n,_) = isExternalName n
- && (nameModule n `elem` mods)
-
-- | Display the persistent linker state.
showLinkerState :: DynFlags -> IO ()
@@ -268,41 +266,45 @@ showLinkerState dflags
-- nothing. This is useful in Template Haskell, where we call it before
-- trying to link.
--
-initDynLinker :: DynFlags -> IO ()
-initDynLinker dflags =
+initDynLinker :: HscEnv -> IO ()
+initDynLinker hsc_env =
modifyPLS_ $ \pls0 -> do
done <- readIORef v_InitLinkerDone
if done then return pls0
else do writeIORef v_InitLinkerDone True
- reallyInitDynLinker dflags
+ reallyInitDynLinker hsc_env
-reallyInitDynLinker :: DynFlags -> IO PersistentLinkerState
-reallyInitDynLinker dflags =
- do { -- Initialise the linker state
- let pls0 = emptyPLS dflags
+reallyInitDynLinker :: HscEnv -> IO PersistentLinkerState
+reallyInitDynLinker hsc_env = do
+ -- Initialise the linker state
+ let dflags = hsc_dflags hsc_env
+ pls0 = emptyPLS dflags
- -- (a) initialise the C dynamic linker
- ; initObjLinker
+ -- (a) initialise the C dynamic linker
+ initObjLinker hsc_env
- -- (b) Load packages from the command-line (Note [preload packages])
- ; pls <- linkPackages' dflags (preloadPackages (pkgState dflags)) pls0
+ -- (b) Load packages from the command-line (Note [preload packages])
+ pls <- linkPackages' hsc_env (preloadPackages (pkgState dflags)) pls0
- -- steps (c), (d) and (e)
- ; linkCmdLineLibs' dflags pls
- }
+ -- steps (c), (d) and (e)
+ linkCmdLineLibs' hsc_env pls
-linkCmdLineLibs :: DynFlags -> IO ()
-linkCmdLineLibs dflags = do
- initDynLinker dflags
+
+linkCmdLineLibs :: HscEnv -> IO ()
+linkCmdLineLibs hsc_env = do
+ initDynLinker hsc_env
modifyPLS_ $ \pls -> do
- linkCmdLineLibs' dflags pls
+ linkCmdLineLibs' hsc_env pls
+
+linkCmdLineLibs' :: HscEnv -> PersistentLinkerState -> IO PersistentLinkerState
+linkCmdLineLibs' hsc_env pls =
+ do
+ let dflags@(DynFlags { ldInputs = cmdline_ld_inputs
+ , libraryPaths = lib_paths}) = hsc_dflags hsc_env
-linkCmdLineLibs' :: DynFlags -> PersistentLinkerState -> IO PersistentLinkerState
-linkCmdLineLibs' dflags@(DynFlags { ldInputs = cmdline_ld_inputs
- , libraryPaths = lib_paths}) pls =
- do -- (c) Link libraries from the command-line
+ -- (c) Link libraries from the command-line
let minus_ls = [ lib | Option ('-':'l':lib) <- cmdline_ld_inputs ]
- libspecs <- mapM (locateLib dflags False lib_paths) minus_ls
+ libspecs <- mapM (locateLib hsc_env False lib_paths) minus_ls
-- (d) Link .o files from the command-line
classified_ld_inputs <- mapM (classifyLdInput dflags)
@@ -327,15 +329,15 @@ linkCmdLineLibs' dflags@(DynFlags { ldInputs = cmdline_ld_inputs
++ lib_paths
++ [ takeDirectory dll | DLLPath dll <- libspecs ]
in nub $ map normalise paths
- pathCache <- mapM addLibrarySearchPath all_paths
+ pathCache <- mapM (addLibrarySearchPath hsc_env) all_paths
- pls1 <- foldM (preloadLib dflags lib_paths framework_paths) pls
+ pls1 <- foldM (preloadLib hsc_env lib_paths framework_paths) pls
cmdline_lib_specs
maybePutStr dflags "final link ... "
- ok <- resolveObjs
+ ok <- resolveObjs hsc_env
-- DLLs are loaded, reset the search paths
- mapM_ removeLibrarySearchPath $ reverse pathCache
+ mapM_ (removeLibrarySearchPath hsc_env) $ reverse pathCache
if succeeded ok then maybePutStrLn dflags "done"
else throwGhcExceptionIO (ProgramError "linking extra libraries/objects failed")
@@ -377,56 +379,58 @@ classifyLdInput dflags f
return Nothing
where platform = targetPlatform dflags
-preloadLib :: DynFlags -> [String] -> [String] -> PersistentLinkerState
- -> LibrarySpec -> IO PersistentLinkerState
-preloadLib dflags lib_paths framework_paths pls lib_spec
- = do maybePutStr dflags ("Loading object " ++ showLS lib_spec ++ " ... ")
- case lib_spec of
- Object static_ish
- -> do (b, pls1) <- preload_static lib_paths static_ish
- maybePutStrLn dflags (if b then "done"
- else "not found")
- return pls1
-
- Archive static_ish
- -> do b <- preload_static_archive lib_paths static_ish
- maybePutStrLn dflags (if b then "done"
- else "not found")
- return pls
-
- DLL dll_unadorned
- -> do maybe_errstr <- loadDLL (mkSOName platform dll_unadorned)
- case maybe_errstr of
- Nothing -> maybePutStrLn dflags "done"
- Just mm | platformOS platform /= OSDarwin ->
- preloadFailed mm lib_paths lib_spec
- Just mm | otherwise -> do
- -- As a backup, on Darwin, try to also load a .so file
- -- since (apparently) some things install that way - see
- -- ticket #8770.
- err2 <- loadDLL $ ("lib" ++ dll_unadorned) <.> "so"
- case err2 of
- Nothing -> maybePutStrLn dflags "done"
- Just _ -> preloadFailed mm lib_paths lib_spec
- return pls
-
- DLLPath dll_path
- -> do maybe_errstr <- loadDLL dll_path
- case maybe_errstr of
- Nothing -> maybePutStrLn dflags "done"
- Just mm -> preloadFailed mm lib_paths lib_spec
- return pls
-
- Framework framework ->
- if platformUsesFrameworks (targetPlatform dflags)
- then do maybe_errstr <- loadFramework framework_paths framework
- case maybe_errstr of
- Nothing -> maybePutStrLn dflags "done"
- Just mm -> preloadFailed mm framework_paths lib_spec
- return pls
- else panic "preloadLib Framework"
+preloadLib
+ :: HscEnv -> [String] -> [String] -> PersistentLinkerState
+ -> LibrarySpec -> IO PersistentLinkerState
+preloadLib hsc_env lib_paths framework_paths pls lib_spec = do
+ maybePutStr dflags ("Loading object " ++ showLS lib_spec ++ " ... ")
+ case lib_spec of
+ Object static_ish -> do
+ (b, pls1) <- preload_static lib_paths static_ish
+ maybePutStrLn dflags (if b then "done" else "not found")
+ return pls1
+
+ Archive static_ish -> do
+ b <- preload_static_archive lib_paths static_ish
+ maybePutStrLn dflags (if b then "done" else "not found")
+ return pls
+
+ DLL dll_unadorned -> do
+ maybe_errstr <- loadDLL hsc_env (mkSOName platform dll_unadorned)
+ case maybe_errstr of
+ Nothing -> maybePutStrLn dflags "done"
+ Just mm | platformOS platform /= OSDarwin ->
+ preloadFailed mm lib_paths lib_spec
+ Just mm | otherwise -> do
+ -- As a backup, on Darwin, try to also load a .so file
+ -- since (apparently) some things install that way - see
+ -- ticket #8770.
+ let libfile = ("lib" ++ dll_unadorned) <.> "so"
+ err2 <- loadDLL hsc_env libfile
+ case err2 of
+ Nothing -> maybePutStrLn dflags "done"
+ Just _ -> preloadFailed mm lib_paths lib_spec
+ return pls
+
+ DLLPath dll_path -> do
+ do maybe_errstr <- loadDLL hsc_env dll_path
+ case maybe_errstr of
+ Nothing -> maybePutStrLn dflags "done"
+ Just mm -> preloadFailed mm lib_paths lib_spec
+ return pls
+
+ Framework framework ->
+ if platformUsesFrameworks (targetPlatform dflags)
+ then do maybe_errstr <- loadFramework hsc_env framework_paths framework
+ case maybe_errstr of
+ Nothing -> maybePutStrLn dflags "done"
+ Just mm -> preloadFailed mm framework_paths lib_spec
+ return pls
+ else panic "preloadLib Framework"
where
+ dflags = hsc_dflags hsc_env
+
platform = targetPlatform dflags
preloadFailed :: String -> [String] -> LibrarySpec -> IO ()
@@ -445,9 +449,9 @@ preloadLib dflags lib_paths framework_paths pls lib_spec
= do b <- doesFileExist name
if not b then return (False, pls)
else if dynamicGhc
- then do pls1 <- dynLoadObjs dflags pls [name]
+ then do pls1 <- dynLoadObjs hsc_env pls [name]
return (True, pls1)
- else do loadObj name
+ else do loadObj hsc_env name
return (True, pls)
preload_static_archive _paths name
@@ -455,7 +459,7 @@ preloadLib dflags lib_paths framework_paths pls lib_spec
if not b then return False
else do if dynamicGhc
then panic "Loading archives not supported"
- else loadArchive name
+ else loadArchive hsc_env name
return True
@@ -471,12 +475,11 @@ preloadLib dflags lib_paths framework_paths pls lib_spec
-- Raises an IO exception ('ProgramError') if it can't find a compiled
-- version of the dependents to link.
--
-linkExpr :: HscEnv -> SrcSpan -> UnlinkedBCO -> IO HValue
+linkExpr :: HscEnv -> SrcSpan -> UnlinkedBCO -> IO ForeignHValue
linkExpr hsc_env span root_ul_bco
= do {
-- Initialise the linker (if it's not been done already)
- let dflags = hsc_dflags hsc_env
- ; initDynLinker dflags
+ ; initDynLinker hsc_env
-- Take lock for the actual work.
; modifyPLS $ \pls0 -> do {
@@ -492,8 +495,10 @@ linkExpr hsc_env span root_ul_bco
ce = closure_env pls
-- Link the necessary packages and linkables
- ; (_, (root_hval:_)) <- linkSomeBCOs dflags False ie ce [root_ul_bco]
- ; return (pls, root_hval)
+
+ ; [(_,root_hvref)] <- linkSomeBCOs hsc_env ie ce [root_ul_bco]
+ ; fhv <- mkFinalizedHValue hsc_env root_hvref
+ ; return (pls, fhv)
}}}
where
free_names = nameSetElems (bcoFreeNames root_ul_bco)
@@ -514,6 +519,11 @@ dieWith dflags span msg = throwGhcExceptionIO (ProgramError (showSDoc dflags (mk
checkNonStdWay :: DynFlags -> SrcSpan -> IO (Maybe FilePath)
checkNonStdWay dflags srcspan
+ | gopt Opt_ExternalInterpreter dflags = return Nothing
+ -- with -fexternal-interpreter we load the .o files, whatever way
+ -- they were built. If they were built for a non-std way, then
+ -- we will use the appropriate variant of the iserv binary to load them.
+
| interpWays == haskellWays = return Nothing
-- Only if we are compiling with the same ways as GHC is built
-- with, can we dynamically load those object files. (see #3604)
@@ -533,11 +543,19 @@ normalObjectSuffix = phaseInputExt StopLn
failNonStd :: DynFlags -> SrcSpan -> IO (Maybe FilePath)
failNonStd dflags srcspan = dieWith dflags srcspan $
- ptext (sLit "Dynamic linking required, but this is a non-standard build (eg. prof).") $$
- ptext (sLit "You need to build the program twice: once") <+>
- ghciWay <> ptext (sLit ", and then") $$
- ptext (sLit "in the desired way using -osuf to set the object file suffix.")
- where ghciWay
+ ptext (sLit "Cannot load") <+> compWay <+>
+ ptext (sLit "objects when GHC is built") <+> ghciWay $$
+ ptext (sLit "To fix this, either:") $$
+ ptext (sLit " (1) Use -fexternal-interprter, or") $$
+ ptext (sLit " (2) Build the program twice: once") <+>
+ ghciWay <> ptext (sLit ", and then") $$
+ ptext (sLit " with") <+> compWay <+>
+ ptext (sLit "using -osuf to set a different object file suffix.")
+ where compWay
+ | WayDyn `elem` ways dflags = ptext (sLit "-dynamic")
+ | WayProf `elem` ways dflags = ptext (sLit "-prof")
+ | otherwise = ptext (sLit "normal")
+ ghciWay
| dynamicGhc = ptext (sLit "with -dynamic")
| rtsIsProfiled = ptext (sLit "with -prof")
| otherwise = ptext (sLit "the normal way")
@@ -684,11 +702,10 @@ getLinkDeps hsc_env hpt pls replace_osuf span mods
********************************************************************* -}
-linkDecls :: HscEnv -> SrcSpan -> CompiledByteCode -> IO () --[HValue]
-linkDecls hsc_env span (ByteCode unlinkedBCOs itblEnv) = do
+linkDecls :: HscEnv -> SrcSpan -> CompiledByteCode -> IO ()
+linkDecls hsc_env span (ByteCode unlinkedBCOs itblEnv _) = do
-- Initialise the linker (if it's not been done already)
- let dflags = hsc_dflags hsc_env
- initDynLinker dflags
+ initDynLinker hsc_env
-- Take lock for the actual work.
modifyPLS $ \pls0 -> do
@@ -704,10 +721,11 @@ linkDecls hsc_env span (ByteCode unlinkedBCOs itblEnv) = do
ce = closure_env pls
-- Link the necessary packages and linkables
- (final_gce, _) <- linkSomeBCOs dflags False ie ce unlinkedBCOs
- let pls2 = pls { closure_env = final_gce,
- itbl_env = ie }
- return (pls2, ()) --hvals)
+ new_bindings <- linkSomeBCOs hsc_env ie ce unlinkedBCOs
+ nms_fhvs <- makeForeignNamedHValueRefs hsc_env new_bindings
+ let pls2 = pls { closure_env = extendClosureEnv ce nms_fhvs
+ , itbl_env = ie }
+ return (pls2, ())
where
free_names = concatMap (nameSetElems . bcoFreeNames) unlinkedBCOs
@@ -721,8 +739,6 @@ linkDecls hsc_env span (ByteCode unlinkedBCOs itblEnv) = do
-- All wired-in names are in the base package, which we link
-- by default, so we can safely ignore them here.
-
-
{- **********************************************************************
Loading a single module
@@ -731,7 +747,7 @@ linkDecls hsc_env span (ByteCode unlinkedBCOs itblEnv) = do
linkModule :: HscEnv -> Module -> IO ()
linkModule hsc_env mod = do
- initDynLinker (hsc_dflags hsc_env)
+ initDynLinker hsc_env
modifyPLS_ $ \pls -> do
(pls', ok) <- linkDependencies hsc_env pls noSrcSpan [mod]
if (failed ok) then throwGhcExceptionIO (ProgramError "could not link module")
@@ -745,21 +761,21 @@ linkModule hsc_env mod = do
********************************************************************* -}
-linkModules :: DynFlags -> PersistentLinkerState -> [Linkable]
+linkModules :: HscEnv -> PersistentLinkerState -> [Linkable]
-> IO (PersistentLinkerState, SuccessFlag)
-linkModules dflags pls linkables
+linkModules hsc_env pls linkables
= mask_ $ do -- don't want to be interrupted by ^C in here
let (objs, bcos) = partition isObjectLinkable
(concatMap partitionLinkable linkables)
-- Load objects first; they can't depend on BCOs
- (pls1, ok_flag) <- dynLinkObjs dflags pls objs
+ (pls1, ok_flag) <- dynLinkObjs hsc_env pls objs
if failed ok_flag then
return (pls1, Failed)
else do
- pls2 <- dynLinkBCOs dflags pls1 bcos
+ pls2 <- dynLinkBCOs hsc_env pls1 bcos
return (pls2, Succeeded)
@@ -795,36 +811,37 @@ linkableInSet l objs_loaded =
********************************************************************* -}
-dynLinkObjs :: DynFlags -> PersistentLinkerState -> [Linkable]
+dynLinkObjs :: HscEnv -> PersistentLinkerState -> [Linkable]
-> IO (PersistentLinkerState, SuccessFlag)
-dynLinkObjs dflags pls objs = do
+dynLinkObjs hsc_env pls objs = do
-- Load the object files and link them
let (objs_loaded', new_objs) = rmDupLinkables (objs_loaded pls) objs
pls1 = pls { objs_loaded = objs_loaded' }
unlinkeds = concatMap linkableUnlinked new_objs
wanted_objs = map nameOfObject unlinkeds
- if dynamicGhc
- then do pls2 <- dynLoadObjs dflags pls1 wanted_objs
+ if loadingDynamicHSLibs (hsc_dflags hsc_env)
+ then do pls2 <- dynLoadObjs hsc_env pls1 wanted_objs
return (pls2, Succeeded)
- else do mapM_ loadObj wanted_objs
+ else do mapM_ (loadObj hsc_env) wanted_objs
-- Link them all together
- ok <- resolveObjs
+ ok <- resolveObjs hsc_env
-- If resolving failed, unload all our
-- object modules and carry on
if succeeded ok then do
return (pls1, Succeeded)
else do
- pls2 <- unload_wkr dflags [] pls1
+ pls2 <- unload_wkr hsc_env [] pls1
return (pls2, Failed)
-dynLoadObjs :: DynFlags -> PersistentLinkerState -> [FilePath]
+dynLoadObjs :: HscEnv -> PersistentLinkerState -> [FilePath]
-> IO PersistentLinkerState
-dynLoadObjs _ pls [] = return pls
-dynLoadObjs dflags pls objs = do
+dynLoadObjs _ pls [] = return pls
+dynLoadObjs hsc_env pls objs = do
+ let dflags = hsc_dflags hsc_env
let platform = targetPlatform dflags
(soFile, libPath , libName) <- newTempLibName dflags (soExt platform)
let
@@ -860,7 +877,7 @@ dynLoadObjs dflags pls objs = do
-- symbols in this link we must link all loaded packages again.
linkDynLib dflags2 objs (pkgs_loaded pls)
consIORef (filesToNotIntermediateClean dflags) soFile
- m <- loadDLL soFile
+ m <- loadDLL hsc_env soFile
case m of
Nothing -> return pls { temp_sos = (libPath, libName) : temp_sos pls }
Just err -> panic ("Loading temp shared object failed: " ++ err)
@@ -884,9 +901,9 @@ rmDupLinkables already ls
********************************************************************* -}
-dynLinkBCOs :: DynFlags -> PersistentLinkerState -> [Linkable]
+dynLinkBCOs :: HscEnv -> PersistentLinkerState -> [Linkable]
-> IO PersistentLinkerState
-dynLinkBCOs dflags pls bcos = do
+dynLinkBCOs hsc_env pls bcos = do
let (bcos_loaded', new_bcos) = rmDupLinkables (bcos_loaded pls) bcos
pls1 = pls { bcos_loaded = bcos_loaded' }
@@ -897,46 +914,49 @@ dynLinkBCOs dflags pls bcos = do
cbcs = map byteCodeOfObject unlinkeds
- ul_bcos = [b | ByteCode bs _ <- cbcs, b <- bs]
- ies = [ie | ByteCode _ ie <- cbcs]
+ ul_bcos = [b | ByteCode bs _ _ <- cbcs, b <- bs]
+ ies = [ie | ByteCode _ ie _ <- cbcs]
gce = closure_env pls
final_ie = foldr plusNameEnv (itbl_env pls) ies
- (final_gce, _linked_bcos) <- linkSomeBCOs dflags True final_ie gce ul_bcos
- -- XXX What happens to these linked_bcos?
+ names_and_refs <- linkSomeBCOs hsc_env final_ie gce ul_bcos
- let pls2 = pls1 { closure_env = final_gce,
+ -- We only want to add the external ones to the ClosureEnv
+ let (to_add, to_drop) = partition (isExternalName.fst) names_and_refs
+
+ -- Immediately release any HValueRefs we're not going to add
+ freeHValueRefs hsc_env (map snd to_drop)
+ -- Wrap finalizers on the ones we want to keep
+ new_binds <- makeForeignNamedHValueRefs hsc_env to_add
+
+ let pls2 = pls1 { closure_env = extendClosureEnv gce new_binds,
itbl_env = final_ie }
return pls2
--- Link a bunch of BCOs and return them + updated closure env.
-linkSomeBCOs :: DynFlags
- -> Bool -- False <=> add _all_ BCOs to returned closure env
- -- True <=> add only toplevel BCOs to closure env
+-- Link a bunch of BCOs and return references to their values
+linkSomeBCOs :: HscEnv
-> ItblEnv
-> ClosureEnv
-> [UnlinkedBCO]
- -> IO (ClosureEnv, [HValue])
- -- The returned HValues are associated 1-1 with
+ -> IO [(Name,HValueRef)]
+ -- The returned HValueRefs are associated 1-1 with
-- the incoming unlinked BCOs. Each gives the
-- value of the corresponding unlinked BCO
-linkSomeBCOs dflags toplevs_only ie ce_in ul_bcos
- = do let nms = map unlinkedBCOName ul_bcos
- hvals <- fixIO
- ( \ hvs -> let ce_out = extendClosureEnv ce_in (zipLazy nms hvs)
- in mapM (linkBCO dflags ie ce_out) ul_bcos )
- let ce_all_additions = zip nms hvals
- ce_top_additions = filter (isExternalName.fst) ce_all_additions
- ce_additions = if toplevs_only then ce_top_additions
- else ce_all_additions
- ce_out = -- make sure we're not inserting duplicate names into the
- -- closure environment, which leads to trouble.
- ASSERT(all (not . (`elemNameEnv` ce_in)) (map fst ce_additions))
- extendClosureEnv ce_in ce_additions
- return (ce_out, hvals)
+linkSomeBCOs _ _ _ [] = return []
+linkSomeBCOs hsc_env ie ce ul_bcos = do
+ let names = map unlinkedBCOName ul_bcos
+ bco_ix = mkNameEnv (zip names [0..])
+ resolved <- mapM (linkBCO hsc_env ie ce bco_ix) ul_bcos
+ hvrefs <- iservCmd hsc_env (CreateBCOs resolved)
+ return (zip names hvrefs)
+-- | Useful to apply to the result of 'linkSomeBCOs'
+makeForeignNamedHValueRefs
+ :: HscEnv -> [(Name,HValueRef)] -> IO [(Name,ForeignHValue)]
+makeForeignNamedHValueRefs hsc_env bindings =
+ mapM (\(n, hvref) -> (n,) <$> mkFinalizedHValue hsc_env hvref) bindings
{- **********************************************************************
@@ -958,62 +978,85 @@ linkSomeBCOs dflags toplevs_only ie ce_in ul_bcos
--
-- * we also implicitly unload all temporary bindings at this point.
--
-unload :: DynFlags
+unload :: HscEnv
-> [Linkable] -- ^ The linkables to *keep*.
-> IO ()
-unload dflags linkables
+unload hsc_env linkables
= mask_ $ do -- mask, so we're safe from Ctrl-C in here
-- Initialise the linker (if it's not been done already)
- initDynLinker dflags
+ initDynLinker hsc_env
new_pls
<- modifyPLS $ \pls -> do
- pls1 <- unload_wkr dflags linkables pls
+ pls1 <- unload_wkr hsc_env linkables pls
return (pls1, pls1)
- debugTraceMsg dflags 3 (text "unload: retaining objs" <+> ppr (objs_loaded new_pls))
- debugTraceMsg dflags 3 (text "unload: retaining bcos" <+> ppr (bcos_loaded new_pls))
+ let dflags = hsc_dflags hsc_env
+ debugTraceMsg dflags 3 $
+ text "unload: retaining objs" <+> ppr (objs_loaded new_pls)
+ debugTraceMsg dflags 3 $
+ text "unload: retaining bcos" <+> ppr (bcos_loaded new_pls)
return ()
-unload_wkr :: DynFlags
+unload_wkr :: HscEnv
-> [Linkable] -- stable linkables
-> PersistentLinkerState
-> IO PersistentLinkerState
-- Does the core unload business
-- (the wrapper blocks exceptions and deals with the PLS get and put)
-unload_wkr _ linkables pls
- = do let (objs_to_keep, bcos_to_keep) = partition isObjectLinkable linkables
+unload_wkr hsc_env keep_linkables pls = do
+ let (objs_to_keep, bcos_to_keep) = partition isObjectLinkable keep_linkables
+
+ discard keep l = not (linkableInSet l keep)
+
+ (objs_to_unload, remaining_objs_loaded) =
+ partition (discard objs_to_keep) (objs_loaded pls)
+ (bcos_to_unload, remaining_bcos_loaded) =
+ partition (discard bcos_to_keep) (bcos_loaded pls)
+
+ mapM_ unloadObjs objs_to_unload
+ mapM_ unloadObjs bcos_to_unload
+
+ -- If we unloaded any object files at all, we need to purge the cache
+ -- of lookupSymbol results.
+ when (not (null (objs_to_unload ++
+ filter (not . null . linkableObjs) bcos_to_unload))) $
+ purgeLookupSymbolCache hsc_env
+
+ let bcos_retained = map linkableModule remaining_bcos_loaded
+
+ -- Note that we want to remove all *local*
+ -- (i.e. non-isExternal) names too (these are the
+ -- temporary bindings from the command line).
+ keep_name (n,_) = isExternalName n &&
+ nameModule n `elem` bcos_retained
- objs_loaded' <- filterM (maybeUnload objs_to_keep) (objs_loaded pls)
- bcos_loaded' <- filterM (maybeUnload bcos_to_keep) (bcos_loaded pls)
+ itbl_env' = filterNameEnv keep_name (itbl_env pls)
+ closure_env' = filterNameEnv keep_name (closure_env pls)
- let bcos_retained = map linkableModule bcos_loaded'
- itbl_env' = filterNameMap bcos_retained (itbl_env pls)
- closure_env' = filterNameMap bcos_retained (closure_env pls)
- new_pls = pls { itbl_env = itbl_env',
- closure_env = closure_env',
- bcos_loaded = bcos_loaded',
- objs_loaded = objs_loaded' }
+ new_pls = pls { itbl_env = itbl_env',
+ closure_env = closure_env',
+ bcos_loaded = remaining_bcos_loaded,
+ objs_loaded = remaining_objs_loaded }
- return new_pls
+ return new_pls
where
- maybeUnload :: [Linkable] -> Linkable -> IO Bool
- maybeUnload keep_linkables lnk
- | linkableInSet lnk keep_linkables = return True
- -- We don't do any cleanup when linking objects with the dynamic linker.
- -- Doing so introduces extra complexity for not much benefit.
- | dynamicGhc = return False
+ unloadObjs :: Linkable -> IO ()
+ unloadObjs lnk
+ | dynamicGhc = return ()
+ -- We don't do any cleanup when linking objects with the
+ -- dynamic linker. Doing so introduces extra complexity for
+ -- not much benefit.
| otherwise
- = do mapM_ unloadObj [f | DotO f <- linkableUnlinked lnk]
+ = mapM_ (unloadObj hsc_env) [f | DotO f <- linkableUnlinked lnk]
-- The components of a BCO linkable may contain
-- dot-o files. Which is very confusing.
--
-- But the BCO parts can be unlinked just by
-- letting go of them (plus of course depopulating
-- the symbol table which is done in the main body)
- return False
{- **********************************************************************
@@ -1067,7 +1110,7 @@ showLS (Framework nm) = "(framework) " ++ nm
-- automatically, and it doesn't matter what order you specify the input
-- packages.
--
-linkPackages :: DynFlags -> [UnitId] -> IO ()
+linkPackages :: HscEnv -> [UnitId] -> IO ()
-- NOTE: in fact, since each module tracks all the packages it depends on,
-- we don't really need to use the package-config dependencies.
--
@@ -1076,19 +1119,21 @@ linkPackages :: DynFlags -> [UnitId] -> IO ()
-- perhaps makes the error message a bit more localised if we get a link
-- failure. So the dependency walking code is still here.
-linkPackages dflags new_pkgs = do
+linkPackages hsc_env new_pkgs = do
-- It's probably not safe to try to load packages concurrently, so we take
-- a lock.
- initDynLinker dflags
+ initDynLinker hsc_env
modifyPLS_ $ \pls -> do
- linkPackages' dflags new_pkgs pls
+ linkPackages' hsc_env new_pkgs pls
-linkPackages' :: DynFlags -> [UnitId] -> PersistentLinkerState
+linkPackages' :: HscEnv -> [UnitId] -> PersistentLinkerState
-> IO PersistentLinkerState
-linkPackages' dflags new_pks pls = do
+linkPackages' hsc_env new_pks pls = do
pkgs' <- link (pkgs_loaded pls) new_pks
return $! pls { pkgs_loaded = pkgs' }
where
+ dflags = hsc_dflags hsc_env
+
link :: [UnitId] -> [UnitId] -> IO [UnitId]
link pkgs new_pkgs =
foldM link_one pkgs new_pkgs
@@ -1101,18 +1146,19 @@ linkPackages' dflags new_pks pls = do
= do { -- Link dependents first
pkgs' <- link pkgs (depends pkg_cfg)
-- Now link the package itself
- ; linkPackage dflags pkg_cfg
+ ; linkPackage hsc_env pkg_cfg
; return (new_pkg : pkgs') }
| otherwise
= throwGhcExceptionIO (CmdLineError ("unknown package: " ++ unitIdString new_pkg))
-linkPackage :: DynFlags -> PackageConfig -> IO ()
-linkPackage dflags pkg
+linkPackage :: HscEnv -> PackageConfig -> IO ()
+linkPackage hsc_env pkg
= do
- let platform = targetPlatform dflags
- dirs = Packages.libraryDirs pkg
+ let dflags = hsc_dflags hsc_env
+ platform = targetPlatform dflags
+ dirs = Packages.libraryDirs pkg
let hs_libs = Packages.hsLibraries pkg
-- The FFI GHCi import lib isn't needed as
@@ -1135,8 +1181,8 @@ linkPackage dflags pkg
else Packages.extraGHCiLibraries pkg)
++ [ lib | '-':'l':lib <- Packages.ldOptions pkg ]
- hs_classifieds <- mapM (locateLib dflags True dirs) hs_libs'
- extra_classifieds <- mapM (locateLib dflags False dirs) extra_libs
+ hs_classifieds <- mapM (locateLib hsc_env True dirs) hs_libs'
+ extra_classifieds <- mapM (locateLib hsc_env False dirs) extra_libs
let classifieds = hs_classifieds ++ extra_classifieds
-- Complication: all the .so's must be loaded before any of the .o's.
@@ -1148,27 +1194,28 @@ linkPackage dflags pkg
-- Add directories to library search paths
let dll_paths = map takeDirectory known_dlls
all_paths = nub $ map normalise $ dll_paths ++ dirs
- pathCache <- mapM addLibrarySearchPath all_paths
+ pathCache <- mapM (addLibrarySearchPath hsc_env) all_paths
maybePutStr dflags
("Loading package " ++ sourcePackageIdString pkg ++ " ... ")
-- See comments with partOfGHCi
when (packageName pkg `notElem` partOfGHCi) $ do
- loadFrameworks platform pkg
- mapM_ load_dyn (known_dlls ++ map (mkSOName platform) dlls)
+ loadFrameworks hsc_env platform pkg
+ mapM_ (load_dyn hsc_env)
+ (known_dlls ++ map (mkSOName platform) dlls)
-- DLLs are loaded, reset the search paths
- mapM_ removeLibrarySearchPath $ reverse pathCache
+ mapM_ (removeLibrarySearchPath hsc_env) $ reverse pathCache
-- After loading all the DLLs, we can load the static objects.
-- Ordering isn't important here, because we do one final link
-- step to resolve everything.
- mapM_ loadObj objs
- mapM_ loadArchive archs
+ mapM_ (loadObj hsc_env) objs
+ mapM_ (loadArchive hsc_env) archs
maybePutStr dflags "linking ... "
- ok <- resolveObjs
+ ok <- resolveObjs hsc_env
if succeeded ok
then maybePutStrLn dflags "done."
else let errmsg = "unable to load package `"
@@ -1180,33 +1227,44 @@ linkPackage dflags pkg
-- ("/usr/lib/libfoo.so"), or unqualified ("libfoo.so"). In the latter case,
-- loadDLL is going to search the system paths to find the library.
--
-load_dyn :: FilePath -> IO ()
-load_dyn dll = do r <- loadDLL dll
- case r of
- Nothing -> return ()
- Just err -> throwGhcExceptionIO (CmdLineError ("can't load .so/.DLL for: "
- ++ dll ++ " (" ++ err ++ ")" ))
-
-loadFrameworks :: Platform -> PackageConfig -> IO ()
-loadFrameworks platform pkg
+load_dyn :: HscEnv -> FilePath -> IO ()
+load_dyn hsc_env dll = do
+ r <- loadDLL hsc_env dll
+ case r of
+ Nothing -> return ()
+ Just err -> throwGhcExceptionIO (CmdLineError ("can't load .so/.DLL for: "
+ ++ dll ++ " (" ++ err ++ ")" ))
+
+loadFrameworks :: HscEnv -> Platform -> PackageConfig -> IO ()
+loadFrameworks hsc_env platform pkg
= when (platformUsesFrameworks platform) $ mapM_ load frameworks
where
fw_dirs = Packages.frameworkDirs pkg
frameworks = Packages.frameworks pkg
- load fw = do r <- loadFramework fw_dirs fw
+ load fw = do r <- loadFramework hsc_env fw_dirs fw
case r of
Nothing -> return ()
Just err -> throwGhcExceptionIO (CmdLineError ("can't load framework: "
++ fw ++ " (" ++ err ++ ")" ))
+loadingDynamicHSLibs :: DynFlags -> Bool
+loadingDynamicHSLibs dflags
+ | gopt Opt_ExternalInterpreter dflags = WayDyn `elem` ways dflags
+ | otherwise = dynamicGhc
+
+loadingProfiledHSLibs :: DynFlags -> Bool
+loadingProfiledHSLibs dflags
+ | gopt Opt_ExternalInterpreter dflags = gopt Opt_SccProfilingOn dflags
+ | otherwise = rtsIsProfiled
+
-- Try to find an object file for a given library in the given paths.
-- If it isn't present, we assume that addDLL in the RTS can find it,
-- which generally means that it should be a dynamic library in the
-- standard system search path.
-locateLib :: DynFlags -> Bool -> [FilePath] -> String -> IO LibrarySpec
-locateLib dflags is_hs dirs lib
+locateLib :: HscEnv -> Bool -> [FilePath] -> String -> IO LibrarySpec
+locateLib hsc_env is_hs dirs lib
| not is_hs
-- For non-Haskell libraries (e.g. gmp, iconv):
-- first look in library-dirs for a dynamic library (libfoo.so)
@@ -1224,15 +1282,12 @@ locateLib dflags is_hs dirs lib
findArchive `orElse`
assumeDll
- | dynamicGhc
- -- When the GHC package was compiled as dynamic library (=DYNAMIC set),
- -- we search for .so libraries first.
+ | loading_dynamic_hs_libs -- search for .so libraries first.
= findHSDll `orElse`
findDynObject `orElse`
assumeDll
- | rtsIsProfiled
- -- When the GHC package is profiled, only a libHSfoo_p.a archive will do.
+ | loading_profiled_hs_libs -- only a libHSfoo_p.a archive will do.
= findArchive `orElse`
assumeDll
@@ -1244,10 +1299,15 @@ locateLib dflags is_hs dirs lib
assumeDll
where
+ dflags = hsc_dflags hsc_env
+
obj_file = lib <.> "o"
dyn_obj_file = lib <.> "dyn_o"
arch_file = "lib" ++ lib ++ lib_tag <.> "a"
- lib_tag = if is_hs && rtsIsProfiled then "_p" else ""
+ lib_tag = if is_hs && loading_profiled_hs_libs then "_p" else ""
+
+ loading_profiled_hs_libs = loadingProfiledHSLibs dflags
+ loading_dynamic_hs_libs = loadingDynamicHSLibs dflags
hs_dyn_lib_name = lib ++ '-':programName dflags ++ projectVersion dflags
hs_dyn_lib_file = mkHsSOName platform hs_dyn_lib_name
@@ -1265,7 +1325,7 @@ locateLib dflags is_hs dirs lib
in liftM2 (<|>) local linked
findHSDll = liftM (fmap DLLPath) $ findFile dirs hs_dyn_lib_file
findDll = liftM (fmap DLLPath) $ findFile dirs dyn_lib_file
- findSysDll = fmap (fmap $ DLL . takeFileName) $ findSystemLibrary so_name
+ findSysDll = fmap (fmap $ DLL . takeFileName) $ findSystemLibrary hsc_env so_name
tryGcc = let short = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags so_name dirs
full = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags lib_so_name dirs
in liftM2 (<|>) short full
@@ -1297,8 +1357,8 @@ searchForLibUsingGcc dflags so dirs = do
-- Darwin / MacOS X only: load a framework
-- a framework is a dynamic library packaged inside a directory of the same
-- name. They are searched for in different paths than normal libraries.
-loadFramework :: [FilePath] -> FilePath -> IO (Maybe String)
-loadFramework extraPaths rootname
+loadFramework :: HscEnv -> [FilePath] -> FilePath -> IO (Maybe String)
+loadFramework hsc_env extraPaths rootname
= do { either_dir <- tryIO getHomeDirectory
; let homeFrameworkPath = case either_dir of
Left _ -> []
@@ -1306,7 +1366,7 @@ loadFramework extraPaths rootname
ps = extraPaths ++ homeFrameworkPath ++ defaultFrameworkPaths
; mb_fwk <- findFile ps fwk_file
; case mb_fwk of
- Just fwk_path -> loadDLL fwk_path
+ Just fwk_path -> loadDLL hsc_env fwk_path
Nothing -> return (Just "not found") }
-- Tried all our known library paths, but dlopen()
-- has no built-in paths for frameworks: give up
diff --git a/compiler/ghci/ObjLink.hs b/compiler/ghci/ObjLink.hs
deleted file mode 100644
index b1cfe61da9..0000000000
--- a/compiler/ghci/ObjLink.hs
+++ /dev/null
@@ -1,142 +0,0 @@
---
--- (c) The University of Glasgow 2002-2006
---
-
--- ---------------------------------------------------------------------------
--- The dynamic linker for object code (.o .so .dll files)
--- ---------------------------------------------------------------------------
-
--- | Primarily, this module consists of an interface to the C-land
--- dynamic linker.
-module ObjLink (
- initObjLinker, -- :: IO ()
- loadDLL, -- :: String -> IO (Maybe String)
- loadArchive, -- :: String -> IO ()
- loadObj, -- :: String -> IO ()
- unloadObj, -- :: String -> IO ()
- insertSymbol, -- :: String -> String -> Ptr a -> IO ()
- lookupSymbol, -- :: String -> IO (Maybe (Ptr a))
- resolveObjs, -- :: IO SuccessFlag
- addLibrarySearchPath, -- :: FilePath -> IO (Ptr ())
- removeLibrarySearchPath, -- :: Ptr () -> IO Bool
- findSystemLibrary -- :: FilePath -> IO (Maybe FilePath)
- ) where
-
-import Panic
-import BasicTypes ( SuccessFlag, successIf )
-import Config ( cLeadingUnderscore )
-import Util
-
-import Control.Monad ( when )
-import Foreign.C
-import Foreign.Marshal.Alloc ( free )
-import Foreign ( nullPtr )
-import GHC.Exts ( Ptr(..) )
-import System.Posix.Internals ( CFilePath, withFilePath, peekFilePath )
-import System.FilePath ( dropExtension, normalise )
-
-
--- ---------------------------------------------------------------------------
--- RTS Linker Interface
--- ---------------------------------------------------------------------------
-
-insertSymbol :: String -> String -> Ptr a -> IO ()
-insertSymbol obj_name key symbol
- = let str = prefixUnderscore key
- in withFilePath obj_name $ \c_obj_name ->
- withCAString str $ \c_str ->
- c_insertSymbol c_obj_name c_str symbol
-
-lookupSymbol :: String -> IO (Maybe (Ptr a))
-lookupSymbol str_in = do
- let str = prefixUnderscore str_in
- withCAString str $ \c_str -> do
- addr <- c_lookupSymbol c_str
- if addr == nullPtr
- then return Nothing
- else return (Just addr)
-
-prefixUnderscore :: String -> String
-prefixUnderscore
- | cLeadingUnderscore == "YES" = ('_':)
- | otherwise = id
-
--- | loadDLL loads a dynamic library using the OS's native linker
--- (i.e. dlopen() on Unix, LoadLibrary() on Windows). It takes either
--- an absolute pathname to the file, or a relative filename
--- (e.g. "libfoo.so" or "foo.dll"). In the latter case, loadDLL
--- searches the standard locations for the appropriate library.
---
-loadDLL :: String -> IO (Maybe String)
--- Nothing => success
--- Just err_msg => failure
-loadDLL str0 = do
- let
- -- On Windows, addDLL takes a filename without an extension, because
- -- it tries adding both .dll and .drv. To keep things uniform in the
- -- layers above, loadDLL always takes a filename with an extension, and
- -- we drop it here on Windows only.
- str | isWindowsHost = dropExtension str0
- | otherwise = str0
- --
- maybe_errmsg <- withFilePath (normalise str) $ \dll -> c_addDLL dll
- if maybe_errmsg == nullPtr
- then return Nothing
- else do str <- peekCString maybe_errmsg
- free maybe_errmsg
- return (Just str)
-
-loadArchive :: String -> IO ()
-loadArchive str = do
- withFilePath str $ \c_str -> do
- r <- c_loadArchive c_str
- when (r == 0) (panic ("loadArchive " ++ show str ++ ": failed"))
-
-loadObj :: String -> IO ()
-loadObj str = do
- withFilePath str $ \c_str -> do
- r <- c_loadObj c_str
- when (r == 0) (panic ("loadObj " ++ show str ++ ": failed"))
-
-unloadObj :: String -> IO ()
-unloadObj str =
- withFilePath str $ \c_str -> do
- r <- c_unloadObj c_str
- when (r == 0) (panic ("unloadObj " ++ show str ++ ": failed"))
-
-addLibrarySearchPath :: String -> IO (Ptr ())
-addLibrarySearchPath str =
- withFilePath str c_addLibrarySearchPath
-
-removeLibrarySearchPath :: Ptr () -> IO Bool
-removeLibrarySearchPath = c_removeLibrarySearchPath
-
-findSystemLibrary :: String -> IO (Maybe String)
-findSystemLibrary str = do
- result <- withFilePath str c_findSystemLibrary
- case result == nullPtr of
- True -> return Nothing
- False -> do path <- peekFilePath result
- free result
- return $ Just path
-
-resolveObjs :: IO SuccessFlag
-resolveObjs = do
- r <- c_resolveObjs
- return (successIf (r /= 0))
-
--- ---------------------------------------------------------------------------
--- Foreign declarations to RTS entry points which does the real work;
--- ---------------------------------------------------------------------------
-
-foreign import ccall unsafe "addDLL" c_addDLL :: CFilePath -> IO CString
-foreign import ccall unsafe "initLinker" initObjLinker :: IO ()
-foreign import ccall unsafe "insertSymbol" c_insertSymbol :: CFilePath -> CString -> Ptr a -> IO ()
-foreign import ccall unsafe "lookupSymbol" c_lookupSymbol :: CString -> IO (Ptr a)
-foreign import ccall unsafe "loadArchive" c_loadArchive :: CFilePath -> IO Int
-foreign import ccall unsafe "loadObj" c_loadObj :: CFilePath -> IO Int
-foreign import ccall unsafe "unloadObj" c_unloadObj :: CFilePath -> IO Int
-foreign import ccall unsafe "resolveObjs" c_resolveObjs :: IO Int
-foreign import ccall unsafe "addLibrarySearchPath" c_addLibrarySearchPath :: CFilePath -> IO (Ptr ())
-foreign import ccall unsafe "removeLibrarySearchPath" c_removeLibrarySearchPath :: Ptr () -> IO Bool
-foreign import ccall unsafe "findSystemLibrary" c_findSystemLibrary :: CFilePath -> IO CFilePath
diff --git a/compiler/ghci/RtClosureInspect.hs b/compiler/ghci/RtClosureInspect.hs
index 015126fae9..f71c904454 100644
--- a/compiler/ghci/RtClosureInspect.hs
+++ b/compiler/ghci/RtClosureInspect.hs
@@ -27,9 +27,9 @@ module RtClosureInspect(
#include "HsVersions.h"
import DebuggerUtils
-import ByteCodeItbls ( StgInfoTable, peekItbl )
-import qualified ByteCodeItbls as BCI( StgInfoTable(..) )
-import BasicTypes ( HValue )
+import GHCi.RemoteTypes ( HValue )
+import qualified GHCi.InfoTable as InfoTable
+import GHCi.InfoTable (StgInfoTable, peekItbl)
import HscTypes
import DataCon
@@ -185,12 +185,12 @@ getClosureData dflags a =
-- into account the extra entry pointer when
-- !ghciTablesNextToCode, so we must adjust here:
iptr0 `plusPtr` negate (wORD_SIZE dflags)
- itbl <- peekItbl dflags iptr1
- let tipe = readCType (BCI.tipe itbl)
- elems = fromIntegral (BCI.ptrs itbl)
+ itbl <- peekItbl iptr1
+ let tipe = readCType (InfoTable.tipe itbl)
+ elems = fromIntegral (InfoTable.ptrs itbl)
ptrsList = Array 0 (elems - 1) elems ptrs
nptrs_data = [W# (indexWordArray# nptrs i)
- | I# i <- [0.. fromIntegral (BCI.nptrs itbl)-1] ]
+ | I# i <- [0.. fromIntegral (InfoTable.nptrs itbl)-1] ]
ASSERT(elems >= 0) return ()
ptrsList `seq`
return (Closure tipe iptr0 itbl ptrsList nptrs_data)