PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/nativeGen/RegAlloc/Linear/JoinToTargets.hs

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