diff options
author | Bartosz Nitka <niteria@gmail.com> | 2016-05-11 07:47:47 -0700 |
---|---|---|
committer | Bartosz Nitka <niteria@gmail.com> | 2016-05-11 07:47:50 -0700 |
commit | 3edbd091341ab0ab60862ba18d3107f34c7fc876 (patch) | |
tree | e5497ddfaf4e827549c4a02c6ae1d3453b83634f /compiler | |
parent | 0e719885f53e20f2e14a94b32d858b47b516a8fc (diff) | |
download | haskell-3edbd091341ab0ab60862ba18d3107f34c7fc876.tar.gz |
Document SCC determinism
I've documented the guarantees that stronglyConnCompFromEdgedVertices
provides and commented on the call sites to explain why they are
OK from determinism standpoint. I've changed the functions to
nonDetUFM versions, so that it's explicit they could introduce
nondeterminism. I haven't defined container (VarSet, NameSet)
specific versions, so that we have less functions to worry about.
Test Plan: this is mostly just documentation,
it should have no runtime effect
Reviewers: bgamari, simonmar, austin, simonpj
Reviewed By: simonpj
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2194
GHC Trac Issues: #4012
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/basicTypes/NameEnv.hs | 10 | ||||
-rw-r--r-- | compiler/iface/MkIface.hs | 8 | ||||
-rw-r--r-- | compiler/rename/RnBinds.hs | 5 | ||||
-rw-r--r-- | compiler/rename/RnSource.hs | 7 | ||||
-rw-r--r-- | compiler/simplCore/OccurAnal.hs | 11 | ||||
-rw-r--r-- | compiler/typecheck/TcBinds.hs | 6 | ||||
-rw-r--r-- | compiler/typecheck/TcEvidence.hs | 8 | ||||
-rw-r--r-- | compiler/typecheck/TcSMonad.hs | 6 | ||||
-rw-r--r-- | compiler/typecheck/TcTyDecls.hs | 23 | ||||
-rw-r--r-- | compiler/utils/Digraph.hs | 15 | ||||
-rw-r--r-- | compiler/utils/UniqFM.hs | 12 | ||||
-rw-r--r-- | compiler/vectorise/Vectorise/Type/Classify.hs | 6 |
12 files changed, 102 insertions, 15 deletions
diff --git a/compiler/basicTypes/NameEnv.hs b/compiler/basicTypes/NameEnv.hs index 674323d290..740c40605e 100644 --- a/compiler/basicTypes/NameEnv.hs +++ b/compiler/basicTypes/NameEnv.hs @@ -48,6 +48,16 @@ import Maybes ************************************************************************ -} +{- +Note [depAnal determinism] +~~~~~~~~~~~~~~~~~~~~~~~~~~ +depAnal is deterministic provided it gets the nodes in a deterministic order. +The order of lists that get_defs and get_uses return doesn't matter, as these +are only used to construct the edges, and stronglyConnCompFromEdgedVertices is +deterministic even when the edges are not in deterministic order as explained +in Note [Deterministic SCC] in Digraph. +-} + depAnal :: (node -> [Name]) -- Defs -> (node -> [Name]) -- Uses -> [node] diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs index 7f8397bf07..64c78319a6 100644 --- a/compiler/iface/MkIface.hs +++ b/compiler/iface/MkIface.hs @@ -106,6 +106,7 @@ import Maybes import Binary import Fingerprint import Exception +import UniqFM import Control.Monad import Data.Function @@ -396,7 +397,12 @@ addFingerprints hsc_env mb_old_fingerprint iface0 new_decls name_module n = ASSERT2( isExternalName n, ppr n ) nameModule n localOccs = map (getUnique . getParent . getOccName) . filter ((== this_mod) . name_module) - . nameSetElems + . nonDetEltsUFM + -- It's OK to use nonDetEltsUFM as localOccs is only + -- used to construct the edges and + -- stronglyConnCompFromEdgedVertices is deterministic + -- even with non-deterministic order of edges as + -- explained in Note [Deterministic SCC] in Digraph. where getParent occ = lookupOccEnv parent_map occ `orElse` occ -- maps OccNames to their parents in the current module. diff --git a/compiler/rename/RnBinds.hs b/compiler/rename/RnBinds.hs index 45ca7050ad..61f4dd8a3e 100644 --- a/compiler/rename/RnBinds.hs +++ b/compiler/rename/RnBinds.hs @@ -46,6 +46,7 @@ import Bag import Util import Outputable import FastString +import UniqFM import Maybes ( orElse ) import qualified GHC.LanguageExtensions as LangExt @@ -516,7 +517,9 @@ depAnalBinds binds_w_dus = (map get_binds sccs, map get_du sccs) where sccs = depAnal (\(_, defs, _) -> defs) - (\(_, _, uses) -> nameSetElems uses) + (\(_, _, uses) -> nonDetEltsUFM uses) + -- It's OK to use nonDetEltsUFM here as explained in + -- Note [depAnal determinism] in NameEnv. (bagToList binds_w_dus) get_binds (AcyclicSCC (bind, _, _)) = (NonRecursive, unitBag bind) diff --git a/compiler/rename/RnSource.hs b/compiler/rename/RnSource.hs index f92bae9f02..d91ce86cff 100644 --- a/compiler/rename/RnSource.hs +++ b/compiler/rename/RnSource.hs @@ -50,6 +50,7 @@ import Util ( debugIsOn, partitionWith ) import HscTypes ( HscEnv, hsc_dflags ) import ListSetOps ( findDupsEq, removeDups, equivClasses ) import Digraph ( SCC, flattenSCC, flattenSCCs, stronglyConnCompFromEdgedVertices ) +import UniqFM import qualified GHC.LanguageExtensions as LangExt import Control.Monad @@ -1339,8 +1340,12 @@ depAnalTyClDecls :: GlobalRdrEnv depAnalTyClDecls rdr_env ds_w_fvs = stronglyConnCompFromEdgedVertices edges where - edges = [ (d, tcdName (unLoc d), map (getParent rdr_env) (nameSetElems fvs)) + edges = [ (d, tcdName (unLoc d), map (getParent rdr_env) (nonDetEltsUFM fvs)) | (d, fvs) <- ds_w_fvs ] + -- It's OK to use nonDetEltsUFM here as + -- stronglyConnCompFromEdgedVertices is still deterministic + -- even if the edges are in nondeterministic order as explained + -- in Note [Deterministic SCC] in Digraph. toParents :: GlobalRdrEnv -> NameSet -> NameSet toParents rdr_env ns diff --git a/compiler/simplCore/OccurAnal.hs b/compiler/simplCore/OccurAnal.hs index 41a6f7fa71..b9edba7bd9 100644 --- a/compiler/simplCore/OccurAnal.hs +++ b/compiler/simplCore/OccurAnal.hs @@ -691,7 +691,10 @@ instance Outputable Details where makeNode :: OccEnv -> ImpRuleEdges -> VarSet -> (Var, CoreExpr) -> Node Details makeNode env imp_rule_edges bndr_set (bndr, rhs) - = (details, varUnique bndr, keysUFM node_fvs) + = (details, varUnique bndr, nonDetKeysUFM node_fvs) + -- It's OK to use nonDetKeysUFM here as stronglyConnCompFromEdgedVerticesR + -- is still deterministic with edges in nondeterministic order as + -- explained in Note [Deterministic SCC] in Digraph. where details = ND { nd_bndr = bndr , nd_rhs = rhs' @@ -800,7 +803,11 @@ occAnalRec (CyclicSCC nodes) (body_uds, binds) -- See Note [Choosing loop breakers] for loop_breaker_edges loop_breaker_edges = map mk_node tagged_nodes mk_node (details@(ND { nd_inl = inl_fvs }), k, _) - = (details, k, keysUFM (extendFvs_ rule_fv_env inl_fvs)) + = (details, k, nonDetKeysUFM (extendFvs_ rule_fv_env inl_fvs)) + -- It's OK to use nonDetKeysUFM here as + -- stronglyConnCompFromEdgedVerticesR is still deterministic with edges + -- in nondeterministic order as explained in + -- Note [Deterministic SCC] in Digraph. ------------------------------------ rule_fv_env :: IdEnv IdSet diff --git a/compiler/typecheck/TcBinds.hs b/compiler/typecheck/TcBinds.hs index 19b503400b..fc04ec9999 100644 --- a/compiler/typecheck/TcBinds.hs +++ b/compiler/typecheck/TcBinds.hs @@ -58,6 +58,7 @@ import Outputable import Type(mkStrLitTy, tidyOpenType) import PrelNames( mkUnboundName, gHC_PRIM, ipClassName ) import TcValidity (checkValidType) +import UniqFM import qualified GHC.LanguageExtensions as LangExt import Control.Monad @@ -498,10 +499,13 @@ type BKey = Int -- Just number off the bindings mkEdges :: TcSigFun -> LHsBinds Name -> [Node BKey (LHsBind Name)] -- See Note [Polymorphic recursion] in HsBinds. mkEdges sig_fn binds - = [ (bind, key, [key | n <- nameSetElems (bind_fvs (unLoc bind)), + = [ (bind, key, [key | n <- nonDetEltsUFM (bind_fvs (unLoc bind)), Just key <- [lookupNameEnv key_map n], no_sig n ]) | (bind, key) <- keyd_binds ] + -- It's OK to use nonDetEltsUFM here as stronglyConnCompFromEdgedVertices + -- is still deterministic even if the edges are in nondeterministic order + -- as explained in Note [Deterministic SCC] in Digraph. where no_sig :: Name -> Bool no_sig n = noCompleteSig (sig_fn n) diff --git a/compiler/typecheck/TcEvidence.hs b/compiler/typecheck/TcEvidence.hs index c4d02d8248..b86e3f4678 100644 --- a/compiler/typecheck/TcEvidence.hs +++ b/compiler/typecheck/TcEvidence.hs @@ -62,6 +62,7 @@ import Outputable import FastString import SrcLoc import Data.IORef( IORef ) +import UniqFM {- Note [TcCoercions] @@ -695,8 +696,11 @@ sccEvBinds bs = stronglyConnCompFromEdgedVertices edges mk_node :: EvBind -> (EvBind, EvVar, [EvVar]) mk_node b@(EvBind { eb_lhs = var, eb_rhs = term }) - = (b, var, varSetElems (evVarsOfTerm term `unionVarSet` - coVarsOfType (varType var))) + = (b, var, nonDetEltsUFM (evVarsOfTerm term `unionVarSet` + coVarsOfType (varType var))) + -- It's OK to use nonDetEltsUFM here as stronglyConnCompFromEdgedVertices + -- is still deterministic even if the edges are in nondeterministic order + -- as explained in Note [Deterministic SCC] in Digraph. evVarsOfCallStack :: EvCallStack -> VarSet evVarsOfCallStack cs = case cs of diff --git a/compiler/typecheck/TcSMonad.hs b/compiler/typecheck/TcSMonad.hs index 10fd0cce5f..606e3c19e8 100644 --- a/compiler/typecheck/TcSMonad.hs +++ b/compiler/typecheck/TcSMonad.hs @@ -2479,8 +2479,12 @@ checkForCyclicBinds ev_binds is_co_bind (EvBind { eb_lhs = b }) = isEqPred (varType b) edges :: [(EvBind, EvVar, [EvVar])] - edges = [ (bind, bndr, varSetElems (evVarsOfTerm rhs)) + edges = [ (bind, bndr, nonDetEltsUFM (evVarsOfTerm rhs)) | bind@(EvBind { eb_lhs = bndr, eb_rhs = rhs}) <- bagToList ev_binds ] + -- It's OK to use nonDetEltsUFM here as + -- stronglyConnCompFromEdgedVertices is still deterministic even + -- if the edges are in nondeterministic order as explained in + -- Note [Deterministic SCC] in Digraph. #endif nestImplicTcS :: Maybe EvBindsVar -> TyCoVarSet -- bound in this implication diff --git a/compiler/typecheck/TcTyDecls.hs b/compiler/typecheck/TcTyDecls.hs index 62933b5923..2d6637ea29 100644 --- a/compiler/typecheck/TcTyDecls.hs +++ b/compiler/typecheck/TcTyDecls.hs @@ -60,6 +60,7 @@ import Data.List import Bag import FastString import FV +import UniqFM import Control.Monad @@ -130,9 +131,13 @@ synonymTyConsOfType ty -} mkSynEdges :: [LTyClDecl Name] -> [(LTyClDecl Name, Name, [Name])] -mkSynEdges syn_decls = [ (ldecl, name, nameSetElems fvs) +mkSynEdges syn_decls = [ (ldecl, name, nonDetEltsUFM fvs) | ldecl@(L _ (SynDecl { tcdLName = L _ name , tcdFVs = fvs })) <- syn_decls ] + -- It's OK to use nonDetEltsUFM here as + -- stronglyConnCompFromEdgedVertices is still deterministic even + -- if the edges are in nondeterministic order as explained in + -- Note [Deterministic SCC] in Digraph. calcSynCycles :: [LTyClDecl Name] -> [SCC (LTyClDecl Name)] calcSynCycles = stronglyConnCompFromEdgedVertices . mkSynEdges @@ -419,8 +424,10 @@ calcRecFlags boot_details is_boot mrole_env all_tycons nt_edges = [(t, mk_nt_edges t) | t <- new_tycons] mk_nt_edges nt -- Invariant: nt is a newtype - = [ tc | tc <- nameEnvElts (tyConsOfType (new_tc_rhs nt)) + = [ tc | tc <- nonDetEltsUFM (tyConsOfType (new_tc_rhs nt)) -- tyConsOfType looks through synonyms + -- It's OK to use nonDetEltsUFM here, see + -- Note [findLoopBreakers determinism]. , tc `elem` new_tycons ] -- If not (tc `elem` new_tycons) we know that either it's a local *data* type, -- or it's imported. Either way, it can't form part of a newtype cycle @@ -433,7 +440,9 @@ calcRecFlags boot_details is_boot mrole_env all_tycons mk_prod_edges tc -- Invariant: tc is a product tycon = concatMap (mk_prod_edges1 tc) (dataConOrigArgTys (head (tyConDataCons tc))) - mk_prod_edges1 ptc ty = concatMap (mk_prod_edges2 ptc) (nameEnvElts (tyConsOfType ty)) + mk_prod_edges1 ptc ty = concatMap (mk_prod_edges2 ptc) (nonDetEltsUFM (tyConsOfType ty)) + -- It's OK to use nonDetEltsUFM here, see + -- Note [findLoopBreakers determinism]. mk_prod_edges2 ptc tc | tc `elem` prod_tycons = [tc] -- Local product @@ -447,6 +456,14 @@ calcRecFlags boot_details is_boot mrole_env all_tycons new_tc_rhs :: TyCon -> Type new_tc_rhs tc = snd (newTyConRhs tc) -- Ignore the type variables +{- +Note [findLoopBreakers determinism] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The order of edges doesn't matter for determinism here as explained in +Note [Deterministic SCC] in Digraph. It's enough for the order of nodes +to be deterministic. +-} + findLoopBreakers :: [(TyCon, [TyCon])] -> [Name] -- Finds a set of tycons that cut all loops findLoopBreakers deps diff --git a/compiler/utils/Digraph.hs b/compiler/utils/Digraph.hs index 2c90c1e41e..1d6ef24e61 100644 --- a/compiler/utils/Digraph.hs +++ b/compiler/utils/Digraph.hs @@ -94,6 +94,7 @@ type Node key payload = (payload, key, [key]) emptyGraph :: Graph a emptyGraph = Graph (array (1, 0) []) (error "emptyGraph") (const Nothing) +-- See Note [Deterministic SCC] graphFromEdgedVertices :: Ord key -- We only use Ord for efficiency, -- it doesn't effect the result, so @@ -200,6 +201,18 @@ depend on earlier ones, but not vice versa i.e. later components only have edges going from them to earlier ones. -} +{- +Note [Deterministic SCC] +~~~~~~~~~~~~~~~~~~~~~~~~ +stronglyConnCompFromEdgedVertices and stronglyConnCompFromEdgedVerticesR +provide a following guarantee: +Given a deterministically ordered list of nodes it returns a deterministically +ordered list of strongly connected components, where the list of vertices +in an SCC is also deterministically ordered. +Note that the order of edges doesn't need to be deterministic for this to work. +We use the order of nodes to normalize the order of edges. +-} + stronglyConnCompG :: Graph node -> [SCC node] stronglyConnCompG graph = decodeSccs graph forest where forest = {-# SCC "Digraph.scc" #-} scc (gr_int_graph graph) @@ -216,6 +229,7 @@ decodeSccs Graph { gr_int_graph = graph, gr_vertex_to_node = vertex_fn } forest -- The following two versions are provided for backwards compatability: +-- See Note [Deterministic SCC] stronglyConnCompFromEdgedVertices :: Ord key => [Node key payload] @@ -226,6 +240,7 @@ stronglyConnCompFromEdgedVertices -- The "R" interface is used when you expect to apply SCC to -- (some of) the result of SCC, so you dont want to lose the dependency info +-- See Note [Deterministic SCC] stronglyConnCompFromEdgedVerticesR :: Ord key => [Node key payload] diff --git a/compiler/utils/UniqFM.hs b/compiler/utils/UniqFM.hs index 590358ab40..2ff635268d 100644 --- a/compiler/utils/UniqFM.hs +++ b/compiler/utils/UniqFM.hs @@ -61,7 +61,7 @@ module UniqFM ( isNullUFM, lookupUFM, lookupUFM_Directly, lookupWithDefaultUFM, lookupWithDefaultUFM_Directly, - nonDetEltsUFM, eltsUFM, keysUFM, splitUFM, + nonDetEltsUFM, eltsUFM, nonDetKeysUFM, keysUFM, splitUFM, ufmToSet_Directly, ufmToList, ufmToIntMap, joinUFM, pprUniqFM, pprUFM, pluralUFM @@ -303,10 +303,18 @@ anyUFM p (UFM m) = M.fold ((||) . p) False m allUFM :: (elt -> Bool) -> UniqFM elt -> Bool allUFM p (UFM m) = M.fold ((&&) . p) True m --- See Note [Deterministic UniqFM] to learn about nondeterminism +-- See Note [Deterministic UniqFM] to learn about nondeterminism. +-- If you use this please provide a justification why it doesn't introduce +-- nondeterminism. nonDetEltsUFM :: UniqFM elt -> [elt] nonDetEltsUFM (UFM m) = M.elems m +-- See Note [Deterministic UniqFM] to learn about nondeterminism. +-- If you use this please provide a justification why it doesn't introduce +-- nondeterminism. +nonDetKeysUFM :: UniqFM elt -> [Unique] +nonDetKeysUFM (UFM m) = map getUnique $ M.keys m + ufmToIntMap :: UniqFM elt -> M.IntMap elt ufmToIntMap (UFM m) = m diff --git a/compiler/vectorise/Vectorise/Type/Classify.hs b/compiler/vectorise/Vectorise/Type/Classify.hs index 0140989233..75d43d4e36 100644 --- a/compiler/vectorise/Vectorise/Type/Classify.hs +++ b/compiler/vectorise/Vectorise/Type/Classify.hs @@ -98,8 +98,12 @@ type TyConGroup = ([TyCon], UniqSet TyCon) tyConGroups :: [TyCon] -> [TyConGroup] tyConGroups tcs = map mk_grp (stronglyConnCompFromEdgedVertices edges) where - edges = [((tc, ds), tc, uniqSetToList ds) | tc <- tcs + edges = [((tc, ds), tc, nonDetEltsUFM ds) | tc <- tcs , let ds = tyConsOfTyCon tc] + -- It's OK to use nonDetEltsUFM here as + -- stronglyConnCompFromEdgedVertices is still deterministic even + -- if the edges are in nondeterministic order as explained in + -- Note [Deterministic SCC] in Digraph. mk_grp (AcyclicSCC (tc, ds)) = ([tc], ds) mk_grp (CyclicSCC els) = (tcs, unionManyUniqSets dss) |