derive-storable-plugin-0.2.3.4: src/Foreign/Storable/Generic/Plugin/Internal.hs
{-|
Module : Foreign.Storable.Generic.Plugin.Internal
Copyright : (c) Mateusz Kłoczko, 2016
License : MIT
Maintainer : mateusz.p.kloczko@gmail.com
Stability : experimental
Portability : GHC-only
Contains methods for calculating type ordering and performing the compile-substitution optimisation.
-}
{-#LANGUAGE CPP #-}
module Foreign.Storable.Generic.Plugin.Internal
( groupTypes
, gstorableSubstitution)
where
-- Management of Core.
import Prelude hiding ((<>))
#if MIN_VERSION_GLASGOW_HASKELL(9,0,1,0)
import GHC.Core (Bind(..),Expr(..), CoreExpr, CoreBind, CoreProgram, Alt)
import GHC.Types.Literal (Literal(..))
import GHC.Types.Id (isLocalId, isGlobalId,Id, modifyInlinePragma, setInlinePragma, idInfo)
import GHC.Types.Id.Info
import GHC.Types.Var (Var(..))
import GHC.Types.Name (getOccName,mkOccName)
import GHC.Types.Name.Occurrence (OccName(..), occNameString)
import qualified GHC.Types.Name as N (varName)
import GHC.Types.SrcLoc (noSrcSpan)
import GHC.Types.Unique (getUnique)
import GHC.Driver.Main (hscCompileCoreExpr, getHscEnv)
import GHC.Driver.Types (HscEnv,ModGuts(..))
import GHC.Core.Opt.Monad
(CoreM, CoreToDo(..),
getHscEnv, getDynFlags, putMsg, putMsgS)
import GHC.Types.Basic (CompilerPhase(..))
import GHC.Core.Type (isAlgType, splitTyConApp_maybe)
import GHC.Core.TyCon (tyConKind, algTyConRhs, visibleDataCons)
import GHC.Core.TyCo.Rep (Type(..), TyBinder(..))
import GHC.Builtin.Types (intDataCon)
import GHC.Core.DataCon (dataConWorkId,dataConOrigArgTys)
import GHC.Core.Make (mkWildValBinder)
import GHC.Utils.Outputable
(cat, ppr, SDoc, showSDocUnsafe, showSDoc,
($$), ($+$), hsep, vcat, empty,text,
(<>), (<+>), nest, int, colon,hcat, comma,
punctuate, fsep)
import GHC.Core.Opt.Monad (putMsg, putMsgS)
#elif MIN_VERSION_GLASGOW_HASKELL(8,2,1,0)
import CoreSyn (Bind(..),Expr(..), CoreExpr, CoreBind, CoreProgram, Alt)
import Literal (Literal(..))
import Id (isLocalId, isGlobalId,Id, modifyInlinePragma, setInlinePragma, idInfo)
import IdInfo
import Var (Var(..))
import Name (getOccName,mkOccName)
import OccName (OccName(..), occNameString)
import qualified Name as N (varName)
import SrcLoc (noSrcSpan)
import Unique (getUnique)
import HscMain (hscCompileCoreExpr)
import HscTypes (HscEnv,ModGuts(..))
import CoreMonad
(CoreM, CoreToDo(..),
getHscEnv, getDynFlags, putMsg, putMsgS)
import BasicTypes (CompilerPhase(..))
import Type (isAlgType, splitTyConApp_maybe)
import TyCon (tyConKind, algTyConRhs, visibleDataCons)
import TyCoRep (Type(..), TyBinder(..))
import TysWiredIn (intDataCon)
import DataCon (dataConWorkId,dataConOrigArgTys)
import MkCore (mkWildValBinder)
import Outputable
(cat, ppr, SDoc, showSDocUnsafe, showSDoc,
($$), ($+$), hsep, vcat, empty,text,
(<>), (<+>), nest, int, colon,hcat, comma,
punctuate, fsep)
import CoreMonad (putMsg, putMsgS)
#endif
import Data.List
import Data.Maybe
import Data.Either
import Data.IORef
import Debug.Trace
import Control.Monad.IO.Class
import Control.Monad
import Foreign.Storable.Generic.Plugin.Internal.Error
import Foreign.Storable.Generic.Plugin.Internal.Compile
import Foreign.Storable.Generic.Plugin.Internal.GroupTypes
import Foreign.Storable.Generic.Plugin.Internal.Helpers
import Foreign.Storable.Generic.Plugin.Internal.Predicates
import Foreign.Storable.Generic.Plugin.Internal.Types
--------------------
-- Grouping types --
--------------------
groupTypes_errors :: Flags -> [Error] -> CoreM ()
groupTypes_errors flags errors = do
let (Flags verb to_crash) = flags
crasher errs = case errs of
[] -> return ()
_ -> error "Crashing..."
print_header txt = case verb of
None -> empty
other -> text "Errors while grouping types - types not found for: "
$$ nest 4 txt
print_tyNotF verb id = case verb of
None -> empty
other -> ppr id $$ nest 13 (text "::") <+> ppr (varType id)
print_err err = case err of
TypeNotFound id -> print_tyNotF verb id
other -> pprError verb other
printer errs = case errs of
[] -> return ()
ls -> putMsg $ print_header (vcat (map print_err errs))
-- Do printing
-- Eventually crash.
printer errors
when to_crash $ crasher errors
groupTypes_info :: Flags -> [[Type]] -> CoreM ()
groupTypes_info flags types = do
let (Flags verb _) = flags
-- If verbosity is set, do the printing
print_header txt = case verb of
None -> empty
other -> text "GStorable instances will be optimised in the following order"
$+$ nest 4 txt
$+$ text ""
print_layer layer ix = int ix <> text ":" <+> fsep (punctuate comma $ map ppr layer)
-- Print groups of types
printer groups = case groups of
[] -> return ()
_ -> putMsg $ print_header (vcat $ zipWith print_layer groups [1..])
-- Do the printing
printer types
-- | Find GStorable identifiers, obtain their types, and calculate
-- the order of compilation.
groupTypes :: Flags -> IORef [[Type]] -> ModGuts -> CoreM ModGuts
groupTypes flags type_order_ref guts = do
let binds = mg_binds guts
-- Get GStorable ids that are fully defined.
all_ids = concatMap getIdsBind binds
with_typecheck = withTypeCheck getGStorableType isGStorableId
predicate id = and [ with_typecheck id
, not (hasGStorableConstraints $ varType id)
]
gstorable_ids = filter predicate all_ids
-- Now process them - different ids
-- will have different type signatures.
-- It is possible to fetch the types from them.
m_gstorable_types = map (getGStorableType.varType) gstorable_ids
-- Grab any errors related to types not found.
bad_types_zip id m_t = case m_t of
Nothing -> Just $ TypeNotFound id
Just _ -> Nothing
bad_types = catMaybes $ zipWith bad_types_zip gstorable_ids m_gstorable_types
-- type_list is used instead of type_set because Type has no uniquable instance.
type_list = [ t | Just t <- m_gstorable_types]
-- Calculate the type ordering.
(type_order,m_error) = calcGroupOrder type_list
groupTypes_info flags type_order
groupTypes_errors flags bad_types
liftIO $ writeIORef type_order_ref type_order
return guts
------------------------------------------------
-- Grouping and compiling GStorable CoreBinds --
------------------------------------------------
-- | Print errors related to CoreBind grouping.
-- Return the badly grouped bindings, and perhaps crash
-- the compiler.
grouping_errors :: Flags -- ^ Verbosity and ToCrash options
-> Maybe Error -- ^ The error
-> CoreM [CoreBind] -- ^ Recovered bindings.
grouping_errors flags m_err = do
let (Flags _ to_crash) = flags
verb = Some
crasher m_e = case m_e of
Nothing -> return ()
Just _ -> error "Crashing..."
print_header txt = case verb of
None -> empty
other -> text "Errors while grouping bindings: "
$$ nest 4 txt
printer m_err = case m_err of
Nothing -> return ()
Just err -> putMsg $ print_header (pprError verb err)
ungroup m_e = case m_e of
Just (OrderingFailedBinds _ rest) -> rest
_ -> []
printer m_err
when to_crash $ crasher m_err
return $ ungroup m_err
-- | Print the information related to found GStorable ids.
foundBinds_info :: Flags -- ^ Verbosity and ToCrash options
-> [Id] -- ^ GStorable ids.
-> CoreM ()
foundBinds_info flags ids = do
-- For Pretty printing
dyn_flags <- getDynFlags
let (Flags verb _) = flags
-- If verbosity is set, do the printing
print_header txt = case verb of
None -> empty
other -> text "The following bindings are to be optimised:"
$+$ nest 4 txt
print_binding id = ppr id
max_nest = maximum $ 0 : map (length.(showSDoc dyn_flags).ppr) ids
-- Print groups of types
printer the_groups = case the_groups of
[] -> return ()
_ -> putMsg $ print_header $ vcat (map print_group the_groups)
-- Use eqType for maybes
eqType_maybe (Just t1) (Just t2) = t1 `eqType` t2
eqType_maybe _ _ = False
-- group and sort the bindings
grouped = groupBy (\i1 i2 -> (getGStorableType $ varType i1) `eqType_maybe` (getGStorableType $ varType i2) ) ids
sorting = sortBy (\i1 i2 -> varName i1 `compare` varName i2)
sorted = map sorting grouped
-- print groups of bindings
print_group the_group = case the_group of
[] -> empty
(h:_) -> case getGStorableType $ varType h of
Just gtype -> ppr gtype
$+$ (fsep $ punctuate comma (map print_binding the_group))
Nothing -> ppr "Could not get the type of a binding:"
$+$ nest 4 (ppr h <+> text "::" <+> ppr (varType h))
-- Print the ids
printer sorted
-- | Do the optimisation for GStorable bindings.
gstorableSubstitution :: Flags -- ^ Verbosity and ToCrash options.
-> IORef [[Type]] -- ^ Reference to grouped types.
-> ModGuts -- ^ Information about compiled module.
-> CoreM ModGuts -- ^ Information about compiled module, with GStorable optimisations.
gstorableSubstitution flags type_order_ref guts = do
type_hierarchy <- liftIO $ readIORef type_order_ref
let binds = mg_binds guts
-- Get all GStorable binds.
-- Check whether the type has no constraints.
typeCheck t = if hasGStorableConstraints t
then Nothing
else getGStorableMethodType t
-- predicate = toIsBind (withTypeCheck typeCheck isGStorableMethodId)
predicate = toIsBind (isGStorableMethodId)
(gstorable_binds,rest) = partition predicate binds
-- Check if there are any recursives somehow
-- The plugin won't be able to handle them.
(nonrecs, recs) = partition isNonRecBind gstorable_binds
-- Group the gstorables by nestedness
(grouped_binds, m_err_group) = groupBinds type_hierarchy nonrecs
foundBinds_info flags $ concatMap getIdsBind $ concat grouped_binds
-- Check for errors
not_grouped <- grouping_errors flags m_err_group
-- Compile and replace gstorable bindings
new_gstorables <- compileGroups flags grouped_binds rest -- perhaps return errors here ?
return $ guts {mg_binds = concat [new_gstorables, not_grouped,recs,rest]}