+-- For now just select the continuation orders in the order they are in the set with no gaps
+
+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
+
+-----------------------------------------------------------------------------