2 -- | Converts C-- with full proceedures and parameters
3 -- to a CPS transformed C-- with the stack made manifest.
7 #include "HsVersions.h"
41 -----------------------------------------------------------------------------
42 -- |Top level driver for the CPS pass
43 -----------------------------------------------------------------------------
44 cmmCPS :: DynFlags -- ^ Dynamic flags: -dcmm-lint -ddump-cps-cmm
45 -> [GenCmm CmmStatic CmmInfo CmmStmt] -- ^ Input C-- with Proceedures
46 -> IO [GenCmm CmmStatic [CmmStatic] CmmStmt] -- ^ Output CPS transformed C--
47 cmmCPS dflags abstractC = do
48 when (dopt Opt_DoCmmLinting dflags) $
49 do showPass dflags "CmmLint"
50 case firstJust $ map cmmLint abstractC of
51 Just err -> do printDump err
56 -- TODO: more lint checking
57 -- check for use of branches to non-existant blocks
58 -- check for use of Sp, SpLim, R1, R2, etc.
60 uniqSupply <- mkSplitUniqSupply 'p'
61 let supplies = listSplitUniqSupply uniqSupply
62 let doCpsProc s (Cmm c) =
63 Cmm $ concat $ zipWith cpsProc (listSplitUniqSupply s) c
64 let continuationC = zipWith doCpsProc supplies abstractC
66 dumpIfSet_dyn dflags Opt_D_dump_cps_cmm "CPS Cmm" (pprCmms continuationC)
68 -- TODO: add option to dump Cmm to file
72 stg_gc_gen = mkRtsApFastLabel SLIT("gen_cg_TODO") --panic "Need the label for gc"
73 make_gc_block block_id fun_label formals safety = BasicBlock block_id stmts
75 stmts = [CmmCall stg_gc_gen_target [] [] safety,
76 CmmJump fun_expr actuals]
78 CmmForeignCall (CmmLit (CmmLabel stg_gc_gen)) CmmCallConv
79 actuals = map (\x -> (CmmReg (CmmLocal x), NoHint)) formals
80 fun_expr = CmmLit (CmmLabel fun_label)
82 force_gc_block old_info block_id fun_label formals blocks =
84 CmmNonInfo (Just _) -> (old_info, [])
85 CmmInfo _ (Just _) _ _ -> (old_info, [])
87 -> (CmmNonInfo (Just block_id),
88 [make_gc_block block_id fun_label formals (CmmSafe NoC_SRT)])
89 CmmInfo prof Nothing type_tag type_info
90 -> (CmmInfo prof (Just block_id) type_tag type_info,
91 [make_gc_block block_id fun_label formals (CmmSafe srt)])
93 srt = case type_info of
94 ConstrInfo _ _ _ -> NoC_SRT
95 FunInfo _ srt' _ _ _ _ -> srt'
96 ThunkInfo _ srt' -> srt'
97 ThunkSelectorInfo _ srt' -> srt'
98 ContInfo _ srt' -> srt'
100 -----------------------------------------------------------------------------
101 -- |CPS a single CmmTop (proceedure)
102 -- Only 'CmmProc' are transformed 'CmmData' will be left alone.
103 -----------------------------------------------------------------------------
105 cpsProc :: UniqSupply
106 -> GenCmmTop CmmStatic CmmInfo CmmStmt -- ^Input proceedure
107 -> [GenCmmTop CmmStatic [CmmStatic] CmmStmt] -- ^Output proceedure and continuations
108 cpsProc uniqSupply (CmmData sec dat) = [CmmData sec dat]
109 cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
111 uniques :: [[Unique]]
112 uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
113 (gc_unique:info_uniques):block_uniques = uniques
116 forced_gc :: (CmmInfo, [CmmBasicBlock])
117 forced_gc = force_gc_block info (BlockId gc_unique) ident params blocks
119 forced_info = fst forced_gc
120 forced_blocks = blocks ++ snd forced_gc
121 forced_gc_id = case forced_info of
122 CmmNonInfo (Just x) -> x
123 CmmInfo _ (Just x) _ _ -> x
125 -- Break the block at each function call.
126 -- The part after the function call will have to become a continuation.
127 broken_blocks :: [BrokenBlock]
129 concat $ zipWith3 breakBlock block_uniques forced_blocks
130 (FunctionEntry forced_info ident params:repeat ControlEntry)
132 -- Calculate live variables for each broken block.
134 -- Nothing can be live on entry to the first block
135 -- so we could take the tail, but for now we wont
136 -- to help future proof the code.
137 live :: BlockEntryLiveness
138 live = cmmLiveness $ map cmmBlockFromBrokenBlock broken_blocks
140 -- Calculate which blocks must be made into full fledged procedures.
141 proc_points :: UniqSet BlockId
142 proc_points = calculateProcPoints broken_blocks
144 -- Construct a map so we can lookup a broken block by its 'BlockId'.
145 block_env :: BlockEnv BrokenBlock
146 block_env = blocksToBlockEnv broken_blocks
148 -- Group the blocks into continuations based on the set of proc-points.
149 continuations :: [Continuation (Either C_SRT CmmInfo)]
150 continuations = zipWith
151 (gatherBlocksIntoContinuation proc_points block_env)
152 (uniqSetToList proc_points)
153 (Just forced_gc_id : repeat Nothing)
155 -- Select the stack format on entry to each continuation.
156 -- Return the max stack offset and an association list
158 -- This is an association list instead of a UniqFM because
159 -- CLabel's don't have a 'Uniqueable' instance.
160 formats :: [(CLabel, -- key
161 (Maybe CLabel, -- label in top slot
162 [Maybe LocalReg]))] -- slots
163 formats = selectStackFormat live continuations
165 -- Do a little meta-processing on the stack formats such as
166 -- getting the individual frame sizes and the maximum frame size
167 formats' :: (WordOff, [(CLabel, StackFormat)])
168 formats' = processFormats formats
170 -- TODO FIXME NOW: calculate a real max stack (including function call args)
171 -- TODO: from the maximum frame size get the maximum stack size.
172 -- The difference is due to the size taken by function calls.
174 -- Update the info table data on the continuations with
175 -- the selected stack formats.
176 continuations' :: [Continuation CmmInfo]
177 continuations' = map (applyStackFormat (snd formats')) continuations
179 -- Do the actual CPS transform.
180 cps_procs :: [CmmTop]
181 cps_procs = map (continuationToProc formats') continuations'
183 -- Convert the info tables from CmmInfo to [CmmStatic]
184 -- We might want to put this in another pass eventually
185 info_procs :: [RawCmmTop]
186 info_procs = concat (zipWith mkInfoTable info_uniques cps_procs)
188 --------------------------------------------------------------------------------
190 -- The format for the call to a continuation
191 -- The fst is the arguments that must be passed to the continuation
192 -- by the continuation's caller.
193 -- The snd is the live values that must be saved on stack.
194 -- A Nothing indicates an ignored slot.
195 -- The head of each list is the stack top or the first parameter.
197 -- The format for live values for a particular continuation
198 -- All on stack for now.
199 -- Head element is the top of the stack (or just under the header).
200 -- Nothing means an empty slot.
201 -- Future possibilities include callee save registers (i.e. passing slots in register)
202 -- and heap memory (not sure if that's usefull at all though, but it may
203 -- be worth exploring the design space).
205 continuationLabel (Continuation _ l _ _) = l
206 data Continuation info =
208 info -- Left <=> Continuation created by the CPS
209 -- Right <=> Function or Proc point
210 CLabel -- Used to generate both info & entry labels
211 CmmFormals -- Argument locals live on entry (C-- procedure params)
212 [BrokenBlock] -- Code, may be empty. The first block is
213 -- the entry point. The order is otherwise initially
214 -- unimportant, but at some point the code gen will
217 -- the BlockId of the first block does not give rise
218 -- to a label. To jump to the first block in a Proc,
219 -- use the appropriate CLabel.
223 stack_label :: Maybe CLabel, -- The label occupying the top slot
224 stack_frame_size :: WordOff, -- Total frame size in words (not including arguments)
225 stack_live :: [Maybe LocalReg] -- local reg offsets from stack top
228 -- A block can be a continuation of a call
229 -- A block can be a continuation of another block (w/ or w/o joins)
230 -- A block can be an entry to a function
232 -----------------------------------------------------------------------------
234 collectNonProcPointTargets ::
235 UniqSet BlockId -> BlockEnv BrokenBlock
236 -> UniqSet BlockId -> [BlockId] -> UniqSet BlockId
237 collectNonProcPointTargets proc_points blocks current_targets new_blocks =
238 if sizeUniqSet current_targets == sizeUniqSet new_targets
241 (collectNonProcPointTargets proc_points blocks)
245 blocks' = map (lookupWithDefaultUFM blocks (panic "TODO")) new_blocks
247 -- Note the subtlety that since the extra branch after a call
248 -- will always be to a block that is a proc-point,
249 -- this subtraction will always remove that case
250 uniqSetToList $ (unionManyUniqSets $ map (mkUniqSet . brokenBlockTargets) blocks')
251 `minusUniqSet` proc_points
252 -- TODO: remove redundant uniqSetToList
253 new_targets = current_targets `unionUniqSets` (mkUniqSet targets)
255 -- TODO: insert proc point code here
256 -- * Branches and switches to proc points may cause new blocks to be created
257 -- (or proc points could leave behind phantom blocks that just jump to them)
258 -- * Proc points might get some live variables passed as arguments
260 gatherBlocksIntoContinuation ::
261 UniqSet BlockId -> BlockEnv BrokenBlock
262 -> BlockId -> Maybe BlockId -> Continuation (Either C_SRT CmmInfo)
263 gatherBlocksIntoContinuation proc_points blocks start gc =
264 Continuation info_table clabel params body
266 start_and_gc = start : maybeToList gc
267 children = (collectNonProcPointTargets proc_points blocks (mkUniqSet start_and_gc) start_and_gc) `minusUniqSet` (mkUniqSet start_and_gc)
268 start_block = lookupWithDefaultUFM blocks (panic "TODO") start
269 gc_block = map (lookupWithDefaultUFM blocks (panic "TODO)")) (maybeToList gc)
270 children_blocks = map (lookupWithDefaultUFM blocks (panic "TODO")) (uniqSetToList children)
271 body = start_block : gc_block ++ children_blocks
273 -- We can't properly annotate the continuation's stack parameters
274 -- at this point because this is before stack selection
275 -- but we want to keep the C_SRT around so we use 'Either'.
276 info_table = case start_block_entry of
277 FunctionEntry info _ _ -> Right info
278 ContinuationEntry _ srt -> Left srt
279 ControlEntry -> Right (CmmNonInfo Nothing)
281 start_block_entry = brokenBlockEntry start_block
282 clabel = case start_block_entry of
283 FunctionEntry _ label _ -> label
284 _ -> mkReturnPtLabel $ getUnique start
285 params = case start_block_entry of
286 FunctionEntry _ _ args -> args
287 ContinuationEntry args _ -> args
288 ControlEntry -> [] -- TODO: it's a proc-point, we could pass lives in parameter registers
290 --------------------------------------------------------------------------------
291 -- For now just select the continuation orders in the order they are in the set with no gaps
293 selectStackFormat :: BlockEnv CmmLive
294 -> [Continuation (Either C_SRT CmmInfo)]
295 -> [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
296 selectStackFormat live continuations =
297 map (\c -> (continuationLabel c, selectStackFormat' c)) continuations
299 selectStackFormat' (Continuation
300 (Right (CmmInfo _ _ _ (ContInfo format srt)))
301 label _ _) = (Just label, format)
302 selectStackFormat' (Continuation (Right _) _ _ _) = (Nothing, [])
303 selectStackFormat' (Continuation (Left srt) label _ blocks) =
304 -- TODO: assumes the first block is the entry block
305 let ident = brokenBlockId $ head blocks -- TODO: CLabel isn't a uniquable, but we need a better way than this
307 map Just $ uniqSetToList $
308 lookupWithDefaultUFM live unknown_block ident)
310 unknown_block = panic "unknown BlockId in selectStackFormat"
312 processFormats :: [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
313 -> (WordOff, [(CLabel, StackFormat)])
314 processFormats formats = (max_size, formats')
316 max_size = foldl max 0 (map (stack_frame_size . snd) formats')
317 formats' = map make_format formats
318 make_format (label, format) =
321 stack_label = fst format,
322 stack_frame_size = stack_size (snd format) +
323 if isJust (fst format)
326 stack_live = snd format })
328 -- TODO: get rid of "+ 1" etc.
329 label_size = 1 :: WordOff
332 stack_size (Nothing:formats) = 1 + stack_size formats -- one dead word
333 stack_size (Just reg:formats) = width + stack_size formats
335 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
336 -- TODO: it would be better if we had a machRepWordWidth
338 -----------------------------------------------------------------------------
339 applyStackFormat :: [(CLabel, StackFormat)]
340 -> Continuation (Either C_SRT CmmInfo)
341 -> Continuation CmmInfo
343 -- User written continuations
344 applyStackFormat formats (Continuation
345 (Right (CmmInfo prof gc tag (ContInfo _ srt)))
346 label formals blocks) =
347 Continuation (CmmInfo prof gc tag (ContInfo format srt))
350 format = stack_live $ maybe unknown_block id $ lookup label formats
351 unknown_block = panic "unknown BlockId in applyStackFormat"
353 -- User written non-continuation code
354 applyStackFormat formats (Continuation (Right info) label formals blocks) =
355 Continuation info label formals blocks
357 -- CPS generated continuations
358 applyStackFormat formats (Continuation (Left srt) label formals blocks) =
359 Continuation (CmmInfo prof gc tag (ContInfo (stack_live $ format) srt))
362 gc = Nothing -- Generated continuations never need a stack check
363 -- TODO prof: this is the same as the current implementation
364 -- but I think it could be improved
365 prof = ProfilingInfo zeroCLit zeroCLit
366 tag = rET_SMALL -- cmmToRawCmm may convert it to rET_BIG
367 format = maybe unknown_block id $ lookup label formats
368 unknown_block = panic "unknown BlockId in applyStackFormat"
370 -----------------------------------------------------------------------------
371 continuationToProc :: (WordOff, [(CLabel, StackFormat)])
372 -> Continuation CmmInfo
374 continuationToProc (max_stack, formats)
375 (Continuation info label formals blocks) =
376 CmmProc info label formals (map continuationToProc' blocks)
378 curr_format = maybe unknown_block id $ lookup label formats
379 unknown_block = panic "unknown BlockId in continuationToProc"
381 continuationToProc' :: BrokenBlock -> CmmBasicBlock
382 continuationToProc' (BrokenBlock ident entry stmts _ exit) =
383 BasicBlock ident (prefix++stmts++postfix)
385 prefix = case entry of
387 FunctionEntry (CmmInfo _ (Just gc_block) _ _) _ formals ->
388 gc_stack_check gc_block max_stack ++
389 function_entry formals curr_format
390 FunctionEntry (CmmInfo _ Nothing _ _) _ formals ->
391 panic "continuationToProc: missing GC block"
392 FunctionEntry (CmmNonInfo (Just gc_block)) _ formals ->
393 gc_stack_check gc_block max_stack ++
394 function_entry formals curr_format
395 FunctionEntry (CmmNonInfo Nothing) _ formals ->
396 panic "continuationToProc: missing non-info GC block"
397 ContinuationEntry formals _ ->
398 function_entry formals curr_format
399 postfix = case exit of
400 FinalBranch next -> [CmmBranch next]
401 FinalSwitch expr targets -> [CmmSwitch expr targets]
402 FinalReturn arguments ->
403 tail_call (stack_frame_size curr_format)
404 (CmmLoad (CmmReg spReg) wordRep)
406 FinalJump target arguments ->
407 tail_call (stack_frame_size curr_format) target arguments
408 FinalCall next (CmmForeignCall target CmmCallConv)
410 pack_continuation curr_format cont_format ++
411 tail_call (stack_frame_size curr_format - stack_frame_size cont_format)
414 cont_format = maybe unknown_block id $
415 lookup (mkReturnPtLabel $ getUnique next) formats
416 FinalCall next _ results arguments -> panic "unimplemented CmmCall"
418 -----------------------------------------------------------------------------
419 -- Functions that generate CmmStmt sequences
420 -- for packing/unpacking continuations
421 -- and entering/exiting functions
423 tail_call :: WordOff -> CmmExpr -> CmmActuals -> [CmmStmt]
424 tail_call spRel target arguments
425 = store_arguments ++ adjust_spReg ++ jump where
427 [stack_put spRel expr offset
428 | ((expr, _), StackParam offset) <- argument_formats] ++
429 [global_put expr global
430 | ((expr, _), RegisterParam global) <- argument_formats]
434 else [CmmAssign spReg (CmmRegOff spReg (spRel*wORD_SIZE))]
435 jump = [CmmJump target arguments]
437 argument_formats = assignArguments (cmmExprRep . fst) arguments
439 gc_stack_check :: BlockId -> WordOff -> [CmmStmt]
440 gc_stack_check gc_block max_frame_size
441 = check_stack_limit where
442 check_stack_limit = [
444 (CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
445 [CmmRegOff spReg (-max_frame_size*wORD_SIZE),
449 -- TODO: fix branches to proc point
450 -- (we have to insert a new block to marshel the continuation)
451 pack_continuation :: StackFormat -> StackFormat -> [CmmStmt]
452 pack_continuation (StackFormat curr_id curr_frame_size _)
453 (StackFormat cont_id cont_frame_size live_regs)
454 = store_live_values ++ set_stack_header where
455 -- TODO: only save variables when actually needed
456 -- (may be handled by latter pass)
458 [stack_put spRel (CmmReg (CmmLocal reg)) offset
459 | (reg, offset) <- cont_offsets]
462 then [stack_put spRel continuation_function 0]
465 -- TODO: factor with function_entry and CmmInfo.hs(?)
466 cont_offsets = mkOffsets label_size live_regs
468 label_size = 1 :: WordOff
470 mkOffsets size [] = []
471 mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
472 mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
474 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
475 -- TODO: it would be better if we had a machRepWordWidth
477 spRel = curr_frame_size - cont_frame_size
478 continuation_function = CmmLit $ CmmLabel $ fromJust cont_id
480 case (curr_id, cont_id) of
481 (Just x, Just y) -> x /= y
484 -- Lazy adjustment of stack headers assumes all blocks
485 -- that could branch to eachother (i.e. control blocks)
486 -- have the same stack format (this causes a problem
487 -- only for proc-point).
488 function_entry :: CmmFormals -> StackFormat -> [CmmStmt]
489 function_entry formals (StackFormat _ _ live_regs)
490 = load_live_values ++ load_args where
491 -- TODO: only save variables when actually needed
492 -- (may be handled by latter pass)
494 [stack_get 0 reg offset
495 | (reg, offset) <- curr_offsets]
497 [stack_get 0 reg offset
498 | (reg, StackParam offset) <- argument_formats] ++
499 [global_get reg global
500 | (reg, RegisterParam global) <- argument_formats]
502 argument_formats = assignArguments (localRegRep) formals
504 -- TODO: eliminate copy/paste with pack_continuation
505 curr_offsets = mkOffsets label_size live_regs
507 label_size = 1 :: WordOff
509 mkOffsets size [] = []
510 mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
511 mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
513 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
514 -- TODO: it would be better if we had a machRepWordWidth
516 -----------------------------------------------------------------------------
517 -- Section: Stack and argument register puts and gets
518 -----------------------------------------------------------------------------
521 -- |Construct a 'CmmStmt' that will save a value on the stack
522 stack_put :: WordOff -- ^ Offset from the real 'Sp' that 'offset'
523 -- is relative to (added to offset)
524 -> CmmExpr -- ^ What to store onto the stack
525 -> WordOff -- ^ Where on the stack to store it
526 -- (positive <=> higher addresses)
528 stack_put spRel expr offset =
529 CmmStore (CmmRegOff spReg (wORD_SIZE*(spRel + offset))) expr
531 --------------------------------
537 stack_get spRel reg offset =
538 CmmAssign (CmmLocal reg)
539 (CmmLoad (CmmRegOff spReg (wORD_SIZE*(spRel + offset)))
541 global_put :: CmmExpr -> GlobalReg -> CmmStmt
542 global_put expr global = CmmAssign (CmmGlobal global) expr
543 global_get :: LocalReg -> GlobalReg -> CmmStmt
544 global_get reg global = CmmAssign (CmmLocal reg) (CmmReg (CmmGlobal global))