+-----------------------------------------------------------------------------
+-- |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"
+
+ -- TODO: more lint checking
+ -- check for use of branches to non-existant blocks
+ -- check for use of Sp, SpLim, R1, R2, etc.
+
+ 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
+
+ dumpIfSet_dyn dflags Opt_D_dump_cps_cmm "CPS Cmm" (pprCmms continuationC)
+
+ -- TODO: add option to dump Cmm to file
+
+ return continuationC
+
+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)
+
+force_gc_block old_info block_id fun_label formals blocks =
+ case old_info of
+ CmmNonInfo (Just _) -> (old_info, [])
+ CmmInfo _ (Just _) _ _ -> (old_info, [])
+ CmmNonInfo Nothing
+ -> (CmmNonInfo (Just block_id),
+ [make_gc_block block_id fun_label formals (CmmSafe NoC_SRT)])
+ 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'
+
+-----------------------------------------------------------------------------
+-- |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
+ uniques :: [[Unique]]
+ uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply
+ (gc_unique:info_uniques):block_uniques = uniques
+
+ -- Ensure that
+ forced_gc :: (CmmInfo, [CmmBasicBlock])
+ forced_gc = force_gc_block info (BlockId gc_unique) ident params blocks
+
+ 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 =
+ concat $ zipWith3 breakBlock block_uniques forced_blocks
+ (FunctionEntry forced_info ident params:repeat ControlEntry)
+
+ -- 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 proc_points block_env)
+ (uniqSetToList proc_points)
+ (Just forced_gc_id : repeat Nothing)
+
+ -- 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, (Maybe CLabel, [Maybe LocalReg]))]
+ formats = selectStackFormat 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' = processFormats formats
+
+ -- TODO FIXME NOW: calculate a real max stack (including function call args)
+ -- TODO: from the maximum frame size get the maximum stack size.
+ -- The difference is due to the size taken by function calls.
+
+ -- Update the info table data on the continuations with
+ -- the selected stack formats.
+ continuations' :: [Continuation CmmInfo]
+ continuations' = map (applyStackFormat (snd formats')) continuations
+
+ -- Do the actual CPS transform.
+ cps_procs :: [CmmTop]
+ cps_procs = map (continuationToProc formats') 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)
+