PageRenderTime 99ms CodeModel.GetById 24ms RepoModel.GetById 2ms app.codeStats 1ms

/compiler/simplCore/SAT.lhs

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