-import UniqSupply
-import UniqFM
-import UniqSet
-import Unique
-
-import Monad
-import IO
-
---------------------------------------------------------------------------------
--- Monad for the CPSer
--- Contains:
--- * State for the uniqSupply
-
-data CPSState = CPSState { cps_uniqs :: UniqSupply }
-
-data CPS a = CPS { runCPS :: CPSState -> (CPSState, a) }
-
-instance Monad CPS where
- return a = CPS $ \s -> (s, a)
- (CPS m) >>= f = CPS $ \s ->
- let (s', m') = m s
- in runCPS (f m') s'
-
---------------------------------------------------------------------------------
--- Utility functions
-
-getState = CPS $ \s -> (s, s)
-putState s = CPS $ \_ -> (s, ())
-
-newLabelCPS = do
- state <- getState
- let (us1, us2) = splitUniqSupply (cps_uniqs state)
- putState $ state { cps_uniqs = us1 }
- return $ BlockId (uniqFromSupply us2)
-
-mapMCmmTop :: (Monad m) => (CmmTop -> m [CmmTop]) -> Cmm -> m Cmm
-mapMCmmTop f (Cmm xs) = liftM Cmm $ liftM concat $ mapM f xs
-
---------------------------------------------------------------------------------
-
--- The format for the call to a continuation
--- The fst is the arguments that must be passed to the continuation
--- by the continuation's caller.
--- The snd is the live values that must be saved on stack.
--- A Nothing indicates an ignored slot.
--- The head of each list is the stack top or the first parameter.
-
--- The format for live values for a particular continuation
--- All on stack for now.
--- Head element is the top of the stack (or just under the header).
--- Nothing means an empty slot.
--- Future possibilities include callee save registers (i.e. passing slots in register)
--- and heap memory (not sure if that's usefull at all though, but it may
--- be worth exploring the design space).
-
-data BrokenBlock
- = BrokenBlock
- BlockId -- Like a CmmBasicBlock
- BlockEntryInfo -- How this block can be entered
- [CmmStmt] -- Like a CmmBasicBlock (but without
- -- the last statement)
- BlockExitInfo -- How the block can be left
-
-data BlockEntryInfo
- = FunctionEntry -- Beginning of function
-
- | ContinuationEntry -- Return point of a call
- CmmFormals {- return values -}
- -- TODO | ProcPointEntry {- no return values, but some live might end up as params -}
-
- | ControlEntry -- A label in the input
-
-data BlockExitInfo
- = ControlExit [BlockId] -- blocks branched to conditionally
- BlockId -- next block (must be a ControlEntry)
-
- | ReturnExit [BlockId] -- blocks branched to conditionally
- CmmActuals -- return values
-
- | TailCallExit [BlockId] -- blocks branched to conditionally
- CmmExpr -- the function to call
- CmmActuals -- arguments to call
-
- | CallExit [BlockId] -- blocks branched to conditionally
- BlockId -- next block after call (must be a ContinuationEntry)
- CmmCallTarget -- the function to call
- CmmFormals -- results from call (redundant with ContinuationEntry)
- CmmActuals -- arguments to call
- (Maybe [GlobalReg]) -- registers that must be saved (TODO)
- -- TODO: | ProcPointExit (needed?)
-
-data CPSBlockInfo
- = ControlBlock -- Consider whether a proc-point might want arguments on stack
- | ContinuationBlock [(CmmReg,MachHint)] {- params -}
- | EntryBlock
-
---type StackFormat = [Maybe LocalReg] -- TODO: consider params as part of format
-data StackFormat
- = StackFormat
- BlockId {- block that is the start of the continuation. may or may not be the current block -}
- WordOff {- total frame size -}
- [(CmmReg, WordOff)] {- local reg offsets from stack top -}
-
--- A block can be a continuation of a call
--- A block can be a continuation of another block (w/ or w/o joins)
--- A block can be an entry to a function
-
---------------------------------------------------------------------------------
--- For now just select the continuation orders in the order they are in the set with no gaps
--- TODO: select a format that keeps blocks that can jump to each other the same
--- Assumed that jumps, calls
-selectStackFormat :: UniqFM {-BlockId-} CmmFormals -> UniqFM {-BlockId-} CmmLive -> UniqFM {-BlockId-} [(CPSBlockInfo, CmmBasicBlock)] -> UniqFM {-BlockId-} StackFormat
-selectStackFormat = undefined
-{-
-selectStackFormat param live blocks = fixedpoint
-listToUFM $ map live_to_format $ ufmToList live
- where
- live_to_format (unique, live) = (unique, format) where
- format = foldl extend_format
- (StackFormat (BlockId unique) retAddrSizeW [])
- (uniqSetToList live)
- extend_format :: StackFormat -> LocalReg -> StackFormat
- extend_format (StackFormat block size offsets) reg =
- StackFormat block (slot_size reg + size) ((CmmLocal reg, size) : offsets)
--}
-
-selectStackFormat2 :: UniqFM {-BlockId-} CmmLive -> [BrokenBlock] -> UniqFM {-BlockId-} StackFormat
-selectStackFormat2 live blocks = fixedpoint dependants update (map brokenBlockId blocks) emptyUFM where
- blocks_ufm = listToUFM $ map (\b -> (brokenBlockId b, b)) blocks
- dependants ident =
- case lookupWithDefaultUFM blocks_ufm (panic "TODO") ident of
- (BrokenBlock _ _ _ (ControlExit exits next)) -> next:exits
- (BrokenBlock _ _ _ (ReturnExit exits _)) -> exits
- (BrokenBlock _ _ _ (TailCallExit exits _ _)) -> exits
- (BrokenBlock _ _ _ (CallExit exits _ _ _ _ _)) -> exits
- update ident cause formats =
- let BrokenBlock _ entry _ _ = lookupWithDefaultUFM blocks_ufm (panic "unknown BlockId in selectStackFormat:live") ident in
- case cause of
- -- Propagate only to blocks entered by branches (not function entry blocks or continuation entry blocks)
- Just cause_name ->
- let cause_format = lookupWithDefaultUFM formats (panic "update signaled for block not in format") cause_name
- in case entry of
- ControlEntry -> Just $ addToUFM formats ident cause_format
- FunctionEntry -> Nothing
- ContinuationEntry _ -> Nothing
- -- Do initial calculates for function blocks
- Nothing ->
- case entry of
- ControlEntry -> Nothing
- FunctionEntry -> Just $ addToUFM formats ident $ StackFormat ident 0 []
- ContinuationEntry _ -> Just $ addToUFM formats ident $ live_to_format ident $ lookupWithDefaultUFM live (panic "TODO") ident
- live_to_format label live =
- foldl extend_format
- (StackFormat label retAddrSizeW [])
- (uniqSetToList live)
- extend_format :: StackFormat -> LocalReg -> StackFormat
- extend_format (StackFormat block size offsets) reg =
- StackFormat block (slot_size reg + size) ((CmmLocal reg, size) : offsets)
-
-slot_size reg = ((machRepByteWidth (localRegRep reg) - 1) `div` wORD_SIZE) + 1
-
-transformReturn :: UniqFM {-BlockId-} CPSBlockInfo -> UniqFM {-BlockId-} StackFormat -> CmmBasicBlock -> CmmBasicBlock
-transformReturn block_infos formats (BasicBlock ident stmts) =
- -- NOTE: assumes that return/jump can *only* appear at end of block
- case last stmts of
- CmmReturn arguments ->
- BasicBlock ident $
- (init stmts) ++
- exit_function curr_format (CmmLoad (CmmReg spReg) wordRep) arguments
- CmmJump target arguments ->
- BasicBlock ident $
- (init stmts) ++
- exit_function curr_format target arguments
- _ -> BasicBlock ident stmts
- where
- curr_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
-
-destructContinuation :: UniqFM {-BlockId-} CPSBlockInfo -> UniqFM {-BlockId-} StackFormat -> CmmBasicBlock -> CmmBasicBlock
-destructContinuation block_infos formats (BasicBlock ident stmts) =
- case info of
- ControlBlock -> BasicBlock ident stmts
- ContinuationBlock _ -> BasicBlock ident (unpack_continuation curr_format ++ stmts)
- where
- info = lookupWithDefaultUFM block_infos (panic $ "info: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
- curr_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
-
-constructContinuation2 :: UniqFM {-BlockId-} StackFormat -> BrokenBlock -> CmmBasicBlock
-constructContinuation2 formats (BrokenBlock ident entry stmts exit) =
- BasicBlock ident (prefix++stmts++postfix)
- where
- curr_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
- prefix = case entry of
- ControlEntry -> []
- FunctionEntry -> []
- ContinuationEntry formals -> unpack_continuation curr_format
- postfix = case exit of
- ControlExit _ next -> [CmmBranch next]
- ReturnExit _ arguments -> exit_function curr_format (CmmLoad (CmmReg spReg) wordRep) arguments
- TailCallExit _ target arguments -> exit_function curr_format target arguments
- -- TODO: do something about global saves
- CallExit _ next (CmmForeignCall target CmmCallConv) results arguments saves ->
- let cont_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique next)) next
- in pack_continuation curr_format cont_format ++
- [CmmJump target arguments]
- CallExit _ next _ results arguments saves -> panic "unimplemented CmmCall"
-
-constructContinuation :: UniqFM {-BlockId-} CPSBlockInfo -> UniqFM {-BlockId-} StackFormat -> CmmBasicBlock -> CmmBasicBlock
-constructContinuation block_infos formats (BasicBlock ident stmts) =
- case last $ init stmts of
- -- TODO: global_saves
- --CmmCall (CmmForeignCall target CmmCallConv) results arguments (Just []) -> --TODO: handle globals
- CmmCall (CmmForeignCall target CmmCallConv) results arguments _ ->
- BasicBlock ident $
- init (init stmts) ++
- pack_continuation curr_format cont_format ++
- [CmmJump target arguments]
- CmmCall target results arguments _ -> panic "unimplemented CmmCall"
- -- TODO: branches for proc-points
- -- _ -> BasicBlock ident $ (init stmts) ++ build_block_branch
- _ -> BasicBlock ident stmts
- where
- info = lookupWithDefaultUFM block_infos (panic $ "info: unknown block " ++ (showSDoc $ ppr $ getUnique next_block)) next_block
- cont_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique next_block)) next_block
- curr_format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique next_block)) ident
- next_block = case last stmts of
- CmmBranch next -> next
- -- TODO: blocks with jump at end
- -- TODO: blocks with return at end
- _ -> panic $ "basic block without a branch at the end (unimplemented) " ++ (showSDoc $ ppr $ stmts)
- next_block_as_proc_expr = CmmLit $ CmmLabel $ mkReturnPtLabel $ getUnique next_block
- block_needs_call = True -- TODO: use a table (i.e. proc-point)
- build_block_branch =
- if block_needs_call
- then [CmmJump next_block_as_proc_expr [] {- TODO: pass live -}] {- NOTE: a block can never be both a continuation and a controll block -}
- else [CmmBranch next_block]
-
---------------------------------------------------------------------------------
--- Functions that generate CmmStmt sequences
--- for packing/unpacking continuations
--- and entering/exiting functions
-
-exit_function :: StackFormat -> CmmExpr -> CmmActuals -> [CmmStmt]
-exit_function (StackFormat curr_id curr_frame_size curr_offsets) target arguments
- = adjust_spReg ++ jump where
- adjust_spReg = [
- CmmAssign spReg
- (CmmRegOff spReg (curr_frame_size*wORD_SIZE))]
- jump = [CmmJump target arguments]
-
-enter_function :: WordOff -> [CmmStmt]
-enter_function max_frame_size
- = check_stack_limit where
- check_stack_limit = [
- CmmCondBranch
- (CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
- [CmmRegOff spReg max_frame_size, CmmReg spLimReg])
- gc_block]
- gc_block = undefined -- TODO: get stack and heap checks to go to same
-
--- TODO: fix branches to proc point (we have to insert a new block to marshel the continuation)
-pack_continuation :: StackFormat -> StackFormat -> [CmmStmt]
-pack_continuation (StackFormat curr_id curr_frame_size curr_offsets)
- (StackFormat cont_id cont_frame_size cont_offsets)
- = save_live_values ++ set_stack_header ++ adjust_spReg where
- -- TODO: only save variables when actually needed
- save_live_values =
- [CmmStore
- (CmmRegOff
- spReg (wORD_SIZE*(curr_frame_size - cont_frame_size + offset)))
- (CmmReg reg)
- | (reg, offset) <- cont_offsets]
- set_stack_header = -- TODO: only set when needed
- [CmmStore (CmmRegOff spReg (wORD_SIZE*(curr_frame_size - cont_frame_size))) continuation_function]
- continuation_function = CmmLit $ CmmLabel $ mkReturnPtLabel $ getUnique cont_id
- adjust_spReg =
- if curr_frame_size == cont_frame_size
- then []
- else [CmmAssign spReg (CmmRegOff spReg ((curr_frame_size - cont_frame_size)*wORD_SIZE))]
-
--- Lazy adjustment of stack headers assumes all blocks
--- that could branch to eachother (i.e. control blocks)
--- have the same stack format (this causes a problem
--- only for proc-point).
-unpack_continuation :: StackFormat -> [CmmStmt]
-unpack_continuation (StackFormat curr_id curr_frame_size curr_offsets)
- = load_live_values where
- -- TODO: only save variables when actually needed
- load_live_values =
- [CmmAssign
- reg
- (CmmLoad (CmmRegOff spReg (wORD_SIZE*offset)) (cmmRegRep reg))
- | (reg, offset) <- curr_offsets]
-
--- TODO: TBD when to adjust the stack
-
-cpsProc :: CmmTop -> CPS [CmmTop]
-cpsProc x@(CmmData _ _) = return [x]
-cpsProc x@(CmmProc info_table ident params blocks) = do
-
- broken_blocks <- liftM concat $ mapM breakBlock blocks
- broken_blocks2 <- liftM concat (zipWithM breakBlock2 blocks (FunctionEntry:repeat ControlEntry))
- -- broken_blocks :: [BrokenBlock]
-
- let live = cmmLiveness (map snd broken_blocks)
- let live2 :: BlockEntryLiveness
- live2 = cmmLiveness2 broken_blocks2
-
- let blocks_with_live = map (cmmLivenessComment live . snd) broken_blocks
-
- let formats = selectStackFormat (panic "params to selectStackFormat" {-TODO-}) live (undefined)
- let formats2 :: BlockEnv StackFormat -- Stack format on entry
- formats2 = selectStackFormat2 live2 broken_blocks2
-
- let block_infos = listToUFM $ map (\(info, block) -> (blockId block, info)) broken_blocks
- --let blocks_with_live' = map (constructContinuation block_infos formats) blocks_with_live
- --let blocks_with_live'' = map (destructContinuation block_infos formats) blocks_with_live'
- --let blocks_with_live''' = map (transformReturn block_infos formats) blocks_with_live''
-
- return $ [CmmProc info_table ident params $ map (constructContinuation2 formats2) broken_blocks2]
-{-
- return $ [CmmProc info_table ident params $
- map (constructContinuation block_infos formats .
- destructContinuation block_infos formats .
- transformReturn block_infos formats)
- blocks_with_live]