module RegAlloc.Linear.SPARC.FreeRegs
where
import GhcPrelude
import SPARC.Regs
import RegClass
import Reg
import CodeGen.Platform
import Outputable
import Platform
import Data.Word
import Data.Bits
import Data.Foldable (foldl')
data FreeRegs
        = FreeRegs
                !Word32         
                !Word32         
                !Word32         
instance Show FreeRegs where
        show = showFreeRegs
noFreeRegs :: FreeRegs
noFreeRegs = FreeRegs 0 0 0
initFreeRegs :: Platform -> FreeRegs
initFreeRegs platform
 =      foldl' (flip $ releaseReg platform) noFreeRegs allocatableRegs
getFreeRegs :: RegClass -> FreeRegs -> [RealReg]        
getFreeRegs cls (FreeRegs g f d)
        | RcInteger <- cls = map RealRegSingle                  $ go 1 g 1 0
        | RcFloat   <- cls = map RealRegSingle                  $ go 1 f 1 32
        | RcDouble  <- cls = map (\i -> RealRegPair i (i+1))    $ go 2 d 1 32
        | otherwise = pprPanic "RegAllocLinear.getFreeRegs: Bad register class " (ppr cls)
        where
                go _    _      0    _
                        = []
                go step bitmap mask ix
                        | bitmap .&. mask /= 0
                        = ix : (go step bitmap (mask `shiftL` step) $! ix + step)
                        | otherwise
                        = go step bitmap (mask `shiftL` step) $! ix + step
allocateReg :: Platform -> RealReg -> FreeRegs -> FreeRegs
allocateReg platform
         reg@(RealRegSingle r)
             (FreeRegs g f d)
        
        | not $ freeReg platform r
        = pprPanic "SPARC.FreeRegs.allocateReg: not allocating pinned reg" (ppr reg)
        
        | r <= 31
        = let   mask    = complement (bitMask r)
          in    FreeRegs
                        (g .&. mask)
                        f
                        d
        
        | r >= 32, r <= 63
        = let   mask    = complement (bitMask (r - 32))
                
                maskLow = if r `mod` 2 == 0
                                then complement (bitMask (r - 32))
                                else complement (bitMask (r - 32 - 1))
          in    FreeRegs
                        g
                        (f .&. mask)
                        (d .&. maskLow)
        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not allocating bad reg" (ppr reg)
allocateReg _
         reg@(RealRegPair r1 r2)
             (FreeRegs g f d)
        | r1 >= 32, r1 <= 63, r1 `mod` 2 == 0
        , r2 >= 32, r2 <= 63
        = let   mask1   = complement (bitMask (r1 - 32))
                mask2   = complement (bitMask (r2 - 32))
          in
                FreeRegs
                        g
                        ((f .&. mask1) .&. mask2)
                        (d .&. mask1)
        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not allocating bad reg" (ppr reg)
releaseReg :: Platform -> RealReg -> FreeRegs -> FreeRegs
releaseReg platform
         reg@(RealRegSingle r)
        regs@(FreeRegs g f d)
        
        | not $ freeReg platform r
        = regs
        
        | r <= 31
        = let   mask    = bitMask r
          in    FreeRegs (g .|. mask) f d
        
        | r >= 32, r <= 63
        = let   mask    = bitMask (r - 32)
                
                maskLow = if r `mod` 2 == 0
                                then bitMask (r - 32)
                                else bitMask (r - 32 - 1)
          in    FreeRegs
                        g
                        (f .|. mask)
                        (d .|. maskLow)
        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not releasing bad reg" (ppr reg)
releaseReg _
         reg@(RealRegPair r1 r2)
             (FreeRegs g f d)
        | r1 >= 32, r1 <= 63, r1 `mod` 2 == 0
        , r2 >= 32, r2 <= 63
        = let   mask1   = bitMask (r1 - 32)
                mask2   = bitMask (r2 - 32)
          in
                FreeRegs
                        g
                        ((f .|. mask1) .|. mask2)
                        (d .|. mask1)
        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not releasing bad reg" (ppr reg)
bitMask :: Int -> Word32
bitMask n       = 1 `shiftL` n
showFreeRegs :: FreeRegs -> String
showFreeRegs regs
        =  "FreeRegs\n"
        ++ "    integer: " ++ (show $ getFreeRegs RcInteger regs)       ++ "\n"
        ++ "      float: " ++ (show $ getFreeRegs RcFloat   regs)       ++ "\n"
        ++ "     double: " ++ (show $ getFreeRegs RcDouble  regs)       ++ "\n"