module GHC.CmmToAsm.Dwarf (
dwarfGen
) where
import GHC.Prelude
import GHC.Cmm.CLabel
import GHC.Cmm.Expr ( GlobalReg(..) )
import GHC.Settings.Config ( cProjectName, cProjectVersion )
import GHC.Types.Tickish ( CmmTickish, GenTickish(..) )
import GHC.Cmm.DebugBlock
import GHC.Unit.Module
import GHC.Utils.Outputable
import GHC.Platform
import GHC.Types.Unique
import GHC.Types.Unique.Supply
import GHC.CmmToAsm.Dwarf.Constants
import GHC.CmmToAsm.Dwarf.Types
import GHC.CmmToAsm.Config
import Control.Arrow ( first )
import Control.Monad ( mfilter )
import Data.Maybe
import Data.List ( sortBy )
import Data.Ord ( comparing )
import qualified Data.Map as Map
import System.FilePath
import System.Directory ( getCurrentDirectory )
import qualified GHC.Cmm.Dataflow.Label as H
import qualified GHC.Cmm.Dataflow.Collections as H
dwarfGen :: NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock]
-> IO (SDoc, UniqSupply)
dwarfGen _ _ us [] = return (empty, us)
dwarfGen config modLoc us blocks = do
let platform = ncgPlatform config
let procs = debugSplitProcs blocks
stripBlocks dbg
| ncgDwarfStripBlockInfo config = dbg { dblBlocks = [] }
| otherwise = dbg
compPath <- getCurrentDirectory
let lowLabel = dblCLabel $ head procs
highLabel = mkAsmTempProcEndLabel $ dblCLabel $ last procs
dwarfUnit = DwarfCompileUnit
{ dwChildren = map (procToDwarf config) (map stripBlocks procs)
, dwName = fromMaybe "" (ml_hs_file modLoc)
, dwCompDir = addTrailingPathSeparator compPath
, dwProducer = cProjectName ++ " " ++ cProjectVersion
, dwLowLabel = lowLabel
, dwHighLabel = highLabel
, dwLineLabel = dwarfLineLabel
}
let haveSrcIn blk = isJust (dblSourceTick blk) && isJust (dblPosition blk)
|| any haveSrcIn (dblBlocks blk)
haveSrc = any haveSrcIn procs
let abbrevSct = pprAbbrevDecls platform haveSrc
let
(unitU, us') = takeUniqFromSupply us
infoSct = vcat [ ptext dwarfInfoLabel <> colon
, dwarfInfoSection platform
, compileUnitHeader platform unitU
, pprDwarfInfo platform haveSrc dwarfUnit
, compileUnitFooter platform unitU
]
let lineSct = dwarfLineSection platform $$
ptext dwarfLineLabel <> colon
let (framesU, us'') = takeUniqFromSupply us'
frameSct = dwarfFrameSection platform $$
ptext dwarfFrameLabel <> colon $$
pprDwarfFrame platform (debugFrame framesU procs)
let aranges' | ncgSplitSections config = map mkDwarfARange procs
| otherwise = [DwarfARange lowLabel highLabel]
let aranges = dwarfARangesSection platform $$ pprDwarfARanges platform aranges' unitU
return (infoSct $$ abbrevSct $$ lineSct $$ frameSct $$ aranges, us'')
mkDwarfARange :: DebugBlock -> DwarfARange
mkDwarfARange proc = DwarfARange lbl end
where
lbl = dblCLabel proc
end = mkAsmTempProcEndLabel lbl
compileUnitHeader :: Platform -> Unique -> SDoc
compileUnitHeader platform unitU =
let cuLabel = mkAsmTempLabel unitU
length = pdoc platform (mkAsmTempEndLabel cuLabel) <> char '-' <> pdoc platform cuLabel
<> text "-4"
in vcat [ pdoc platform cuLabel <> colon
, text "\t.long " <> length
, pprHalf 3
, sectionOffset platform (ptext dwarfAbbrevLabel) (ptext dwarfAbbrevLabel)
, text "\t.byte " <> ppr (platformWordSizeInBytes platform)
]
compileUnitFooter :: Platform -> Unique -> SDoc
compileUnitFooter platform unitU =
let cuEndLabel = mkAsmTempEndLabel $ mkAsmTempLabel unitU
in pdoc platform cuEndLabel <> colon
debugSplitProcs :: [DebugBlock] -> [DebugBlock]
debugSplitProcs b = concat $ H.mapElems $ mergeMaps $ map (split Nothing) b
where mergeMaps = foldr (H.mapUnionWithKey (const (++))) H.mapEmpty
split :: Maybe DebugBlock -> DebugBlock -> H.LabelMap [DebugBlock]
split parent blk = H.mapInsert prc [blk'] nested
where prc = dblProcedure blk
blk' = blk { dblBlocks = own_blks
, dblParent = parent
}
own_blks = fromMaybe [] $ H.mapLookup prc nested
nested = mergeMaps $ map (split parent') $ dblBlocks blk
parent'
| Nothing <- dblPosition blk = parent
| otherwise = Just blk
procToDwarf :: NCGConfig -> DebugBlock -> DwarfInfo
procToDwarf config prc
= DwarfSubprogram { dwChildren = map (blockToDwarf config) (dblBlocks prc)
, dwName = case dblSourceTick prc of
Just s@SourceNote{} -> sourceName s
_otherwise -> show (dblLabel prc)
, dwLabel = dblCLabel prc
, dwParent = fmap mkAsmTempDieLabel
$ mfilter goodParent
$ fmap dblCLabel (dblParent prc)
}
where
goodParent a | a == dblCLabel prc = False
goodParent a | not (externallyVisibleCLabel a)
, ncgDwarfStripBlockInfo config = False
goodParent _ = True
blockToDwarf :: NCGConfig -> DebugBlock -> DwarfInfo
blockToDwarf config blk
= DwarfBlock { dwChildren = map (blockToDwarf config) (dblBlocks blk) ++ srcNotes
, dwLabel = dblCLabel blk
, dwMarker = marker
}
where
srcNotes
| ncgDwarfSourceNotes config = concatMap tickToDwarf (dblTicks blk)
| otherwise = []
marker
| Just _ <- dblPosition blk = Just $ mkAsmTempLabel $ dblLabel blk
| otherwise = Nothing
tickToDwarf :: CmmTickish -> [DwarfInfo]
tickToDwarf (SourceNote ss _) = [DwarfSrcNote ss]
tickToDwarf _ = []
debugFrame :: Unique -> [DebugBlock] -> DwarfFrame
debugFrame u procs
= DwarfFrame { dwCieLabel = mkAsmTempLabel u
, dwCieInit = initUws
, dwCieProcs = map (procToFrame initUws) procs
}
where
initUws :: UnwindTable
initUws = Map.fromList [(Sp, Just (UwReg Sp 0))]
procToFrame :: UnwindTable -> DebugBlock -> DwarfFrameProc
procToFrame initUws blk
= DwarfFrameProc { dwFdeProc = dblCLabel blk
, dwFdeHasInfo = dblHasInfoTbl blk
, dwFdeBlocks = map (uncurry blockToFrame)
(setHasInfo blockUws)
}
where blockUws :: [(DebugBlock, [UnwindPoint])]
blockUws = map snd $ sortBy (comparing fst) $ flatten blk
flatten :: DebugBlock
-> [(Int, (DebugBlock, [UnwindPoint]))]
flatten b@DebugBlock{ dblPosition=pos, dblUnwind=uws, dblBlocks=blocks }
| Just p <- pos = (p, (b, uws')):nested
| otherwise = nested
where uws' = addDefaultUnwindings initUws uws
nested = concatMap flatten blocks
setHasInfo :: [(DebugBlock, [UnwindPoint])]
-> [(DebugBlock, [UnwindPoint])]
setHasInfo [] = []
setHasInfo (c0:cs) = first setIt c0 : cs
where
setIt child =
child { dblHasInfoTbl = dblHasInfoTbl child
|| dblHasInfoTbl blk }
blockToFrame :: DebugBlock -> [UnwindPoint] -> DwarfFrameBlock
blockToFrame blk uws
= DwarfFrameBlock { dwFdeBlkHasInfo = dblHasInfoTbl blk
, dwFdeUnwind = uws
}
addDefaultUnwindings :: UnwindTable -> [UnwindPoint] -> [UnwindPoint]
addDefaultUnwindings tbl pts =
[ UnwindPoint lbl (tbl' `mappend` tbl)
| UnwindPoint lbl tbl' <- pts
]