PageRenderTime 57ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/cmm/CmmCommonBlockElim.hs

http://github.com/ghc/ghc
Haskell | 302 lines | 193 code | 46 blank | 63 comment | 29 complexity | 6dce7c5e8e4e2ce183eea06327dc2f09 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, GPL-3.0
  1. {-# LANGUAGE GADTs, BangPatterns #-}
  2. module CmmCommonBlockElim
  3. ( elimCommonBlocks
  4. )
  5. where
  6. import BlockId
  7. import Cmm
  8. import CmmUtils
  9. import CmmSwitch (eqSwitchTargetWith)
  10. import CmmContFlowOpt
  11. -- import PprCmm ()
  12. import Prelude hiding (iterate, succ, unzip, zip)
  13. import Hoopl hiding (ChangeFlag)
  14. import Data.Bits
  15. import Data.Maybe (mapMaybe)
  16. import qualified Data.List as List
  17. import Data.Word
  18. import qualified Data.Map as M
  19. import Outputable
  20. import UniqFM
  21. import UniqDFM
  22. import qualified TrieMap as TM
  23. import Unique
  24. import Control.Arrow (first, second)
  25. -- -----------------------------------------------------------------------------
  26. -- Eliminate common blocks
  27. -- If two blocks are identical except for the label on the first node,
  28. -- then we can eliminate one of the blocks. To ensure that the semantics
  29. -- of the program are preserved, we have to rewrite each predecessor of the
  30. -- eliminated block to proceed with the block we keep.
  31. -- The algorithm iterates over the blocks in the graph,
  32. -- checking whether it has seen another block that is equal modulo labels.
  33. -- If so, then it adds an entry in a map indicating that the new block
  34. -- is made redundant by the old block.
  35. -- Otherwise, it is added to the useful blocks.
  36. -- To avoid comparing every block with every other block repeatedly, we group
  37. -- them by
  38. -- * a hash of the block, ignoring labels (explained below)
  39. -- * the list of outgoing labels
  40. -- The hash is invariant under relabeling, so we only ever compare within
  41. -- the same group of blocks.
  42. --
  43. -- The list of outgoing labels is updated as we merge blocks (that is why they
  44. -- are not included in the hash, which we want to calculate only once).
  45. --
  46. -- All in all, two blocks should never be compared if they have different
  47. -- hashes, and at most once otherwise. Previously, we were slower, and people
  48. -- rightfully complained: #10397
  49. -- TODO: Use optimization fuel
  50. elimCommonBlocks :: CmmGraph -> CmmGraph
  51. elimCommonBlocks g = replaceLabels env $ copyTicks env g
  52. where
  53. env = iterate mapEmpty blocks_with_key
  54. groups = groupByInt hash_block (postorderDfs g)
  55. blocks_with_key = [ [ (successors b, [b]) | b <- bs] | bs <- groups]
  56. -- Invariant: The blocks in the list are pairwise distinct
  57. -- (so avoid comparing them again)
  58. type DistinctBlocks = [CmmBlock]
  59. type Key = [Label]
  60. type Subst = BlockEnv BlockId
  61. -- The outer list groups by hash. We retain this grouping throughout.
  62. iterate :: Subst -> [[(Key, DistinctBlocks)]] -> Subst
  63. iterate subst blocks
  64. | mapNull new_substs = subst
  65. | otherwise = iterate subst' updated_blocks
  66. where
  67. grouped_blocks :: [[(Key, [DistinctBlocks])]]
  68. grouped_blocks = map groupByLabel blocks
  69. merged_blocks :: [[(Key, DistinctBlocks)]]
  70. (new_substs, merged_blocks) = List.mapAccumL (List.mapAccumL go) mapEmpty grouped_blocks
  71. where
  72. go !new_subst1 (k,dbs) = (new_subst1 `mapUnion` new_subst2, (k,db))
  73. where
  74. (new_subst2, db) = mergeBlockList subst dbs
  75. subst' = subst `mapUnion` new_substs
  76. updated_blocks = map (map (first (map (lookupBid subst')))) merged_blocks
  77. mergeBlocks :: Subst -> DistinctBlocks -> DistinctBlocks -> (Subst, DistinctBlocks)
  78. mergeBlocks subst existing new = go new
  79. where
  80. go [] = (mapEmpty, existing)
  81. go (b:bs) = case List.find (eqBlockBodyWith (eqBid subst) b) existing of
  82. -- This block is a duplicate. Drop it, and add it to the substitution
  83. Just b' -> first (mapInsert (entryLabel b) (entryLabel b')) $ go bs
  84. -- This block is not a duplicate, keep it.
  85. Nothing -> second (b:) $ go bs
  86. mergeBlockList :: Subst -> [DistinctBlocks] -> (Subst, DistinctBlocks)
  87. mergeBlockList _ [] = pprPanic "mergeBlockList" empty
  88. mergeBlockList subst (b:bs) = go mapEmpty b bs
  89. where
  90. go !new_subst1 b [] = (new_subst1, b)
  91. go !new_subst1 b1 (b2:bs) = go new_subst b bs
  92. where
  93. (new_subst2, b) = mergeBlocks subst b1 b2
  94. new_subst = new_subst1 `mapUnion` new_subst2
  95. -- -----------------------------------------------------------------------------
  96. -- Hashing and equality on blocks
  97. -- Below here is mostly boilerplate: hashing blocks ignoring labels,
  98. -- and comparing blocks modulo a label mapping.
  99. -- To speed up comparisons, we hash each basic block modulo jump labels.
  100. -- The hashing is a bit arbitrary (the numbers are completely arbitrary),
  101. -- but it should be fast and good enough.
  102. -- We want to get as many small buckets as possible, as comparing blocks is
  103. -- expensive. So include as much as possible in the hash. Ideally everything
  104. -- that is compared with (==) in eqBlockBodyWith.
  105. type HashCode = Int
  106. hash_block :: CmmBlock -> HashCode
  107. hash_block block =
  108. fromIntegral (foldBlockNodesB3 (hash_fst, hash_mid, hash_lst) block (0 :: Word32) .&. (0x7fffffff :: Word32))
  109. -- UniqFM doesn't like negative Ints
  110. where hash_fst _ h = h
  111. hash_mid m h = hash_node m + h `shiftL` 1
  112. hash_lst m h = hash_node m + h `shiftL` 1
  113. hash_node :: CmmNode O x -> Word32
  114. hash_node n | dont_care n = 0 -- don't care
  115. hash_node (CmmUnwind _ e) = hash_e e
  116. hash_node (CmmAssign r e) = hash_reg r + hash_e e
  117. hash_node (CmmStore e e') = hash_e e + hash_e e'
  118. hash_node (CmmUnsafeForeignCall t _ as) = hash_tgt t + hash_list hash_e as
  119. hash_node (CmmBranch _) = 23 -- NB. ignore the label
  120. hash_node (CmmCondBranch p _ _ _) = hash_e p
  121. hash_node (CmmCall e _ _ _ _ _) = hash_e e
  122. hash_node (CmmForeignCall t _ _ _ _ _ _) = hash_tgt t
  123. hash_node (CmmSwitch e _) = hash_e e
  124. hash_node _ = error "hash_node: unknown Cmm node!"
  125. hash_reg :: CmmReg -> Word32
  126. hash_reg (CmmLocal localReg) = hash_unique localReg -- important for performance, see #10397
  127. hash_reg (CmmGlobal _) = 19
  128. hash_e :: CmmExpr -> Word32
  129. hash_e (CmmLit l) = hash_lit l
  130. hash_e (CmmLoad e _) = 67 + hash_e e
  131. hash_e (CmmReg r) = hash_reg r
  132. hash_e (CmmMachOp _ es) = hash_list hash_e es -- pessimal - no operator check
  133. hash_e (CmmRegOff r i) = hash_reg r + cvt i
  134. hash_e (CmmStackSlot _ _) = 13
  135. hash_lit :: CmmLit -> Word32
  136. hash_lit (CmmInt i _) = fromInteger i
  137. hash_lit (CmmFloat r _) = truncate r
  138. hash_lit (CmmVec ls) = hash_list hash_lit ls
  139. hash_lit (CmmLabel _) = 119 -- ugh
  140. hash_lit (CmmLabelOff _ i) = cvt $ 199 + i
  141. hash_lit (CmmLabelDiffOff _ _ i) = cvt $ 299 + i
  142. hash_lit (CmmBlock _) = 191 -- ugh
  143. hash_lit (CmmHighStackMark) = cvt 313
  144. hash_tgt (ForeignTarget e _) = hash_e e
  145. hash_tgt (PrimTarget _) = 31 -- lots of these
  146. hash_list f = foldl (\z x -> f x + z) (0::Word32)
  147. cvt = fromInteger . toInteger
  148. hash_unique :: Uniquable a => a -> Word32
  149. hash_unique = cvt . getKey . getUnique
  150. -- | Ignore these node types for equality
  151. dont_care :: CmmNode O x -> Bool
  152. dont_care CmmComment {} = True
  153. dont_care CmmTick {} = True
  154. dont_care _other = False
  155. -- Utilities: equality and substitution on the graph.
  156. -- Given a map ``subst'' from BlockID -> BlockID, we define equality.
  157. eqBid :: BlockEnv BlockId -> BlockId -> BlockId -> Bool
  158. eqBid subst bid bid' = lookupBid subst bid == lookupBid subst bid'
  159. lookupBid :: BlockEnv BlockId -> BlockId -> BlockId
  160. lookupBid subst bid = case mapLookup bid subst of
  161. Just bid -> lookupBid subst bid
  162. Nothing -> bid
  163. -- Middle nodes and expressions can contain BlockIds, in particular in
  164. -- CmmStackSlot and CmmBlock, so we have to use a special equality for
  165. -- these.
  166. --
  167. eqMiddleWith :: (BlockId -> BlockId -> Bool)
  168. -> CmmNode O O -> CmmNode O O -> Bool
  169. eqMiddleWith eqBid (CmmAssign r1 e1) (CmmAssign r2 e2)
  170. = r1 == r2 && eqExprWith eqBid e1 e2
  171. eqMiddleWith eqBid (CmmStore l1 r1) (CmmStore l2 r2)
  172. = eqExprWith eqBid l1 l2 && eqExprWith eqBid r1 r2
  173. eqMiddleWith eqBid (CmmUnsafeForeignCall t1 r1 a1)
  174. (CmmUnsafeForeignCall t2 r2 a2)
  175. = t1 == t2 && r1 == r2 && and (zipWith (eqExprWith eqBid) a1 a2)
  176. eqMiddleWith _ _ _ = False
  177. eqExprWith :: (BlockId -> BlockId -> Bool)
  178. -> CmmExpr -> CmmExpr -> Bool
  179. eqExprWith eqBid = eq
  180. where
  181. CmmLit l1 `eq` CmmLit l2 = eqLit l1 l2
  182. CmmLoad e1 _ `eq` CmmLoad e2 _ = e1 `eq` e2
  183. CmmReg r1 `eq` CmmReg r2 = r1==r2
  184. CmmRegOff r1 i1 `eq` CmmRegOff r2 i2 = r1==r2 && i1==i2
  185. CmmMachOp op1 es1 `eq` CmmMachOp op2 es2 = op1==op2 && es1 `eqs` es2
  186. CmmStackSlot a1 i1 `eq` CmmStackSlot a2 i2 = eqArea a1 a2 && i1==i2
  187. _e1 `eq` _e2 = False
  188. xs `eqs` ys = and (zipWith eq xs ys)
  189. eqLit (CmmBlock id1) (CmmBlock id2) = eqBid id1 id2
  190. eqLit l1 l2 = l1 == l2
  191. eqArea Old Old = True
  192. eqArea (Young id1) (Young id2) = eqBid id1 id2
  193. eqArea _ _ = False
  194. -- Equality on the body of a block, modulo a function mapping block
  195. -- IDs to block IDs.
  196. eqBlockBodyWith :: (BlockId -> BlockId -> Bool) -> CmmBlock -> CmmBlock -> Bool
  197. eqBlockBodyWith eqBid block block'
  198. {-
  199. | equal = pprTrace "equal" (vcat [ppr block, ppr block']) True
  200. | otherwise = pprTrace "not equal" (vcat [ppr block, ppr block']) False
  201. -}
  202. = equal
  203. where (_,m,l) = blockSplit block
  204. nodes = filter (not . dont_care) (blockToList m)
  205. (_,m',l') = blockSplit block'
  206. nodes' = filter (not . dont_care) (blockToList m')
  207. equal = and (zipWith (eqMiddleWith eqBid) nodes nodes') &&
  208. eqLastWith eqBid l l'
  209. eqLastWith :: (BlockId -> BlockId -> Bool) -> CmmNode O C -> CmmNode O C -> Bool
  210. eqLastWith eqBid (CmmBranch bid1) (CmmBranch bid2) = eqBid bid1 bid2
  211. eqLastWith eqBid (CmmCondBranch c1 t1 f1 l1) (CmmCondBranch c2 t2 f2 l2) =
  212. c1 == c2 && l1 == l2 && eqBid t1 t2 && eqBid f1 f2
  213. eqLastWith eqBid (CmmCall t1 c1 g1 a1 r1 u1) (CmmCall t2 c2 g2 a2 r2 u2) =
  214. t1 == t2 && eqMaybeWith eqBid c1 c2 && a1 == a2 && r1 == r2 && u1 == u2 && g1 == g2
  215. eqLastWith eqBid (CmmSwitch e1 ids1) (CmmSwitch e2 ids2) =
  216. e1 == e2 && eqSwitchTargetWith eqBid ids1 ids2
  217. eqLastWith _ _ _ = False
  218. eqMaybeWith :: (a -> b -> Bool) -> Maybe a -> Maybe b -> Bool
  219. eqMaybeWith eltEq (Just e) (Just e') = eltEq e e'
  220. eqMaybeWith _ Nothing Nothing = True
  221. eqMaybeWith _ _ _ = False
  222. -- | Given a block map, ensure that all "target" blocks are covered by
  223. -- the same ticks as the respective "source" blocks. This not only
  224. -- means copying ticks, but also adjusting tick scopes where
  225. -- necessary.
  226. copyTicks :: BlockEnv BlockId -> CmmGraph -> CmmGraph
  227. copyTicks env g
  228. | mapNull env = g
  229. | otherwise = ofBlockMap (g_entry g) $ mapMap copyTo blockMap
  230. where -- Reverse block merge map
  231. blockMap = toBlockMap g
  232. revEnv = mapFoldWithKey insertRev M.empty env
  233. insertRev k x = M.insertWith (const (k:)) x [k]
  234. -- Copy ticks and scopes into the given block
  235. copyTo block = case M.lookup (entryLabel block) revEnv of
  236. Nothing -> block
  237. Just ls -> foldr copy block $ mapMaybe (flip mapLookup blockMap) ls
  238. copy from to =
  239. let ticks = blockTicks from
  240. CmmEntry _ scp0 = firstNode from
  241. (CmmEntry lbl scp1, code) = blockSplitHead to
  242. in CmmEntry lbl (combineTickScopes scp0 scp1) `blockJoinHead`
  243. foldr blockCons code (map CmmTick ticks)
  244. -- Group by [Label]
  245. groupByLabel :: [(Key, a)] -> [(Key, [a])]
  246. groupByLabel = go (TM.emptyTM :: TM.ListMap UniqDFM a)
  247. where
  248. go !m [] = TM.foldTM (:) m []
  249. go !m ((k,v) : entries) = go (TM.alterTM k' adjust m) entries
  250. where k' = map getUnique k
  251. adjust Nothing = Just (k,[v])
  252. adjust (Just (_,vs)) = Just (k,v:vs)
  253. groupByInt :: (a -> Int) -> [a] -> [[a]]
  254. groupByInt f xs = nonDetEltsUFM $ List.foldl' go emptyUFM xs
  255. -- See Note [Unique Determinism and code generation]
  256. where go m x = alterUFM (Just . maybe [x] (x:)) m (f x)