module GHC.CmmToAsm.X86.Instr
( Instr(..)
, Operand(..)
, PrefetchVariant(..)
, JumpDest(..)
, getJumpDestBlockId
, canShortcut
, shortcutStatics
, shortcutJump
, allocMoreStack
, maxSpillSlots
, archWordFormat
, takeRegRegMoveInstr
, regUsageOfInstr
, takeDeltaInstr
, mkLoadInstr
, mkJumpInstr
, mkStackAllocInstr
, mkStackDeallocInstr
, mkSpillInstr
, mkRegRegMoveInstr
, jumpDestsOfInstr
, patchRegsOfInstr
, patchJumpInstr
, isMetaInstr
, isJumpishInstr
)
where
#include "HsVersions.h"
import GHC.Prelude
import GHC.CmmToAsm.X86.Cond
import GHC.CmmToAsm.X86.Regs
import GHC.CmmToAsm.Format
import GHC.CmmToAsm.Types
import GHC.CmmToAsm.Utils
import GHC.CmmToAsm.Instr (RegUsage(..), noUsage)
import GHC.Platform.Reg.Class
import GHC.Platform.Reg
import GHC.CmmToAsm.Reg.Target
import GHC.CmmToAsm.Config
import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Collections
import GHC.Cmm.Dataflow.Label
import GHC.Platform.Regs
import GHC.Cmm
import GHC.Data.FastString
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Platform
import GHC.Cmm.CLabel
import GHC.Types.Unique.Set
import GHC.Types.Unique
import GHC.Types.Unique.Supply
import GHC.Types.Basic (Alignment)
import GHC.Cmm.DebugBlock (UnwindTable)
import Control.Monad
import Data.Maybe (fromMaybe)
archWordFormat :: Bool -> Format
archWordFormat is32Bit
| is32Bit = II32
| otherwise = II64
data Instr
= COMMENT FastString
| LOCATION Int Int Int String
| LDATA Section (Alignment, RawCmmStatics)
| NEWBLOCK BlockId
| UNWIND CLabel UnwindTable
| DELTA Int
| MOV Format Operand Operand
| CMOV Cond Format Operand Reg
| MOVZxL Format Operand Operand
| MOVSxL Format Operand Operand
| LEA Format Operand Operand
| ADD Format Operand Operand
| ADC Format Operand Operand
| SUB Format Operand Operand
| SBB Format Operand Operand
| MUL Format Operand Operand
| MUL2 Format Operand
| IMUL Format Operand Operand
| IMUL2 Format Operand
| DIV Format Operand
| IDIV Format Operand
| ADD_CC Format Operand Operand
| SUB_CC Format Operand Operand
| AND Format Operand Operand
| OR Format Operand Operand
| XOR Format Operand Operand
| NOT Format Operand
| NEGI Format Operand
| BSWAP Format Reg
| SHL Format Operand Operand
| SAR Format Operand Operand
| SHR Format Operand Operand
| BT Format Imm Operand
| NOP
| X87Store Format AddrMode
| CVTSS2SD Reg Reg
| CVTSD2SS Reg Reg
| CVTTSS2SIQ Format Operand Reg
| CVTTSD2SIQ Format Operand Reg
| CVTSI2SS Format Operand Reg
| CVTSI2SD Format Operand Reg
| FDIV Format Operand Operand
| SQRT Format Operand Reg
| TEST Format Operand Operand
| CMP Format Operand Operand
| SETCC Cond Operand
| PUSH Format Operand
| POP Format Operand
| JMP Operand [Reg]
| JXX Cond BlockId
| JXX_GBL Cond Imm
| JMP_TBL Operand
[Maybe JumpDest]
Section
CLabel
| CALL (Either Imm Reg)
[Reg]
| CLTD Format
| FETCHGOT Reg
| FETCHPC Reg
| POPCNT Format Operand Reg
| LZCNT Format Operand Reg
| TZCNT Format Operand Reg
| BSF Format Operand Reg
| BSR Format Operand Reg
| PDEP Format Operand Operand Reg
| PEXT Format Operand Operand Reg
| PREFETCH PrefetchVariant Format Operand
| LOCK Instr
| XADD Format Operand Operand
| CMPXCHG Format Operand Operand
| XCHG Format Operand Reg
| MFENCE
data PrefetchVariant = NTA | Lvl0 | Lvl1 | Lvl2
data Operand
= OpReg Reg
| OpImm Imm
| OpAddr AddrMode
regUsageOfInstr :: Platform -> Instr -> RegUsage
regUsageOfInstr platform instr
= case instr of
MOV _ src dst -> usageRW src dst
CMOV _ _ src dst -> mkRU (use_R src [dst]) [dst]
MOVZxL _ src dst -> usageRW src dst
MOVSxL _ src dst -> usageRW src dst
LEA _ src dst -> usageRW src dst
ADD _ src dst -> usageRM src dst
ADC _ src dst -> usageRM src dst
SUB _ src dst -> usageRM src dst
SBB _ src dst -> usageRM src dst
IMUL _ src dst -> usageRM src dst
IMUL2 II8 src -> mkRU (eax:use_R src []) [eax]
IMUL2 _ src -> mkRU (eax:use_R src []) [eax,edx]
MUL _ src dst -> usageRM src dst
MUL2 _ src -> mkRU (eax:use_R src []) [eax,edx]
DIV _ op -> mkRU (eax:edx:use_R op []) [eax,edx]
IDIV _ op -> mkRU (eax:edx:use_R op []) [eax,edx]
ADD_CC _ src dst -> usageRM src dst
SUB_CC _ src dst -> usageRM src dst
AND _ src dst -> usageRM src dst
OR _ src dst -> usageRM src dst
XOR _ (OpReg src) (OpReg dst)
| src == dst -> mkRU [] [dst]
XOR _ src dst -> usageRM src dst
NOT _ op -> usageM op
BSWAP _ reg -> mkRU [reg] [reg]
NEGI _ op -> usageM op
SHL _ imm dst -> usageRM imm dst
SAR _ imm dst -> usageRM imm dst
SHR _ imm dst -> usageRM imm dst
BT _ _ src -> mkRUR (use_R src [])
PUSH _ op -> mkRUR (use_R op [])
POP _ op -> mkRU [] (def_W op)
TEST _ src dst -> mkRUR (use_R src $! use_R dst [])
CMP _ src dst -> mkRUR (use_R src $! use_R dst [])
SETCC _ op -> mkRU [] (def_W op)
JXX _ _ -> mkRU [] []
JXX_GBL _ _ -> mkRU [] []
JMP op regs -> mkRUR (use_R op regs)
JMP_TBL op _ _ _ -> mkRUR (use_R op [])
CALL (Left _) params -> mkRU params (callClobberedRegs platform)
CALL (Right reg) params -> mkRU (reg:params) (callClobberedRegs platform)
CLTD _ -> mkRU [eax] [edx]
NOP -> mkRU [] []
X87Store _ dst -> mkRUR ( use_EA dst [])
CVTSS2SD src dst -> mkRU [src] [dst]
CVTSD2SS src dst -> mkRU [src] [dst]
CVTTSS2SIQ _ src dst -> mkRU (use_R src []) [dst]
CVTTSD2SIQ _ src dst -> mkRU (use_R src []) [dst]
CVTSI2SS _ src dst -> mkRU (use_R src []) [dst]
CVTSI2SD _ src dst -> mkRU (use_R src []) [dst]
FDIV _ src dst -> usageRM src dst
SQRT _ src dst -> mkRU (use_R src []) [dst]
FETCHGOT reg -> mkRU [] [reg]
FETCHPC reg -> mkRU [] [reg]
COMMENT _ -> noUsage
LOCATION{} -> noUsage
UNWIND{} -> noUsage
DELTA _ -> noUsage
POPCNT _ src dst -> mkRU (use_R src []) [dst]
LZCNT _ src dst -> mkRU (use_R src []) [dst]
TZCNT _ src dst -> mkRU (use_R src []) [dst]
BSF _ src dst -> mkRU (use_R src []) [dst]
BSR _ src dst -> mkRU (use_R src []) [dst]
PDEP _ src mask dst -> mkRU (use_R src $ use_R mask []) [dst]
PEXT _ src mask dst -> mkRU (use_R src $ use_R mask []) [dst]
PREFETCH _ _ src -> mkRU (use_R src []) []
LOCK i -> regUsageOfInstr platform i
XADD _ src dst -> usageMM src dst
CMPXCHG _ src dst -> usageRMM src dst (OpReg eax)
XCHG _ src dst -> usageMM src (OpReg dst)
MFENCE -> noUsage
_other -> panic "regUsage: unrecognised instr"
where
usageRW :: Operand -> Operand -> RegUsage
usageRW op (OpReg reg) = mkRU (use_R op []) [reg]
usageRW op (OpAddr ea) = mkRUR (use_R op $! use_EA ea [])
usageRW _ _ = panic "X86.RegInfo.usageRW: no match"
usageRM :: Operand -> Operand -> RegUsage
usageRM op (OpReg reg) = mkRU (use_R op [reg]) [reg]
usageRM op (OpAddr ea) = mkRUR (use_R op $! use_EA ea [])
usageRM _ _ = panic "X86.RegInfo.usageRM: no match"
usageMM :: Operand -> Operand -> RegUsage
usageMM (OpReg src) (OpReg dst) = mkRU [src, dst] [src, dst]
usageMM (OpReg src) (OpAddr ea) = mkRU (use_EA ea [src]) [src]
usageMM (OpAddr ea) (OpReg dst) = mkRU (use_EA ea [dst]) [dst]
usageMM _ _ = panic "X86.RegInfo.usageMM: no match"
usageRMM :: Operand -> Operand -> Operand -> RegUsage
usageRMM (OpReg src) (OpReg dst) (OpReg reg) = mkRU [src, dst, reg] [dst, reg]
usageRMM (OpReg src) (OpAddr ea) (OpReg reg) = mkRU (use_EA ea [src, reg]) [reg]
usageRMM _ _ _ = panic "X86.RegInfo.usageRMM: no match"
usageM :: Operand -> RegUsage
usageM (OpReg reg) = mkRU [reg] [reg]
usageM (OpAddr ea) = mkRUR (use_EA ea [])
usageM _ = panic "X86.RegInfo.usageM: no match"
def_W (OpReg reg) = [reg]
def_W (OpAddr _ ) = []
def_W _ = panic "X86.RegInfo.def_W: no match"
use_R (OpReg reg) tl = reg : tl
use_R (OpImm _) tl = tl
use_R (OpAddr ea) tl = use_EA ea tl
use_EA (ImmAddr _ _) tl = tl
use_EA (AddrBaseIndex base index _) tl =
use_base base $! use_index index tl
where use_base (EABaseReg r) tl = r : tl
use_base _ tl = tl
use_index EAIndexNone tl = tl
use_index (EAIndex i _) tl = i : tl
mkRUR src = src' `seq` RU src' []
where src' = filter (interesting platform) src
mkRU src dst = src' `seq` dst' `seq` RU src' dst'
where src' = filter (interesting platform) src
dst' = filter (interesting platform) dst
interesting :: Platform -> Reg -> Bool
interesting _ (RegVirtual _) = True
interesting platform (RegReal (RealRegSingle i)) = freeReg platform i
interesting _ (RegReal (RealRegPair{})) = panic "X86.interesting: no reg pairs on this arch"
patchRegsOfInstr :: Instr -> (Reg -> Reg) -> Instr
patchRegsOfInstr instr env
= case instr of
MOV fmt src dst -> patch2 (MOV fmt) src dst
CMOV cc fmt src dst -> CMOV cc fmt (patchOp src) (env dst)
MOVZxL fmt src dst -> patch2 (MOVZxL fmt) src dst
MOVSxL fmt src dst -> patch2 (MOVSxL fmt) src dst
LEA fmt src dst -> patch2 (LEA fmt) src dst
ADD fmt src dst -> patch2 (ADD fmt) src dst
ADC fmt src dst -> patch2 (ADC fmt) src dst
SUB fmt src dst -> patch2 (SUB fmt) src dst
SBB fmt src dst -> patch2 (SBB fmt) src dst
IMUL fmt src dst -> patch2 (IMUL fmt) src dst
IMUL2 fmt src -> patch1 (IMUL2 fmt) src
MUL fmt src dst -> patch2 (MUL fmt) src dst
MUL2 fmt src -> patch1 (MUL2 fmt) src
IDIV fmt op -> patch1 (IDIV fmt) op
DIV fmt op -> patch1 (DIV fmt) op
ADD_CC fmt src dst -> patch2 (ADD_CC fmt) src dst
SUB_CC fmt src dst -> patch2 (SUB_CC fmt) src dst
AND fmt src dst -> patch2 (AND fmt) src dst
OR fmt src dst -> patch2 (OR fmt) src dst
XOR fmt src dst -> patch2 (XOR fmt) src dst
NOT fmt op -> patch1 (NOT fmt) op
BSWAP fmt reg -> BSWAP fmt (env reg)
NEGI fmt op -> patch1 (NEGI fmt) op
SHL fmt imm dst -> patch1 (SHL fmt imm) dst
SAR fmt imm dst -> patch1 (SAR fmt imm) dst
SHR fmt imm dst -> patch1 (SHR fmt imm) dst
BT fmt imm src -> patch1 (BT fmt imm) src
TEST fmt src dst -> patch2 (TEST fmt) src dst
CMP fmt src dst -> patch2 (CMP fmt) src dst
PUSH fmt op -> patch1 (PUSH fmt) op
POP fmt op -> patch1 (POP fmt) op
SETCC cond op -> patch1 (SETCC cond) op
JMP op regs -> JMP (patchOp op) regs
JMP_TBL op ids s lbl -> JMP_TBL (patchOp op) ids s lbl
X87Store fmt dst -> X87Store fmt (lookupAddr dst)
CVTSS2SD src dst -> CVTSS2SD (env src) (env dst)
CVTSD2SS src dst -> CVTSD2SS (env src) (env dst)
CVTTSS2SIQ fmt src dst -> CVTTSS2SIQ fmt (patchOp src) (env dst)
CVTTSD2SIQ fmt src dst -> CVTTSD2SIQ fmt (patchOp src) (env dst)
CVTSI2SS fmt src dst -> CVTSI2SS fmt (patchOp src) (env dst)
CVTSI2SD fmt src dst -> CVTSI2SD fmt (patchOp src) (env dst)
FDIV fmt src dst -> FDIV fmt (patchOp src) (patchOp dst)
SQRT fmt src dst -> SQRT fmt (patchOp src) (env dst)
CALL (Left _) _ -> instr
CALL (Right reg) p -> CALL (Right (env reg)) p
FETCHGOT reg -> FETCHGOT (env reg)
FETCHPC reg -> FETCHPC (env reg)
NOP -> instr
COMMENT _ -> instr
LOCATION {} -> instr
UNWIND {} -> instr
DELTA _ -> instr
JXX _ _ -> instr
JXX_GBL _ _ -> instr
CLTD _ -> instr
POPCNT fmt src dst -> POPCNT fmt (patchOp src) (env dst)
LZCNT fmt src dst -> LZCNT fmt (patchOp src) (env dst)
TZCNT fmt src dst -> TZCNT fmt (patchOp src) (env dst)
PDEP fmt src mask dst -> PDEP fmt (patchOp src) (patchOp mask) (env dst)
PEXT fmt src mask dst -> PEXT fmt (patchOp src) (patchOp mask) (env dst)
BSF fmt src dst -> BSF fmt (patchOp src) (env dst)
BSR fmt src dst -> BSR fmt (patchOp src) (env dst)
PREFETCH lvl format src -> PREFETCH lvl format (patchOp src)
LOCK i -> LOCK (patchRegsOfInstr i env)
XADD fmt src dst -> patch2 (XADD fmt) src dst
CMPXCHG fmt src dst -> patch2 (CMPXCHG fmt) src dst
XCHG fmt src dst -> XCHG fmt (patchOp src) (env dst)
MFENCE -> instr
_other -> panic "patchRegs: unrecognised instr"
where
patch1 :: (Operand -> a) -> Operand -> a
patch1 insn op = insn $! patchOp op
patch2 :: (Operand -> Operand -> a) -> Operand -> Operand -> a
patch2 insn src dst = (insn $! patchOp src) $! patchOp dst
patchOp (OpReg reg) = OpReg $! env reg
patchOp (OpImm imm) = OpImm imm
patchOp (OpAddr ea) = OpAddr $! lookupAddr ea
lookupAddr (ImmAddr imm off) = ImmAddr imm off
lookupAddr (AddrBaseIndex base index disp)
= ((AddrBaseIndex $! lookupBase base) $! lookupIndex index) disp
where
lookupBase EABaseNone = EABaseNone
lookupBase EABaseRip = EABaseRip
lookupBase (EABaseReg r) = EABaseReg $! env r
lookupIndex EAIndexNone = EAIndexNone
lookupIndex (EAIndex r i) = (EAIndex $! env r) i
isJumpishInstr
:: Instr -> Bool
isJumpishInstr instr
= case instr of
JMP{} -> True
JXX{} -> True
JXX_GBL{} -> True
JMP_TBL{} -> True
CALL{} -> True
_ -> False
jumpDestsOfInstr
:: Instr
-> [BlockId]
jumpDestsOfInstr insn
= case insn of
JXX _ id -> [id]
JMP_TBL _ ids _ _ -> [id | Just (DestBlockId id) <- ids]
_ -> []
patchJumpInstr
:: Instr -> (BlockId -> BlockId) -> Instr
patchJumpInstr insn patchF
= case insn of
JXX cc id -> JXX cc (patchF id)
JMP_TBL op ids section lbl
-> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section lbl
_ -> insn
where
patchJumpDest f (DestBlockId id) = DestBlockId (f id)
patchJumpDest _ dest = dest
mkSpillInstr
:: NCGConfig
-> Reg
-> Int
-> Int
-> Instr
mkSpillInstr config reg delta slot
= let off = spillSlotToOffset platform slot delta
in
case targetClassOfReg platform reg of
RcInteger -> MOV (archWordFormat is32Bit)
(OpReg reg) (OpAddr (spRel platform off))
RcDouble -> MOV FF64 (OpReg reg) (OpAddr (spRel platform off))
_ -> panic "X86.mkSpillInstr: no match"
where platform = ncgPlatform config
is32Bit = target32Bit platform
mkLoadInstr
:: NCGConfig
-> Reg
-> Int
-> Int
-> Instr
mkLoadInstr config reg delta slot
= let off = spillSlotToOffset platform slot delta
in
case targetClassOfReg platform reg of
RcInteger -> MOV (archWordFormat is32Bit)
(OpAddr (spRel platform off)) (OpReg reg)
RcDouble -> MOV FF64 (OpAddr (spRel platform off)) (OpReg reg)
_ -> panic "X86.mkLoadInstr"
where platform = ncgPlatform config
is32Bit = target32Bit platform
spillSlotSize :: Platform -> Int
spillSlotSize platform
| target32Bit platform = 12
| otherwise = 8
maxSpillSlots :: NCGConfig -> Int
maxSpillSlots config
= ((ncgSpillPreallocSize config 64) `div` spillSlotSize (ncgPlatform config)) 1
stackAlign :: Int
stackAlign = 16
spillSlotToOffset :: Platform -> Int -> Int
spillSlotToOffset platform slot
= 64 + spillSlotSize platform * 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
UNWIND{} -> True
DELTA{} -> True
_ -> False
mkRegRegMoveInstr
:: Platform
-> Reg
-> Reg
-> Instr
mkRegRegMoveInstr platform src dst
= case targetClassOfReg platform src of
RcInteger -> case platformArch platform of
ArchX86 -> MOV II32 (OpReg src) (OpReg dst)
ArchX86_64 -> MOV II64 (OpReg src) (OpReg dst)
_ -> panic "X86.mkRegRegMoveInstr: Bad arch"
RcDouble -> MOV FF64 (OpReg src) (OpReg dst)
_ -> panic "X86.RegInfo.mkRegRegMoveInstr: no match"
takeRegRegMoveInstr
:: Instr
-> Maybe (Reg,Reg)
takeRegRegMoveInstr (MOV _ (OpReg r1) (OpReg r2))
= Just (r1,r2)
takeRegRegMoveInstr _ = Nothing
mkJumpInstr
:: BlockId
-> [Instr]
mkJumpInstr id
= [JXX ALWAYS id]
needs_probe_call :: Platform -> Int -> Bool
needs_probe_call platform amount
= case platformOS platform of
OSMinGW32 -> case platformArch platform of
ArchX86 -> amount > (4 * 1024)
ArchX86_64 -> amount > (4 * 1024)
_ -> False
_ -> False
mkStackAllocInstr
:: Platform
-> Int
-> [Instr]
mkStackAllocInstr platform amount
= case platformOS platform of
OSMinGW32 ->
case platformArch platform of
ArchX86 | needs_probe_call platform amount ->
[ MOV II32 (OpImm (ImmInt amount)) (OpReg eax)
, CALL (Left $ strImmLit "___chkstk_ms") [eax]
, SUB II32 (OpReg eax) (OpReg esp)
]
| otherwise ->
[ SUB II32 (OpImm (ImmInt amount)) (OpReg esp)
, TEST II32 (OpReg esp) (OpReg esp)
]
ArchX86_64 | needs_probe_call platform amount ->
[ MOV II64 (OpImm (ImmInt amount)) (OpReg rax)
, CALL (Left $ strImmLit "___chkstk_ms") [rax]
, SUB II64 (OpReg rax) (OpReg rsp)
]
| otherwise ->
[ SUB II64 (OpImm (ImmInt amount)) (OpReg rsp)
, TEST II64 (OpReg rsp) (OpReg rsp)
]
_ -> panic "X86.mkStackAllocInstr"
_ ->
case platformArch platform of
ArchX86 -> [ SUB II32 (OpImm (ImmInt amount)) (OpReg esp) ]
ArchX86_64 -> [ SUB II64 (OpImm (ImmInt amount)) (OpReg rsp) ]
_ -> panic "X86.mkStackAllocInstr"
mkStackDeallocInstr
:: Platform
-> Int
-> [Instr]
mkStackDeallocInstr platform amount
= case platformArch platform of
ArchX86 -> [ADD II32 (OpImm (ImmInt amount)) (OpReg esp)]
ArchX86_64 -> [ADD II64 (OpImm (ImmInt amount)) (OpReg rsp)]
_ -> panic "X86.mkStackDeallocInstr"
allocMoreStack
:: Platform
-> Int
-> NatCmmDecl statics GHC.CmmToAsm.X86.Instr.Instr
-> UniqSM (NatCmmDecl statics GHC.CmmToAsm.X86.Instr.Instr, [(BlockId,BlockId)])
allocMoreStack _ _ top@(CmmData _ _) = return (top,[])
allocMoreStack platform slots proc@(CmmProc info lbl live (ListGraph code)) = do
let entries = entryBlocks proc
uniqs <- replicateM (length entries) getUniqueM
let
delta = ((x + stackAlign 1) `quot` stackAlign) * stackAlign
where x = slots * spillSlotSize platform
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 ++ [JXX ALWAYS new_blockid]
, 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)
JXX_GBL _ _ -> panic "insert_dealloc: cannot handle JXX_GBL"
_other -> patchJumpInstr insn retarget : r
where 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 JumpDest = DestBlockId BlockId | DestImm Imm
instance Outputable JumpDest where
ppr (DestBlockId bid) = text "jd<blk>:" <> ppr bid
ppr (DestImm _imm) = text "jd<imm>:noShow"
getJumpDestBlockId :: JumpDest -> Maybe BlockId
getJumpDestBlockId (DestBlockId bid) = Just bid
getJumpDestBlockId _ = Nothing
canShortcut :: Instr -> Maybe JumpDest
canShortcut (JXX ALWAYS id) = Just (DestBlockId id)
canShortcut (JMP (OpImm imm) _) = Just (DestImm imm)
canShortcut _ = Nothing
shortcutJump :: (BlockId -> Maybe JumpDest) -> Instr -> Instr
shortcutJump fn insn = shortcutJump' fn (setEmpty :: LabelSet) insn
where
shortcutJump' :: (BlockId -> Maybe JumpDest) -> LabelSet -> Instr -> Instr
shortcutJump' fn seen insn@(JXX cc id) =
if setMember id seen then insn
else case fn id of
Nothing -> insn
Just (DestBlockId id') -> shortcutJump' fn seen' (JXX cc id')
Just (DestImm imm) -> shortcutJump' fn seen' (JXX_GBL cc imm)
where seen' = setInsert id seen
shortcutJump' fn _ (JMP_TBL addr blocks section tblId) =
let updateBlock (Just (DestBlockId bid)) =
case fn bid of
Nothing -> Just (DestBlockId bid )
Just dest -> Just dest
updateBlock dest = dest
blocks' = map updateBlock blocks
in JMP_TBL addr blocks' section tblId
shortcutJump' _ _ other = other
shortcutStatics :: (BlockId -> Maybe JumpDest) -> (Alignment, RawCmmStatics) -> (Alignment, RawCmmStatics)
shortcutStatics fn (align, CmmStaticsRaw lbl statics)
= (align, CmmStaticsRaw lbl $ map (shortcutStatic fn) statics)
shortcutLabel :: (BlockId -> Maybe JumpDest) -> CLabel -> CLabel
shortcutLabel fn lab
| Just blkId <- maybeLocalBlockLabel lab = shortBlockId fn emptyUniqSet blkId
| otherwise = lab
shortcutStatic :: (BlockId -> Maybe JumpDest) -> CmmStatic -> CmmStatic
shortcutStatic fn (CmmStaticLit (CmmLabel lab))
= CmmStaticLit (CmmLabel (shortcutLabel fn lab))
shortcutStatic fn (CmmStaticLit (CmmLabelDiffOff lbl1 lbl2 off w))
= CmmStaticLit (CmmLabelDiffOff (shortcutLabel fn lbl1) lbl2 off w)
shortcutStatic _ other_static
= other_static
shortBlockId
:: (BlockId -> Maybe JumpDest)
-> UniqSet Unique
-> BlockId
-> CLabel
shortBlockId fn seen blockid =
case (elementOfUniqSet uq seen, fn blockid) of
(True, _) -> blockLbl blockid
(_, Nothing) -> blockLbl blockid
(_, Just (DestBlockId blockid')) -> shortBlockId fn (addOneToUniqSet seen uq) blockid'
(_, Just (DestImm (ImmCLbl lbl))) -> lbl
(_, _other) -> panic "shortBlockId"
where uq = getUnique blockid