+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))