#if __GLASGOW_HASKELL__ >= 701
#endif
module CmmStackLayout
( SlotEnv, liveSlotAnal, liveSlotTransfers, removeLiveSlotDefs
, getSpEntryMap, layout, manifestSP, igraph, areaBuilder
, stubSlotsOnDeath )
where
import Constants
import Prelude hiding (succ, zip, unzip, last)
import BlockId
import Cmm
import CmmExpr
import CmmProcPoint
import Maybes
import MkGraph (stackStubExpr)
import Control.Monad
import OptimizationFuel
import Outputable
import SMRep (ByteOff)
import Compiler.Hoopl
import Data.Map (Map)
import qualified Data.Map as Map
import qualified FiniteMap as Map
slotLattice :: DataflowLattice SubAreaSet
slotLattice = DataflowLattice "live slots" Map.empty add
where add _ (OldFact old) (NewFact new) = case Map.foldRightWithKey addArea (False, old) new of
(change, x) -> (changeIf change, x)
addArea a newSlots z = foldr (addSlot a) z newSlots
addSlot a slot (changed, map) =
let (c, live) = liveGen slot $ Map.findWithDefault [] a map
in (c || changed, Map.insert a live map)
slotLatticeJoin :: [SubAreaSet] -> SubAreaSet
slotLatticeJoin facts = foldr extend (fact_bot slotLattice) facts
where extend fact res = snd $ fact_join slotLattice undefined (OldFact fact) (NewFact res)
type SlotEnv = BlockEnv SubAreaSet
liveSlotAnal :: CmmGraph -> FuelUniqSM SlotEnv
liveSlotAnal g = liftM snd $ dataflowPassBwd g [] $ analBwd slotLattice liveSlotTransfers
liveGen :: SubArea -> [SubArea] -> (Bool, [SubArea])
liveGen s set = liveGen' s set []
where liveGen' s [] z = (True, s : z)
liveGen' s@(a, hi, w) (s'@(a', hi', w') : rst) z =
if a /= a' || hi < lo' || lo > hi' then
liveGen' s rst (s' : z)
else if s' `contains` s then
(False, set)
else
let new_hi = max hi hi'
new_lo = min lo lo'
in liveGen' (a, new_hi, new_hi new_lo) rst z
where lo = hi w
lo' = hi' w'
contains (a, hi, w) (a', hi', w') =
a == a' && hi >= hi' && hi w <= hi' w'
liveKill :: SubArea -> [SubArea] -> [SubArea]
liveKill (a, hi, w) set =
liveKill' set []
where liveKill' [] z = z
liveKill' (s'@(a', hi', w') : rst) z =
if a /= a' || hi < lo' || lo > hi' then
liveKill' rst (s' : z)
else
let z' = if hi' > hi then (a, hi', hi' hi) : z else z
z'' = if lo > lo' then (a, lo, lo lo') : z' else z'
in liveKill' rst z''
where lo = hi w
lo' = hi' w'
liveSlotTransfers :: BwdTransfer CmmNode SubAreaSet
liveSlotTransfers = mkBTransfer3 frt mid lst
where frt :: CmmNode C O -> SubAreaSet -> SubAreaSet
frt (CmmEntry l) f = Map.delete (CallArea (Young l)) f
mid :: CmmNode O O -> SubAreaSet -> SubAreaSet
mid n f = foldSlotsUsed addSlot (removeLiveSlotDefs f n) n
lst :: CmmNode O C -> FactBase SubAreaSet -> SubAreaSet
lst n f = liveInSlots n $ case n of
CmmCall {cml_cont=Nothing, cml_args=args} -> add_area (CallArea Old) args out
CmmCall {cml_cont=Just k, cml_args=args} -> add_area (CallArea Old) args (add_area (CallArea (Young k)) args out)
CmmForeignCall {succ=k, updfr=oldend} -> add_area (CallArea Old) oldend (add_area (CallArea (Young k)) wORD_SIZE out)
_ -> out
where out = joinOutFacts slotLattice n f
add_area _ n live | n == 0 = live
add_area a n live = Map.insert a (snd $ liveGen (a, n, n) $ Map.findWithDefault [] a live) live
liftToArea :: Area -> ([SubArea] -> [SubArea]) -> SubAreaSet -> SubAreaSet
addSlot, removeSlot :: SubAreaSet -> SubArea -> SubAreaSet
elemSlot :: SubAreaSet -> SubArea -> Bool
liftToArea a f map = Map.insert a (f (Map.findWithDefault [] a map)) map
addSlot live (a, i, w) = liftToArea a (snd . liveGen (a, i, w)) live
removeSlot live (a, i, w) = liftToArea a (liveKill (a, i, w)) live
elemSlot live (a, i, w) =
not $ fst $ liveGen (a, i, w) (Map.findWithDefault [] a live)
removeLiveSlotDefs :: (DefinerOfSlots s, UserOfSlots s) => SubAreaSet -> s -> SubAreaSet
removeLiveSlotDefs = foldSlotsDefd removeSlot
liveInSlots :: (DefinerOfSlots s, UserOfSlots s) => s -> SubAreaSet -> SubAreaSet
liveInSlots x live = foldSlotsUsed addSlot (removeLiveSlotDefs live x) x
liveLastIn :: CmmNode O C -> (BlockId -> SubAreaSet) -> SubAreaSet
liveLastIn l env = liveInSlots l (liveLastOut env l)
liveLastOut :: (BlockId -> SubAreaSet) -> CmmNode O C -> SubAreaSet
liveLastOut env l =
case l of
CmmCall _ Nothing n _ _ ->
add_area (CallArea Old) n out
CmmCall _ (Just k) n _ _ ->
add_area (CallArea Old) n (add_area (CallArea (Young k)) n out)
CmmForeignCall { succ = k, updfr = oldend } ->
add_area (CallArea Old) oldend (add_area (CallArea (Young k)) wORD_SIZE out)
_ -> out
where out = slotLatticeJoin $ map env $ successors l
add_area _ n live | n == 0 = live
add_area a n live =
Map.insert a (snd $ liveGen (a, n, n) $ Map.findWithDefault [] a live) live
type Set x = Map x ()
data IGraphBuilder n =
Builder { foldNodes :: forall z. SubArea -> (n -> z -> z) -> z -> z
, _wordsOccupied :: AreaSizeMap -> AreaMap -> n -> [Int]
}
areaBuilder :: IGraphBuilder Area
areaBuilder = Builder fold words
where fold (a, _, _) f z = f a z
words areaSize areaMap a =
case Map.lookup a areaMap of
Just addr -> [addr .. addr + (Map.lookup a areaSize `orElse`
pprPanic "wordsOccupied: unknown area" (ppr areaSize <+> ppr a))]
Nothing -> []
type IGraph x = Map x (Set x)
type IGPair x = (IGraph x, IGraphBuilder x)
igraph :: (Ord x) => IGraphBuilder x -> SlotEnv -> CmmGraph -> IGraph x
igraph builder env g = foldr interfere Map.empty (postorderDfs g)
where foldN = foldNodes builder
interfere block igraph = foldBlockNodesB3 (first, middle, last) block igraph
where first _ (igraph, _) = igraph
middle node (igraph, liveOut) =
(addEdges igraph node liveOut, liveInSlots node liveOut)
last node igraph =
(addEdges igraph node $ liveLastOut env' node, liveLastIn node env')
addEdges igraph i out = fst $ foldSlotsDefd addDef (igraph, out) i
addDef (igraph, out) def@(a, _, _) =
(foldN def (addDefN out) igraph,
Map.insert a (snd $ liveGen def (Map.findWithDefault [] a out)) out)
addDefN out n igraph =
let addEdgeNO o igraph = foldN o addEdgeNN igraph
addEdgeNN n' igraph = addEdgeNN' n n' $ addEdgeNN' n' n igraph
addEdgeNN' n n' igraph = Map.insert n (Map.insert n' () set) igraph
where set = Map.findWithDefault Map.empty n igraph
in Map.foldRightWithKey (\ _ os igraph -> foldr addEdgeNO igraph os) igraph out
env' bid = mapLookup bid env `orElse` panic "unknown blockId in igraph"
type AreaSizeMap = AreaMap
getAreaSize :: ByteOff -> CmmGraph -> AreaSizeMap
getAreaSize entry_off g =
foldGraphBlocks (foldBlockNodesF3 (first, add_regslots, last))
(Map.singleton (CallArea Old) entry_off) g
where first _ z = z
last :: CmmNode O C -> Map Area Int -> Map Area Int
last l@(CmmCall _ Nothing args res _) z = add_regslots l (add (add z area args) area res)
where area = CallArea Old
last l@(CmmCall _ (Just k) args res _) z = add_regslots l (add (add z area args) area res)
where area = CallArea (Young k)
last l@(CmmForeignCall {succ = k}) z = add_regslots l (add z area wORD_SIZE)
where area = CallArea (Young k)
last l z = add_regslots l z
add_regslots i z = foldSlotsUsed addSlot (foldSlotsDefd addSlot z i) i
addSlot z (a@(RegSlot (LocalReg _ ty)), _, _) =
add z a $ widthInBytes $ typeWidth ty
addSlot z _ = z
add z a off = Map.insert a (max off (Map.findWithDefault 0 a z)) z
conflictSlots :: Ord x => IGPair x -> AreaSizeMap -> AreaMap -> SubArea -> Set Int
conflictSlots (ig, Builder foldNodes wordsOccupied) areaSize areaMap subarea =
foldNodes subarea foldNode Map.empty
where foldNode n set = Map.foldRightWithKey conflict set $ Map.findWithDefault Map.empty n ig
conflict n' () set = liveInSlots areaMap n' set
liveInSlots areaMap n set = foldr setAdd set (wordsOccupied areaSize areaMap n)
setAdd w s = Map.insert w () s
freeSlotFrom :: Ord x => IGPair x -> AreaSizeMap -> Int -> AreaMap -> Area -> Int
freeSlotFrom ig areaSize offset areaMap area =
let size = Map.lookup area areaSize `orElse` 0
conflicts = conflictSlots ig areaSize areaMap (area, size, size)
align = case area of CallArea _ -> align'
RegSlot r | isGcPtrType (localRegType r) -> align'
RegSlot _ -> id
align' n = (n + (wORD_SIZE 1)) `div` wORD_SIZE * wORD_SIZE
findSpace curr 0 = curr
findSpace curr cnt =
if Map.member curr conflicts then
findSpace (align (curr + size)) size
else findSpace (curr 1) (cnt 1)
in findSpace (align (offset + size)) size
allocSlotFrom :: Ord x => IGPair x -> AreaSizeMap -> Int -> AreaMap -> Area -> AreaMap
allocSlotFrom ig areaSize from areaMap area =
if Map.member area areaMap then areaMap
else Map.insert area (freeSlotFrom ig areaSize from areaMap area) areaMap
type SpEntryMap = BlockEnv Int
getSpEntryMap :: Int -> CmmGraph -> SpEntryMap
getSpEntryMap entry_off g@(CmmGraph {g_entry = entry})
= foldGraphBlocks add_sp_off (mapInsert entry entry_off emptyBlockMap) g
where add_sp_off :: CmmBlock -> BlockEnv Int -> BlockEnv Int
add_sp_off b env =
case lastNode b of
CmmCall {cml_cont=Just succ, cml_ret_args=off} -> mapInsert succ off env
CmmForeignCall {succ=succ} -> mapInsert succ wORD_SIZE env
_ -> env
layout :: ProcPointSet -> SpEntryMap -> SlotEnv -> ByteOff -> CmmGraph -> AreaMap
layout procPoints spEntryMap env entry_off g =
let ig = (igraph areaBuilder env g, areaBuilder)
env' bid = mapLookup bid env `orElse` panic "unknown blockId in igraph"
areaSize = getAreaSize entry_off g
youngest_live :: AreaMap
-> SubAreaSet
-> ByteOff
youngest_live areaMap live = fold_subareas young_slot live 0
where young_slot (a, o, _) z = case Map.lookup a areaMap of
Just top -> max z $ top + o
Nothing -> z
fold_subareas f m z = Map.foldRightWithKey (\_ s z -> foldr f z s) z m
allocVarSlot = allocSlotFrom ig areaSize 0
setSuccSPs inSp bid areaMap =
case (Map.lookup area areaMap , mapLookup bid (toBlockMap g)) of
(Just _, _) -> areaMap
(Nothing, Just _) ->
if setMember bid procPoints then
let young = youngest_live areaMap $ env' bid
start = young
in allocSlotFrom ig areaSize start areaMap area
else Map.insert area inSp areaMap
(_, Nothing) -> panic "Block not found in cfg"
where area = CallArea (Young bid)
layoutAreas areaMap block = foldBlockNodesF3 (flip const, allocMid, allocLast (entryLabel block)) block areaMap
allocMid m areaMap = foldSlotsDefd alloc' (foldSlotsUsed alloc' areaMap m) m
allocLast bid l areaMap =
foldr (setSuccSPs inSp) areaMap' (successors l)
where inSp = slot + spOffset
spOffset = mapLookup bid spEntryMap `orElse` 0
slot = expectJust "slot in" $ Map.lookup (CallArea (Young bid)) areaMap
areaMap' = foldSlotsDefd alloc' (foldSlotsUsed alloc' areaMap l) l
alloc' areaMap (a@(RegSlot _), _, _) = allocVarSlot areaMap a
alloc' areaMap _ = areaMap
initMap = Map.insert (CallArea (Young (g_entry g))) 0
. Map.insert (CallArea Old) 0
$ Map.empty
areaMap = foldl layoutAreas initMap (postorderDfs g)
in
areaMap
manifestSP :: SpEntryMap -> AreaMap -> ByteOff -> CmmGraph -> FuelUniqSM CmmGraph
manifestSP spEntryMap areaMap entry_off g@(CmmGraph {g_entry=entry}) =
ofBlockMap entry `liftM` foldl replB (return mapEmpty) (postorderDfs g)
where slot a =
Map.lookup a areaMap `orElse` panic "unallocated Area"
slot' (Just id) = slot $ CallArea (Young id)
slot' Nothing = slot $ CallArea Old
sp_high = maxSlot slot g
proc_entry_sp = slot (CallArea Old) + entry_off
spOffset id = mapLookup id spEntryMap `orElse` 0
sp_on_entry id | id == entry = proc_entry_sp
sp_on_entry id = slot' (Just id) + spOffset id
replB :: FuelUniqSM (BlockEnv CmmBlock) -> CmmBlock -> FuelUniqSM (BlockEnv CmmBlock)
replB blocks block =
do let (head, middles, JustC tail :: MaybeC C (CmmNode O C)) = blockToNodeList block
middles' = map (middle spIn) middles
bs <- replLast head middles' tail
flip (foldr insertBlock) bs `liftM` blocks
where spIn = sp_on_entry (entryLabel block)
middle spOff m = mapExpDeep (replSlot spOff) m
last spOff l = mapExpDeep (replSlot spOff) l
replSlot spOff (CmmStackSlot a i) = CmmRegOff (CmmGlobal Sp) (spOff (slot a + i))
replSlot _ (CmmLit CmmHighStackMark) =
CmmLit (CmmInt (toInteger (max 0 (sp_high proc_entry_sp))) (typeWidth bWord))
replSlot _ (CmmMachOp (MO_U_Lt _)
[CmmMachOp (MO_Sub _)
[ CmmReg (CmmGlobal Sp)
, CmmLit (CmmInt 0 _)],
CmmReg (CmmGlobal SpLim)]) = CmmLit (CmmInt 0 wordWidth)
replSlot _ e = e
replLast :: MaybeC C (CmmNode C O) -> [CmmNode O O] -> CmmNode O C -> FuelUniqSM [CmmBlock]
replLast h m l@(CmmCall _ k n _ _) = updSp (slot' k + n) h m l
replLast h m l@(CmmForeignCall {succ=k}) = updSp (slot' (Just k) + wORD_SIZE) h m l
replLast h m l@(CmmBranch k) = updSp (sp_on_entry k) h m l
replLast h m l = uncurry (:) `liftM` foldr succ (return (b, [])) (successors l)
where b :: CmmBlock
b = updSp' spIn h m l
succ succId z =
let succSp = sp_on_entry succId in
if succSp /= spIn then
do (b, bs) <- z
(b', bs') <- insertBetween b (adjustSp succSp) succId
return (b', bs' ++ bs)
else z
updSp sp h m l = return [updSp' sp h m l]
updSp' sp h m l | sp == spIn = blockOfNodeList (h, m, JustC $ last sp l)
| otherwise = blockOfNodeList (h, m ++ adjustSp sp, JustC $ last sp l)
adjustSp sp = [CmmAssign (CmmGlobal Sp) e]
where e = CmmMachOp (MO_Add wordWidth) [CmmReg (CmmGlobal Sp), off]
off = CmmLit $ CmmInt (toInteger $ spIn sp) wordWidth
maxSlot :: (Area -> Int) -> CmmGraph -> Int
maxSlot slotOff g = foldGraphBlocks (foldBlockNodesF3 (flip const, highSlot, highSlot)) 0 g
where highSlot i z = foldSlotsUsed add (foldSlotsDefd add z i) i
add z (a, i, _) = max z (slotOff a + i)
stubSlotsOnDeath :: CmmGraph -> FuelUniqSM CmmGraph
stubSlotsOnDeath g = liftM fst $ dataflowPassBwd g [] $ analRewBwd slotLattice
liveSlotTransfers
rewrites
where rewrites = mkBRewrite3 frt mid lst
frt _ _ = return Nothing
mid m liveSlots = return $ foldSlotsUsed (stub liveSlots m) Nothing m
lst _ _ = return Nothing
stub liveSlots m rst subarea@(a, off, w) =
if elemSlot liveSlots subarea then rst
else let store = mkMiddle $ CmmStore (CmmStackSlot a off)
(stackStubExpr (widthFromBytes w))
in case rst of Nothing -> Just (mkMiddle m <*> store)
Just g -> Just (g <*> store)