#include "HsVersions.h"
module GHC.CmmToAsm.PPC.Instr
( Instr(..)
, RI(..)
, archWordFormat
, stackFrameHeaderSize
, maxSpillSlots
, allocMoreStack
, makeFarBranches
, mkJumpInstr
, mkLoadInstr
, mkSpillInstr
, patchJumpInstr
, patchRegsOfInstr
, jumpDestsOfInstr
, takeRegRegMoveInstr
, takeDeltaInstr
, mkRegRegMoveInstr
, mkStackAllocInstr
, mkStackDeallocInstr
, regUsageOfInstr
, isJumpishInstr
, isMetaInstr
)
where
import GHC.Prelude
import GHC.CmmToAsm.PPC.Regs
import GHC.CmmToAsm.PPC.Cond
import GHC.CmmToAsm.Types
import GHC.CmmToAsm.Instr (RegUsage(..), noUsage)
import GHC.CmmToAsm.Format
import GHC.CmmToAsm.Reg.Target
import GHC.CmmToAsm.Config
import GHC.Platform.Reg.Class
import GHC.Platform.Reg
import GHC.Platform.Regs
import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Collections
import GHC.Cmm.Dataflow.Label
import GHC.Cmm
import GHC.Cmm.Info
import GHC.Data.FastString
import GHC.Cmm.CLabel
import GHC.Utils.Panic
import GHC.Platform
import GHC.Types.Unique.FM (listToUFM, lookupUFM)
import GHC.Types.Unique.Supply
import Control.Monad (replicateM)
import Data.Maybe (fromMaybe)
archWordFormat :: Bool -> Format
archWordFormat is32Bit
| is32Bit = II32
| otherwise = II64
mkStackAllocInstr :: Platform -> Int -> [Instr]
mkStackAllocInstr platform amount
= mkStackAllocInstr' platform (amount)
mkStackDeallocInstr :: Platform -> Int -> [Instr]
mkStackDeallocInstr platform amount
= mkStackAllocInstr' platform amount
mkStackAllocInstr' :: Platform -> Int -> [Instr]
mkStackAllocInstr' platform amount
| fits16Bits amount
= [ LD fmt r0 (AddrRegImm sp zero)
, STU fmt r0 (AddrRegImm sp immAmount)
]
| otherwise
= [ LD fmt r0 (AddrRegImm sp zero)
, ADDIS tmp sp (HA immAmount)
, ADD tmp tmp (RIImm (LO immAmount))
, STU fmt r0 (AddrRegReg sp tmp)
]
where
fmt = intFormat $ widthFromBytes (platformWordSizeInBytes platform)
zero = ImmInt 0
tmp = tmpReg platform
immAmount = ImmInt amount
allocMoreStack
:: Platform
-> Int
-> NatCmmDecl statics GHC.CmmToAsm.PPC.Instr.Instr
-> UniqSM (NatCmmDecl statics GHC.CmmToAsm.PPC.Instr.Instr, [(BlockId,BlockId)])
allocMoreStack _ _ top@(CmmData _ _) = return (top,[])
allocMoreStack platform slots (CmmProc info lbl live (ListGraph code)) = do
let
infos = mapKeys info
entries = case code of
[] -> infos
BasicBlock entry _ : _
| entry `elem` infos -> infos
| otherwise -> entry : infos
uniqs <- replicateM (length entries) getUniqueM
let
delta = ((x + stackAlign 1) `quot` stackAlign) * stackAlign
where x = slots * spillSlotSize
alloc = mkStackAllocInstr platform delta
dealloc = mkStackDeallocInstr platform delta
retargetList = (zip entries (map mkBlockId uniqs))
new_blockmap :: LabelMap BlockId
new_blockmap = mapFromList retargetList
insert_stack_insns (BasicBlock id insns)
| Just new_blockid <- mapLookup id new_blockmap
= [ BasicBlock id $ alloc ++ [BCC ALWAYS new_blockid Nothing]
, BasicBlock new_blockid block'
]
| otherwise
= [ BasicBlock id block' ]
where
block' = foldr insert_dealloc [] insns
insert_dealloc insn r
= case insn of
JMP _ _ -> dealloc ++ (insn : r)
BCTR [] Nothing _ -> dealloc ++ (insn : r)
BCTR ids label rs -> BCTR (map (fmap retarget) ids) label rs : r
BCCFAR cond b p -> BCCFAR cond (retarget b) p : r
BCC cond b p -> BCC cond (retarget b) p : r
_ -> insn : r
retarget :: BlockId -> BlockId
retarget b
= fromMaybe b (mapLookup b new_blockmap)
new_code
= concatMap insert_stack_insns code
return (CmmProc info lbl live (ListGraph new_code),retargetList)
data RI
= RIReg Reg
| RIImm Imm
data Instr
= COMMENT FastString
| LOCATION Int Int Int String
| LDATA Section RawCmmStatics
| NEWBLOCK BlockId
| DELTA Int
| LD Format Reg AddrMode
| LDFAR Format Reg AddrMode
| LDR Format Reg AddrMode
| LA Format Reg AddrMode
| ST Format Reg AddrMode
| STFAR Format Reg AddrMode
| STU Format Reg AddrMode
| STC Format Reg AddrMode
| LIS Reg Imm
| LI Reg Imm
| MR Reg Reg
| CMP Format Reg RI
| CMPL Format Reg RI
| BCC Cond BlockId (Maybe Bool)
| BCCFAR Cond BlockId (Maybe Bool)
| JMP CLabel [Reg]
| MTCTR Reg
| BCTR [Maybe BlockId] (Maybe CLabel) [Reg]
| BL CLabel [Reg]
| BCTRL [Reg]
| ADD Reg Reg RI
| ADDO Reg Reg Reg
| ADDC Reg Reg Reg
| ADDE Reg Reg Reg
| ADDZE Reg Reg
| ADDIS Reg Reg Imm
| SUBF Reg Reg Reg
| SUBFO Reg Reg Reg
| SUBFC Reg Reg RI
| SUBFE Reg Reg Reg
| MULL Format Reg Reg RI
| MULLO Format Reg Reg Reg
| MFOV Format Reg
| MULHU Format Reg Reg Reg
| DIV Format Bool Reg Reg Reg
| AND Reg Reg RI
| ANDC Reg Reg Reg
| NAND Reg Reg Reg
| OR Reg Reg RI
| ORIS Reg Reg Imm
| XOR Reg Reg RI
| XORIS Reg Reg Imm
| EXTS Format Reg Reg
| CNTLZ Format Reg Reg
| NEG Reg Reg
| NOT Reg Reg
| SL Format Reg Reg RI
| SR Format Reg Reg RI
| SRA Format Reg Reg RI
| RLWINM Reg Reg Int Int Int
| CLRLI Format Reg Reg Int
| CLRRI Format Reg Reg Int
| FADD Format Reg Reg Reg
| FSUB Format Reg Reg Reg
| FMUL Format Reg Reg Reg
| FDIV Format Reg Reg Reg
| FABS Reg Reg
| FNEG Reg Reg
| FCMP Reg Reg
| FCTIWZ Reg Reg
| FCTIDZ Reg Reg
| FCFID Reg Reg
| FRSP Reg Reg
| CRNOR Int Int Int
| MFCR Reg
| MFLR Reg
| FETCHPC Reg
| HWSYNC
| ISYNC
| LWSYNC
| NOP
regUsageOfInstr :: Platform -> Instr -> RegUsage
regUsageOfInstr platform instr
= case instr of
LD _ reg addr -> usage (regAddr addr, [reg])
LDFAR _ reg addr -> usage (regAddr addr, [reg])
LDR _ reg addr -> usage (regAddr addr, [reg])
LA _ reg addr -> usage (regAddr addr, [reg])
ST _ reg addr -> usage (reg : regAddr addr, [])
STFAR _ reg addr -> usage (reg : regAddr addr, [])
STU _ reg addr -> usage (reg : regAddr addr, [])
STC _ reg addr -> usage (reg : regAddr addr, [])
LIS reg _ -> usage ([], [reg])
LI reg _ -> usage ([], [reg])
MR reg1 reg2 -> usage ([reg2], [reg1])
CMP _ reg ri -> usage (reg : regRI ri,[])
CMPL _ reg ri -> usage (reg : regRI ri,[])
BCC _ _ _ -> noUsage
BCCFAR _ _ _ -> noUsage
JMP _ regs -> usage (regs, [])
MTCTR reg -> usage ([reg],[])
BCTR _ _ regs -> usage (regs, [])
BL _ params -> usage (params, callClobberedRegs platform)
BCTRL params -> usage (params, callClobberedRegs platform)
ADD reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
ADDO reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
ADDC reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
ADDE reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
ADDZE reg1 reg2 -> usage ([reg2], [reg1])
ADDIS reg1 reg2 _ -> usage ([reg2], [reg1])
SUBF reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
SUBFO reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
SUBFC reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
SUBFE reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
MULL _ reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
MULLO _ reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
MFOV _ reg -> usage ([], [reg])
MULHU _ reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
DIV _ _ reg1 reg2 reg3
-> usage ([reg2,reg3], [reg1])
AND reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
ANDC reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
NAND reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1])
OR reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
ORIS reg1 reg2 _ -> usage ([reg2], [reg1])
XOR reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
XORIS reg1 reg2 _ -> usage ([reg2], [reg1])
EXTS _ reg1 reg2 -> usage ([reg2], [reg1])
CNTLZ _ reg1 reg2 -> usage ([reg2], [reg1])
NEG reg1 reg2 -> usage ([reg2], [reg1])
NOT reg1 reg2 -> usage ([reg2], [reg1])
SL _ reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
SR _ reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
SRA _ reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1])
RLWINM reg1 reg2 _ _ _ -> usage ([reg2], [reg1])
CLRLI _ reg1 reg2 _ -> usage ([reg2], [reg1])
CLRRI _ reg1 reg2 _ -> usage ([reg2], [reg1])
FADD _ r1 r2 r3 -> usage ([r2,r3], [r1])
FSUB _ r1 r2 r3 -> usage ([r2,r3], [r1])
FMUL _ r1 r2 r3 -> usage ([r2,r3], [r1])
FDIV _ r1 r2 r3 -> usage ([r2,r3], [r1])
FABS r1 r2 -> usage ([r2], [r1])
FNEG r1 r2 -> usage ([r2], [r1])
FCMP r1 r2 -> usage ([r1,r2], [])
FCTIWZ r1 r2 -> usage ([r2], [r1])
FCTIDZ r1 r2 -> usage ([r2], [r1])
FCFID r1 r2 -> usage ([r2], [r1])
FRSP r1 r2 -> usage ([r2], [r1])
MFCR reg -> usage ([], [reg])
MFLR reg -> usage ([], [reg])
FETCHPC reg -> usage ([], [reg])
_ -> noUsage
where
usage (src, dst) = RU (filter (interesting platform) src)
(filter (interesting platform) dst)
regAddr (AddrRegReg r1 r2) = [r1, r2]
regAddr (AddrRegImm r1 _) = [r1]
regRI (RIReg r) = [r]
regRI _ = []
interesting :: Platform -> Reg -> Bool
interesting _ (RegVirtual _) = True
interesting platform (RegReal (RealRegSingle i)) = freeReg platform i
interesting _ (RegReal (RealRegPair{}))
= panic "PPC.Instr.interesting: no reg pairs on this arch"
patchRegsOfInstr :: Instr -> (Reg -> Reg) -> Instr
patchRegsOfInstr instr env
= case instr of
LD fmt reg addr -> LD fmt (env reg) (fixAddr addr)
LDFAR fmt reg addr -> LDFAR fmt (env reg) (fixAddr addr)
LDR fmt reg addr -> LDR fmt (env reg) (fixAddr addr)
LA fmt reg addr -> LA fmt (env reg) (fixAddr addr)
ST fmt reg addr -> ST fmt (env reg) (fixAddr addr)
STFAR fmt reg addr -> STFAR fmt (env reg) (fixAddr addr)
STU fmt reg addr -> STU fmt (env reg) (fixAddr addr)
STC fmt reg addr -> STC fmt (env reg) (fixAddr addr)
LIS reg imm -> LIS (env reg) imm
LI reg imm -> LI (env reg) imm
MR reg1 reg2 -> MR (env reg1) (env reg2)
CMP fmt reg ri -> CMP fmt (env reg) (fixRI ri)
CMPL fmt reg ri -> CMPL fmt (env reg) (fixRI ri)
BCC cond lbl p -> BCC cond lbl p
BCCFAR cond lbl p -> BCCFAR cond lbl p
JMP l regs -> JMP l regs
MTCTR reg -> MTCTR (env reg)
BCTR targets lbl rs -> BCTR targets lbl rs
BL imm argRegs -> BL imm argRegs
BCTRL argRegs -> BCTRL argRegs
ADD reg1 reg2 ri -> ADD (env reg1) (env reg2) (fixRI ri)
ADDO reg1 reg2 reg3 -> ADDO (env reg1) (env reg2) (env reg3)
ADDC reg1 reg2 reg3 -> ADDC (env reg1) (env reg2) (env reg3)
ADDE reg1 reg2 reg3 -> ADDE (env reg1) (env reg2) (env reg3)
ADDZE reg1 reg2 -> ADDZE (env reg1) (env reg2)
ADDIS reg1 reg2 imm -> ADDIS (env reg1) (env reg2) imm
SUBF reg1 reg2 reg3 -> SUBF (env reg1) (env reg2) (env reg3)
SUBFO reg1 reg2 reg3 -> SUBFO (env reg1) (env reg2) (env reg3)
SUBFC reg1 reg2 ri -> SUBFC (env reg1) (env reg2) (fixRI ri)
SUBFE reg1 reg2 reg3 -> SUBFE (env reg1) (env reg2) (env reg3)
MULL fmt reg1 reg2 ri
-> MULL fmt (env reg1) (env reg2) (fixRI ri)
MULLO fmt reg1 reg2 reg3
-> MULLO fmt (env reg1) (env reg2) (env reg3)
MFOV fmt reg -> MFOV fmt (env reg)
MULHU fmt reg1 reg2 reg3
-> MULHU fmt (env reg1) (env reg2) (env reg3)
DIV fmt sgn reg1 reg2 reg3
-> DIV fmt sgn (env reg1) (env reg2) (env reg3)
AND reg1 reg2 ri -> AND (env reg1) (env reg2) (fixRI ri)
ANDC reg1 reg2 reg3 -> ANDC (env reg1) (env reg2) (env reg3)
NAND reg1 reg2 reg3 -> NAND (env reg1) (env reg2) (env reg3)
OR reg1 reg2 ri -> OR (env reg1) (env reg2) (fixRI ri)
ORIS reg1 reg2 imm -> ORIS (env reg1) (env reg2) imm
XOR reg1 reg2 ri -> XOR (env reg1) (env reg2) (fixRI ri)
XORIS reg1 reg2 imm -> XORIS (env reg1) (env reg2) imm
EXTS fmt reg1 reg2 -> EXTS fmt (env reg1) (env reg2)
CNTLZ fmt reg1 reg2 -> CNTLZ fmt (env reg1) (env reg2)
NEG reg1 reg2 -> NEG (env reg1) (env reg2)
NOT reg1 reg2 -> NOT (env reg1) (env reg2)
SL fmt reg1 reg2 ri
-> SL fmt (env reg1) (env reg2) (fixRI ri)
SR fmt reg1 reg2 ri
-> SR fmt (env reg1) (env reg2) (fixRI ri)
SRA fmt reg1 reg2 ri
-> SRA fmt (env reg1) (env reg2) (fixRI ri)
RLWINM reg1 reg2 sh mb me
-> RLWINM (env reg1) (env reg2) sh mb me
CLRLI fmt reg1 reg2 n -> CLRLI fmt (env reg1) (env reg2) n
CLRRI fmt reg1 reg2 n -> CLRRI fmt (env reg1) (env reg2) n
FADD fmt r1 r2 r3 -> FADD fmt (env r1) (env r2) (env r3)
FSUB fmt r1 r2 r3 -> FSUB fmt (env r1) (env r2) (env r3)
FMUL fmt r1 r2 r3 -> FMUL fmt (env r1) (env r2) (env r3)
FDIV fmt r1 r2 r3 -> FDIV fmt (env r1) (env r2) (env r3)
FABS r1 r2 -> FABS (env r1) (env r2)
FNEG r1 r2 -> FNEG (env r1) (env r2)
FCMP r1 r2 -> FCMP (env r1) (env r2)
FCTIWZ r1 r2 -> FCTIWZ (env r1) (env r2)
FCTIDZ r1 r2 -> FCTIDZ (env r1) (env r2)
FCFID r1 r2 -> FCFID (env r1) (env r2)
FRSP r1 r2 -> FRSP (env r1) (env r2)
MFCR reg -> MFCR (env reg)
MFLR reg -> MFLR (env reg)
FETCHPC reg -> FETCHPC (env reg)
_ -> instr
where
fixAddr (AddrRegReg r1 r2) = AddrRegReg (env r1) (env r2)
fixAddr (AddrRegImm r1 i) = AddrRegImm (env r1) i
fixRI (RIReg r) = RIReg (env r)
fixRI other = other
isJumpishInstr :: Instr -> Bool
isJumpishInstr instr
= case instr of
BCC{} -> True
BCCFAR{} -> True
BCTR{} -> True
BCTRL{} -> True
BL{} -> True
JMP{} -> True
_ -> False
jumpDestsOfInstr :: Instr -> [BlockId]
jumpDestsOfInstr insn
= case insn of
BCC _ id _ -> [id]
BCCFAR _ id _ -> [id]
BCTR targets _ _ -> [id | Just id <- targets]
_ -> []
patchJumpInstr :: Instr -> (BlockId -> BlockId) -> Instr
patchJumpInstr insn patchF
= case insn of
BCC cc id p -> BCC cc (patchF id) p
BCCFAR cc id p -> BCCFAR cc (patchF id) p
BCTR ids lbl rs -> BCTR (map (fmap patchF) ids) lbl rs
_ -> insn
mkSpillInstr
:: NCGConfig
-> Reg
-> Int
-> Int
-> Instr
mkSpillInstr config reg delta slot
= let platform = ncgPlatform config
off = spillSlotToOffset platform slot
arch = platformArch platform
in
let fmt = case targetClassOfReg platform reg of
RcInteger -> case arch of
ArchPPC -> II32
_ -> II64
RcDouble -> FF64
_ -> panic "PPC.Instr.mkSpillInstr: no match"
instr = case makeImmediate W32 True (offdelta) of
Just _ -> ST
Nothing -> STFAR
in instr fmt reg (AddrRegImm sp (ImmInt (offdelta)))
mkLoadInstr
:: NCGConfig
-> Reg
-> Int
-> Int
-> Instr
mkLoadInstr config reg delta slot
= let platform = ncgPlatform config
off = spillSlotToOffset platform slot
arch = platformArch platform
in
let fmt = case targetClassOfReg platform reg of
RcInteger -> case arch of
ArchPPC -> II32
_ -> II64
RcDouble -> FF64
_ -> panic "PPC.Instr.mkLoadInstr: no match"
instr = case makeImmediate W32 True (offdelta) of
Just _ -> LD
Nothing -> LDFAR
in instr fmt reg (AddrRegImm sp (ImmInt (offdelta)))
stackFrameHeaderSize :: Platform -> Int
stackFrameHeaderSize platform
= case platformOS platform of
OSAIX -> 24 + 8 * 4
_ -> case platformArch platform of
ArchPPC -> 64
ArchPPC_64 ELF_V1 -> 48 + 8 * 8
ArchPPC_64 ELF_V2 -> 32 + 8 * 8
_ -> panic "PPC.stackFrameHeaderSize: not defined for this OS"
spillSlotSize :: Int
spillSlotSize = 8
maxSpillSlots :: NCGConfig -> Int
maxSpillSlots config
= let platform = ncgPlatform config
in ((ncgSpillPreallocSize config stackFrameHeaderSize platform)
`div` spillSlotSize) 1
stackAlign :: Int
stackAlign = 16
spillSlotToOffset :: Platform -> Int -> Int
spillSlotToOffset platform slot
= stackFrameHeaderSize platform + spillSlotSize * slot
takeDeltaInstr
:: Instr
-> Maybe Int
takeDeltaInstr instr
= case instr of
DELTA i -> Just i
_ -> Nothing
isMetaInstr
:: Instr
-> Bool
isMetaInstr instr
= case instr of
COMMENT{} -> True
LOCATION{} -> True
LDATA{} -> True
NEWBLOCK{} -> True
DELTA{} -> True
_ -> False
mkRegRegMoveInstr
:: Reg
-> Reg
-> Instr
mkRegRegMoveInstr src dst
= MR dst src
mkJumpInstr
:: BlockId
-> [Instr]
mkJumpInstr id
= [BCC ALWAYS id Nothing]
takeRegRegMoveInstr :: Instr -> Maybe (Reg,Reg)
takeRegRegMoveInstr (MR dst src) = Just (src,dst)
takeRegRegMoveInstr _ = Nothing
makeFarBranches
:: LabelMap RawCmmStatics
-> [NatBasicBlock Instr]
-> [NatBasicBlock Instr]
makeFarBranches info_env blocks
| last blockAddresses < nearLimit = blocks
| otherwise = zipWith handleBlock blockAddresses blocks
where
blockAddresses = scanl (+) 0 $ map blockLen blocks
blockLen (BasicBlock _ instrs) = length instrs
handleBlock addr (BasicBlock id instrs)
= BasicBlock id (zipWith makeFar [addr..] instrs)
makeFar _ (BCC ALWAYS tgt _) = BCC ALWAYS tgt Nothing
makeFar addr (BCC cond tgt p)
| abs (addr targetAddr) >= nearLimit
= BCCFAR cond tgt p
| otherwise
= BCC cond tgt p
where Just targetAddr = lookupUFM blockAddressMap tgt
makeFar _ other = other
nearLimit = 7000 mapSize info_env * maxRetInfoTableSizeW
blockAddressMap = listToUFM $ zip (map blockId blocks) blockAddresses