Finished support for foreign calls in the CPS pass
[ghc-hetmet.git] / compiler / cmm / CmmCPS.hs
index e4a17a9..4394fb5 100644 (file)
@@ -17,6 +17,8 @@ import CmmCallConv
 import CmmInfo
 import CmmUtils
 
+import CgProf (curCCS, curCCSAddr)
+import CgUtils (cmmOffsetW)
 import Bitmap
 import ClosureInfo
 import MachOp
@@ -25,6 +27,7 @@ import CLabel
 import SMRep
 import Constants
 
+import StaticFlags
 import DynFlags
 import ErrUtils
 import Maybes
@@ -38,6 +41,12 @@ 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
 -----------------------------------------------------------------------------
@@ -79,23 +88,31 @@ make_gc_block block_id fun_label formals safety = BasicBlock block_id stmts
       actuals = map (\x -> (CmmReg (CmmLocal x), NoHint)) formals
       fun_expr = CmmLit (CmmLabel fun_label)
 
-force_gc_block old_info block_id fun_label formals blocks =
+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 _) -> (old_info, [])
-      CmmInfo _ (Just _) _ _ -> (old_info, [])
+      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_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)])
-           where
-             srt = case type_info of
-                     ConstrInfo _ _ _ -> NoC_SRT
-                     FunInfo _ srt' _ _ _ _ -> srt'
-                     ThunkInfo _ srt' -> srt'
-                     ThunkSelectorInfo _ srt' -> srt'
-                     ContInfo _ srt' -> srt'    
+          -> (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)
@@ -108,26 +125,46 @@ cpsProc :: UniqSupply
 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 uniqSupply
-      (gc_unique:info_uniques):block_uniques = uniques
+      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)
 
-      -- Ensure that 
-      forced_gc :: (CmmInfo, [CmmBasicBlock])
-      forced_gc = force_gc_block info (BlockId gc_unique) ident params blocks
+      -- 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_info = fst forced_gc
-      forced_blocks = blocks ++ snd forced_gc
       forced_gc_id = case forced_info of
                        CmmNonInfo (Just x) -> x
                        CmmInfo _ (Just x) _ _ -> x
 
       -- Break the block at each function call.
       -- The part after the function call will have to become a continuation.
-      broken_blocks :: [BrokenBlock]
+      broken_blocks :: ([(BlockId, ContFormat)], [BrokenBlock])
       broken_blocks =
-          concat $ zipWith3 breakBlock block_uniques forced_blocks
-                     (FunctionEntry forced_info ident params:repeat ControlEntry)
+          (\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.
       --
@@ -135,22 +172,22 @@ cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
       -- 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
+      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
+      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
+      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 proc_points block_env)
+                        (gatherBlocksIntoContinuation live proc_points block_env)
                         (uniqSetToList proc_points)
-                        (Just forced_gc_id : repeat Nothing)
+                        (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
@@ -158,23 +195,24 @@ cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
       -- This is an association list instead of a UniqFM because
       -- CLabel's don't have a 'Uniqueable' instance.
       formats :: [(CLabel,              -- key
-                   (Maybe CLabel,       -- label in top slot
+                   (CmmFormals,         -- arguments
+                    Maybe CLabel,       -- label in top slot
                     [Maybe LocalReg]))] -- slots
-      formats = selectStackFormat live continuations
+      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, StackFormat)])
+      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 (applyStackFormat (snd formats')) continuations
+      continuations' = map (applyContinuationFormat (snd formats')) continuations
 
       -- Do the actual CPS transform.
       cps_procs :: [CmmTop]
