packages feed

ghc-9.14.1: GHC/Runtime/Interpreter/Types/SymbolCache.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RecordWildCards #-}

-- | The SymbolCache is used to cache lookups for specific symbols when using
-- the interpreter.
module GHC.Runtime.Interpreter.Types.SymbolCache (
     InterpSymbolCache(..)
   , mkInterpSymbolCache
   , lookupInterpSymbolCache
   , updateInterpSymbolCache
   , purgeInterpSymbolCache
   , InterpSymbol(..)
   , SuffixOrInterpreted(..)
   , interpSymbolName
   , interpSymbolSuffix
   , eliminateInterpSymbol
   , interpretedInterpSymbol
   ) where

import GHC.Prelude

import GHC.Types.Unique.FM
import GHC.Types.Name
import GHC.Data.FastString
import Foreign

import Control.Concurrent
import GHC.Utils.Outputable
import GHC.TypeLits


-- The symbols records the suffix which each cache deals with.
newtype SymbolCache (s :: Symbol) = SymbolCache { _getSymbolCache :: UniqFM Name (Ptr ()) }

-- Each cache is keyed by Name, there is one cache for each type of symbol we will
-- potentially lookup. The caches are keyed by 'Name' so that it is not necessary to consult
-- a complicated `FastString` each time.
data InterpSymbolCache = InterpSymbolCache {
        interpClosureCache :: MVar (SymbolCache "closure")
      , interpConInfoCache :: MVar (SymbolCache "con_info")
      , interpStaticInfoCache :: MVar (SymbolCache "static_info")
      , interpBytesCache   :: MVar (SymbolCache "bytes")
      , interpFaststringCache   :: MVar (UniqFM FastString (Ptr ()))
      }

data SuffixOrInterpreted = Suffix Symbol | Interpreted

data InterpSymbol (s :: SuffixOrInterpreted) where
  IClosureSymbol :: Name -> InterpSymbol (Suffix "closure")
  IConInfoSymbol :: Name -> InterpSymbol (Suffix "con_info")
  IStaticInfoSymbol :: Name -> InterpSymbol (Suffix "static_info")
  IBytesSymbol   :: Name -> InterpSymbol (Suffix "bytes")
  IFaststringSymbol   :: FastString -> InterpSymbol Interpreted

instance Outputable (InterpSymbol s) where
  ppr s = eliminateInterpSymbol s
            (\(IFaststringSymbol s) -> text "interpreted:" <> ppr s)
            (\s -> text (interpSymbolSuffix s) <> colon <> ppr (interpSymbolName s))

eliminateInterpSymbol :: InterpSymbol s -> (InterpSymbol Interpreted -> r)
                                        -> (forall x . InterpSymbol (Suffix x) -> r)
                                        -> r
eliminateInterpSymbol s k1 k2 =
  case s of
    IFaststringSymbol {} -> k1 s
    IBytesSymbol {}      -> k2 s
    IStaticInfoSymbol {} -> k2 s
    IConInfoSymbol {}    -> k2 s
    IClosureSymbol {}    -> k2 s


interpSymbolName :: InterpSymbol (Suffix s) -> Name
interpSymbolName (IClosureSymbol n) = n
interpSymbolName (IConInfoSymbol n) = n
interpSymbolName (IStaticInfoSymbol n) = n
interpSymbolName (IBytesSymbol n) = n

interpretedInterpSymbol :: InterpSymbol Interpreted -> FastString
interpretedInterpSymbol (IFaststringSymbol s) = s

interpSymbolSuffix :: InterpSymbol (Suffix s) -> String
interpSymbolSuffix (IClosureSymbol {}) = "closure"
interpSymbolSuffix (IConInfoSymbol {}) = "con_info"
interpSymbolSuffix (IStaticInfoSymbol {}) = "static_info"
interpSymbolSuffix (IBytesSymbol {})      = "bytes"

emptySymbolCache :: SymbolCache s
emptySymbolCache = SymbolCache emptyUFM

lookupSymbolCache :: InterpSymbol (Suffix s) -> SymbolCache s -> Maybe (Ptr ())
lookupSymbolCache s (SymbolCache cache) = lookupUFM cache (interpSymbolName s)

insertSymbolCache :: InterpSymbol (Suffix s) -> Ptr () -> SymbolCache s -> SymbolCache s
insertSymbolCache s v (SymbolCache cache) = SymbolCache (addToUFM cache (interpSymbolName s) v)

lookupInterpSymbolCache :: InterpSymbol s -> InterpSymbolCache -> IO (Maybe (Ptr ()))
lookupInterpSymbolCache = withInterpSymbolCache
                            (\(IFaststringSymbol f) mvar_var -> (\cache -> lookupUFM cache f) <$> readMVar mvar_var)
                            (\s mvar_var -> lookupSymbolCache s <$> readMVar mvar_var)


updateInterpSymbolCache :: InterpSymbol s
                                 -> InterpSymbolCache -> Ptr () -> IO ()
updateInterpSymbolCache = withInterpSymbolCache
                            (\(IFaststringSymbol f) mvar_var v -> modifyMVar_ mvar_var (\cache -> pure $ addToUFM cache f v))
                            (\s mvar_var v -> modifyMVar_ mvar_var (\cache -> pure $ insertSymbolCache s v cache))

withInterpSymbolCache ::
                (InterpSymbol Interpreted -> MVar (UniqFM FastString (Ptr ())) -> r)
                -> (forall x . InterpSymbol (Suffix x) -> MVar (SymbolCache x) -> r)
                -> InterpSymbol s
                -> InterpSymbolCache
                -> r
withInterpSymbolCache k1 k2 key InterpSymbolCache{..} =
  case key of
    IClosureSymbol {} -> k2 key interpClosureCache
    IConInfoSymbol {} -> k2 key interpConInfoCache
    IStaticInfoSymbol {} -> k2 key interpStaticInfoCache
    IBytesSymbol {} -> k2 key interpBytesCache
    IFaststringSymbol {} -> k1 key interpFaststringCache

-- | Clear all symbol caches.
purgeInterpSymbolCache :: InterpSymbolCache -> IO ()
purgeInterpSymbolCache (InterpSymbolCache a b c d e) = do
  modifyMVar_ a (\_ ->  do
    modifyMVar_ b (\_ -> do
      modifyMVar_ c (\_ -> do
        modifyMVar_ d (\_ -> do
          modifyMVar_ e (\_ -> pure emptyUFM)
          pure emptySymbolCache)
        pure emptySymbolCache)
      pure emptySymbolCache)
    pure emptySymbolCache)

mkInterpSymbolCache :: IO InterpSymbolCache
mkInterpSymbolCache = do
  InterpSymbolCache <$> newMVar emptySymbolCache
                    <*> newMVar emptySymbolCache
                    <*> newMVar emptySymbolCache
                    <*> newMVar emptySymbolCache
                    <*> newMVar emptyUFM