{-# LANGUAGE CPP #-}
#include "HsVersions.h"
#include "nativeGen/NCG.h"
module PPC.Instr (
    archWordFormat,
    RI(..),
    Instr(..),
    stackFrameHeaderSize,
    maxSpillSlots,
    allocMoreStack,
    makeFarBranches
)
where
import GhcPrelude
import PPC.Regs
import PPC.Cond
import Instruction
import Format
import TargetReg
import RegClass
import Reg
import CodeGen.Platform
import BlockId
import Hoopl.Collections
import Hoopl.Label
import DynFlags
import Cmm
import CmmInfo
import FastString
import CLabel
import Outputable
import Platform
import UniqFM (listToUFM, lookupUFM)
import UniqSupply
import Control.Monad (replicateM)
import Data.Maybe (fromMaybe)
archWordFormat :: Bool -> Format
archWordFormat is32Bit
 | is32Bit   = II32
 | otherwise = II64
instance Instruction Instr where
        regUsageOfInstr         = ppc_regUsageOfInstr
        patchRegsOfInstr        = ppc_patchRegsOfInstr
        isJumpishInstr          = ppc_isJumpishInstr
        jumpDestsOfInstr        = ppc_jumpDestsOfInstr
        patchJumpInstr          = ppc_patchJumpInstr
        mkSpillInstr            = ppc_mkSpillInstr
        mkLoadInstr             = ppc_mkLoadInstr
        takeDeltaInstr          = ppc_takeDeltaInstr
        isMetaInstr             = ppc_isMetaInstr
        mkRegRegMoveInstr _     = ppc_mkRegRegMoveInstr
        takeRegRegMoveInstr     = ppc_takeRegRegMoveInstr
        mkJumpInstr             = ppc_mkJumpInstr
        mkStackAllocInstr       = ppc_mkStackAllocInstr
        mkStackDeallocInstr     = ppc_mkStackDeallocInstr
ppc_mkStackAllocInstr :: Platform -> Int -> [Instr]
ppc_mkStackAllocInstr platform amount
  = ppc_mkStackAllocInstr' platform (-amount)
ppc_mkStackDeallocInstr :: Platform -> Int -> [Instr]
ppc_mkStackDeallocInstr platform amount
  = ppc_mkStackAllocInstr' platform amount
ppc_mkStackAllocInstr' :: Platform -> Int -> [Instr]
ppc_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 ((platformWordSize platform) `quot` 8)
    zero = ImmInt 0
    tmp = tmpReg platform
    immAmount = ImmInt amount
allocMoreStack
  :: Platform
  -> Int
  -> NatCmmDecl statics PPC.Instr.Instr
  -> UniqSM (NatCmmDecl statics 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
    
    
    
    | LDATA   Section CmmStatics
    
    
    
    
    | 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                       
                                
                                
ppc_regUsageOfInstr :: Platform -> Instr -> RegUsage
ppc_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"
ppc_patchRegsOfInstr :: Instr -> (Reg -> Reg) -> Instr
ppc_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
ppc_isJumpishInstr :: Instr -> Bool
ppc_isJumpishInstr instr
 = case instr of
    BCC{}       -> True
    BCCFAR{}    -> True
    BCTR{}      -> True
    BCTRL{}     -> True
    BL{}        -> True
    JMP{}       -> True
    _           -> False
ppc_jumpDestsOfInstr :: Instr -> [BlockId]
ppc_jumpDestsOfInstr insn
  = case insn of
        BCC _ id _       -> [id]
        BCCFAR _ id _    -> [id]
        BCTR targets _ _ -> [id | Just id <- targets]
        _                -> []
ppc_patchJumpInstr :: Instr -> (BlockId -> BlockId) -> Instr
ppc_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
ppc_mkSpillInstr
   :: DynFlags
   -> Reg       
   -> Int       
   -> Int       
   -> Instr
ppc_mkSpillInstr dflags reg delta slot
  = let platform = targetPlatform dflags
        off      = spillSlotToOffset dflags 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 (off-delta) of
                Just _  -> ST
                Nothing -> STFAR 
    in instr fmt reg (AddrRegImm sp (ImmInt (off-delta)))
ppc_mkLoadInstr
   :: DynFlags
   -> Reg       
   -> Int       
   -> Int       
   -> Instr
ppc_mkLoadInstr dflags reg delta slot
  = let platform = targetPlatform dflags
        off      = spillSlotToOffset dflags 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 (off-delta) of
                Just _  -> LD
                Nothing -> LDFAR 
    in instr fmt reg (AddrRegImm sp (ImmInt (off-delta)))
stackFrameHeaderSize :: DynFlags -> Int
stackFrameHeaderSize dflags
  = 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"
     where platform = targetPlatform dflags
spillSlotSize :: Int
spillSlotSize = 8
maxSpillSlots :: DynFlags -> Int
maxSpillSlots dflags
    = ((rESERVED_C_STACK_BYTES dflags - stackFrameHeaderSize dflags)
       `div` spillSlotSize) - 1
stackAlign :: Int
stackAlign = 16
spillSlotToOffset :: DynFlags -> Int -> Int
spillSlotToOffset dflags slot
   = stackFrameHeaderSize dflags + spillSlotSize * slot
ppc_takeDeltaInstr
    :: Instr
    -> Maybe Int
ppc_takeDeltaInstr instr
 = case instr of
     DELTA i  -> Just i
     _        -> Nothing
ppc_isMetaInstr
    :: Instr
    -> Bool
ppc_isMetaInstr instr
 = case instr of
    COMMENT{}   -> True
    LDATA{}     -> True
    NEWBLOCK{}  -> True
    DELTA{}     -> True
    _           -> False
ppc_mkRegRegMoveInstr
    :: Reg
    -> Reg
    -> Instr
ppc_mkRegRegMoveInstr src dst
    = MR dst src
ppc_mkJumpInstr
    :: BlockId
    -> [Instr]
ppc_mkJumpInstr id
    = [BCC ALWAYS id Nothing]
ppc_takeRegRegMoveInstr :: Instr -> Maybe (Reg,Reg)
ppc_takeRegRegMoveInstr (MR dst src) = Just (src,dst)
ppc_takeRegRegMoveInstr _  = Nothing
makeFarBranches
        :: LabelMap CmmStatics
        -> [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