import CmmLint
import PprCmm
-import Dataflow
import CmmLive
import CmmBrokenBlock
import CmmProcPoint
import CmmCallConv
+import CmmInfo
+import CmmUtils
+import Bitmap
+import ClosureInfo
import MachOp
import ForeignCall
import CLabel
-- |Top level driver for the CPS pass
-----------------------------------------------------------------------------
cmmCPS :: DynFlags -- ^ Dynamic flags: -dcmm-lint -ddump-cps-cmm
- -> [Cmm] -- ^ Input C-- with Proceedures
- -> IO [Cmm] -- ^ Output CPS transformed C--
+ -> [GenCmm CmmStatic CmmInfo CmmStmt] -- ^ Input C-- with Proceedures
+ -> IO [GenCmm CmmStatic [CmmStatic] CmmStmt] -- ^ Output CPS transformed C--
cmmCPS dflags abstractC = do
when (dopt Opt_DoCmmLinting dflags) $
do showPass dflags "CmmLint"
return continuationC
+stg_gc_gen = mkRtsApFastLabel SLIT("gen_cg_TODO") --panic "Need the label for gc"
+make_gc_block block_id fun_label formals safety = BasicBlock block_id stmts
+ where
+ stmts = [CmmCall stg_gc_gen_target [] [] safety,
+ CmmJump fun_expr actuals]
+ stg_gc_gen_target =
+ CmmForeignCall (CmmLit (CmmLabel stg_gc_gen)) CmmCallConv
+ actuals = map (\x -> (CmmReg (CmmLocal x), NoHint)) formals
+ fun_expr = CmmLit (CmmLabel fun_label)
+
+force_gc_block old_info block_id fun_label formals =
+ case old_info of
+ CmmNonInfo (Just _) -> (old_info, [])
+ CmmInfo _ (Just _) _ _ -> (old_info, [])
+ CmmNonInfo Nothing
+ -> (CmmNonInfo (Just block_id),
+ [make_gc_block block_id fun_label formals (CmmSafe NoC_SRT)])
+ CmmInfo prof Nothing type_tag type_info
+ -> (CmmInfo prof (Just block_id) type_tag type_info,
+ [make_gc_block block_id fun_label formals (CmmSafe srt)])
+ where
+ srt = case type_info of
+ ConstrInfo _ _ _ -> NoC_SRT
+ FunInfo _ srt' _ _ _ _ -> srt'
+ ThunkInfo _ srt' -> srt'
+ ThunkSelectorInfo _ srt' -> srt'
+ ContInfo _ srt' -> srt'
+
-----------------------------------------------------------------------------
-- |CPS a single CmmTop (proceedure)
-- Only 'CmmProc' are transformed 'CmmData' will be left alone.
-----------------------------------------------------------------------------
cpsProc :: UniqSupply
- -> CmmTop -- ^Input proceedure
- -> [CmmTop] -- ^Output proceedure and continuations
-cpsProc uniqSupply x@(CmmData _ _) = [x]
-cpsProc uniqSupply x@(CmmProc info_table ident params blocks) = cps_procs
+ -> GenCmmTop CmmStatic CmmInfo CmmStmt -- ^Input proceedure
+ -> [GenCmmTop CmmStatic [CmmStatic] CmmStmt] -- ^Output proceedure and continuations
+cpsProc uniqSupply (CmmData sec dat) = [CmmData sec dat]
+cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
where
- uniqes :: [[Unique]]
- uniqes = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
+ uniques :: [[Unique]]
+ uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
+ (gc_unique:info_uniques):block_uniques = uniques
+
+ -- Ensure that
+ forced_gc :: (CmmInfo, [CmmBasicBlock])
+ forced_gc = force_gc_block info (BlockId gc_unique) ident params
+
+ forced_info = fst forced_gc
+ forced_blocks = blocks ++ snd forced_gc
+ forced_gc_id = case forced_info of
+ CmmNonInfo (Just x) -> x
+ CmmInfo _ (Just x) _ _ -> x
-- Break the block at each function call.
-- The part after the function call will have to become a continuation.
broken_blocks :: [BrokenBlock]
broken_blocks =
- concat $ zipWith3 breakBlock uniqes blocks
- (FunctionEntry ident params:repeat ControlEntry)
+ concat $ zipWith3 breakBlock block_uniques forced_blocks
+ (FunctionEntry forced_info ident params:repeat ControlEntry)
-- Calculate live variables for each broken block.
--
block_env = blocksToBlockEnv broken_blocks
-- Group the blocks into continuations based on the set of proc-points.
- continuations :: [Continuation]
- continuations = map (gatherBlocksIntoContinuation proc_points block_env)
- (uniqSetToList proc_points)
+ continuations :: [Continuation (Either C_SRT CmmInfo)]
+ continuations = zipWith
+ (gatherBlocksIntoContinuation proc_points block_env)
+ (uniqSetToList proc_points)
+ (Just forced_gc_id : repeat Nothing)
-- Select the stack format on entry to each continuation.
+ -- Return the max stack offset and an association list
--
-- This is an association list instead of a UniqFM because
-- CLabel's don't have a 'Uniqueable' instance.
- formats :: [(CLabel, StackFormat)]
+ formats :: [(CLabel, -- key
+ (Maybe CLabel, -- label in top slot
+ [Maybe LocalReg]))] -- slots
formats = selectStackFormat live continuations
+ -- Do a little meta-processing on the stack formats such as
+ -- getting the individual frame sizes and the maximum frame size
+ formats' :: (WordOff, [(CLabel, StackFormat)])
+ formats' = processFormats formats continuations
+
+ -- Update the info table data on the continuations with
+ -- the selected stack formats.
+ continuations' :: [Continuation CmmInfo]
+ continuations' = map (applyStackFormat (snd formats')) continuations
+
-- Do the actual CPS transform.
cps_procs :: [CmmTop]
- cps_procs = map (continuationToProc formats) continuations
+ cps_procs = map (continuationToProc formats') continuations'
+
+ -- Convert the info tables from CmmInfo to [CmmStatic]
+ -- We might want to put this in another pass eventually
+ info_procs :: [RawCmmTop]
+ info_procs = concat (zipWith mkInfoTable info_uniques cps_procs)
--------------------------------------------------------------------------------
-- and heap memory (not sure if that's usefull at all though, but it may
-- be worth exploring the design space).
-continuationLabel (Continuation _ _ l _ _) = l
-data Continuation =
+continuationLabel (Continuation _ l _ _) = l
+data Continuation info =
Continuation
- Bool -- True => Function entry, False => Continuation/return point
- [CmmStatic] -- Info table, may be empty
+ info -- Left <=> Continuation created by the CPS
+ -- Right <=> Function or Proc point
CLabel -- Used to generate both info & entry labels
CmmFormals -- Argument locals live on entry (C-- procedure params)
- [BrokenBlock] -- Code, may be empty. The first block is
+ [BrokenBlock] -- Code, may be empty. The first block is
-- the entry point. The order is otherwise initially
-- unimportant, but at some point the code gen will
-- fix the order.
-- to a label. To jump to the first block in a Proc,
-- use the appropriate CLabel.
--- Describes the layout of a stack frame for a continuation
data StackFormat
= StackFormat {
stack_label :: Maybe CLabel, -- The label occupying the top slot
stack_frame_size :: WordOff, -- Total frame size in words (not including arguments)
- stack_live :: [(LocalReg, WordOff)] -- local reg offsets from stack top
- -- TODO: see if the above can be LocalReg
+ stack_live :: [Maybe LocalReg] -- local reg offsets from stack top
}
-- A block can be a continuation of a call
collectNonProcPointTargets ::
UniqSet BlockId -> BlockEnv BrokenBlock
- -> UniqSet BlockId -> BlockId -> UniqSet BlockId
-collectNonProcPointTargets proc_points blocks current_targets block =
+ -> UniqSet BlockId -> [BlockId] -> UniqSet BlockId
+collectNonProcPointTargets proc_points blocks current_targets new_blocks =
if sizeUniqSet current_targets == sizeUniqSet new_targets
then current_targets
- else foldl (collectNonProcPointTargets proc_points blocks) new_targets targets
+ else foldl
+ (collectNonProcPointTargets proc_points blocks)
+ new_targets
+ (map (:[]) targets)
where
- block' = lookupWithDefaultUFM blocks (panic "TODO") block
+ blocks' = map (lookupWithDefaultUFM blocks (panic "TODO")) new_blocks
targets =
-- Note the subtlety that since the extra branch after a call
-- will always be to a block that is a proc-point,
-- this subtraction will always remove that case
- uniqSetToList $ (mkUniqSet $ brokenBlockTargets block') `minusUniqSet` proc_points
+ uniqSetToList $ (unionManyUniqSets $ map (mkUniqSet . brokenBlockTargets) blocks')
+ `minusUniqSet` proc_points
-- TODO: remove redundant uniqSetToList
new_targets = current_targets `unionUniqSets` (mkUniqSet targets)
gatherBlocksIntoContinuation ::
UniqSet BlockId -> BlockEnv BrokenBlock
- -> BlockId -> Continuation
-gatherBlocksIntoContinuation proc_points blocks start =
- Continuation is_entry info_table clabel params body
+ -> BlockId -> Maybe BlockId -> Continuation (Either C_SRT CmmInfo)
+gatherBlocksIntoContinuation proc_points blocks start gc =
+ Continuation info_table clabel params body
where
- children = (collectNonProcPointTargets proc_points blocks (unitUniqSet start) start) `delOneFromUniqSet` start
+ start_and_gc = start : maybeToList gc
+ children = (collectNonProcPointTargets proc_points blocks (mkUniqSet start_and_gc) start_and_gc) `minusUniqSet` (mkUniqSet start_and_gc)
start_block = lookupWithDefaultUFM blocks (panic "TODO") start
+ gc_block = map (lookupWithDefaultUFM blocks (panic "TODO)")) (maybeToList gc)
children_blocks = map (lookupWithDefaultUFM blocks (panic "TODO")) (uniqSetToList children)
- body = start_block : children_blocks
- info_table = [] -- TODO
+ body = start_block : gc_block ++ children_blocks
+
+ -- We can't properly annotate the continuation's stack parameters
+ -- at this point because this is before stack selection
+ -- but we want to keep the C_SRT around so we use 'Either'.
+ info_table = case start_block_entry of
+ FunctionEntry info _ _ -> Right info
+ ContinuationEntry _ srt -> Left srt
+ ControlEntry -> Right (CmmNonInfo Nothing)
+
start_block_entry = brokenBlockEntry start_block
- is_entry = case start_block_entry of
- FunctionEntry _ _ -> True
- _ -> False
clabel = case start_block_entry of
- FunctionEntry label _ -> label
+ FunctionEntry _ label _ -> label
_ -> mkReturnPtLabel $ getUnique start
params = case start_block_entry of
- FunctionEntry _ args -> args
- ContinuationEntry args -> args
+ FunctionEntry _ _ args -> args
+ ContinuationEntry args _ -> args
ControlEntry -> [] -- TODO: it's a proc-point, we could pass lives in parameter registers
--------------------------------------------------------------------------------
-- For now just select the continuation orders in the order they are in the set with no gaps
-selectStackFormat :: BlockEnv CmmLive -> [Continuation] -> [(CLabel, StackFormat)]
+selectStackFormat :: BlockEnv CmmLive
+ -> [Continuation (Either C_SRT CmmInfo)]
+ -> [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
selectStackFormat live continuations =
map (\c -> (continuationLabel c, selectStackFormat' c)) continuations
where
- selectStackFormat' (Continuation True info_table label formals blocks) =
- StackFormat (Just label) 0 []
- selectStackFormat' (Continuation False info_table label formals blocks) =
+ selectStackFormat' (Continuation
+ (Right (CmmInfo _ _ _ (ContInfo format srt)))
+ label _ _) = (Just label, format)
+ selectStackFormat' (Continuation (Right _) _ _ _) = (Nothing, [])
+ selectStackFormat' (Continuation (Left srt) label _ blocks) =
-- TODO: assumes the first block is the entry block
let ident = brokenBlockId $ head blocks -- TODO: CLabel isn't a uniquable, but we need a better way than this
- in live_to_format label formals $ lookupWithDefaultUFM live unknown_block ident
+ in (Just label,
+ map Just $ uniqSetToList $
+ lookupWithDefaultUFM live unknown_block ident)
- live_to_format :: CLabel -> CmmFormals -> CmmLive -> StackFormat
- live_to_format label formals live =
- foldl extend_format
- (StackFormat (Just label) retAddrSizeW [])
- (uniqSetToList (live `minusUniqSet` mkUniqSet formals))
+ unknown_block = panic "unknown BlockId in selectStackFormat"
- extend_format :: StackFormat -> LocalReg -> StackFormat
- extend_format (StackFormat label size offsets) reg =
- StackFormat label (slot_size reg + size) ((reg, size) : offsets)
+processFormats :: [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
+ -> [Continuation (Either C_SRT CmmInfo)]
+ -> (WordOff, [(CLabel, StackFormat)])
+processFormats formats continuations = (max_size, formats')
+ where
+ max_size = maximum $
+ 0 : map (continuationMaxStack formats') continuations
+ formats' = map make_format formats
+ make_format (label, format) =
+ (label,
+ StackFormat {
+ stack_label = fst format,
+ stack_frame_size = stack_size (snd format) +
+ if isJust (fst format)
+ then label_size
+ else 0,
+ stack_live = snd format })
+
+ -- TODO: get rid of "+ 1" etc.
+ label_size = 1 :: WordOff
+
+ stack_size [] = 0
+ stack_size (Nothing:formats) = 1 + stack_size formats -- one dead word
+ stack_size (Just reg:formats) = width + stack_size formats
+ where
+ width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
+ -- TODO: it would be better if we had a machRepWordWidth
+
+continuationMaxStack :: [(CLabel, StackFormat)]
+ -> Continuation a
+ -> WordOff
+continuationMaxStack formats (Continuation _ label _ blocks) =
+ max_arg_size + stack_frame_size stack_format
+ where
+ stack_format = maybe unknown_format id $ lookup label formats
+ unknown_format = panic "Unknown format in continuationMaxStack"
+
+ max_arg_size = maximum $ 0 : map block_max_arg_size blocks
+
+ block_max_arg_size block =
+ maximum (final_arg_size (brokenBlockExit block) :
+ map stmt_arg_size (brokenBlockStmts block))
+
+ final_arg_size (FinalReturn args) =
+ argumentsSize (cmmExprRep . fst) args
+ final_arg_size (FinalJump _ args) =
+ argumentsSize (cmmExprRep . fst) args
+ final_arg_size (FinalCall next _ _ args) =
+ -- We have to account for the stack used when we build a frame
+ -- for the *next* continuation from *this* continuation
+ argumentsSize (cmmExprRep . fst) args +
+ stack_frame_size next_format
+ where
+ next_format = maybe unknown_format id $ lookup next' formats
+ next' = mkReturnPtLabel $ getUnique next
+
+ final_arg_size _ = 0
+
+ stmt_arg_size (CmmJump _ args) =
+ argumentsSize (cmmExprRep . fst) args
+ stmt_arg_size (CmmCall _ _ _ (CmmSafe _)) =
+ panic "Safe call in processFormats"
+ stmt_arg_size (CmmReturn _) =
+ panic "CmmReturn in processFormats"
+ stmt_arg_size _ = 0
+
+-----------------------------------------------------------------------------
+applyStackFormat :: [(CLabel, StackFormat)]
+ -> Continuation (Either C_SRT CmmInfo)
+ -> Continuation CmmInfo
+
+-- User written continuations
+applyStackFormat formats (Continuation
+ (Right (CmmInfo prof gc tag (ContInfo _ srt)))
+ label formals blocks) =
+ Continuation (CmmInfo prof gc tag (ContInfo format srt))
+ label formals blocks
+ where
+ format = stack_live $ maybe unknown_block id $ lookup label formats
+ unknown_block = panic "unknown BlockId in applyStackFormat"
- slot_size :: LocalReg -> Int
- slot_size reg = ((machRepByteWidth (localRegRep reg) - 1) `div` wORD_SIZE) + 1
+-- User written non-continuation code
+applyStackFormat formats (Continuation (Right info) label formals blocks) =
+ Continuation info label formals blocks
- unknown_block = panic "unknown BlockId in selectStackFormat"
+-- CPS generated continuations
+applyStackFormat formats (Continuation (Left srt) label formals blocks) =
+ Continuation (CmmInfo prof gc tag (ContInfo (stack_live $ format) srt))
+ label formals blocks
+ where
+ gc = Nothing -- Generated continuations never need a stack check
+ -- TODO prof: this is the same as the current implementation
+ -- but I think it could be improved
+ prof = ProfilingInfo zeroCLit zeroCLit
+ tag = rET_SMALL -- cmmToRawCmm may convert it to rET_BIG
+ format = maybe unknown_block id $ lookup label formats
+ unknown_block = panic "unknown BlockId in applyStackFormat"
-continuationToProc :: [(CLabel, StackFormat)] -> Continuation -> CmmTop
-continuationToProc formats (Continuation is_entry info label formals blocks) =
- CmmProc info label formals (map (continuationToProc' label formats) blocks)
+-----------------------------------------------------------------------------
+continuationToProc :: (WordOff, [(CLabel, StackFormat)])
+ -> Continuation CmmInfo
+ -> CmmTop
+continuationToProc (max_stack, formats)
+ (Continuation info label formals blocks) =
+ CmmProc info label formals (map continuationToProc' blocks)
where
- continuationToProc' :: CLabel -> [(CLabel, StackFormat)] -> BrokenBlock
- -> CmmBasicBlock
- continuationToProc' curr_ident formats (BrokenBlock ident entry stmts _ exit) =
+ curr_format = maybe unknown_block id $ lookup label formats
+ unknown_block = panic "unknown BlockId in continuationToProc"
+
+ continuationToProc' :: BrokenBlock -> CmmBasicBlock
+ continuationToProc' (BrokenBlock ident entry stmts _ exit) =
BasicBlock ident (prefix++stmts++postfix)
where
- curr_format = maybe unknown_block id $ lookup curr_ident formats
- unknown_block = panic "unknown BlockId in continuationToProc"
prefix = case entry of
ControlEntry -> []
- FunctionEntry _ formals -> -- TODO: gc_stack_check
+ FunctionEntry (CmmInfo _ (Just gc_block) _ _) _ formals ->
+ gc_stack_check gc_block max_stack ++
+ function_entry formals curr_format
+ FunctionEntry (CmmInfo _ Nothing _ _) _ formals ->
+ panic "continuationToProc: missing GC block"
+ FunctionEntry (CmmNonInfo (Just gc_block)) _ formals ->
+ gc_stack_check gc_block max_stack ++
function_entry formals curr_format
- ContinuationEntry formals ->
+ FunctionEntry (CmmNonInfo Nothing) _ formals ->
+ panic "continuationToProc: missing non-info GC block"
+ ContinuationEntry formals _ ->
function_entry formals curr_format
postfix = case exit of
FinalBranch next -> [CmmBranch next]
lookup (mkReturnPtLabel $ getUnique next) formats
FinalCall next _ results arguments -> panic "unimplemented CmmCall"
---------------------------------------------------------------------------------
+-----------------------------------------------------------------------------
-- Functions that generate CmmStmt sequences
-- for packing/unpacking continuations
-- and entering/exiting functions
argument_formats = assignArguments (cmmExprRep . fst) arguments
-gc_stack_check :: WordOff -> [CmmStmt]
-gc_stack_check max_frame_size
+gc_stack_check :: BlockId -> WordOff -> [CmmStmt]
+gc_stack_check gc_block max_frame_size
= check_stack_limit where
check_stack_limit = [
CmmCondBranch
(CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
- [CmmRegOff spReg max_frame_size, CmmReg spLimReg])
+ [CmmRegOff spReg (-max_frame_size*wORD_SIZE),
+ CmmReg spLimReg])
gc_block]
- gc_block = panic "gc_check not implemented" -- 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)
+-- 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 _)
- (StackFormat cont_id cont_frame_size cont_offsets)
+ (StackFormat cont_id cont_frame_size live_regs)
= store_live_values ++ set_stack_header where
- -- TODO: only save variables when actually needed (may be handled by latter pass)
+ -- TODO: only save variables when actually needed
+ -- (may be handled by latter pass)
store_live_values =
[stack_put spRel (CmmReg (CmmLocal reg)) offset
| (reg, offset) <- cont_offsets]
set_stack_header =
- if not needs_header
- then []
- else [stack_put spRel continuation_function 0]
+ if needs_header_set
+ then [stack_put spRel continuation_function 0]
+ else []
+
+ -- TODO: factor with function_entry and CmmInfo.hs(?)
+ cont_offsets = mkOffsets label_size live_regs
+
+ label_size = 1 :: WordOff
+
+ mkOffsets size [] = []
+ mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
+ mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
+ where
+ width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
+ -- TODO: it would be better if we had a machRepWordWidth
spRel = curr_frame_size - cont_frame_size
continuation_function = CmmLit $ CmmLabel $ fromJust cont_id
- needs_header =
+ needs_header_set =
case (curr_id, cont_id) of
(Just x, Just y) -> x /= y
_ -> isJust cont_id
-- have the same stack format (this causes a problem
-- only for proc-point).
function_entry :: CmmFormals -> StackFormat -> [CmmStmt]
-function_entry formals (StackFormat _ _ curr_offsets)
+function_entry formals (StackFormat _ _ live_regs)
= load_live_values ++ load_args where
- -- TODO: only save variables when actually needed (may be handled by latter pass)
+ -- TODO: only save variables when actually needed
+ -- (may be handled by latter pass)
load_live_values =
[stack_get 0 reg offset
| (reg, offset) <- curr_offsets]
argument_formats = assignArguments (localRegRep) formals
+ -- TODO: eliminate copy/paste with pack_continuation
+ curr_offsets = mkOffsets label_size live_regs
+
+ label_size = 1 :: WordOff
+
+ mkOffsets size [] = []
+ mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
+ mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
+ where
+ width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
+ -- TODO: it would be better if we had a machRepWordWidth
+
-----------------------------------------------------------------------------
-- Section: Stack and argument register puts and gets
-----------------------------------------------------------------------------
-> WordOff
-> CmmStmt
stack_get spRel reg offset =
- CmmAssign (CmmLocal reg) (CmmLoad (CmmRegOff spReg (wORD_SIZE*(spRel + offset))) (localRegRep reg))
+ CmmAssign (CmmLocal reg)
+ (CmmLoad (CmmRegOff spReg (wORD_SIZE*(spRel + offset)))
+ (localRegRep reg))
global_put :: CmmExpr -> GlobalReg -> CmmStmt
global_put expr global = CmmAssign (CmmGlobal global) expr
global_get :: LocalReg -> GlobalReg -> CmmStmt