1 module CmmCPS (cmmCPS) where
3 #include "HsVersions.h"
9 import Dataflow (fixedpoint)
31 --------------------------------------------------------------------------------
33 -- The format for the call to a continuation
34 -- The fst is the arguments that must be passed to the continuation
35 -- by the continuation's caller.
36 -- The snd is the live values that must be saved on stack.
37 -- A Nothing indicates an ignored slot.
38 -- The head of each list is the stack top or the first parameter.
40 -- The format for live values for a particular continuation
41 -- All on stack for now.
42 -- Head element is the top of the stack (or just under the header).
43 -- Nothing means an empty slot.
44 -- Future possibilities include callee save registers (i.e. passing slots in register)
45 -- and heap memory (not sure if that's usefull at all though, but it may
46 -- be worth exploring the design space).
50 brokenBlockId :: BlockId, -- Like a CmmBasicBlock
51 brokenBlockEntry :: BlockEntryInfo,
52 -- How this block can be entered
54 brokenBlockStmts :: [CmmStmt],
55 -- Like a CmmBasicBlock
56 -- (but without the last statement)
58 brokenBlockTargets :: [BlockId],
59 -- Blocks that this block could
60 -- branch to one either by conditional
61 -- branches or via the last statement
63 brokenBlockExit :: FinalStmt
64 -- How the block can be left
67 continuationLabel (Continuation _ _ l _ _) = l
70 Bool -- True => Function entry, False => Continuation/return point
71 [CmmStatic] -- Info table, may be empty
72 CLabel -- Used to generate both info & entry labels
73 CmmFormals -- Argument locals live on entry (C-- procedure params)
74 [BrokenBlock] -- Code, may be empty. The first block is
75 -- the entry point. The order is otherwise initially
76 -- unimportant, but at some point the code gen will
79 -- the BlockId of the first block does not give rise
80 -- to a label. To jump to the first block in a Proc,
81 -- use the appropriate CLabel.
84 = FunctionEntry -- Beginning of a function
85 CLabel -- The function name
86 CmmFormals -- Aguments to function
88 | ContinuationEntry -- Return point of a call
89 CmmFormals -- return values (argument to continuation)
91 -- | ProcPointEntry -- no return values, but some live might end up as params or possibly in the frame
93 | ControlEntry -- A label in the input
95 -- Final statement in a BlokenBlock
96 -- Constructors and arguments match those in Cmm,
97 -- but are restricted to branches, returns, jumps, calls and switches
100 BlockId -- next block (must be a ControlEntry)
103 CmmActuals -- return values
106 CmmExpr -- the function to call
107 CmmActuals -- arguments to call
110 BlockId -- next block after call (must be a ContinuationEntry)
111 CmmCallTarget -- the function to call
112 CmmFormals -- results from call (redundant with ContinuationEntry)
113 CmmActuals -- arguments to call
114 (Maybe [GlobalReg]) -- registers that must be saved (TODO)
117 CmmExpr [Maybe BlockId] -- Table branch
119 -- TODO: | ProcPointExit (needed?)
123 BlockId {- block that is the start of the continuation. may or may not be the current block -}
124 WordOff {- total frame size -}
125 [(CmmReg, WordOff)] {- local reg offsets from stack top -}
127 -- A block can be a continuation of a call
128 -- A block can be a continuation of another block (w/ or w/o joins)
129 -- A block can be an entry to a function
131 blocksToBlockEnv :: [BrokenBlock] -> BlockEnv BrokenBlock
132 blocksToBlockEnv blocks = listToUFM $ map (\b -> (brokenBlockId b, b)) blocks
134 -----------------------------------------------------------------------------
135 calculateOwnership :: UniqSet BlockId -> [BrokenBlock] -> BlockEnv (UniqSet BlockId)
136 calculateOwnership proc_points blocks =
137 fixedpoint dependants update (map brokenBlockId blocks) emptyUFM
139 blocks_ufm :: BlockEnv BrokenBlock
140 blocks_ufm = blocksToBlockEnv blocks
142 dependants :: BlockId -> [BlockId]
144 brokenBlockTargets $ lookupWithDefaultUFM
145 blocks_ufm unknown_block ident
147 update :: BlockId -> Maybe BlockId
148 -> BlockEnv (UniqSet BlockId) -> Maybe (BlockEnv (UniqSet BlockId))
149 update ident cause owners =
150 case (cause, ident `elementOfUniqSet` proc_points) of
151 (Nothing, True) -> Just $ addToUFM owners ident (unitUniqSet ident)
152 (Nothing, False) -> Nothing
153 (Just cause', True) -> Nothing
154 (Just cause', False) ->
155 if (sizeUniqSet old) == (sizeUniqSet new)
157 else Just $ addToUFM owners ident new
159 old = lookupWithDefaultUFM owners emptyUniqSet ident
160 new = old `unionUniqSets` lookupWithDefaultUFM owners emptyUniqSet cause'
162 unknown_block = panic "unknown BlockId in selectStackFormat"
164 calculateProcPoints :: [BrokenBlock] -> UniqSet BlockId
165 calculateProcPoints blocks = calculateProcPoints' init_proc_points blocks
167 init_proc_points = mkUniqSet $
169 filter always_proc_point blocks
170 always_proc_point BrokenBlock {
171 brokenBlockEntry = FunctionEntry _ _ } = True
172 always_proc_point BrokenBlock {
173 brokenBlockEntry = ContinuationEntry _ } = True
174 always_proc_point _ = False
176 calculateProcPoints' :: UniqSet BlockId -> [BrokenBlock] -> UniqSet BlockId
177 calculateProcPoints' old_proc_points blocks =
178 if sizeUniqSet old_proc_points == sizeUniqSet new_proc_points
180 else calculateProcPoints' new_proc_points blocks
182 owners = calculateOwnership old_proc_points blocks
183 new_proc_points = unionManyUniqSets (old_proc_points:(map (calculateProcPoints'' owners) blocks))
185 calculateProcPoints'' :: BlockEnv (UniqSet BlockId) -> BrokenBlock -> UniqSet BlockId
186 calculateProcPoints'' owners block =
187 unionManyUniqSets (map (f parent_id) child_ids)
189 parent_id = brokenBlockId block
190 child_ids = brokenBlockTargets block
192 f parent_id child_id =
194 then unitUniqSet child_id
197 parent_owners = lookupWithDefaultUFM owners emptyUniqSet parent_id
198 child_owners = lookupWithDefaultUFM owners emptyUniqSet child_id
199 needs_proc_point = not $ isEmptyUniqSet $ child_owners `minusUniqSet` parent_owners
201 collectNonProcPointTargets ::
202 UniqSet BlockId -> BlockEnv BrokenBlock
203 -> UniqSet BlockId -> BlockId -> UniqSet BlockId
204 collectNonProcPointTargets proc_points blocks current_targets block =
205 if sizeUniqSet current_targets == sizeUniqSet new_targets
207 else foldl (collectNonProcPointTargets proc_points blocks) new_targets targets
209 block' = lookupWithDefaultUFM blocks (panic "TODO") block
211 -- Note the subtlety that since the extra branch after a call
212 -- will always be to a block that is a proc-point,
213 -- this subtraction will always remove that case
214 uniqSetToList $ (mkUniqSet $ brokenBlockTargets block') `minusUniqSet` proc_points
215 -- TODO: remove redundant uniqSetToList
216 new_targets = current_targets `unionUniqSets` (mkUniqSet targets)
219 UniqSet BlockId -> BlockEnv BrokenBlock
220 -> BlockId -> Continuation
221 buildContinuation proc_points blocks start =
222 Continuation is_entry info_table clabel params body
224 children = (collectNonProcPointTargets proc_points blocks (unitUniqSet start) start) `delOneFromUniqSet` start
225 start_block = lookupWithDefaultUFM blocks (panic "TODO") start
226 children_blocks = map (lookupWithDefaultUFM blocks (panic "TODO")) (uniqSetToList children)
227 body = start_block : children_blocks
228 info_table = [] -- TODO
229 start_block_entry = brokenBlockEntry start_block
230 is_entry = case start_block_entry of
231 FunctionEntry _ _ -> True
233 clabel = case start_block_entry of
234 FunctionEntry label _ -> label
235 _ -> mkReturnPtLabel $ getUnique start
236 params = case start_block_entry of
237 FunctionEntry _ args -> args
238 ContinuationEntry args -> args
239 ControlEntry -> [] -- TODO: it's a proc-point, we could pass lives in parameter registers
241 --------------------------------------------------------------------------------
242 -- For now just select the continuation orders in the order they are in the set with no gaps
244 selectStackFormat :: BlockEnv CmmLive -> [BrokenBlock] -> BlockEnv StackFormat
245 selectStackFormat live blocks =
246 fixedpoint dependants update (map brokenBlockId blocks) emptyUFM
248 blocks_ufm :: BlockEnv BrokenBlock
249 blocks_ufm = listToUFM $ map (\b -> (brokenBlockId b, b)) blocks
251 dependants :: BlockId -> [BlockId]
253 brokenBlockTargets $ lookupWithDefaultUFM
254 blocks_ufm unknown_block ident
256 update :: BlockId -> Maybe BlockId
257 -> BlockEnv StackFormat -> Maybe (BlockEnv StackFormat)
258 update ident cause formats =
259 if ident `elemUFM` formats
260 then Nothing -- Blocks only need to be updated once
262 brokenBlockEntry $ lookupWithDefaultUFM blocks_ufm
263 unknown_block ident) of
264 -- Propagate only to blocks entered by branches
265 -- (not function entry blocks or continuation entry blocks)
266 (Just cause_name, ControlEntry) ->
267 Just $ addToUFM formats ident cause_format
268 where cause_format = lookupWithDefaultUFM
269 formats unknown_block
271 -- Do initial calculates for function blocks
272 (Nothing, FunctionEntry _ _) ->
274 addToUFM formats ident $
275 StackFormat ident 0 []
276 -- Do initial calculates for continuation blocks
277 (Nothing, ContinuationEntry _) ->
279 addToUFM formats ident $
280 live_to_format ident $
281 lookupWithDefaultUFM live unknown_block ident
284 unknown_block = panic "unknown BlockId in selectStackFormat"
286 live_to_format :: BlockId -> CmmLive -> StackFormat
287 live_to_format label live =
289 (StackFormat label retAddrSizeW [])
292 extend_format :: StackFormat -> LocalReg -> StackFormat
293 extend_format (StackFormat block size offsets) reg =
294 StackFormat block (slot_size reg + size) ((CmmLocal reg, size) : offsets)
296 selectStackFormat2 :: BlockEnv CmmLive -> [Continuation] -> [(CLabel, StackFormat)]
297 selectStackFormat2 live continuations =
298 map (\c -> (continuationLabel c, selectStackFormat' c)) continuations
300 selectStackFormat' (Continuation True info_table label formals blocks) =
301 let ident = brokenBlockId $ head blocks -- TODO: CLabel isn't a uniquable, but we need a better way than this
302 in StackFormat ident 0 []
303 selectStackFormat' (Continuation False info_table label formals blocks) =
304 let ident = brokenBlockId $ head blocks -- TODO: CLabel isn't a uniquable, but we need a better way than this
305 in live_to_format ident $ lookupWithDefaultUFM live unknown_block ident
307 live_to_format :: BlockId -> CmmLive -> StackFormat
308 live_to_format label live =
310 (StackFormat label retAddrSizeW [])
313 extend_format :: StackFormat -> LocalReg -> StackFormat
314 extend_format (StackFormat block size offsets) reg =
315 StackFormat block (slot_size reg + size) ((CmmLocal reg, size) : offsets)
317 unknown_block = panic "unknown BlockId in selectStackFormat"
319 slot_size reg = ((machRepByteWidth (localRegRep reg) - 1) `div` wORD_SIZE) + 1
321 constructContinuation :: [(CLabel, StackFormat)] -> Continuation -> CmmTop
322 constructContinuation formats (Continuation is_entry info label formals blocks) =
323 CmmProc info label formals (map (constructContinuation2' label formats) blocks)
325 constructContinuation2' :: CLabel -> [(CLabel, StackFormat)] -> BrokenBlock
327 constructContinuation2' curr_ident formats (BrokenBlock ident entry stmts _ exit) =
328 BasicBlock ident (prefix++stmts++postfix)
330 curr_format = maybe unknown_block id $ lookup curr_ident formats
331 unknown_block = panic "unknown BlockId in constructContinuation"
332 prefix = case entry of
334 FunctionEntry _ _ -> []
335 ContinuationEntry formals ->
336 unpack_continuation curr_format
337 postfix = case exit of
338 FinalBranch next -> [CmmBranch next]
339 FinalSwitch expr targets -> [CmmSwitch expr targets]
340 FinalReturn arguments ->
341 exit_function curr_format
342 (CmmLoad (CmmReg spReg) wordRep)
344 FinalJump target arguments ->
345 exit_function curr_format target arguments
346 -- TODO: do something about global saves
347 FinalCall next (CmmForeignCall target CmmCallConv)
348 results arguments saves ->
349 pack_continuation curr_format cont_format ++
350 [CmmJump target arguments]
352 cont_format = maybe unknown_block id $
353 lookup (mkReturnPtLabel $ getUnique next) formats
354 FinalCall next _ results arguments saves -> panic "unimplemented CmmCall"
356 --------------------------------------------------------------------------------
357 -- Functions that generate CmmStmt sequences
358 -- for packing/unpacking continuations
359 -- and entering/exiting functions
361 exit_function :: StackFormat -> CmmExpr -> CmmActuals -> [CmmStmt]
362 exit_function (StackFormat curr_id curr_frame_size curr_offsets) target arguments
363 = adjust_spReg ++ jump where
366 (CmmRegOff spReg (curr_frame_size*wORD_SIZE))]
367 jump = [CmmJump target arguments]
369 enter_function :: WordOff -> [CmmStmt]
370 enter_function max_frame_size
371 = check_stack_limit where
372 check_stack_limit = [
374 (CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
375 [CmmRegOff spReg max_frame_size, CmmReg spLimReg])
377 gc_block = undefined -- TODO: get stack and heap checks to go to same
379 -- TODO: fix branches to proc point (we have to insert a new block to marshel the continuation)
380 pack_continuation :: StackFormat -> StackFormat -> [CmmStmt]
381 pack_continuation (StackFormat curr_id curr_frame_size curr_offsets)
382 (StackFormat cont_id cont_frame_size cont_offsets)
383 = save_live_values ++ set_stack_header ++ adjust_spReg where
384 -- TODO: only save variables when actually needed
388 spReg (wORD_SIZE*(curr_frame_size - cont_frame_size + offset)))
390 | (reg, offset) <- cont_offsets]
391 set_stack_header = -- TODO: only set when needed
392 [CmmStore (CmmRegOff spReg (wORD_SIZE*(curr_frame_size - cont_frame_size))) continuation_function]
393 continuation_function = CmmLit $ CmmLabel $ mkReturnPtLabel {-TODO: use the correct function -} $ getUnique cont_id
395 if curr_frame_size == cont_frame_size
397 else [CmmAssign spReg (CmmRegOff spReg ((curr_frame_size - cont_frame_size)*wORD_SIZE))]
399 -- Lazy adjustment of stack headers assumes all blocks
400 -- that could branch to eachother (i.e. control blocks)
401 -- have the same stack format (this causes a problem
402 -- only for proc-point).
403 unpack_continuation :: StackFormat -> [CmmStmt]
404 unpack_continuation (StackFormat curr_id curr_frame_size curr_offsets)
405 = load_live_values where
406 -- TODO: only save variables when actually needed
410 (CmmLoad (CmmRegOff spReg (wORD_SIZE*offset)) (cmmRegRep reg))
411 | (reg, offset) <- curr_offsets]
413 -----------------------------------------------------------------------------
414 -- Breaking basic blocks on function calls
415 -----------------------------------------------------------------------------
417 -----------------------------------------------------------------------------
418 -- Takes a basic block and breaks it up into a list of broken blocks
420 -- Takes a basic block and returns a list of basic blocks that
421 -- each have at most 1 CmmCall in them which must occur at the end.
422 -- Also returns with each basic block, the variables that will
423 -- be arguments to the continuation of the block once the call (if any)
426 breakBlock :: [Unique] -> CmmBasicBlock -> BlockEntryInfo -> [BrokenBlock]
427 breakBlock uniques (BasicBlock ident stmts) entry =
428 breakBlock' uniques ident entry [] [] stmts where
429 breakBlock' uniques current_id entry exits accum_stmts stmts =
431 [] -> panic "block doesn't end in jump, goto or return"
432 [CmmJump target arguments] ->
433 [BrokenBlock current_id entry accum_stmts
435 (FinalJump target arguments)]
436 [CmmReturn arguments] ->
437 [BrokenBlock current_id entry accum_stmts
439 (FinalReturn arguments)]
440 [CmmBranch target] ->
441 [BrokenBlock current_id entry accum_stmts
443 (FinalBranch target)]
444 [CmmSwitch expr targets] ->
445 [BrokenBlock current_id entry accum_stmts
446 (mapMaybe id targets ++ exits)
447 (FinalSwitch expr targets)]
449 panic "jump in middle of block"
451 panic "return in middle of block"
453 panic "branch in middle of block"
455 panic ("switch in middle of block" ++ (showSDoc $ ppr stmts))
456 (CmmCall target results arguments saves:stmts) -> block : rest
458 new_id = BlockId $ head uniques
459 block = BrokenBlock current_id entry accum_stmts
461 (FinalCall new_id target results arguments saves)
462 rest = breakBlock' (tail uniques) new_id
463 (ContinuationEntry results) [] [] stmts
464 (s@(CmmCondBranch test target):stmts) ->
465 breakBlock' uniques current_id entry
466 (target:exits) (accum_stmts++[s]) stmts
468 breakBlock' uniques current_id entry
469 exits (accum_stmts++[s]) stmts
471 --------------------------------
472 -- Convert from a BrokenBlock
473 -- to a CmmBasicBlock so the
474 -- liveness analysis can run
476 --------------------------------
477 cmmBlockFromBrokenBlock :: BrokenBlock -> CmmBasicBlock
478 cmmBlockFromBrokenBlock (BrokenBlock ident _ stmts _ exit) =
479 BasicBlock ident (stmts++exit_stmt)
483 FinalBranch target -> [CmmBranch target]
484 FinalReturn arguments -> [CmmReturn arguments]
485 FinalJump target arguments -> [CmmJump target arguments]
486 FinalSwitch expr targets -> [CmmSwitch expr targets]
487 FinalCall branch_target call_target results arguments saves ->
488 [CmmCall call_target results arguments saves,
489 CmmBranch branch_target]
491 -----------------------------------------------------------------------------
492 -- CPS a single CmmTop (proceedure)
493 -----------------------------------------------------------------------------
495 cpsProc :: UniqSupply -> CmmTop -> [CmmTop]
496 cpsProc uniqSupply x@(CmmData _ _) = [x]
497 cpsProc uniqSupply x@(CmmProc info_table ident params blocks) =
498 --[CmmProc info_table ident params cps_blocks]
502 uniqes = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
504 -- Break the block at each function call
505 broken_blocks :: [BrokenBlock]
506 broken_blocks = concat $ zipWith3 breakBlock uniqes blocks
507 (FunctionEntry ident params:repeat ControlEntry)
509 -- Calculate live variables for each broken block
510 live :: BlockEntryLiveness
511 live = cmmLiveness $ map cmmBlockFromBrokenBlock broken_blocks
512 -- nothing can be live on entry to the first block so we could take the tail
514 proc_points :: UniqSet BlockId
515 proc_points = calculateProcPoints broken_blocks
517 continuations :: [Continuation]
518 continuations = map (buildContinuation proc_points (blocksToBlockEnv broken_blocks)) (uniqSetToList proc_points)
520 -- TODO: insert proc point code here
521 -- * Branches and switches to proc points may cause new blocks to be created
522 -- (or proc points could leave behind phantom blocks that just jump to them)
523 -- * Proc points might get some live variables passed as arguments
525 -- TODO: let blocks_with_live = map (cmmLivenessComment live . snd) broken_blocks
527 --procs = groupBlocksIntoContinuations live broken_blocks
529 -- Select the stack format on entry to each block
530 formats2 :: [(CLabel, StackFormat)]
531 formats2 = selectStackFormat2 live continuations
533 -- Do the actual CPS transform
534 cps_continuations :: [CmmTop]
535 cps_continuations = map (constructContinuation formats2) continuations
537 --------------------------------------------------------------------------------
539 -> [Cmm] -- C-- with Proceedures
540 -> IO [Cmm] -- Output: CPS transformed C--
542 cmmCPS dflags abstractC = do
543 when (dopt Opt_DoCmmLinting dflags) $
544 do showPass dflags "CmmLint"
545 case firstJust $ map cmmLint abstractC of
546 Just err -> do printDump err
549 showPass dflags "CPS"
550 -- TODO: check for use of branches to non-existant blocks
551 -- TODO: check for use of Sp, SpLim, R1, R2, etc.
552 -- TODO: find out if it is valid to create a new unique source like this
553 uniqSupply <- mkSplitUniqSupply 'p'
554 let supplies = listSplitUniqSupply uniqSupply
555 let continuationC = zipWith (\s (Cmm c) -> Cmm $ concat $ zipWith (cpsProc) (listSplitUniqSupply s) c) supplies abstractC
557 dumpIfSet_dyn dflags Opt_D_dump_cps_cmm "CPS Cmm" (pprCmms continuationC)
558 -- TODO: add option to dump Cmm to file