module GHC.CmmToLlvm
( LlvmVersion
, llvmVersionList
, llvmCodeGen
, llvmFixupAsm
)
where
#include "HsVersions.h"
import GHC.Prelude
import GHC.Llvm
import GHC.CmmToLlvm.Base
import GHC.CmmToLlvm.CodeGen
import GHC.CmmToLlvm.Data
import GHC.CmmToLlvm.Ppr
import GHC.CmmToLlvm.Regs
import GHC.CmmToLlvm.Mangler
import GHC.StgToCmm.CgUtils ( fixStgRegisters )
import GHC.Cmm
import GHC.Cmm.Dataflow.Collections
import GHC.Cmm.Ppr
import GHC.Utils.BufHandle
import GHC.Driver.Session
import GHC.Platform ( platformArch, Arch(..) )
import GHC.Utils.Error
import GHC.Data.FastString
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Utils.Logger
import GHC.SysTools ( figureLlvmVersion )
import qualified GHC.Data.Stream as Stream
import Control.Monad ( when, forM_ )
import Data.Maybe ( fromMaybe, catMaybes )
import System.IO
llvmCodeGen :: Logger -> DynFlags -> Handle
-> Stream.Stream IO RawCmmGroup a
-> IO a
llvmCodeGen logger dflags h cmm_stream
= withTiming logger dflags (text "LLVM CodeGen") (const ()) $ do
bufh <- newBufHandle h
showPass logger dflags "LLVM CodeGen"
mb_ver <- figureLlvmVersion logger dflags
forM_ mb_ver $ \ver -> do
debugTraceMsg logger dflags 2
(text "Using LLVM version:" <+> text (llvmVersionStr ver))
let doWarn = wopt Opt_WarnUnsupportedLlvmVersion dflags
when (not (llvmVersionSupported ver) && doWarn) $ putMsg logger dflags $
"You are using an unsupported version of LLVM!" $$
"Currently only" <+> text (llvmVersionStr supportedLlvmVersionMin) <+>
"to" <+> text (llvmVersionStr supportedLlvmVersionMax) <+> "is supported." <+>
"System LLVM version: " <> text (llvmVersionStr ver) $$
"We will try though..."
let isS390X = platformArch (targetPlatform dflags) == ArchS390X
let major_ver = head . llvmVersionList $ ver
when (isS390X && major_ver < 10 && doWarn) $ putMsg logger dflags $
"Warning: For s390x the GHC calling convention is only supported since LLVM version 10." <+>
"You are using LLVM version: " <> text (llvmVersionStr ver)
let llvm_ver :: LlvmVersion
llvm_ver = fromMaybe supportedLlvmVersionMin mb_ver
a <- runLlvm logger dflags llvm_ver bufh $
llvmCodeGen' dflags cmm_stream
bFlush bufh
return a
llvmCodeGen' :: DynFlags -> Stream.Stream IO RawCmmGroup a -> LlvmM a
llvmCodeGen' dflags cmm_stream
= do
renderLlvm header
ghcInternalFunctions
cmmMetaLlvmPrelude
a <- Stream.consume cmm_stream liftIO llvmGroupLlvmGens
opts <- getLlvmOpts
renderLlvm . pprLlvmData opts =<< generateExternDecls
cmmUsedLlvmGens
return a
where
header :: SDoc
header =
let target = platformMisc_llvmTarget $ platformMisc dflags
in text ("target datalayout = \"" ++ getDataLayout (llvmConfig dflags) target ++ "\"")
$+$ text ("target triple = \"" ++ target ++ "\"")
getDataLayout :: LlvmConfig -> String -> String
getDataLayout config target =
case lookup target (llvmTargets config) of
Just (LlvmTarget {lDataLayout=dl}) -> dl
Nothing -> pprPanic "Failed to lookup LLVM data layout" $
text "Target:" <+> text target $$
hang (text "Available targets:") 4
(vcat $ map (text . fst) $ llvmTargets config)
llvmGroupLlvmGens :: RawCmmGroup -> LlvmM ()
llvmGroupLlvmGens cmm = do
let split (CmmData s d' ) = return $ Just (s, d')
split (CmmProc h l live g) = do
let l' = case mapLookup (g_entry g) h :: Maybe RawCmmStatics of
Nothing -> l
Just (CmmStaticsRaw info_lbl _) -> info_lbl
lml <- strCLabel_llvm l'
funInsert lml =<< llvmFunTy live
return Nothing
cdata <- fmap catMaybes $ mapM split cmm
cmmDataLlvmGens cdata
mapM_ cmmLlvmGen cmm
cmmDataLlvmGens :: [(Section,RawCmmStatics)] -> LlvmM ()
cmmDataLlvmGens statics
= do lmdatas <- mapM genLlvmData statics
let (concat -> gs, tss) = unzip lmdatas
let regGlobal (LMGlobal (LMGlobalVar l ty _ _ _ _) _)
= funInsert l ty
regGlobal _ = pure ()
mapM_ regGlobal gs
gss' <- mapM aliasify $ gs
opts <- getLlvmOpts
renderLlvm $ pprLlvmData opts (concat gss', concat tss)
cmmLlvmGen ::RawCmmDecl -> LlvmM ()
cmmLlvmGen cmm@CmmProc{} = do
platform <- getPlatform
let fixed_cmm = fixStgRegisters platform cmm
dumpIfSetLlvm Opt_D_dump_opt_cmm "Optimised Cmm"
FormatCMM (pprCmmGroup platform [fixed_cmm])
llvmBC <- withClearVars $ genLlvmProc fixed_cmm
(docs, ivars) <- fmap unzip $ mapM pprLlvmCmmDecl llvmBC
renderLlvm (vcat docs)
mapM_ markUsedVar $ concat ivars
cmmLlvmGen _ = return ()
cmmMetaLlvmPrelude :: LlvmM ()
cmmMetaLlvmPrelude = do
metas <- flip mapM stgTBAA $ \(uniq, name, parent) -> do
tbaaId <- getMetaUniqueId
setUniqMeta uniq tbaaId
parentId <- maybe (return Nothing) getUniqMeta parent
return $ MetaUnnamed tbaaId $ MetaStruct $
case parentId of
Just p -> [ MetaStr name, MetaNode p ]
Nothing -> [ MetaStr name ]
opts <- getLlvmOpts
renderLlvm $ ppLlvmMetas opts metas
cmmUsedLlvmGens :: LlvmM ()
cmmUsedLlvmGens = do
ivars <- getUsedVars
let cast x = LMBitc (LMStaticPointer (pVarLift x)) i8Ptr
ty = (LMArray (length ivars) i8Ptr)
usedArray = LMStaticArray (map cast ivars) ty
sectName = Just $ fsLit "llvm.metadata"
lmUsedVar = LMGlobalVar (fsLit "llvm.used") ty Appending sectName Nothing Constant
lmUsed = LMGlobal lmUsedVar (Just usedArray)
opts <- getLlvmOpts
if null ivars
then return ()
else renderLlvm $ pprLlvmData opts ([lmUsed], [])