Finished support for foreign calls in the CPS pass
[ghc-hetmet.git] / compiler / cmm / CmmCPS.hs
index e708ebb..4394fb5 100644 (file)
@@ -1,4 +1,8 @@
-module CmmCPS (cmmCPS) where
+module CmmCPS (
+  -- | Converts C-- with full proceedures and parameters
+  -- to a CPS transformed C-- with the stack made manifest.
+  cmmCPS
+) where
 
 #include "HsVersions.h"
 
@@ -6,12 +10,24 @@ import Cmm
 import CmmLint
 import PprCmm
 
-import Dataflow (cmmLivenessComment, cmmLiveness, CmmLive)
-
+import CmmLive
+import CmmBrokenBlock
+import CmmProcPoint
+import CmmCallConv
+import CmmInfo
+import CmmUtils
+
+import CgProf (curCCS, curCCSAddr)
+import CgUtils (cmmOffsetW)
+import Bitmap
+import ClosureInfo
 import MachOp
 import ForeignCall
 import CLabel
+import SMRep
+import Constants
 
+import StaticFlags
 import DynFlags
 import ErrUtils
 import Maybes
@@ -23,36 +39,185 @@ import Unique
 
 import Monad
 import IO
+import Data.List
+
+import MachRegs (callerSaveVolatileRegs)
+  -- HACK: this is part of the NCG so we shouldn't use this, but we need
+  -- it for now to eliminate the need for saved regs to be in CmmCall.
+  -- The long term solution is to factor callerSaveVolatileRegs
+  -- from nativeGen into CPS
+
+-----------------------------------------------------------------------------
+-- |Top level driver for the CPS pass
+-----------------------------------------------------------------------------
+cmmCPS :: DynFlags -- ^ Dynamic flags: -dcmm-lint -ddump-cps-cmm
+       -> [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"
+         case firstJust $ map cmmLint abstractC of
+           Just err -> do printDump err
+                          ghcExit dflags 1
+           Nothing  -> return ()
+  showPass dflags "CPS"
 
---------------------------------------------------------------------------------
--- Monad for the CPSer
--- Contains:
---  * State for the uniqSupply
-
-data CPSState = CPSState { cps_uniqs :: UniqSupply }
-
-data CPS a = CPS { runCPS :: CPSState -> (CPSState, a) }
+  -- TODO: more lint checking
+  --        check for use of branches to non-existant blocks
+  --        check for use of Sp, SpLim, R1, R2, etc.
 
-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'
+  uniqSupply <- mkSplitUniqSupply 'p'
+  let supplies = listSplitUniqSupply uniqSupply
+  let doCpsProc s (Cmm c) =
+          Cmm $ concat $ zipWith cpsProc (listSplitUniqSupply s) c
+  let continuationC = zipWith doCpsProc supplies abstractC
 
---------------------------------------------------------------------------------
--- Utility functions
+  dumpIfSet_dyn dflags Opt_D_dump_cps_cmm "CPS Cmm" (pprCmms continuationC)
 
-getState = CPS $ \s -> (s, s)
-putState s = CPS $ \_ -> (s, ())
+  -- TODO: add option to dump Cmm to file
 
-newLabelCPS = do
-  state <- getState
-  let (us1, us2) = splitUniqSupply (cps_uniqs state)
-  putState $ state { cps_uniqs = us1 }
-  return $ BlockId (uniqFromSupply us2)
+  return continuationC
 
-mapMCmmTop :: (Monad m) => (CmmTop -> m [CmmTop]) -> Cmm -> m Cmm
-mapMCmmTop f (Cmm xs) = liftM Cmm $ liftM concat $ mapM f xs
+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)
+
+make_gc_check stack_use gc_block =
+    [CmmCondBranch
+     (CmmMachOp (MO_U_Lt $ cmmRegRep spReg)
+                    [CmmReg stack_use, CmmReg spLimReg])
+    gc_block]
+
+force_gc_block old_info stack_use block_id fun_label formals =
+    case old_info of
+      CmmNonInfo (Just existing) -> (old_info, [], make_gc_check stack_use existing)
+      CmmInfo _ (Just existing) _ _ -> (old_info, [], make_gc_check stack_use existing)
+      CmmNonInfo Nothing
+          -> (CmmNonInfo (Just block_id),
+              [make_gc_block block_id fun_label formals (CmmSafe NoC_SRT)],
+              make_gc_check stack_use block_id)
+      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)],
+              make_gc_check stack_use block_id)
+             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 
+        -> 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
+      (uniqSupply1, uniqSupply2) = splitUniqSupply uniqSupply
+      uniques :: [[Unique]]
+      uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply1
+      (gc_unique:stack_use_unique:info_uniques):adaptor_uniques:block_uniques = uniques
+      proc_uniques = map (map uniqsFromSupply . listSplitUniqSupply) $ listSplitUniqSupply uniqSupply2
+
+      stack_use = CmmLocal (LocalReg stack_use_unique (cmmRegRep spReg) KindPtr)
+
+      -- TODO: doc
+      forced_gc :: (CmmInfo, [CmmBasicBlock], [CmmStmt])
+      forced_gc = force_gc_block info stack_use (BlockId gc_unique) ident params
+      (forced_info, gc_blocks, check_stmts) = forced_gc
+
+      forced_blocks =
+          case blocks of
+            (BasicBlock id stmts) : bs ->
+                (BasicBlock id (check_stmts ++ stmts)) : (bs ++ gc_blocks)
+            [] -> [] -- If there is no code then we don't need a stack check
+
+      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 :: ([(BlockId, ContFormat)], [BrokenBlock])
+      broken_blocks =
+          (\x -> (concatMap fst x, concatMap snd x)) $
+          zipWith3 (breakBlock [forced_gc_id])
+                     block_uniques
+                     forced_blocks
+                     (FunctionEntry forced_info ident params :
+                      repeat ControlEntry)
+
+      f' = selectContinuations (fst broken_blocks)
+      broken_blocks' = map (makeContinuationEntries f') $
+                       concat $
+                       zipWith (adaptBlockToFormat f')
+                               adaptor_uniques
+                               (snd broken_blocks)
+
+      -- Calculate live variables for each broken block.
+      --
+      -- Nothing can be live on entry to the first block
+      -- so we could take the tail, but for now we wont
+      -- to help future proof the code.
+      live :: BlockEntryLiveness
+      live = cmmLiveness $ map cmmBlockFromBrokenBlock broken_blocks'
+
+      -- Calculate which blocks must be made into full fledged procedures.
+      proc_points :: UniqSet BlockId
+      proc_points = calculateProcPoints broken_blocks'
+
+      -- Construct a map so we can lookup a broken block by its 'BlockId'.
+      block_env :: BlockEnv BrokenBlock
+      block_env = blocksToBlockEnv broken_blocks'
+
+      -- Group the blocks into continuations based on the set of proc-points.
+      continuations :: [Continuation (Either C_SRT CmmInfo)]
+      continuations = zipWith
+                        (gatherBlocksIntoContinuation live proc_points block_env)
+                        (uniqSetToList proc_points)
+                        (Just forced_gc_id : repeat Nothing) {-dead-}
+
+      -- 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,              -- key
+                   (CmmFormals,         -- arguments
+                    Maybe CLabel,       -- label in top slot
+                    [Maybe LocalReg]))] -- slots
+      formats = selectContinuationFormat 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, ContinuationFormat)])
+      formats' = processFormats formats continuations
+
+      -- Update the info table data on the continuations with
+      -- the selected stack formats.
+      continuations' :: [Continuation CmmInfo]
+      continuations' = map (applyContinuationFormat (snd formats')) continuations
+
+      -- Do the actual CPS transform.
+      cps_procs :: [CmmTop]
+      cps_procs = zipWith (continuationToProc formats' stack_use) proc_uniques 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)
 
 --------------------------------------------------------------------------------
 
@@ -71,141 +236,630 @@ mapMCmmTop f (Cmm xs) = liftM Cmm $ liftM concat $ mapM f xs
 -- and heap memory (not sure if that's usefull at all though, but it may
 -- be worth exploring the design space).
 
-data CPSBlockInfo
-  = ControlBlock -- Consider whether a proc-point might want arguments on stack
-  | ContinuationBlock [(CmmReg,MachHint)] {- params -}
-
-type ContinuationFormat = [Maybe LocalReg] -- TODO: consider params as part of format
+continuationLabel (Continuation _ l _ _ _) = l
+data Continuation info =
+  Continuation
+     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)
+     Bool              -- ^ True <=> GC block so ignore stack size
+     [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.
+
+                      -- the BlockId of the first block does not give rise
+                      -- to a label.  To jump to the first block in a Proc,
+                      -- use the appropriate CLabel.
+
+data ContinuationFormat
+    = ContinuationFormat {
+        continuation_formals :: CmmFormals,
+        continuation_label :: Maybe CLabel,    -- The label occupying the top slot
+        continuation_frame_size :: WordOff,    -- Total frame size in words (not including arguments)
+        continuation_stack :: [Maybe LocalReg] -- 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
 
-type CmmParam = [(CmmReg,MachHint)]
-
--- For now just select the continuation orders in the order they are in the set with no gaps
-selectContinuationFormat :: UniqFM {-BlockId-} CmmParam -> UniqFM {-BlockId-} CmmLive -> UniqFM {-BlockId-} ContinuationFormat
-selectContinuationFormat param live = mapUFM (map Just . uniqSetToList) live
-
-transformReturn block_infos formats (BasicBlock ident stmts) =
-  case last $ init stmts of
-    CmmReturn arguments ->
-        BasicBlock ident $ (init $ init stmts) ++ 
-                         [CmmJump (CmmReg spReg) arguments]
-    -- TODO: tail calls
-    -- TODO: return direct at the end of a block
-    _ -> BasicBlock ident stmts
-
-destructContinuation :: UniqFM {-BlockId-} CPSBlockInfo -> UniqFM {-BlockId-} ContinuationFormat -> CmmBasicBlock -> CmmBasicBlock
-destructContinuation block_infos formats (BasicBlock ident stmts) =
-  case info of
-    ControlBlock -> BasicBlock ident stmts
-    ContinuationBlock _ -> BasicBlock ident (unpack_continuation ++ stmts)
-  where
-  info = lookupWithDefaultUFM block_infos (panic $ "info: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
-  format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique ident)) ident
-  unpack_continuation = CmmAssign spReg (CmmRegOff spReg frame_size) :
-                        [CmmAssign (CmmLocal reg) (CmmLoad (CmmRegOff spReg (i*stack_slot_size)) (localRegRep reg))
-                         | (i, Just reg) <- zip [1..] format]
-  frame_size = stack_header_size + stack_slot_size * (length format)
-  stack_header_size = stack_slot_size -- TODO: check if this could be different than stack_slot_size
-  stack_slot_size = 4 -- TODO: find actual variables to be used instead of this
-
-constructContinuation :: UniqFM {-BlockId-} CPSBlockInfo -> UniqFM {-BlockId-} ContinuationFormat -> 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 ++
-                   [CmmJump target arguments]
-    CmmCall target results arguments _ -> panic "unimplemented CmmCall"
-    _ -> BasicBlock ident $ (init stmts) ++ build_block_branch
-  where
-  info = lookupWithDefaultUFM block_infos (panic $ "info: unknown block " ++ (showSDoc $ ppr $ getUnique next_block)) next_block
-  format = lookupWithDefaultUFM formats (panic $ "format: unknown block " ++ (showSDoc $ ppr $ getUnique next_block)) next_block
-  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)"
-  next_block_as_proc_expr = CmmLit $ CmmLabel $ mkReturnPtLabel $ getUnique next_block
-  pack_continuation = CmmAssign spReg (CmmRegOff spReg (-frame_size)) :
-                       CmmStore (CmmReg spReg) next_block_as_proc_expr :
-                       [CmmStore (CmmRegOff spReg (i*stack_slot_size)) (CmmReg $ CmmLocal reg)
-                        | (i, Just reg) <- zip [1..] format]
-  frame_size = stack_header_size + stack_slot_size * (length format)
-  stack_header_size = stack_slot_size -- TODO: check if this could be different than stack_slot_size (e.g. fixedHdrSize depends on PAR and GRAN)
-  stack_slot_size = 4 -- TODO: find actual variables to be used instead of this (e.g. cgRepSizeW)
-  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]
-
--- 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
-  let live = cmmLiveness (map snd broken_blocks)
-  let blocks_with_live = map (cmmLivenessComment live . snd) broken_blocks
-  let formats = selectContinuationFormat (undefined {-TODO-}) live
-  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 blocks_with_live''']
+-----------------------------------------------------------------------------
+
+collectNonProcPointTargets ::
+    UniqSet BlockId -> BlockEnv BrokenBlock
+    -> 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
+                (map (:[]) targets)
+    where
+      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 $ (unionManyUniqSets $ map (mkUniqSet . brokenBlockTargets) blocks')
+                          `minusUniqSet` proc_points
+        -- TODO: remove redundant uniqSetToList
+      new_targets = current_targets `unionUniqSets` (mkUniqSet targets)
+
+-- TODO: insert proc point code here
+--  * Branches and switches to proc points may cause new blocks to be created
+--    (or proc points could leave behind phantom blocks that just jump to them)
+--  * Proc points might get some live variables passed as arguments
+
+gatherBlocksIntoContinuation ::
+    BlockEntryLiveness -> UniqSet BlockId -> BlockEnv BrokenBlock
+    -> BlockId -> Maybe BlockId -> Continuation (Either C_SRT CmmInfo)
+gatherBlocksIntoContinuation live proc_points blocks start gc =
+  Continuation info_table clabel params is_gc_cont body
+    where
+      --start_and_gc = [start] -- : maybeToList gc
+      --children = (collectNonProcPointTargets proc_points blocks (mkUniqSet start_and_gc) start_and_gc) `minusUniqSet` (mkUniqSet start_and_gc)
+      children = (collectNonProcPointTargets proc_points blocks (unitUniqSet start) [start]) `minusUniqSet` (unitUniqSet start)
+      start_block = lookupWithDefaultUFM blocks (panic "TODO") start
+      unknown_block = panic "unknown block in gatherBlocksIntoContinuation"
+      --gc_block = map (lookupWithDefaultUFM blocks (panic "TODO)"))
+      --               (maybeToList gc)
+      children_blocks = map (lookupWithDefaultUFM blocks (panic "TODO")) (uniqSetToList children)
+      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)
+
+      is_gc_cont = case start_block_entry of
+                     FunctionEntry _ _ _ -> False
+                     ContinuationEntry _ _ gc_cont -> gc_cont
+                     ControlEntry -> False
+
+      start_block_entry = brokenBlockEntry start_block
+      clabel = case start_block_entry of
+                 FunctionEntry _ label _ -> label
+                 _ -> mkReturnPtLabel $ getUnique start
+      params = case start_block_entry of
+                 FunctionEntry _ _ args -> args
+                 ContinuationEntry args _ _ -> args
+                 ControlEntry ->
+                     uniqSetToList $
+                     lookupWithDefaultUFM live unknown_block start
+                     -- it's a proc-point, pass lives in parameter registers
 
 --------------------------------------------------------------------------------
--- Takes a basic block and returns a list of basic blocks that
--- each have at most 1 CmmCall in them which must occur at the end.
--- Also returns with each basic block, the variables that will
--- be arguments to the continuation of the block once the call (if any) returns.
-
-cmmBlockifyCalls :: [CmmBasicBlock] -> CPS [(CPSBlockInfo, CmmBasicBlock)]
-cmmBlockifyCalls blocks = liftM concat $ mapM breakBlock blocks
-
--- [(CmmReg,MachHint)] is the results from the previous block that are expected as parameters
---breakBlock :: CmmBasicBlock -> CPS [(Maybe BlockId, CmmBasicBlock)]
-breakBlock :: CmmBasicBlock -> CPS [(CPSBlockInfo, CmmBasicBlock)]
-breakBlock (BasicBlock ident stmts) = breakBlock' ident ControlBlock [] stmts
-
-breakBlock' current_id block_info accum_stmts [] =
-  return [(block_info, BasicBlock current_id accum_stmts)]
--- TODO: notice a call just before a branch, jump, call, etc.
-breakBlock' current_id block_info accum_stmts (stmt@(CmmCall _ results _ _):stmts) = do
-  new_id <- newLabelCPS
-  let new_block = (block_info, BasicBlock current_id (accum_stmts ++ [stmt, CmmBranch new_id]))
-  rest <- breakBlock' new_id (ContinuationBlock results) [] stmts
-  return $ (new_block:rest)
-breakBlock' current_id arguments accum_stmts (stmt:stmts) =
-  breakBlock' current_id arguments (accum_stmts ++ [stmt]) stmts
-
---------------------------------------------------------------------------------
-cmmCPS :: DynFlags
-       -> [Cmm]                 -- C-- with Proceedures
-       -> IO [Cmm]             -- Output: CPS transformed C--
+-- For now just select the continuation orders in the order they are in the set with no gaps
 
-cmmCPS dflags abstractC = do
-  when (dopt Opt_DoCmmLinting dflags) $
-       do showPass dflags "CmmLint"
-         case firstJust $ map cmmLint abstractC of
-           Just err -> do printDump err
-                          ghcExit dflags 1
-           Nothing  -> return ()
-  showPass dflags "CPS"
-  -- TODO: check for use of branches to non-existant blocks
-  -- TODO: check for use of Sp, SpLim, R1, R2, etc.
-  -- continuationC <- return abstractC
-  -- TODO: find out if it is valid to create a new unique source like this
-  uniqSupply <- mkSplitUniqSupply 'p'
-  let (_, continuationC) = runCPS (mapM (mapMCmmTop cpsProc) abstractC) (CPSState uniqSupply)
+selectContinuationFormat :: BlockEnv CmmLive
+                  -> [Continuation (Either C_SRT CmmInfo)]
+                  -> [(CLabel, (CmmFormals, Maybe CLabel, [Maybe LocalReg]))]
+selectContinuationFormat live continuations =
+    map (\c -> (continuationLabel c, selectContinuationFormat' c)) continuations
+    where
+      -- User written continuations
+      selectContinuationFormat' (Continuation
+                          (Right (CmmInfo _ _ _ (ContInfo format srt)))
+                          label formals _ _) =
+          (formals, Just label, format)
+      -- Either user written non-continuation code
+      -- or CPS generated proc-points
+      selectContinuationFormat' (Continuation (Right _) _ formals _ _) =
+          (formals, Nothing, [])
+      -- CPS generated continuations
+      selectContinuationFormat' (Continuation (Left srt) label formals _ 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 (formals,
+              Just label,
+              map Just $ uniqSetToList $
+              lookupWithDefaultUFM live unknown_block ident)
+
+      unknown_block = panic "unknown BlockId in selectContinuationFormat"
+
+processFormats :: [(CLabel, (CmmFormals, Maybe CLabel, [Maybe LocalReg]))]
+               -> [Continuation (Either C_SRT CmmInfo)]
+               -> (WordOff, [(CLabel, ContinuationFormat)])
+processFormats formats continuations = (max_size, formats')
+    where
+      max_size = maximum $
+                 0 : map (continuationMaxStack formats') continuations
+      formats' = map make_format formats
+      make_format (label, (formals, top, stack)) =
+          (label,
+           ContinuationFormat {
+             continuation_formals = formals,
+             continuation_label = top,
+             continuation_frame_size = stack_size stack +
+                                if isJust top
+                                then label_size
+                                else 0,
+             continuation_stack = stack })
+
+      -- 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, ContinuationFormat)]
+                     -> Continuation a
+                     -> WordOff
+continuationMaxStack _ (Continuation _ _ _ True _) = 0
+continuationMaxStack formats (Continuation _ label _ False blocks) =
+    max_arg_size + continuation_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 _ True) = 0
+      final_arg_size (FinalCall next _ _ args _ False) =
+          -- We have to account for the stack used when we build a frame
+          -- for the *next* continuation from *this* continuation
+          argumentsSize (cmmExprRep . fst) args +
+          continuation_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
+
+-----------------------------------------------------------------------------
+applyContinuationFormat :: [(CLabel, ContinuationFormat)]
+                 -> Continuation (Either C_SRT CmmInfo)
+                 -> Continuation CmmInfo
+
+-- User written continuations
+applyContinuationFormat formats (Continuation
+                          (Right (CmmInfo prof gc tag (ContInfo _ srt)))
+                          label formals is_gc blocks) =
+    Continuation (CmmInfo prof gc tag (ContInfo format srt))
+                 label formals is_gc blocks
+    where
+      format = continuation_stack $ maybe unknown_block id $ lookup label formats
+      unknown_block = panic "unknown BlockId in applyContinuationFormat"
+
+-- Either user written non-continuation code or CPS generated proc-point
+applyContinuationFormat formats (Continuation
+                          (Right info) label formals is_gc blocks) =
+    Continuation info label formals is_gc blocks
+
+-- CPS generated continuations
+applyContinuationFormat formats (Continuation
+                          (Left srt) label formals is_gc blocks) =
+    Continuation (CmmInfo prof gc tag (ContInfo (continuation_stack $ format) srt))
+                 label formals is_gc 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 applyContinuationFormat"
+
+-----------------------------------------------------------------------------
+continuationToProc :: (WordOff, [(CLabel, ContinuationFormat)])
+                   -> CmmReg
+                   -> [[Unique]]
+                   -> Continuation CmmInfo
+                   -> CmmTop
+continuationToProc (max_stack, formats) stack_use uniques
+                   (Continuation info label formals _ blocks) =
+    CmmProc info label formals (concat $ zipWith3 continuationToProc' uniques blocks (True : repeat False))
+    where
+      curr_format = maybe unknown_block id $ lookup label formats
+      unknown_block = panic "unknown BlockId in continuationToProc"
+      curr_stack = continuation_frame_size curr_format
+      arg_stack = argumentsSize localRegRep formals
+
+      param_stmts :: [CmmStmt]
+      param_stmts = function_entry curr_format
+
+      gc_stmts :: [CmmStmt]
+      gc_stmts =
+          case info of
+            CmmInfo _ (Just gc_block) _ _ ->
+                gc_stack_check' stack_use arg_stack (max_stack - curr_stack)
+            CmmInfo _ Nothing _ _ ->
+                panic "continuationToProc: missing GC block"
+            CmmNonInfo (Just gc_block) ->
+                gc_stack_check' stack_use arg_stack (max_stack - curr_stack)
+            CmmNonInfo Nothing ->
+                panic "continuationToProc: missing non-info GC block"
+
+-- At present neither the Cmm parser nor the code generator
+-- produce code that will allow the target of a CmmCondBranch
+-- or a CmmSwitch to become a continuation or a proc-point.
+-- If future revisions, might allow these to happen
+-- then special care will have to be take to allow for that case.
+      continuationToProc' :: [Unique]
+                          -> BrokenBlock
+                          -> Bool
+                          -> [CmmBasicBlock]
+      continuationToProc' uniques (BrokenBlock ident entry stmts _ exit) is_entry =
+          prefix_blocks ++ [main_block]
+          where
+            prefix_blocks =
+                case gc_prefix ++ param_prefix of
+                  [] -> []
+                  entry_stmts -> [BasicBlock prefix_id
+                                  (entry_stmts ++ [CmmBranch ident])]
+
+            prefix_unique : call_uniques = uniques
+            toCLabel = mkReturnPtLabel . getUnique
+
+            block_for_branch unique next
+                | (Just cont_format) <- lookup (toCLabel next) formats
+                = let
+                    new_next = BlockId unique
+                    cont_stack = continuation_frame_size cont_format
+                    arguments = map formal_to_actual (continuation_formals cont_format)
+                  in (new_next,
+                     [BasicBlock new_next $
+                      pack_continuation False curr_format cont_format ++
+                      tail_call (curr_stack - cont_stack)
+                              (CmmLit $ CmmLabel $ toCLabel next)
+                              arguments])
+                | otherwise
+                = (next, [])
+
+            block_for_branch' :: Unique -> Maybe BlockId -> (Maybe BlockId, [CmmBasicBlock])
+            block_for_branch' _ Nothing = (Nothing, [])
+            block_for_branch' unique (Just next) = (Just new_next, new_blocks)
+              where (new_next, new_blocks) = block_for_branch unique next
+
+            main_block = BasicBlock ident (stmts ++ postfix_stmts)
+            prefix_id = BlockId prefix_unique
+            gc_prefix = case entry of
+                       FunctionEntry _ _ _ -> gc_stmts
+                       ControlEntry -> []
+                       ContinuationEntry _ _ _ -> []
+            param_prefix = if is_entry
+                           then param_stmts
+                           else []
+            postfix_stmts = case exit of
+                        FinalBranch next ->
+                            if (mkReturnPtLabel $ getUnique next) == label
+                            then [CmmBranch next]
+                            else case lookup (mkReturnPtLabel $ getUnique next) formats of
+                              Nothing -> [CmmBranch next]
+                              Just cont_format ->
+                                pack_continuation False curr_format cont_format ++
+                                tail_call (curr_stack - cont_stack)
+                                          (CmmLit $ CmmLabel $ mkReturnPtLabel $ getUnique next)
+                                          arguments
+                                where
+                                  cont_stack = continuation_frame_size cont_format
+                                  arguments = map formal_to_actual (continuation_formals cont_format)
+                        FinalSwitch expr targets -> [CmmSwitch expr targets]
+                        FinalReturn arguments ->
+                            tail_call curr_stack
+                                (CmmLoad (CmmReg spReg) wordRep)
+                                arguments
+                        FinalJump target arguments ->
+                            tail_call curr_stack target arguments
+
+                        -- A regular Cmm function call
+                        FinalCall next (CmmForeignCall target CmmCallConv)
+                            results arguments _ _ ->
+                                pack_continuation True curr_format cont_format ++
+                                tail_call (curr_stack - cont_stack)
+                                              target arguments
+                            where
+                              cont_format = maybe unknown_block id $
+                                            lookup (mkReturnPtLabel $ getUnique next) formats
+                              cont_stack = continuation_frame_size cont_format
+
+                        -- A safe foreign call
+                        FinalCall next (CmmForeignCall target conv)
+                            results arguments _ _ ->
+                                target_stmts ++
+                                foreignCall call_uniques' (CmmForeignCall new_target conv)
+                                            results arguments
+                            where
+                              (call_uniques', target_stmts, new_target) =
+                                  maybeAssignTemp call_uniques target
+
+                        -- A safe prim call
+                        FinalCall next (CmmPrim target)
+                            results arguments _ _ ->
+                                foreignCall call_uniques (CmmPrim target)
+                                            results arguments
+
+formal_to_actual reg = (CmmReg (CmmLocal reg), NoHint)
+
+foreignCall :: [Unique] -> CmmCallTarget -> CmmHintFormals -> CmmActuals -> [CmmStmt]
+foreignCall uniques call results arguments =
+    arg_stmts ++
+    saveThreadState ++
+    caller_save ++
+    [CmmCall (CmmForeignCall suspendThread CCallConv)
+                [ (id,PtrHint) ]
+                [ (CmmReg (CmmGlobal BaseReg), PtrHint) ]
+                CmmUnsafe,
+     CmmCall call results new_args CmmUnsafe,
+     CmmCall (CmmForeignCall resumeThread CCallConv)
+                 [ (new_base, PtrHint) ]
+                [ (CmmReg (CmmLocal id), PtrHint) ]
+                CmmUnsafe,
+     -- Assign the result to BaseReg: we
+     -- might now have a different Capability!
+     CmmAssign (CmmGlobal BaseReg) (CmmReg (CmmLocal new_base))] ++
+    caller_load ++
+    loadThreadState tso_unique ++
+    [CmmJump (CmmReg spReg) (map (formal_to_actual . fst) results)]
+    where
+      (_, arg_stmts, new_args) =
+          loadArgsIntoTemps argument_uniques arguments
+      (caller_save, caller_load) =
+          callerSaveVolatileRegs (Just [{-only system regs-}])
+      new_base = LocalReg base_unique (cmmRegRep (CmmGlobal BaseReg)) KindNonPtr
+      id = LocalReg id_unique wordRep KindNonPtr
+      tso_unique : base_unique : id_unique : argument_uniques = uniques
+
+-- -----------------------------------------------------------------------------
+-- Save/restore the thread state in the TSO
+
+suspendThread = CmmLit (CmmLabel (mkRtsCodeLabel SLIT("suspendThread")))
+resumeThread  = CmmLit (CmmLabel (mkRtsCodeLabel SLIT("resumeThread")))
+
+-- This stuff can't be done in suspendThread/resumeThread, because it
+-- refers to global registers which aren't available in the C world.
+
+saveThreadState =
+  -- CurrentTSO->sp = Sp;
+  [CmmStore (cmmOffset stgCurrentTSO tso_SP) stgSp,
+  closeNursery] ++
+  -- and save the current cost centre stack in the TSO when profiling:
+  if opt_SccProfilingOn
+  then [CmmStore (cmmOffset stgCurrentTSO tso_CCCS) curCCS]
+  else []
+
+   -- CurrentNursery->free = Hp+1;
+closeNursery = CmmStore nursery_bdescr_free (cmmOffsetW stgHp 1)
+
+loadThreadState tso_unique =
+  [
+       -- tso = CurrentTSO;
+       CmmAssign (CmmLocal tso) stgCurrentTSO,
+       -- Sp = tso->sp;
+       CmmAssign sp (CmmLoad (cmmOffset (CmmReg (CmmLocal tso)) tso_SP)
+                             wordRep),
+       -- SpLim = tso->stack + RESERVED_STACK_WORDS;
+       CmmAssign spLim (cmmOffsetW (cmmOffset (CmmReg (CmmLocal tso)) tso_STACK)
+                                   rESERVED_STACK_WORDS)
+  ] ++
+  openNursery ++
+  -- and load the current cost centre stack from the TSO when profiling:
+  if opt_SccProfilingOn 
+  then [CmmStore curCCSAddr 
+       (CmmLoad (cmmOffset (CmmReg (CmmLocal tso)) tso_CCCS) wordRep)]
+  else []
+  where tso = LocalReg tso_unique wordRep KindNonPtr -- TODO FIXME NOW
+
+
+openNursery = [
+        -- Hp = CurrentNursery->free - 1;
+       CmmAssign hp (cmmOffsetW (CmmLoad nursery_bdescr_free wordRep) (-1)),
+
+        -- HpLim = CurrentNursery->start + 
+       --              CurrentNursery->blocks*BLOCK_SIZE_W - 1;
+       CmmAssign hpLim
+           (cmmOffsetExpr
+               (CmmLoad nursery_bdescr_start wordRep)
+               (cmmOffset
+                 (CmmMachOp mo_wordMul [
+                   CmmMachOp (MO_S_Conv I32 wordRep)
+                     [CmmLoad nursery_bdescr_blocks I32],
+                   CmmLit (mkIntCLit bLOCK_SIZE)
+                  ])
+                 (-1)
+               )
+           )
+   ]
+
+
+nursery_bdescr_free   = cmmOffset stgCurrentNursery oFFSET_bdescr_free
+nursery_bdescr_start  = cmmOffset stgCurrentNursery oFFSET_bdescr_start
+nursery_bdescr_blocks = cmmOffset stgCurrentNursery oFFSET_bdescr_blocks
+
+tso_SP    = tsoFieldB     oFFSET_StgTSO_sp
+tso_STACK = tsoFieldB     oFFSET_StgTSO_stack
+tso_CCCS  = tsoProfFieldB oFFSET_StgTSO_CCCS
+
+-- The TSO struct has a variable header, and an optional StgTSOProfInfo in
+-- the middle.  The fields we're interested in are after the StgTSOProfInfo.
+tsoFieldB :: ByteOff -> ByteOff
+tsoFieldB off
+  | opt_SccProfilingOn = off + sIZEOF_StgTSOProfInfo + fixedHdrSize * wORD_SIZE
+  | otherwise          = off + fixedHdrSize * wORD_SIZE
+
+tsoProfFieldB :: ByteOff -> ByteOff
+tsoProfFieldB off = off + fixedHdrSize * wORD_SIZE
+
+stgSp            = CmmReg sp
+stgHp            = CmmReg hp
+stgCurrentTSO    = CmmReg currentTSO
+stgCurrentNursery = CmmReg currentNursery
+
+sp               = CmmGlobal Sp
+spLim            = CmmGlobal SpLim
+hp               = CmmGlobal Hp
+hpLim            = CmmGlobal HpLim
+currentTSO       = CmmGlobal CurrentTSO
+currentNursery           = CmmGlobal CurrentNursery
+
+-----------------------------------------------------------------------------
+-- Functions that generate CmmStmt sequences
+-- for packing/unpacking continuations
+-- and entering/exiting functions
+
+tail_call :: WordOff -> CmmExpr -> CmmActuals -> [CmmStmt]
+tail_call spRel target arguments
+  = store_arguments ++ adjust_spReg ++ jump where
+    store_arguments =
+        [stack_put spRel expr offset
+         | ((expr, _), StackParam offset) <- argument_formats] ++
+        [global_put expr global
+         | ((expr, _), RegisterParam global) <- argument_formats]
+    adjust_spReg =
+        if spRel == 0
+        then []
+        else [CmmAssign spReg (CmmRegOff spReg (spRel*wORD_SIZE))]
+    jump = [CmmJump target arguments]
+
+    argument_formats = assignArguments (cmmExprRep . fst) arguments
+
+gc_stack_check' stack_use arg_stack max_frame_size =
+    if max_frame_size > arg_stack
+    then [CmmAssign stack_use (CmmRegOff spReg (-max_frame_size*wORD_SIZE))]
+    else [CmmAssign stack_use (CmmReg spLimReg)]
+         -- Trick the optimizer into eliminating the branch for us
+  
+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*wORD_SIZE),
+                     CmmReg spLimReg])
+     gc_block]
+
+
+-- TODO: fix branches to proc point
+-- (we have to insert a new block to marshel the continuation)
+
+
+pack_continuation :: Bool               -- ^ Whether to set the top/header
+                                        -- of the stack.  We only need to
+                                        -- set it if we are calling down
+                                        -- as opposed to continuation
+                                        -- adaptors.
+                  -> ContinuationFormat -- ^ The current format
+                  -> ContinuationFormat -- ^ The return point format
+                  -> [CmmStmt]
+pack_continuation allow_header_set
+                      (ContinuationFormat _ curr_id curr_frame_size _)
+                      (ContinuationFormat _ 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)
+    store_live_values =
+        [stack_put spRel (CmmReg (CmmLocal reg)) offset
+         | (reg, offset) <- cont_offsets]
+    set_stack_header =
+        if needs_header_set && allow_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_set =
+        case (curr_id, cont_id) of
+          (Just x, Just y) -> x /= y
+          _ -> isJust cont_id
+
+-- 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).
+function_entry :: ContinuationFormat -> [CmmStmt]
+function_entry (ContinuationFormat formals _ _ live_regs)
+  = load_live_values ++ load_args where
+    -- 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]
+    load_args =
+        [stack_get 0 reg offset
+         | (reg, StackParam offset) <- argument_formats] ++
+        [global_get reg global
+         | (reg, RegisterParam global) <- argument_formats]
+
+    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
+-----------------------------------------------------------------------------
+-- TODO: document
+
+-- |Construct a 'CmmStmt' that will save a value on the stack
+stack_put :: WordOff            -- ^ Offset from the real 'Sp' that 'offset'
+                                -- is relative to (added to offset)
+          -> CmmExpr            -- ^ What to store onto the stack
+          -> WordOff            -- ^ Where on the stack to store it
+                                -- (positive <=> higher addresses)
+          -> CmmStmt
+stack_put spRel expr offset =
+    CmmStore (CmmRegOff spReg (wORD_SIZE*(spRel + offset))) expr
+
+--------------------------------
+-- |Construct a 
+stack_get :: WordOff
+          -> LocalReg
+          -> WordOff
+          -> CmmStmt
+stack_get spRel reg offset =
+    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
+global_get reg global = CmmAssign (CmmLocal reg) (CmmReg (CmmGlobal global))
 
-  dumpIfSet_dyn dflags Opt_D_dump_cps_cmm "CPS Cmm" (pprCmms continuationC)
-  -- TODO: add option to dump Cmm to file
-  return continuationC