PageRenderTime 24ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ghc-7.0.4/compiler/nativeGen/RegAlloc/Linear/JoinToTargets.hs

http://picorec.googlecode.com/
Haskell | 341 lines | 158 code | 69 blank | 114 comment | 9 complexity | 3db58a342a37ea413f53c26436128425 MD5 | raw file
Possible License(s): BSD-3-Clause, BSD-2-Clause
  1. {-# OPTIONS -fno-warn-missing-signatures #-}
  2. -- | Handles joining of a jump instruction to its targets.
  3. -- The first time we encounter a jump to a particular basic block, we
  4. -- record the assignment of temporaries. The next time we encounter a
  5. -- jump to the same block, we compare our current assignment to the
  6. -- stored one. They might be different if spilling has occrred in one
  7. -- branch; so some fixup code will be required to match up the assignments.
  8. --
  9. module RegAlloc.Linear.JoinToTargets (
  10. joinToTargets
  11. )
  12. where
  13. import RegAlloc.Linear.State
  14. import RegAlloc.Linear.Base
  15. import RegAlloc.Linear.FreeRegs
  16. import RegAlloc.Liveness
  17. import Instruction
  18. import Reg
  19. import BlockId
  20. import Cmm hiding (RegSet)
  21. import Digraph
  22. import Outputable
  23. import Unique
  24. import UniqFM
  25. import UniqSet
  26. -- | For a jump instruction at the end of a block, generate fixup code so its
  27. -- vregs are in the correct regs for its destination.
  28. --
  29. joinToTargets
  30. :: Instruction instr
  31. => BlockMap RegSet -- ^ maps the unique of the blockid to the set of vregs
  32. -- that are known to be live on the entry to each block.
  33. -> BlockId -- ^ id of the current block
  34. -> instr -- ^ branch instr on the end of the source block.
  35. -> RegM ([NatBasicBlock instr] -- fresh blocks of fixup code.
  36. , instr) -- the original branch instruction, but maybe patched to jump
  37. -- to a fixup block first.
  38. joinToTargets block_live id instr
  39. -- we only need to worry about jump instructions.
  40. | not $ isJumpishInstr instr
  41. = return ([], instr)
  42. | otherwise
  43. = joinToTargets' block_live [] id instr (jumpDestsOfInstr instr)
  44. -----
  45. joinToTargets'
  46. :: Instruction instr
  47. => BlockMap RegSet -- ^ maps the unique of the blockid to the set of vregs
  48. -- that are known to be live on the entry to each block.
  49. -> [NatBasicBlock instr] -- ^ acc blocks of fixup code.
  50. -> BlockId -- ^ id of the current block
  51. -> instr -- ^ branch instr on the end of the source block.
  52. -> [BlockId] -- ^ branch destinations still to consider.
  53. -> RegM ( [NatBasicBlock instr]
  54. , instr)
  55. -- no more targets to consider. all done.
  56. joinToTargets' _ new_blocks _ instr []
  57. = return (new_blocks, instr)
  58. -- handle a branch target.
  59. joinToTargets' block_live new_blocks block_id instr (dest:dests)
  60. = do
  61. -- get the map of where the vregs are stored on entry to each basic block.
  62. block_assig <- getBlockAssigR
  63. -- get the assignment on entry to the branch instruction.
  64. assig <- getAssigR
  65. -- adjust the current assignment to remove any vregs that are not live
  66. -- on entry to the destination block.
  67. let Just live_set = lookupBlockEnv block_live dest
  68. let still_live uniq _ = uniq `elemUniqSet_Directly` live_set
  69. let adjusted_assig = filterUFM_Directly still_live assig
  70. -- and free up those registers which are now free.
  71. let to_free =
  72. [ r | (reg, loc) <- ufmToList assig
  73. , not (elemUniqSet_Directly reg live_set)
  74. , r <- regsOfLoc loc ]
  75. case lookupBlockEnv block_assig dest of
  76. Nothing
  77. -> joinToTargets_first
  78. block_live new_blocks block_id instr dest dests
  79. block_assig adjusted_assig to_free
  80. Just (_, dest_assig)
  81. -> joinToTargets_again
  82. block_live new_blocks block_id instr dest dests
  83. adjusted_assig dest_assig
  84. -- this is the first time we jumped to this block.
  85. joinToTargets_first block_live new_blocks block_id instr dest dests
  86. block_assig src_assig
  87. (to_free :: [RealReg])
  88. = do -- free up the regs that are not live on entry to this block.
  89. freeregs <- getFreeRegsR
  90. let freeregs' = foldr releaseReg freeregs to_free
  91. -- remember the current assignment on entry to this block.
  92. setBlockAssigR (extendBlockEnv block_assig dest
  93. (freeregs', src_assig))
  94. joinToTargets' block_live new_blocks block_id instr dests
  95. -- we've jumped to this block before
  96. joinToTargets_again
  97. block_live new_blocks block_id instr dest dests
  98. src_assig dest_assig
  99. -- the assignments already match, no problem.
  100. | ufmToList dest_assig == ufmToList src_assig
  101. = joinToTargets' block_live new_blocks block_id instr dests
  102. -- assignments don't match, need fixup code
  103. | otherwise
  104. = do
  105. -- make a graph of what things need to be moved where.
  106. let graph = makeRegMovementGraph src_assig dest_assig
  107. -- look for cycles in the graph. This can happen if regs need to be swapped.
  108. -- Note that we depend on the fact that this function does a
  109. -- bottom up traversal of the tree-like portions of the graph.
  110. --
  111. -- eg, if we have
  112. -- R1 -> R2 -> R3
  113. --
  114. -- ie move value in R1 to R2 and value in R2 to R3.
  115. --
  116. -- We need to do the R2 -> R3 move before R1 -> R2.
  117. --
  118. let sccs = stronglyConnCompFromEdgedVerticesR graph
  119. {- -- debugging
  120. pprTrace
  121. ("joinToTargets: making fixup code")
  122. (vcat [ text " in block: " <> ppr block_id
  123. , text " jmp instruction: " <> ppr instr
  124. , text " src assignment: " <> ppr src_assig
  125. , text " dest assignment: " <> ppr dest_assig
  126. , text " movement graph: " <> ppr graph
  127. , text " sccs of graph: " <> ppr sccs
  128. , text ""])
  129. (return ())
  130. -}
  131. delta <- getDeltaR
  132. fixUpInstrs_ <- mapM (handleComponent delta instr) sccs
  133. let fixUpInstrs = concat fixUpInstrs_
  134. -- make a new basic block containing the fixup code.
  135. -- A the end of the current block we will jump to the fixup one,
  136. -- then that will jump to our original destination.
  137. fixup_block_id <- getUniqueR
  138. let block = BasicBlock (BlockId fixup_block_id)
  139. $ fixUpInstrs ++ mkJumpInstr dest
  140. {- pprTrace
  141. ("joinToTargets: fixup code is:")
  142. (vcat [ ppr block
  143. , text ""])
  144. (return ())
  145. -}
  146. -- if we didn't need any fixups, then don't include the block
  147. case fixUpInstrs of
  148. [] -> joinToTargets' block_live new_blocks block_id instr dests
  149. -- patch the original branch instruction so it goes to our
  150. -- fixup block instead.
  151. _ -> let instr' = patchJumpInstr instr
  152. (\bid -> if bid == dest
  153. then BlockId fixup_block_id
  154. else dest)
  155. in joinToTargets' block_live (block : new_blocks) block_id instr' dests
  156. -- | Construct a graph of register\/spill movements.
  157. --
  158. -- Cyclic components seem to occur only very rarely.
  159. --
  160. -- We cut some corners by not handling memory-to-memory moves.
  161. -- This shouldn't happen because every temporary gets its own stack slot.
  162. --
  163. makeRegMovementGraph :: RegMap Loc -> RegMap Loc -> [(Unique, Loc, [Loc])]
  164. makeRegMovementGraph adjusted_assig dest_assig
  165. = let
  166. mkNodes src vreg
  167. = expandNode vreg src
  168. $ lookupWithDefaultUFM_Directly
  169. dest_assig
  170. (panic "RegAllocLinear.makeRegMovementGraph")
  171. vreg
  172. in [ node | (vreg, src) <- ufmToList adjusted_assig
  173. , node <- mkNodes src vreg ]
  174. -- | Expand out the destination, so InBoth destinations turn into
  175. -- a combination of InReg and InMem.
  176. -- The InBoth handling is a little tricky here. If the destination is
  177. -- InBoth, then we must ensure that the value ends up in both locations.
  178. -- An InBoth destination must conflict with an InReg or InMem source, so
  179. -- we expand an InBoth destination as necessary.
  180. --
  181. -- An InBoth source is slightly different: we only care about the register
  182. -- that the source value is in, so that we can move it to the destinations.
  183. --
  184. expandNode
  185. :: a
  186. -> Loc -- ^ source of move
  187. -> Loc -- ^ destination of move
  188. -> [(a, Loc, [Loc])]
  189. expandNode vreg loc@(InReg src) (InBoth dst mem)
  190. | src == dst = [(vreg, loc, [InMem mem])]
  191. | otherwise = [(vreg, loc, [InReg dst, InMem mem])]
  192. expandNode vreg loc@(InMem src) (InBoth dst mem)
  193. | src == mem = [(vreg, loc, [InReg dst])]
  194. | otherwise = [(vreg, loc, [InReg dst, InMem mem])]
  195. expandNode _ (InBoth _ src) (InMem dst)
  196. | src == dst = [] -- guaranteed to be true
  197. expandNode _ (InBoth src _) (InReg dst)
  198. | src == dst = []
  199. expandNode vreg (InBoth src _) dst
  200. = expandNode vreg (InReg src) dst
  201. expandNode vreg src dst
  202. | src == dst = []
  203. | otherwise = [(vreg, src, [dst])]
  204. -- | Generate fixup code for a particular component in the move graph
  205. -- This component tells us what values need to be moved to what
  206. -- destinations. We have eliminated any possibility of single-node
  207. -- cycles in expandNode above.
  208. --
  209. handleComponent
  210. :: Instruction instr
  211. => Int -> instr -> SCC (Unique, Loc, [Loc]) -> RegM [instr]
  212. -- If the graph is acyclic then we won't get the swapping problem below.
  213. -- In this case we can just do the moves directly, and avoid having to
  214. -- go via a spill slot.
  215. --
  216. handleComponent delta _ (AcyclicSCC (vreg, src, dsts))
  217. = mapM (makeMove delta vreg src) dsts
  218. -- Handle some cyclic moves.
  219. -- This can happen if we have two regs that need to be swapped.
  220. -- eg:
  221. -- vreg source loc dest loc
  222. -- (vreg1, InReg r1, [InReg r2])
  223. -- (vreg2, InReg r2, [InReg r1])
  224. --
  225. -- To avoid needing temp register, we just spill all the source regs, then
  226. -- reaload them into their destination regs.
  227. --
  228. -- Note that we can not have cycles that involve memory locations as
  229. -- sources as single destination because memory locations (stack slots)
  230. -- are allocated exclusively for a virtual register and therefore can not
  231. -- require a fixup.
  232. --
  233. handleComponent delta instr
  234. (CyclicSCC ( (vreg, InReg sreg, (InReg dreg: _)) : rest))
  235. -- dest list may have more than one element, if the reg is also InMem.
  236. = do
  237. -- spill the source into its slot
  238. (instrSpill, slot)
  239. <- spillR (RegReal sreg) vreg
  240. -- reload into destination reg
  241. instrLoad <- loadR (RegReal dreg) slot
  242. remainingFixUps <- mapM (handleComponent delta instr)
  243. (stronglyConnCompFromEdgedVerticesR rest)
  244. -- make sure to do all the reloads after all the spills,
  245. -- so we don't end up clobbering the source values.
  246. return ([instrSpill] ++ concat remainingFixUps ++ [instrLoad])
  247. handleComponent _ _ (CyclicSCC _)
  248. = panic "Register Allocator: handleComponent cyclic"
  249. -- | Move a vreg between these two locations.
  250. --
  251. makeMove
  252. :: Instruction instr
  253. => Int -- ^ current C stack delta.
  254. -> Unique -- ^ unique of the vreg that we're moving.
  255. -> Loc -- ^ source location.
  256. -> Loc -- ^ destination location.
  257. -> RegM instr -- ^ move instruction.
  258. makeMove _ vreg (InReg src) (InReg dst)
  259. = do recordSpill (SpillJoinRR vreg)
  260. return $ mkRegRegMoveInstr (RegReal src) (RegReal dst)
  261. makeMove delta vreg (InMem src) (InReg dst)
  262. = do recordSpill (SpillJoinRM vreg)
  263. return $ mkLoadInstr (RegReal dst) delta src
  264. makeMove delta vreg (InReg src) (InMem dst)
  265. = do recordSpill (SpillJoinRM vreg)
  266. return $ mkSpillInstr (RegReal src) delta dst
  267. -- we don't handle memory to memory moves.
  268. -- they shouldn't happen because we don't share stack slots between vregs.
  269. makeMove _ vreg src dst
  270. = panic $ "makeMove " ++ show vreg ++ " (" ++ show src ++ ") ("
  271. ++ show dst ++ ")"
  272. ++ " we don't handle mem->mem moves."