{-# LANGUAGE CPP #-}
module X86.Regs (
        
        virtualRegSqueeze,
        realRegSqueeze,
        
        Imm(..),
        strImmLit,
        litToImm,
        
        AddrMode(..),
        addrOffset,
        
        spRel,
        argRegs,
        allArgRegs,
        allIntArgRegs,
        callClobberedRegs,
        instrClobberedRegs,
        allMachRegNos,
        classOfRealReg,
        showReg,
        
        EABase(..), EAIndex(..), addrModeRegs,
        eax, ebx, ecx, edx, esi, edi, ebp, esp,
        rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp,
        r8,  r9,  r10, r11, r12, r13, r14, r15,
        lastint,
        xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
        xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
        xmm,
        firstxmm, lastxmm,
        ripRel,
        allFPArgRegs,
        allocatableRegs
)
where
#include "nativeGen/NCG.h"
#include "HsVersions.h"
import GhcPrelude
import CodeGen.Platform
import Reg
import RegClass
import Cmm
import CLabel           ( CLabel )
import DynFlags
import Outputable
import Platform
import qualified Data.Array as A
{-# INLINE virtualRegSqueeze #-}
virtualRegSqueeze :: RegClass -> VirtualReg -> Int
virtualRegSqueeze cls vr
 = case cls of
        RcInteger
         -> case vr of
                VirtualRegI{}           -> 1
                VirtualRegHi{}          -> 1
                _other                  -> 0
        RcDouble
         -> case vr of
                VirtualRegD{}           -> 1
                VirtualRegF{}           -> 0
                _other                  -> 0
        _other -> 0
{-# INLINE realRegSqueeze #-}
realRegSqueeze :: RegClass -> RealReg -> Int
realRegSqueeze cls rr
 = case cls of
        RcInteger
         -> case rr of
                RealRegSingle regNo
                        | regNo < firstxmm -> 1
                        | otherwise     -> 0
                RealRegPair{}           -> 0
        RcDouble
         -> case rr of
                RealRegSingle regNo
                        | regNo >= firstxmm  -> 1
                        | otherwise     -> 0
                RealRegPair{}           -> 0
        _other -> 0
data Imm
  = ImmInt      Int
  | ImmInteger  Integer     
  | ImmCLbl     CLabel      
  | ImmLit      SDoc        
  | ImmIndex    CLabel Int
  | ImmFloat    Rational
  | ImmDouble   Rational
  | ImmConstantSum Imm Imm
  | ImmConstantDiff Imm Imm
strImmLit :: String -> Imm
strImmLit s = ImmLit (text s)
litToImm :: CmmLit -> Imm
litToImm (CmmInt i w)        = ImmInteger (narrowS w i)
                
                
                
litToImm (CmmFloat f W32)    = ImmFloat f
litToImm (CmmFloat f W64)    = ImmDouble f
litToImm (CmmLabel l)        = ImmCLbl l
litToImm (CmmLabelOff l off) = ImmIndex l off
litToImm (CmmLabelDiffOff l1 l2 off _)
                             = ImmConstantSum
                               (ImmConstantDiff (ImmCLbl l1) (ImmCLbl l2))
                               (ImmInt off)
litToImm _                   = panic "X86.Regs.litToImm: no match"
data AddrMode
        = AddrBaseIndex EABase EAIndex Displacement
        | ImmAddr Imm Int
data EABase       = EABaseNone  | EABaseReg Reg | EABaseRip
data EAIndex      = EAIndexNone | EAIndex Reg Int
type Displacement = Imm
addrOffset :: AddrMode -> Int -> Maybe AddrMode
addrOffset addr off
  = case addr of
      ImmAddr i off0      -> Just (ImmAddr i (off0 + off))
      AddrBaseIndex r i (ImmInt n) -> Just (AddrBaseIndex r i (ImmInt (n + off)))
      AddrBaseIndex r i (ImmInteger n)
        -> Just (AddrBaseIndex r i (ImmInt (fromInteger (n + toInteger off))))
      AddrBaseIndex r i (ImmCLbl lbl)
        -> Just (AddrBaseIndex r i (ImmIndex lbl off))
      AddrBaseIndex r i (ImmIndex lbl ix)
        -> Just (AddrBaseIndex r i (ImmIndex lbl (ix+off)))
      _ -> Nothing  
addrModeRegs :: AddrMode -> [Reg]
addrModeRegs (AddrBaseIndex b i _) =  b_regs ++ i_regs
  where
   b_regs = case b of { EABaseReg r -> [r]; _ -> [] }
   i_regs = case i of { EAIndex r _ -> [r]; _ -> [] }
addrModeRegs _ = []
spRel :: DynFlags
      -> Int 
      -> AddrMode
spRel dflags n
 | target32Bit (targetPlatform dflags)
    = AddrBaseIndex (EABaseReg esp) EAIndexNone (ImmInt n)
 | otherwise
    = AddrBaseIndex (EABaseReg rsp) EAIndexNone (ImmInt n)
firstxmm :: RegNo
firstxmm  = 16
lastxmm :: Platform -> RegNo
lastxmm platform
 | target32Bit platform = firstxmm + 7  
 | otherwise            = firstxmm + 15 
lastint :: Platform -> RegNo
lastint platform
 | target32Bit platform = 7 
 | otherwise            = 15
intregnos :: Platform -> [RegNo]
intregnos platform = [0 .. lastint platform]
xmmregnos :: Platform -> [RegNo]
xmmregnos platform = [firstxmm  .. lastxmm platform]
floatregnos :: Platform -> [RegNo]
floatregnos platform = xmmregnos platform
argRegs :: RegNo -> [Reg]
argRegs _       = panic "MachRegs.argRegs(x86): should not be used!"
allMachRegNos :: Platform -> [RegNo]
allMachRegNos platform = intregnos platform ++ floatregnos platform
{-# INLINE classOfRealReg #-}
classOfRealReg :: Platform -> RealReg -> RegClass
classOfRealReg platform reg
    = case reg of
        RealRegSingle i
            | i <= lastint platform -> RcInteger
            | i <= lastxmm platform -> RcDouble
            | otherwise             -> panic "X86.Reg.classOfRealReg registerSingle too high"
        _   -> panic "X86.Regs.classOfRealReg: RegPairs on this arch"
showReg :: Platform -> RegNo -> String
showReg platform n
        | n >= firstxmm && n <= lastxmm  platform = "%xmm" ++ show (n-firstxmm)
        | n >= 8   && n < firstxmm      = "%r" ++ show n
        | otherwise      = regNames platform A.! n
regNames :: Platform -> A.Array Int String
regNames platform
    = if target32Bit platform
      then A.listArray (0,8) ["%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp", "%esp"]
      else A.listArray (0,8) ["%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%rbp", "%rsp"]
eax, ebx, ecx, edx, esp, ebp, esi, edi :: Reg
eax   = regSingle 0
ebx   = regSingle 1
ecx   = regSingle 2
edx   = regSingle 3
esi   = regSingle 4
edi   = regSingle 5
ebp   = regSingle 6
esp   = regSingle 7
rax, rbx, rcx, rdx, rsp, rbp, rsi, rdi,
  r8, r9, r10, r11, r12, r13, r14, r15,
  xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
  xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 :: Reg
rax   = regSingle 0
rbx   = regSingle 1
rcx   = regSingle 2
rdx   = regSingle 3
rsi   = regSingle 4
rdi   = regSingle 5
rbp   = regSingle 6
rsp   = regSingle 7
r8    = regSingle 8
r9    = regSingle 9
r10   = regSingle 10
r11   = regSingle 11
r12   = regSingle 12
r13   = regSingle 13
r14   = regSingle 14
r15   = regSingle 15
xmm0  = regSingle 16
xmm1  = regSingle 17
xmm2  = regSingle 18
xmm3  = regSingle 19
xmm4  = regSingle 20
xmm5  = regSingle 21
xmm6  = regSingle 22
xmm7  = regSingle 23
xmm8  = regSingle 24
xmm9  = regSingle 25
xmm10 = regSingle 26
xmm11 = regSingle 27
xmm12 = regSingle 28
xmm13 = regSingle 29
xmm14 = regSingle 30
xmm15 = regSingle 31
ripRel :: Displacement -> AddrMode
ripRel imm      = AddrBaseIndex EABaseRip EAIndexNone imm
 
xmm :: RegNo -> Reg
xmm n = regSingle (firstxmm+n)
callClobberedRegs       :: Platform -> [Reg]
callClobberedRegs platform
 | target32Bit platform = [eax,ecx,edx] ++ map regSingle (floatregnos platform)
 | platformOS platform == OSMinGW32
   = [rax,rcx,rdx,r8,r9,r10,r11]
   
   
   
   ++ map xmm [0  .. 5]
 | otherwise
    
    
    = [rax,rcx,rdx,rsi,rdi,r8,r9,r10,r11]
   ++ map regSingle (floatregnos platform)
allArgRegs :: Platform -> [(Reg, Reg)]
allArgRegs platform
 | platformOS platform == OSMinGW32 = zip [rcx,rdx,r8,r9]
                                          (map regSingle [firstxmm ..])
 | otherwise = panic "X86.Regs.allArgRegs: not defined for this arch"
allIntArgRegs :: Platform -> [Reg]
allIntArgRegs platform
 | (platformOS platform == OSMinGW32) || target32Bit platform
    = panic "X86.Regs.allIntArgRegs: not defined for this platform"
 | otherwise = [rdi,rsi,rdx,rcx,r8,r9]
allFPArgRegs :: Platform -> [Reg]
allFPArgRegs platform
 | platformOS platform == OSMinGW32
    = panic "X86.Regs.allFPArgRegs: not defined for this platform"
 | otherwise = map regSingle [firstxmm .. firstxmm + 7 ]
instrClobberedRegs :: Platform -> [Reg]
instrClobberedRegs platform
 | target32Bit platform = [ eax, ecx, edx ]
 | otherwise            = [ rax, rcx, rdx ]
allocatableRegs :: Platform -> [RealReg]
allocatableRegs platform
   = let isFree i = freeReg platform i
     in  map RealRegSingle $ filter isFree (allMachRegNos platform)