PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ghc-7.0.4/compiler/simplCore/SAT.lhs

http://picorec.googlecode.com/
Haskell | 424 lines | 318 code | 78 blank | 28 comment | 13 complexity | ab16071bcedd6a7bfbc2804e4749021f MD5 | raw file
Possible License(s): BSD-3-Clause, BSD-2-Clause
  1. %
  2. % (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
  3. %
  4. %************************************************************************
  5. Static Argument Transformation pass
  6. %************************************************************************
  7. May be seen as removing invariants from loops:
  8. Arguments of recursive functions that do not change in recursive
  9. calls are removed from the recursion, which is done locally
  10. and only passes the arguments which effectively change.
  11. Example:
  12. map = /\ ab -> \f -> \xs -> case xs of
  13. [] -> []
  14. (a:b) -> f a : map f b
  15. as map is recursively called with the same argument f (unmodified)
  16. we transform it to
  17. map = /\ ab -> \f -> \xs -> let map' ys = case ys of
  18. [] -> []
  19. (a:b) -> f a : map' b
  20. in map' xs
  21. Notice that for a compiler that uses lambda lifting this is
  22. useless as map' will be transformed back to what map was.
  23. We could possibly do the same for big lambdas, but we don't as
  24. they will eventually be removed in later stages of the compiler,
  25. therefore there is no penalty in keeping them.
  26. We only apply the SAT when the number of static args is > 2. This
  27. produces few bad cases. See
  28. should_transform
  29. in saTransform.
  30. Here are the headline nofib results:
  31. Size Allocs Runtime
  32. Min +0.0% -13.7% -21.4%
  33. Max +0.1% +0.0% +5.4%
  34. Geometric Mean +0.0% -0.2% -6.9%
  35. The previous patch, to fix polymorphic floatout demand signatures, is
  36. essential to make this work well!
  37. \begin{code}
  38. module SAT ( doStaticArgs ) where
  39. import Var
  40. import CoreSyn
  41. import CoreUtils
  42. import Type
  43. import Id
  44. import Name
  45. import VarEnv
  46. import UniqSupply
  47. import Util
  48. import UniqFM
  49. import VarSet
  50. import Unique
  51. import UniqSet
  52. import Outputable
  53. import Data.List
  54. import FastString
  55. #include "HsVersions.h"
  56. \end{code}
  57. \begin{code}
  58. doStaticArgs :: UniqSupply -> [CoreBind] -> [CoreBind]
  59. doStaticArgs us binds = snd $ mapAccumL sat_bind_threaded_us us binds
  60. where
  61. sat_bind_threaded_us us bind =
  62. let (us1, us2) = splitUniqSupply us
  63. in (us1, fst $ runSAT us2 (satBind bind emptyUniqSet))
  64. \end{code}
  65. \begin{code}
  66. -- We don't bother to SAT recursive groups since it can lead
  67. -- to massive code expansion: see Andre Santos' thesis for details.
  68. -- This means we only apply the actual SAT to Rec groups of one element,
  69. -- but we want to recurse into the others anyway to discover other binds
  70. satBind :: CoreBind -> IdSet -> SatM (CoreBind, IdSATInfo)
  71. satBind (NonRec binder expr) interesting_ids = do
  72. (expr', sat_info_expr, expr_app) <- satExpr expr interesting_ids
  73. return (NonRec binder expr', finalizeApp expr_app sat_info_expr)
  74. satBind (Rec [(binder, rhs)]) interesting_ids = do
  75. let interesting_ids' = interesting_ids `addOneToUniqSet` binder
  76. (rhs_binders, rhs_body) = collectBinders rhs
  77. (rhs_body', sat_info_rhs_body) <- satTopLevelExpr rhs_body interesting_ids'
  78. let sat_info_rhs_from_args = unitVarEnv binder (bindersToSATInfo rhs_binders)
  79. sat_info_rhs' = mergeIdSATInfo sat_info_rhs_from_args sat_info_rhs_body
  80. shadowing = binder `elementOfUniqSet` interesting_ids
  81. sat_info_rhs'' = if shadowing
  82. then sat_info_rhs' `delFromUFM` binder -- For safety
  83. else sat_info_rhs'
  84. bind' <- saTransformMaybe binder (lookupUFM sat_info_rhs' binder)
  85. rhs_binders rhs_body'
  86. return (bind', sat_info_rhs'')
  87. satBind (Rec pairs) interesting_ids = do
  88. let (binders, rhss) = unzip pairs
  89. rhss_SATed <- mapM (\e -> satTopLevelExpr e interesting_ids) rhss
  90. let (rhss', sat_info_rhss') = unzip rhss_SATed
  91. return (Rec (zipEqual "satBind" binders rhss'), mergeIdSATInfos sat_info_rhss')
  92. \end{code}
  93. \begin{code}
  94. data App = VarApp Id | TypeApp Type
  95. data Staticness a = Static a | NotStatic
  96. type IdAppInfo = (Id, SATInfo)
  97. type SATInfo = [Staticness App]
  98. type IdSATInfo = IdEnv SATInfo
  99. emptyIdSATInfo :: IdSATInfo
  100. emptyIdSATInfo = emptyUFM
  101. {-
  102. pprIdSATInfo id_sat_info = vcat (map pprIdAndSATInfo (Map.toList id_sat_info))
  103. where pprIdAndSATInfo (v, sat_info) = hang (ppr v <> colon) 4 (pprSATInfo sat_info)
  104. -}
  105. pprSATInfo :: SATInfo -> SDoc
  106. pprSATInfo staticness = hcat $ map pprStaticness staticness
  107. pprStaticness :: Staticness App -> SDoc
  108. pprStaticness (Static (VarApp _)) = ptext (sLit "SV")
  109. pprStaticness (Static (TypeApp _)) = ptext (sLit "ST")
  110. pprStaticness NotStatic = ptext (sLit "NS")
  111. mergeSATInfo :: SATInfo -> SATInfo -> SATInfo
  112. mergeSATInfo [] _ = []
  113. mergeSATInfo _ [] = []
  114. mergeSATInfo (NotStatic:statics) (_:apps) = NotStatic : mergeSATInfo statics apps
  115. mergeSATInfo (_:statics) (NotStatic:apps) = NotStatic : mergeSATInfo statics apps
  116. mergeSATInfo ((Static (VarApp v)):statics) ((Static (VarApp v')):apps) = (if v == v' then Static (VarApp v) else NotStatic) : mergeSATInfo statics apps
  117. mergeSATInfo ((Static (TypeApp t)):statics) ((Static (TypeApp t')):apps) = (if t `coreEqType` t' then Static (TypeApp t) else NotStatic) : mergeSATInfo statics apps
  118. mergeSATInfo l r = pprPanic "mergeSATInfo" $ ptext (sLit "Left:") <> pprSATInfo l <> ptext (sLit ", ")
  119. <> ptext (sLit "Right:") <> pprSATInfo r
  120. mergeIdSATInfo :: IdSATInfo -> IdSATInfo -> IdSATInfo
  121. mergeIdSATInfo = plusUFM_C mergeSATInfo
  122. mergeIdSATInfos :: [IdSATInfo] -> IdSATInfo
  123. mergeIdSATInfos = foldl' mergeIdSATInfo emptyIdSATInfo
  124. bindersToSATInfo :: [Id] -> SATInfo
  125. bindersToSATInfo vs = map (Static . binderToApp) vs
  126. where binderToApp v = if isId v
  127. then VarApp v
  128. else TypeApp $ mkTyVarTy v
  129. finalizeApp :: Maybe IdAppInfo -> IdSATInfo -> IdSATInfo
  130. finalizeApp Nothing id_sat_info = id_sat_info
  131. finalizeApp (Just (v, sat_info')) id_sat_info =
  132. let sat_info'' = case lookupUFM id_sat_info v of
  133. Nothing -> sat_info'
  134. Just sat_info -> mergeSATInfo sat_info sat_info'
  135. in extendVarEnv id_sat_info v sat_info''
  136. \end{code}
  137. \begin{code}
  138. satTopLevelExpr :: CoreExpr -> IdSet -> SatM (CoreExpr, IdSATInfo)
  139. satTopLevelExpr expr interesting_ids = do
  140. (expr', sat_info_expr, expr_app) <- satExpr expr interesting_ids
  141. return (expr', finalizeApp expr_app sat_info_expr)
  142. satExpr :: CoreExpr -> IdSet -> SatM (CoreExpr, IdSATInfo, Maybe IdAppInfo)
  143. satExpr var@(Var v) interesting_ids = do
  144. let app_info = if v `elementOfUniqSet` interesting_ids
  145. then Just (v, [])
  146. else Nothing
  147. return (var, emptyIdSATInfo, app_info)
  148. satExpr lit@(Lit _) _ = do
  149. return (lit, emptyIdSATInfo, Nothing)
  150. satExpr (Lam binders body) interesting_ids = do
  151. (body', sat_info, this_app) <- satExpr body interesting_ids
  152. return (Lam binders body', finalizeApp this_app sat_info, Nothing)
  153. satExpr (App fn arg) interesting_ids = do
  154. (fn', sat_info_fn, fn_app) <- satExpr fn interesting_ids
  155. let satRemainder = boring fn' sat_info_fn
  156. case fn_app of
  157. Nothing -> satRemainder Nothing
  158. Just (fn_id, fn_app_info) ->
  159. -- TODO: remove this use of append somehow (use a data structure with O(1) append but a left-to-right kind of interface)
  160. let satRemainderWithStaticness arg_staticness = satRemainder $ Just (fn_id, fn_app_info ++ [arg_staticness])
  161. in case arg of
  162. Type t -> satRemainderWithStaticness $ Static (TypeApp t)
  163. Var v -> satRemainderWithStaticness $ Static (VarApp v)
  164. _ -> satRemainderWithStaticness $ NotStatic
  165. where
  166. boring :: CoreExpr -> IdSATInfo -> Maybe IdAppInfo -> SatM (CoreExpr, IdSATInfo, Maybe IdAppInfo)
  167. boring fn' sat_info_fn app_info =
  168. do (arg', sat_info_arg, arg_app) <- satExpr arg interesting_ids
  169. let sat_info_arg' = finalizeApp arg_app sat_info_arg
  170. sat_info = mergeIdSATInfo sat_info_fn sat_info_arg'
  171. return (App fn' arg', sat_info, app_info)
  172. satExpr (Case expr bndr ty alts) interesting_ids = do
  173. (expr', sat_info_expr, expr_app) <- satExpr expr interesting_ids
  174. let sat_info_expr' = finalizeApp expr_app sat_info_expr
  175. zipped_alts' <- mapM satAlt alts
  176. let (alts', sat_infos_alts) = unzip zipped_alts'
  177. return (Case expr' bndr ty alts', mergeIdSATInfo sat_info_expr' (mergeIdSATInfos sat_infos_alts), Nothing)
  178. where
  179. satAlt (con, bndrs, expr) = do
  180. (expr', sat_info_expr) <- satTopLevelExpr expr interesting_ids
  181. return ((con, bndrs, expr'), sat_info_expr)
  182. satExpr (Let bind body) interesting_ids = do
  183. (body', sat_info_body, body_app) <- satExpr body interesting_ids
  184. (bind', sat_info_bind) <- satBind bind interesting_ids
  185. return (Let bind' body', mergeIdSATInfo sat_info_body sat_info_bind, body_app)
  186. satExpr (Note note expr) interesting_ids = do
  187. (expr', sat_info_expr, expr_app) <- satExpr expr interesting_ids
  188. return (Note note expr', sat_info_expr, expr_app)
  189. satExpr ty@(Type _) _ = do
  190. return (ty, emptyIdSATInfo, Nothing)
  191. satExpr (Cast expr coercion) interesting_ids = do
  192. (expr', sat_info_expr, expr_app) <- satExpr expr interesting_ids
  193. return (Cast expr' coercion, sat_info_expr, expr_app)
  194. \end{code}
  195. %************************************************************************
  196. Static Argument Transformation Monad
  197. %************************************************************************
  198. \begin{code}
  199. type SatM result = UniqSM result
  200. runSAT :: UniqSupply -> SatM a -> a
  201. runSAT = initUs_
  202. newUnique :: SatM Unique
  203. newUnique = getUniqueUs
  204. \end{code}
  205. %************************************************************************
  206. Static Argument Transformation Monad
  207. %************************************************************************
  208. To do the transformation, the game plan is to:
  209. 1. Create a small nonrecursive RHS that takes the
  210. original arguments to the function but discards
  211. the ones that are static and makes a call to the
  212. SATed version with the remainder. We intend that
  213. this will be inlined later, removing the overhead
  214. 2. Bind this nonrecursive RHS over the original body
  215. WITH THE SAME UNIQUE as the original body so that
  216. any recursive calls to the original now go via
  217. the small wrapper
  218. 3. Rebind the original function to a new one which contains
  219. our SATed function and just makes a call to it:
  220. we call the thing making this call the local body
  221. Example: transform this
  222. map :: forall a b. (a->b) -> [a] -> [b]
  223. map = /\ab. \(f:a->b) (as:[a]) -> body[map]
  224. to
  225. map :: forall a b. (a->b) -> [a] -> [b]
  226. map = /\ab. \(f:a->b) (as:[a]) ->
  227. letrec map' :: [a] -> [b]
  228. -- The "worker function
  229. map' = \(as:[a]) ->
  230. let map :: forall a' b'. (a -> b) -> [a] -> [b]
  231. -- The "shadow function
  232. map = /\a'b'. \(f':(a->b) (as:[a]).
  233. map' as
  234. in body[map]
  235. in map' as
  236. Note [Shadow binding]
  237. ~~~~~~~~~~~~~~~~~~~~~
  238. The calls to the inner map inside body[map] should get inlined
  239. by the local re-binding of 'map'. We call this the "shadow binding".
  240. But we can't use the original binder 'map' unchanged, because
  241. it might be exported, in which case the shadow binding won't be
  242. discarded as dead code after it is inlined.
  243. So we use a hack: we make a new SysLocal binder with the *same* unique
  244. as binder. (Another alternative would be to reset the export flag.)
  245. Note [Binder type capture]
  246. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  247. Notice that in the inner map (the "shadow function"), the static arguments
  248. are discarded -- it's as if they were underscores. Instead, mentions
  249. of these arguments (notably in the types of dynamic arguments) are bound
  250. by the *outer* lambdas of the main function. So we must make up fresh
  251. names for the static arguments so that they do not capture variables
  252. mentioned in the types of dynamic args.
  253. In the map example, the shadow function must clone the static type
  254. argument a,b, giving a',b', to ensure that in the \(as:[a]), the 'a'
  255. is bound by the outer forall. We clone f' too for consistency, but
  256. that doesn't matter either way because static Id arguments aren't
  257. mentioned in the shadow binding at all.
  258. If we don't we get something like this:
  259. [Exported]
  260. [Arity 3]
  261. GHC.Base.until =
  262. \ (@ a_aiK)
  263. (p_a6T :: a_aiK -> GHC.Bool.Bool)
  264. (f_a6V :: a_aiK -> a_aiK)
  265. (x_a6X :: a_aiK) ->
  266. letrec {
  267. sat_worker_s1aU :: a_aiK -> a_aiK
  268. []
  269. sat_worker_s1aU =
  270. \ (x_a6X :: a_aiK) ->
  271. let {
  272. sat_shadow_r17 :: forall a_a3O.
  273. (a_a3O -> GHC.Bool.Bool) -> (a_a3O -> a_a3O) -> a_a3O -> a_a3O
  274. []
  275. sat_shadow_r17 =
  276. \ (@ a_aiK)
  277. (p_a6T :: a_aiK -> GHC.Bool.Bool)
  278. (f_a6V :: a_aiK -> a_aiK)
  279. (x_a6X :: a_aiK) ->
  280. sat_worker_s1aU x_a6X } in
  281. case p_a6T x_a6X of wild_X3y [ALWAYS Dead Nothing] {
  282. GHC.Bool.False -> GHC.Base.until @ a_aiK p_a6T f_a6V (f_a6V x_a6X);
  283. GHC.Bool.True -> x_a6X
  284. }; } in
  285. sat_worker_s1aU x_a6X
  286. Where sat_shadow has captured the type variables of x_a6X etc as it has a a_aiK
  287. type argument. This is bad because it means the application sat_worker_s1aU x_a6X
  288. is not well typed.
  289. \begin{code}
  290. saTransformMaybe :: Id -> Maybe SATInfo -> [Id] -> CoreExpr -> SatM CoreBind
  291. saTransformMaybe binder maybe_arg_staticness rhs_binders rhs_body
  292. | Just arg_staticness <- maybe_arg_staticness
  293. , should_transform arg_staticness
  294. = saTransform binder arg_staticness rhs_binders rhs_body
  295. | otherwise
  296. = return (Rec [(binder, mkLams rhs_binders rhs_body)])
  297. where
  298. should_transform staticness = n_static_args > 1 -- THIS IS THE DECISION POINT
  299. where
  300. n_static_args = length (filter isStaticValue staticness)
  301. saTransform :: Id -> SATInfo -> [Id] -> CoreExpr -> SatM CoreBind
  302. saTransform binder arg_staticness rhs_binders rhs_body
  303. = do { shadow_lam_bndrs <- mapM clone binders_w_staticness
  304. ; uniq <- newUnique
  305. ; return (NonRec binder (mk_new_rhs uniq shadow_lam_bndrs)) }
  306. where
  307. -- Running example: foldr
  308. -- foldr \alpha \beta c n xs = e, for some e
  309. -- arg_staticness = [Static TypeApp, Static TypeApp, Static VarApp, Static VarApp, NonStatic]
  310. -- rhs_binders = [\alpha, \beta, c, n, xs]
  311. -- rhs_body = e
  312. binders_w_staticness = rhs_binders `zip` (arg_staticness ++ repeat NotStatic)
  313. -- Any extra args are assumed NotStatic
  314. non_static_args :: [Var]
  315. -- non_static_args = [xs]
  316. -- rhs_binders_without_type_capture = [\alpha', \beta', c, n, xs]
  317. non_static_args = [v | (v, NotStatic) <- binders_w_staticness]
  318. clone (bndr, NotStatic) = return bndr
  319. clone (bndr, _ ) = do { uniq <- newUnique
  320. ; return (setVarUnique bndr uniq) }
  321. -- new_rhs = \alpha beta c n xs ->
  322. -- let sat_worker = \xs -> let sat_shadow = \alpha' beta' c n xs ->
  323. -- sat_worker xs
  324. -- in e
  325. -- in sat_worker xs
  326. mk_new_rhs uniq shadow_lam_bndrs
  327. = mkLams rhs_binders $
  328. Let (Rec [(rec_body_bndr, rec_body)])
  329. local_body
  330. where
  331. local_body = mkVarApps (Var rec_body_bndr) non_static_args
  332. rec_body = mkLams non_static_args $
  333. Let (NonRec shadow_bndr shadow_rhs) rhs_body
  334. -- See Note [Binder type capture]
  335. shadow_rhs = mkLams shadow_lam_bndrs local_body
  336. -- nonrec_rhs = \alpha' beta' c n xs -> sat_worker xs
  337. rec_body_bndr = mkSysLocal (fsLit "sat_worker") uniq (exprType rec_body)
  338. -- rec_body_bndr = sat_worker
  339. -- See Note [Shadow binding]; make a SysLocal
  340. shadow_bndr = mkSysLocal (occNameFS (getOccName binder))
  341. (idUnique binder)
  342. (exprType shadow_rhs)
  343. isStaticValue :: Staticness App -> Bool
  344. isStaticValue (Static (VarApp _)) = True
  345. isStaticValue _ = False
  346. \end{code}