-- | Register coalescing.

{-# OPTIONS -fno-warn-tabs #-}
-- The above warning supression flag is a temporary kludge.
-- While working on this module you are encouraged to remove it and
-- detab the module (please do the detabbing in a separate patch). See
--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces
-- for details

module RegAlloc.Graph.Coalesce (


import RegAlloc.Liveness
import Instruction
import Reg

import OldCmm
import Bag
import Digraph
import UniqFM
import UniqSet
import UniqSupply

import Data.List

-- | Do register coalescing on this top level thing
--	For Reg -> Reg moves, if the first reg dies at the same time the second reg is born
--	then the mov only serves to join live ranges. The two regs can be renamed to be 
--	the same and the move instruction safely erased.
	:: Instruction instr
	=> [LiveCmmDecl statics instr] 
	-> UniqSM [LiveCmmDecl statics instr]

regCoalesce code
 = do	
 	let joins	= foldl' unionBags emptyBag
			$ map slurpJoinMovs code

	let alloc	= foldl' buildAlloc emptyUFM 
			$ bagToList joins

	let patched	= map (patchEraseLive (sinkReg alloc)) code
	return patched

buildAlloc :: UniqFM Reg -> (Reg, Reg) -> UniqFM Reg
buildAlloc fm (r1, r2)
 = let	rmin	= min r1 r2
 	rmax	= max r1 r2
   in	addToUFM fm rmax rmin

sinkReg :: UniqFM Reg -> Reg -> Reg
sinkReg fm r
 = case lookupUFM fm r of
 	Nothing	-> r
	Just r'	-> sinkReg fm r'	

-- | Slurp out mov instructions that only serve to join live ranges.
--	During a mov, if the source reg dies and the destiation reg is born
--	then we can rename the two regs to the same thing and eliminate the move.
	:: Instruction instr
	=> LiveCmmDecl statics instr 
	-> Bag (Reg, Reg)

slurpJoinMovs live
	= slurpCmm emptyBag live
  	slurpCmm   rs  CmmData{}		    = rs
	slurpCmm   rs (CmmProc _ _ sccs) 	= foldl' slurpBlock rs (flattenSCCs sccs)
	slurpBlock rs (BasicBlock _ instrs)	= foldl' slurpLI    rs instrs
	slurpLI    rs (LiveInstr _	Nothing)    = rs
	slurpLI    rs (LiveInstr instr (Just live))
	 	| Just (r1, r2)	<- takeRegRegMoveInstr instr
		, elementOfUniqSet r1 $ liveDieRead live
		, elementOfUniqSet r2 $ liveBorn live

		-- only coalesce movs between two virtuals for now, else we end up with
		--	allocatable regs in the live regs list.. 
		, isVirtualReg r1 && isVirtualReg r2
		= consBag (r1, r2) rs
		| otherwise
		= rs