--- The format for the call to a continuation
--- The fst is the arguments that must be passed to the continuation
--- by the continuation's caller.
--- The snd is the live values that must be saved on stack.
--- A Nothing indicates an ignored slot.
--- The head of each list is the stack top or the first parameter.
-
--- The format for live values for a particular continuation
--- All on stack for now.
--- Head element is the top of the stack (or just under the header).
--- Nothing means an empty slot.
--- Future possibilities include callee save registers (i.e. passing slots in register)
--- 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
-data Continuation =
- Continuation
- Bool -- True => Function entry, False => Continuation/return point
- [CmmStatic] -- Info table, may be empty
- CLabel -- Used to generate both info & entry labels
- CmmFormals -- Argument locals live on entry (C-- procedure params)
- [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.
-
--- Describes the layout of a stack frame for a continuation
-data StackFormat
- = StackFormat
- (Maybe CLabel) -- The label occupying the top slot
- WordOff -- Total frame size in words
- [(CmmReg, WordOff)] -- 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
+ -- 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)
+
+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
+ CmmInfo (Just existing) _ _
+ -> (old_info, [], make_gc_check stack_use existing)
+ CmmInfo Nothing update_frame info_table
+ -> (CmmInfo (Just block_id) update_frame info_table,
+ [make_gc_block block_id fun_label formals (CmmSafe $ cmmInfoTableSRT info_table)],
+ make_gc_check stack_use block_id)
+
+cmmInfoTableSRT CmmNonInfoTable = NoC_SRT
+cmmInfoTableSRT (CmmInfoTable _ _ (ConstrInfo _ _ _)) = NoC_SRT
+cmmInfoTableSRT (CmmInfoTable _ _ (FunInfo _ srt _ _ _ _)) = srt
+cmmInfoTableSRT (CmmInfoTable _ _ (ThunkInfo _ srt)) = srt
+cmmInfoTableSRT (CmmInfoTable _ _ (ThunkSelectorInfo _ srt)) = srt
+cmmInfoTableSRT (CmmInfoTable _ _ (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 CmmInfo CmmStmt] -- ^Output proceedure and continuations
+
+-- Data blocks don't need to be CPS transformed
+cpsProc uniqSupply proc@(CmmData _ _) = [proc]
+
+-- Empty functions just don't work with the CPS algorithm, but
+-- they don't need the transformation anyway so just output them directly
+cpsProc uniqSupply proc@(CmmProc _ _ _ []) = [proc]
+
+-- CPS transform for those procs that actually need it
+cpsProc uniqSupply (CmmProc info ident params blocks) = cps_procs
+ where
+ (uniqSupply1, uniqSupply2) = splitUniqSupply uniqSupply
+ uniques :: [[Unique]]
+ uniques = map uniqsFromSupply $ listSplitUniqSupply uniqSupply1
+ (gc_unique:gc_block_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
+ gc_block_id = BlockId gc_block_unique
+
+ forced_blocks =
+ BasicBlock gc_block_id
+ (check_stmts++[CmmBranch $ blockId $ head blocks]) :
+ blocks ++ gc_blocks
+
+ forced_gc_id = case forced_info of
+ CmmInfo (Just x) _ _ -> x
+
+ update_frame = case info of CmmInfo _ u _ -> u
+
+ -- 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 = map (gatherBlocksIntoContinuation live proc_points block_env)
+ (uniqSetToList proc_points)
+
+ -- 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, WordOff, [(CLabel, ContinuationFormat)])
+ formats'@(_, _, format_list) = processFormats formats update_frame continuations
+
+ -- Update the info table data on the continuations with
+ -- the selected stack formats.
+ continuations' :: [Continuation CmmInfo]
+ continuations' = map (applyContinuationFormat format_list) continuations
+
+ -- Do the actual CPS transform.
+ cps_procs :: [CmmTop]
+ cps_procs = zipWith (continuationToProc formats' stack_use) proc_uniques continuations'