-      cps_procs = map (continuationToProc formats') continuations'
+      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
@@ -198,13 +236,14 @@ cpsProc uniqSupply (CmmProc info ident params blocks) = info_procs
 -- and heap memory (not sure if that's usefull at all though, but it may
 -- be worth exploring the design space).
 
-continuationLabel (Continuation _ l _ _) = l
+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
@@ -214,11 +253,12 @@ data Continuation info =
                       -- to a label.  To jump to the first block in a Proc,
                       -- use the appropriate CLabel.
 
-data StackFormat
-    = StackFormat {
-         stack_label :: Maybe CLabel,  -- The label occupying the top slot
-         stack_frame_size :: WordOff,  -- Total frame size in words (not including arguments)
-         stack_live :: [Maybe LocalReg]        -- local reg offsets from stack top
+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
@@ -254,74 +294,93 @@ collectNonProcPointTargets proc_points blocks current_targets new_blocks =
 --  * Proc points might get some live variables passed as arguments
 
 gatherBlocksIntoContinuation ::
-    UniqSet BlockId -> BlockEnv BrokenBlock
+    BlockEntryLiveness -> UniqSet BlockId -> BlockEnv BrokenBlock
     -> BlockId -> Maybe BlockId -> Continuation (Either C_SRT CmmInfo)
-gatherBlocksIntoContinuation proc_points blocks start gc =
-  Continuation info_table clabel params body
+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)
+      --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
-      gc_block = map (lookupWithDefaultUFM blocks (panic "TODO)")) (maybeToList gc)
+      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
+      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
+                     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 -> [] -- TODO: it's a proc-point, we could pass lives in parameter registers
+                 ContinuationEntry args _ _ -> args
+                 ControlEntry ->
+                     uniqSetToList $
+                     lookupWithDefaultUFM live unknown_block start
+                     -- it's a proc-point, pass lives in parameter registers
 
 --------------------------------------------------------------------------------
 -- For now just select the continuation orders in the order they are in the set with no gaps
 
-selectStackFormat :: BlockEnv CmmLive
+selectContinuationFormat :: BlockEnv CmmLive
                   -> [Continuation (Either C_SRT CmmInfo)]
-                  -> [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
-selectStackFormat live continuations =
-    map (\c -> (continuationLabel c, selectStackFormat' c)) continuations
+                  -> [(CLabel, (CmmFormals, Maybe CLabel, [Maybe LocalReg]))]
+selectContinuationFormat live continuations =
+    map (\c -> (continuationLabel c, selectContinuationFormat' c)) continuations
     where
-      selectStackFormat' (Continuation
+      -- User written continuations
+      selectContinuationFormat' (Continuation
                           (Right (CmmInfo _ _ _ (ContInfo format srt)))
-                          label _ _) = (Just label, format)
-      selectStackFormat' (Continuation (Right _) _ _ _) = (Nothing, [])
-      selectStackFormat' (Continuation (Left srt) label _ blocks) =
+                          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 (Just label,
+          in (formals,
+              Just label,
               map Just $ uniqSetToList $
               lookupWithDefaultUFM live unknown_block ident)
 
-      unknown_block = panic "unknown BlockId in selectStackFormat"
+      unknown_block = panic "unknown BlockId in selectContinuationFormat"
 
-processFormats :: [(CLabel, (Maybe CLabel, [Maybe LocalReg]))]
+processFormats :: [(CLabel, (CmmFormals, Maybe CLabel, [Maybe LocalReg]))]
                -> [Continuation (Either C_SRT CmmInfo)]
-               -> (WordOff, [(CLabel, StackFormat)])
+               -> (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, format) =
+      make_format (label, (formals, top, stack)) =
           (label,
-           StackFormat {
-             stack_label = fst format,
-             stack_frame_size = stack_size (snd format) +
-                                if isJust (fst format)
+           ContinuationFormat {
+             continuation_formals = formals,
+             continuation_label = top,
+             continuation_frame_size = stack_size stack +
+                                if isJust top
                                 then label_size
                                 else 0,
-             stack_live = snd format })
+             continuation_stack = stack })
 
       -- TODO: get rid of "+ 1" etc.
       label_size = 1 :: WordOff
@@ -333,11 +392,12 @@ processFormats formats continuations = (max_size, formats')
             width = machRepByteWidth (localRegRep reg) `quot` wORD_SIZE
             -- TODO: it would be better if we had a machRepWordWidth
 
-continuationMaxStack :: [(CLabel, StackFormat)]
+continuationMaxStack :: [(CLabel, ContinuationFormat)]
                      -> Continuation a
                      -> WordOff
-continuationMaxStack formats (Continuation _ label _ blocks) =
-    max_arg_size + stack_frame_size stack_format
+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"
@@ -352,11 +412,12 @@ continuationMaxStack formats (Continuation _ label _ blocks) =
           argumentsSize (cmmExprRep . fst) args
       final_arg_size (FinalJump _ args) =
           argumentsSize (cmmExprRep . fst) args
-      final_arg_size (FinalCall next _ _ 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 +
-          stack_frame_size next_format
+          continuation_frame_size next_format
           where 
             next_format = maybe unknown_format id $ lookup next' formats
             next' = mkReturnPtLabel $ getUnique next
@@ -372,28 +433,30 @@ continuationMaxStack formats (Continuation _ label _ blocks) =
       stmt_arg_size _ = 0
 
 -----------------------------------------------------------------------------
-applyStackFormat :: [(CLabel, StackFormat)]
+applyContinuationFormat :: [(CLabel, ContinuationFormat)]
                  -> Continuation (Either C_SRT CmmInfo)
                  -> Continuation CmmInfo
 
 -- User written continuations
-applyStackFormat formats (Continuation
+applyContinuationFormat formats (Continuation
                           (Right (CmmInfo prof gc tag (ContInfo _ srt)))
-                          label formals blocks) =
+                          label formals is_gc blocks) =
     Continuation (CmmInfo prof gc tag (ContInfo format srt))
-                 label formals blocks
+                 label formals is_gc blocks
     where
-      format = stack_live $ maybe unknown_block id $ lookup label formats
-      unknown_block = panic "unknown BlockId in applyStackFormat"
+      format = continuation_stack $ maybe unknown_block id $ lookup label formats
+      unknown_block = panic "unknown BlockId in applyContinuationFormat"
 
--- User written non-continuation code
-applyStackFormat formats (Continuation (Right info) label formals blocks) =
-    Continuation info label formals blocks
+-- 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
-applyStackFormat formats (Continuation (Left srt) label formals blocks) =
-    Continuation (CmmInfo prof gc tag (ContInfo (stack_live $ format) srt))
-                 label formals blocks
+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
@@ -401,55 +464,259 @@ applyStackFormat formats (Continuation (Left srt) label formals blocks) =
       prof = ProfilingInfo zeroCLit zeroCLit
       tag = rET_SMALL -- cmmToRawCmm may convert it to rET_BIG
       format = maybe unknown_block id $ lookup label formats
-      unknown_block = panic "unknown BlockId in applyStackFormat"
+      unknown_block = panic "unknown BlockId in applyContinuationFormat"
 
 -----------------------------------------------------------------------------
-continuationToProc :: (WordOff, [(CLabel, StackFormat)])
+continuationToProc :: (WordOff, [(CLabel, ContinuationFormat)])
+                   -> CmmReg
+                   -> [[Unique]]
                    -> Continuation CmmInfo
                    -> CmmTop
-continuationToProc (max_stack, formats)
-                   (Continuation info label formals blocks) =
-    CmmProc info label formals (map continuationToProc' blocks)
+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"
-
-      continuationToProc' :: BrokenBlock -> CmmBasicBlock
-      continuationToProc' (BrokenBlock ident entry stmts _ exit) =
-          BasicBlock ident (prefix++stmts++postfix)
+      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 = case entry of
+            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 -> []
-                       FunctionEntry (CmmInfo _ (Just gc_block) _ _) _ formals ->
-                           gc_stack_check gc_block max_stack ++
-                           function_entry formals curr_format
-                       FunctionEntry (CmmInfo _ Nothing _ _) _ formals ->
-                           panic "continuationToProc: missing GC block"
-                       FunctionEntry (CmmNonInfo (Just gc_block)) _ formals ->
-                           gc_stack_check gc_block max_stack ++
-                           function_entry formals curr_format
-                       FunctionEntry (CmmNonInfo Nothing) _ formals ->
-                           panic "continuationToProc: missing non-info GC block"
-                       ContinuationEntry formals _ ->
-                           function_entry formals curr_format
-            postfix = case exit of
-                        FinalBranch next -> [CmmBranch next]
+                       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 (stack_frame_size curr_format)
+                            tail_call curr_stack
                                 (CmmLoad (CmmReg spReg) wordRep)
                                 arguments
                         FinalJump target arguments ->
-                            tail_call (stack_frame_size curr_format) target arguments
+                            tail_call curr_stack target arguments
+
+                        -- A regular Cmm function call
                         FinalCall next (CmmForeignCall target CmmCallConv)
-                            results arguments ->
-                                pack_continuation curr_format cont_format ++
-                                tail_call (stack_frame_size curr_format - stack_frame_size cont_format)
+                            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
-                        FinalCall next _ results arguments -> panic "unimplemented CmmCall"
+                              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
@@ -472,6 +739,12 @@ tail_call spRel 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
@@ -482,11 +755,22 @@ gc_stack_check gc_block max_frame_size
                      CmmReg spLimReg])
      gc_block]
 
+
 -- TODO: fix branches to proc point
 -- (we have to insert a new block to marshel the continuation)
-pack_continuation :: StackFormat -> StackFormat -> [CmmStmt]
-pack_continuation (StackFormat curr_id curr_frame_size _)
-                       (StackFormat cont_id cont_frame_size live_regs)
+
+
+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)
@@ -494,7 +778,7 @@ pack_continuation (StackFormat curr_id curr_frame_size _)
         [stack_put spRel (CmmReg (CmmLocal reg)) offset
          | (reg, offset) <- cont_offsets]
     set_stack_header =
-        if needs_header_set
+        if needs_header_set && allow_header_set
         then [stack_put spRel continuation_function 0]
         else []
 
@@ -521,8 +805,8 @@ pack_continuation (StackFormat curr_id curr_frame_size _)
 -- that could branch to eachother (i.e. control blocks)
 -- have the same stack format (this causes a problem
 -- only for proc-point).
-function_entry :: CmmFormals -> StackFormat -> [CmmStmt]
-function_entry formals (StackFormat _ _ live_regs)
+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)