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 -----------------------------------------------------------------------------
73 -- |CPS a single CmmTop (proceedure)
74 -- Only 'CmmProc' are transformed 'CmmData' will be left alone.
75 -----------------------------------------------------------------------------
78 -> GenCmmTop CmmStatic CmmInfo CmmStmt -- ^Input proceedure
79 -> [GenCmmTop CmmStatic [CmmStatic] CmmStmt] -- ^Output proceedure and continuations
80 cpsProc uniqSupply (CmmData sec dat) = [CmmData sec dat]
81 cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
84 uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
85 info_uniques:block_uniques = uniques
87 -- Break the block at each function call.
88 -- The part after the function call will have to become a continuation.
89 broken_blocks :: [BrokenBlock]
91 concat $ zipWith3 breakBlock block_uniques blocks
92 (FunctionEntry info ident params:repeat ControlEntry)
94 -- Calculate live variables for each broken block.
96 -- Nothing can be live on entry to the first block
97 -- so we could take the tail, but for now we wont
98 -- to help future proof the code.
99 live :: BlockEntryLiveness
100 live = cmmLiveness $ map cmmBlockFromBrokenBlock broken_blocks
102 -- Calculate which blocks must be made into full fledged procedures.
103 proc_points :: UniqSet BlockId
104 proc_points = calculateProcPoints broken_blocks
106 -- Construct a map so we can lookup a broken block by its 'BlockId'.
107 block_env :: BlockEnv BrokenBlock
108 block_env = blocksToBlockEnv broken_blocks
110 -- Group the blocks into continuations based on the set of proc-points.
111 continuations :: [Continuation (Either C_SRT CmmInfo)]
112 continuations = map (gatherBlocksIntoContinuation proc_points block_env)
113 (uniqSetToList proc_points)
115 -- Select the stack format on entry to each continuation.
116 -- Return the max stack offset and an association list
118 -- This is an association list instead of a UniqFM because
119 -- CLabel's don't have a 'Uniqueable' instance.
120 formats :: [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
121 formats = selectStackFormat live continuations
123 -- Do a little meta-processing on the stack formats such as
124 -- getting the individual frame sizes and the maximum frame size
125 formats' :: (WordOff, [(CLabel, StackFormat)])
126 formats' = processFormats formats
128 -- TODO FIXME NOW: calculate a real max stack (including function call args)
129 -- TODO: from the maximum frame size get the maximum stack size.
130 -- The difference is due to the size taken by function calls.
132 -- Update the info table data on the continuations with
133 -- the selected stack formats.
134 continuations' :: [Continuation CmmInfo]
135 continuations' = map (applyStackFormat (snd formats')) continuations
137 -- Do the actual CPS transform.
138 cps_procs :: [CmmTop]
139 cps_procs = map (continuationToProc formats') continuations'
141 -- Convert the info tables from CmmInfo to [CmmStatic]
142 -- We might want to put this in another pass eventually
143 info_procs :: [RawCmmTop]
144 info_procs = concat (zipWith mkInfoTable info_uniques cps_procs)
146 --------------------------------------------------------------------------------
148 -- The format for the call to a continuation
149 -- The fst is the arguments that must be passed to the continuation
150 -- by the continuation's caller.
151 -- The snd is the live values that must be saved on stack.
152 -- A Nothing indicates an ignored slot.
153 -- The head of each list is the stack top or the first parameter.
155 -- The format for live values for a particular continuation
156 -- All on stack for now.
157 -- Head element is the top of the stack (or just under the header).
158 -- Nothing means an empty slot.
159 -- Future possibilities include callee save registers (i.e. passing slots in register)
160 -- and heap memory (not sure if that's usefull at all though, but it may
161 -- be worth exploring the design space).
163 continuationLabel (Continuation _ l _ _) = l
164 data Continuation info =
166 info --(Either C_SRT CmmInfo) -- Left <=> Continuation created by the CPS
167 -- Right <=> Function or Proc point
168 CLabel -- Used to generate both info & entry labels
169 CmmFormals -- Argument locals live on entry (C-- procedure params)
170 [BrokenBlock] -- Code, may be empty. The first block is
171 -- the entry point. The order is otherwise initially
172 -- unimportant, but at some point the code gen will
175 -- the BlockId of the first block does not give rise
176 -- to a label. To jump to the first block in a Proc,
177 -- use the appropriate CLabel.
181 stack_label :: Maybe CLabel, -- The label occupying the top slot
182 stack_frame_size :: WordOff, -- Total frame size in words (not including arguments)
183 stack_live :: [Maybe LocalReg] -- local reg offsets from stack top
186 -- A block can be a continuation of a call
187 -- A block can be a continuation of another block (w/ or w/o joins)
188 -- A block can be an entry to a function
190 -----------------------------------------------------------------------------
192 collectNonProcPointTargets ::
193 UniqSet BlockId -> BlockEnv BrokenBlock
194 -> UniqSet BlockId -> BlockId -> UniqSet BlockId
195 collectNonProcPointTargets proc_points blocks current_targets block =
196 if sizeUniqSet current_targets == sizeUniqSet new_targets
198 else foldl (collectNonProcPointTargets proc_points blocks) new_targets targets
200 block' = lookupWithDefaultUFM blocks (panic "TODO") block
202 -- Note the subtlety that since the extra branch after a call
203 -- will always be to a block that is a proc-point,
204 -- this subtraction will always remove that case
205 uniqSetToList $ (mkUniqSet $ brokenBlockTargets block') `minusUniqSet` proc_points
206 -- TODO: remove redundant uniqSetToList
207 new_targets = current_targets `unionUniqSets` (mkUniqSet targets)
209 -- TODO: insert proc point code here
210 -- * Branches and switches to proc points may cause new blocks to be created
211 -- (or proc points could leave behind phantom blocks that just jump to them)
212 -- * Proc points might get some live variables passed as arguments
214 gatherBlocksIntoContinuation ::
215 UniqSet BlockId -> BlockEnv BrokenBlock
216 -> BlockId -> Continuation (Either C_SRT CmmInfo)
217 gatherBlocksIntoContinuation proc_points blocks start =
218 Continuation info_table clabel params body
220 children = (collectNonProcPointTargets proc_points blocks (unitUniqSet start) start) `delOneFromUniqSet` start
221 start_block = lookupWithDefaultUFM blocks (panic "TODO") start
222 children_blocks = map (lookupWithDefaultUFM blocks (panic "TODO")) (uniqSetToList children)
223 body = start_block : children_blocks
225 -- We can't properly annotate the continuation's stack parameters
226 -- at this point because this is before stack selection
227 -- but we want to keep the C_SRT around so we use 'Either'.
228 info_table = case start_block_entry of
229 FunctionEntry info _ _ -> Right info
230 ContinuationEntry _ srt -> Left srt
231 ControlEntry -> Right CmmNonInfo
233 start_block_entry = brokenBlockEntry start_block
234 clabel = case start_block_entry of
235 FunctionEntry _ label _ -> label
236 _ -> mkReturnPtLabel $ getUnique start
237 params = case start_block_entry of
238 FunctionEntry _ _ args -> args
239 ContinuationEntry args _ -> args
240 ControlEntry -> [] -- TODO: it's a proc-point, we could pass lives in parameter registers
242 --------------------------------------------------------------------------------
243 -- For now just select the continuation orders in the order they are in the set with no gaps
245 selectStackFormat :: BlockEnv CmmLive
246 -> [Continuation (Either C_SRT CmmInfo)]
247 -> [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
248 selectStackFormat live continuations =
249 map (\c -> (continuationLabel c, selectStackFormat' c)) continuations
251 selectStackFormat' (Continuation
252 (Right (CmmInfo _ _ _ (ContInfo format srt)))
253 label _ _) = (Just label, format)
254 selectStackFormat' (Continuation (Right _) _ _ _) = (Nothing, [])
255 selectStackFormat' (Continuation (Left srt) label _ blocks) =
256 -- TODO: assumes the first block is the entry block
257 let ident = brokenBlockId $ head blocks -- TODO: CLabel isn't a uniquable, but we need a better way than this
259 map Just $ uniqSetToList $
260 lookupWithDefaultUFM live unknown_block ident)
262 unknown_block = panic "unknown BlockId in selectStackFormat"
264 processFormats :: [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
265 -> (WordOff, [(CLabel, StackFormat)])
266 processFormats formats = (max_size, formats')
268 max_size = foldl max 0 (map (stack_frame_size . snd) formats')
269 formats' = map make_format formats
270 make_format (label, format) =
273 stack_label = fst format,
274 stack_frame_size = stack_size (snd format) +
275 if isJust (fst format)
278 stack_live = snd format })
280 -- TODO: get rid of "+ 1" etc.
281 label_size = 1 :: WordOff
284 stack_size (Nothing:formats) = 1 + stack_size formats -- one dead word
285 stack_size (Just reg:formats) = width + stack_size formats
287 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
288 -- TODO: it would be better if we had a machRepWordWidth
290 -----------------------------------------------------------------------------
291 applyStackFormat :: [(CLabel, StackFormat)]
292 -> Continuation (Either C_SRT CmmInfo)
293 -> Continuation CmmInfo
295 -- User written continuations
296 applyStackFormat formats (Continuation
297 (Right (CmmInfo prof gc tag (ContInfo _ srt)))
298 label formals blocks) =
299 Continuation (CmmInfo prof gc tag (ContInfo format srt))
302 format = stack_live $ maybe unknown_block id $ lookup label formats
303 unknown_block = panic "unknown BlockId in applyStackFormat"
305 -- User written non-continuation code
306 applyStackFormat formats (Continuation (Right info) label formals blocks) =
307 Continuation info label formals blocks
309 -- CPS generated continuations
310 applyStackFormat formats (Continuation (Left srt) label formals blocks) =
311 Continuation (CmmInfo prof gc tag (ContInfo (stack_live $ format) srt))
314 gc = Nothing -- Generated continuations never need a stack check
315 -- TODO prof: this is the same as the current implementation
316 -- but I think it could be improved
317 prof = ProfilingInfo zeroCLit zeroCLit
318 tag = if stack_frame_size format > mAX_SMALL_BITMAP_SIZE
321 format = maybe unknown_block id $ lookup label formats
322 unknown_block = panic "unknown BlockId in applyStackFormat"
324 -----------------------------------------------------------------------------
325 continuationToProc :: (WordOff, [(CLabel, StackFormat)])
326 -> Continuation CmmInfo
328 continuationToProc (max_stack, formats)
329 (Continuation info label formals blocks) =
330 CmmProc info label formals (map continuationToProc' blocks)
332 curr_format = maybe unknown_block id $ lookup label formats
333 unknown_block = panic "unknown BlockId in continuationToProc"
335 continuationToProc' :: BrokenBlock -> CmmBasicBlock
336 continuationToProc' (BrokenBlock ident entry stmts _ exit) =
337 BasicBlock ident (prefix++stmts++postfix)
339 prefix = case entry of
341 FunctionEntry (CmmInfo _ (Just gc_block) _ _) _ formals ->
342 gc_stack_check gc_block max_stack ++
343 function_entry formals curr_format
344 FunctionEntry (CmmInfo _ Nothing _ _) _ formals ->
345 panic "continuationToProc: TODO generate GC block" ++
346 function_entry formals curr_format
347 FunctionEntry CmmNonInfo _ formals ->
348 panic "TODO: gc_stack_check gc_block max_stack" ++
349 function_entry formals curr_format
350 ContinuationEntry formals _ ->
351 function_entry formals curr_format
352 postfix = case exit of
353 FinalBranch next -> [CmmBranch next]
354 FinalSwitch expr targets -> [CmmSwitch expr targets]
355 FinalReturn arguments ->
356 tail_call (stack_frame_size curr_format)
357 (CmmLoad (CmmReg spReg) wordRep)
359 FinalJump target arguments ->
360 tail_call (stack_frame_size curr_format) target arguments
361 FinalCall next (CmmForeignCall target CmmCallConv)
363 pack_continuation curr_format cont_format ++
364 tail_call (stack_frame_size curr_format - stack_frame_size cont_format)
367 cont_format = maybe unknown_block id $
368 lookup (mkReturnPtLabel $ getUnique next) formats
369 FinalCall next _ results arguments -> panic "unimplemented CmmCall"
371 -----------------------------------------------------------------------------
372 -- Functions that generate CmmStmt sequences
373 -- for packing/unpacking continuations
374 -- and entering/exiting functions
376 tail_call :: WordOff -> CmmExpr -> CmmActuals -> [CmmStmt]
377 tail_call spRel target arguments
378 = store_arguments ++ adjust_spReg ++ jump where
380 [stack_put spRel expr offset
381 | ((expr, _), StackParam offset) <- argument_formats] ++
382 [global_put expr global
383 | ((expr, _), RegisterParam global) <- argument_formats]
387 else [CmmAssign spReg (CmmRegOff spReg (spRel*wORD_SIZE))]
388 jump = [CmmJump target arguments]
390 argument_formats = assignArguments (cmmExprRep . fst) arguments
392 gc_stack_check :: BlockId -> WordOff -> [CmmStmt]
393 gc_stack_check gc_block max_frame_size
394 = check_stack_limit where
395 check_stack_limit = [
397 (CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
398 [CmmRegOff spReg max_frame_size, CmmReg spLimReg])
401 -- TODO: fix branches to proc point (we have to insert a new block to marshel the continuation)
402 pack_continuation :: StackFormat -> StackFormat -> [CmmStmt]
403 pack_continuation (StackFormat curr_id curr_frame_size _)
404 (StackFormat cont_id cont_frame_size live_regs)
405 = store_live_values ++ set_stack_header where
406 -- TODO: only save variables when actually needed
407 -- (may be handled by latter pass)
409 [stack_put spRel (CmmReg (CmmLocal reg)) offset
410 | (reg, offset) <- cont_offsets]
413 then [stack_put spRel continuation_function 0]
416 -- TODO: factor with function_entry and CmmInfo.hs(?)
417 cont_offsets = mkOffsets label_size live_regs
419 label_size = 1 :: WordOff
421 mkOffsets size [] = []
422 mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
423 mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
425 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
426 -- TODO: it would be better if we had a machRepWordWidth
428 spRel = curr_frame_size - cont_frame_size
429 continuation_function = CmmLit $ CmmLabel $ fromJust cont_id
431 case (curr_id, cont_id) of
432 (Just x, Just y) -> x /= y
435 -- Lazy adjustment of stack headers assumes all blocks
436 -- that could branch to eachother (i.e. control blocks)
437 -- have the same stack format (this causes a problem
438 -- only for proc-point).
439 function_entry :: CmmFormals -> StackFormat -> [CmmStmt]
440 function_entry formals (StackFormat _ _ live_regs)
441 = load_live_values ++ load_args where
442 -- TODO: only save variables when actually needed
443 -- (may be handled by latter pass)
445 [stack_get 0 reg offset
446 | (reg, offset) <- curr_offsets]
448 [stack_get 0 reg offset
449 | (reg, StackParam offset) <- argument_formats] ++
450 [global_get reg global
451 | (reg, RegisterParam global) <- argument_formats]
453 argument_formats = assignArguments (localRegRep) formals
455 -- TODO: eliminate copy/paste with pack_continuation
456 curr_offsets = mkOffsets label_size live_regs
458 label_size = 1 :: WordOff
460 mkOffsets size [] = []
461 mkOffsets size (Nothing:regs) = mkOffsets (size+1) regs
462 mkOffsets size (Just reg:regs) = (reg, size):mkOffsets (size + width) regs
464 width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
465 -- TODO: it would be better if we had a machRepWordWidth
467 -----------------------------------------------------------------------------
468 -- Section: Stack and argument register puts and gets
469 -----------------------------------------------------------------------------
472 -- |Construct a 'CmmStmt' that will save a value on the stack
473 stack_put :: WordOff -- ^ Offset from the real 'Sp' that 'offset'
474 -- is relative to (added to offset)
475 -> CmmExpr -- ^ What to store onto the stack
476 -> WordOff -- ^ Where on the stack to store it
477 -- (positive <=> higher addresses)
479 stack_put spRel expr offset =
480 CmmStore (CmmRegOff spReg (wORD_SIZE*(spRel + offset))) expr
482 --------------------------------
488 stack_get spRel reg offset =
489 CmmAssign (CmmLocal reg)
490 (CmmLoad (CmmRegOff spReg (wORD_SIZE*(spRel + offset)))
492 global_put :: CmmExpr -> GlobalReg -> CmmStmt
493 global_put expr global = CmmAssign (CmmGlobal global) expr
494 global_get :: LocalReg -> GlobalReg -> CmmStmt
495 global_get reg global = CmmAssign (CmmLocal reg) (CmmReg (CmmGlobal global))