module LlvmCodeGen.Base (
LlvmCmmDecl, LlvmBasicBlock,
LiveGlobalRegs,
LlvmUnresData, LlvmData, UnresLabel, UnresStatic,
LlvmVersion, defaultLlvmVersion, minSupportLlvmVersion,
maxSupportLlvmVersion,
LlvmM,
runLlvm, liftStream, withClearVars, varLookup, varInsert,
markStackReg, checkStackReg,
funLookup, funInsert, getLlvmVer, getDynFlags, getDynFlag, getLlvmPlatform,
dumpIfSetLlvm, renderLlvm, runUs, markUsedVar, getUsedVars,
ghcInternalFunctions,
getMetaUniqueId,
setUniqMeta, getUniqMeta,
freshSectionId,
cmmToLlvmType, widthToLlvmFloat, widthToLlvmInt, llvmFunTy,
llvmFunSig, llvmStdFunAttrs, llvmFunAlign, llvmInfAlign,
llvmPtrBits, mkLlvmFunc, tysToParams,
strCLabel_llvm, strDisplayName_llvm, strProcedureName_llvm,
getGlobalPtr, generateAliases,
) where
#include "HsVersions.h"
import Llvm
import LlvmCodeGen.Regs
import CLabel
import CodeGen.Platform ( activeStgRegs )
import DynFlags
import FastString
import Cmm
import qualified Outputable as Outp
import qualified Pretty as Prt
import Platform
import UniqFM
import Unique
import BufWrite ( BufHandle )
import UniqSet
import UniqSupply
import ErrUtils
import qualified Stream
type LlvmCmmDecl = GenCmmDecl [LlvmData] (Maybe CmmStatics) (ListGraph LlvmStatement)
type LlvmBasicBlock = GenBasicBlock LlvmStatement
type LiveGlobalRegs = [GlobalReg]
type LlvmUnresData = (CLabel, Section, LlvmType, [UnresStatic])
type LlvmData = ([LMGlobal], [LlvmType])
type UnresLabel = CmmLit
type UnresStatic = Either UnresLabel LlvmStatic
cmmToLlvmType :: CmmType -> LlvmType
cmmToLlvmType ty | isVecType ty = LMVector (vecLength ty) (cmmToLlvmType (vecElemType ty))
| isFloatType ty = widthToLlvmFloat $ typeWidth ty
| otherwise = widthToLlvmInt $ typeWidth ty
widthToLlvmFloat :: Width -> LlvmType
widthToLlvmFloat W32 = LMFloat
widthToLlvmFloat W64 = LMDouble
widthToLlvmFloat W80 = LMFloat80
widthToLlvmFloat W128 = LMFloat128
widthToLlvmFloat w = panic $ "widthToLlvmFloat: Bad float size: " ++ show w
widthToLlvmInt :: Width -> LlvmType
widthToLlvmInt w = LMInt $ widthInBits w
llvmGhcCC :: DynFlags -> LlvmCallConvention
llvmGhcCC dflags
| platformUnregisterised (targetPlatform dflags) = CC_Ccc
| otherwise = CC_Ncc 10
llvmFunTy :: LiveGlobalRegs -> LlvmM LlvmType
llvmFunTy live = return . LMFunction =<< llvmFunSig' live (fsLit "a") ExternallyVisible
llvmFunSig :: LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LlvmM LlvmFunctionDecl
llvmFunSig live lbl link = do
lbl' <- strCLabel_llvm lbl
llvmFunSig' live lbl' link
llvmFunSig' :: LiveGlobalRegs -> LMString -> LlvmLinkageType -> LlvmM LlvmFunctionDecl
llvmFunSig' live lbl link
= do let toParams x | isPointer x = (x, [NoAlias, NoCapture])
| otherwise = (x, [])
dflags <- getDynFlags
return $ LlvmFunctionDecl lbl link (llvmGhcCC dflags) LMVoid FixedArgs
(map (toParams . getVarType) (llvmFunArgs dflags live))
(llvmFunAlign dflags)
mkLlvmFunc :: LiveGlobalRegs -> CLabel -> LlvmLinkageType -> LMSection -> LlvmBlocks
-> LlvmM LlvmFunction
mkLlvmFunc live lbl link sec blks
= do funDec <- llvmFunSig live lbl link
dflags <- getDynFlags
let funArgs = map (fsLit . Outp.showSDoc dflags . ppPlainName) (llvmFunArgs dflags live)
return $ LlvmFunction funDec funArgs llvmStdFunAttrs sec blks
llvmFunAlign :: DynFlags -> LMAlign
llvmFunAlign dflags = Just (wORD_SIZE dflags)
llvmInfAlign :: DynFlags -> LMAlign
llvmInfAlign dflags = Just (wORD_SIZE dflags)
llvmFunArgs :: DynFlags -> LiveGlobalRegs -> [LlvmVar]
llvmFunArgs dflags live =
map (lmGlobalRegArg dflags) (filter isPassed (activeStgRegs platform))
where platform = targetPlatform dflags
isLive r = not (isSSE r) || r `elem` alwaysLive || r `elem` live
isPassed r = not (isSSE r) || isLive r
isSSE (FloatReg _) = True
isSSE (DoubleReg _) = True
isSSE (XmmReg _) = True
isSSE _ = False
llvmStdFunAttrs :: [LlvmFuncAttr]
llvmStdFunAttrs = [NoUnwind]
tysToParams :: [LlvmType] -> [LlvmParameter]
tysToParams = map (\ty -> (ty, []))
llvmPtrBits :: DynFlags -> Int
llvmPtrBits dflags = widthInBits $ typeWidth $ gcWord dflags
type LlvmVersion = Int
defaultLlvmVersion :: LlvmVersion
defaultLlvmVersion = 30
minSupportLlvmVersion :: LlvmVersion
minSupportLlvmVersion = 28
maxSupportLlvmVersion :: LlvmVersion
maxSupportLlvmVersion = 34
data LlvmEnv = LlvmEnv
{ envVersion :: LlvmVersion
, envDynFlags :: DynFlags
, envOutput :: BufHandle
, envUniq :: UniqSupply
, envNextSection :: Int
, envFreshMeta :: Int
, envUniqMeta :: UniqFM Int
, envFunMap :: LlvmEnvMap
, envAliases :: UniqSet LMString
, envUsedVars :: [LlvmVar]
, envVarMap :: LlvmEnvMap
, envStackRegs :: [GlobalReg]
}
type LlvmEnvMap = UniqFM LlvmType
newtype LlvmM a = LlvmM { runLlvmM :: LlvmEnv -> IO (a, LlvmEnv) }
instance Monad LlvmM where
return x = LlvmM $ \env -> return (x, env)
m >>= f = LlvmM $ \env -> do (x, env') <- runLlvmM m env
runLlvmM (f x) env'
instance Functor LlvmM where
fmap f m = LlvmM $ \env -> do (x, env') <- runLlvmM m env
return (f x, env')
instance HasDynFlags LlvmM where
getDynFlags = LlvmM $ \env -> return (envDynFlags env, env)
liftIO :: IO a -> LlvmM a
liftIO m = LlvmM $ \env -> do x <- m
return (x, env)
runLlvm :: DynFlags -> LlvmVersion -> BufHandle -> UniqSupply -> LlvmM () -> IO ()
runLlvm dflags ver out us m = do
_ <- runLlvmM m env
return ()
where env = LlvmEnv { envFunMap = emptyUFM
, envVarMap = emptyUFM
, envStackRegs = []
, envUsedVars = []
, envAliases = emptyUniqSet
, envVersion = ver
, envDynFlags = dflags
, envOutput = out
, envUniq = us
, envFreshMeta = 0
, envUniqMeta = emptyUFM
, envNextSection = 1
}
getEnv :: (LlvmEnv -> a) -> LlvmM a
getEnv f = LlvmM (\env -> return (f env, env))
modifyEnv :: (LlvmEnv -> LlvmEnv) -> LlvmM ()
modifyEnv f = LlvmM (\env -> return ((), f env))
liftStream :: Stream.Stream IO a x -> Stream.Stream LlvmM a x
liftStream s = Stream.Stream $ do
r <- liftIO $ Stream.runStream s
case r of
Left b -> return (Left b)
Right (a, r2) -> return (Right (a, liftStream r2))
withClearVars :: LlvmM a -> LlvmM a
withClearVars m = LlvmM $ \env -> do
(x, env') <- runLlvmM m env { envVarMap = emptyUFM, envStackRegs = [] }
return (x, env' { envVarMap = emptyUFM, envStackRegs = [] })
varInsert, funInsert :: Uniquable key => key -> LlvmType -> LlvmM ()
varInsert s t = modifyEnv $ \env -> env { envVarMap = addToUFM (envVarMap env) s t }
funInsert s t = modifyEnv $ \env -> env { envFunMap = addToUFM (envFunMap env) s t }
varLookup, funLookup :: Uniquable key => key -> LlvmM (Maybe LlvmType)
varLookup s = getEnv (flip lookupUFM s . envVarMap)
funLookup s = getEnv (flip lookupUFM s . envFunMap)
markStackReg :: GlobalReg -> LlvmM ()
markStackReg r = modifyEnv $ \env -> env { envStackRegs = r : envStackRegs env }
checkStackReg :: GlobalReg -> LlvmM Bool
checkStackReg r = getEnv ((elem r) . envStackRegs)
getMetaUniqueId :: LlvmM Int
getMetaUniqueId = LlvmM $ \env -> return (envFreshMeta env, env { envFreshMeta = envFreshMeta env + 1})
getLlvmVer :: LlvmM LlvmVersion
getLlvmVer = getEnv envVersion
getDynFlag :: (DynFlags -> a) -> LlvmM a
getDynFlag f = getEnv (f . envDynFlags)
getLlvmPlatform :: LlvmM Platform
getLlvmPlatform = getDynFlag targetPlatform
dumpIfSetLlvm :: DumpFlag -> String -> Outp.SDoc -> LlvmM ()
dumpIfSetLlvm flag hdr doc = do
dflags <- getDynFlags
liftIO $ dumpIfSet_dyn dflags flag hdr doc
renderLlvm :: Outp.SDoc -> LlvmM ()
renderLlvm sdoc = do
dflags <- getDynFlags
out <- getEnv envOutput
let doc = Outp.withPprStyleDoc dflags (Outp.mkCodeStyle Outp.CStyle) sdoc
liftIO $ Prt.bufLeftRender out doc
dumpIfSetLlvm Opt_D_dump_llvm "LLVM Code" sdoc
return ()
runUs :: UniqSM a -> LlvmM a
runUs m = LlvmM $ \env -> do
let (x, us') = initUs (envUniq env) m
return (x, env { envUniq = us' })
markUsedVar :: LlvmVar -> LlvmM ()
markUsedVar v = modifyEnv $ \env -> env { envUsedVars = v : envUsedVars env }
getUsedVars :: LlvmM [LlvmVar]
getUsedVars = getEnv envUsedVars
saveAlias :: LMString -> LlvmM ()
saveAlias lbl = modifyEnv $ \env -> env { envAliases = addOneToUniqSet (envAliases env) lbl }
setUniqMeta :: Unique -> Int -> LlvmM ()
setUniqMeta f m = modifyEnv $ \env -> env { envUniqMeta = addToUFM (envUniqMeta env) f m }
getUniqMeta :: Unique -> LlvmM (Maybe Int)
getUniqMeta s = getEnv (flip lookupUFM s . envUniqMeta)
freshSectionId :: LlvmM Int
freshSectionId = LlvmM $ \env -> return (envNextSection env, env { envNextSection = envNextSection env + 1})
ghcInternalFunctions :: LlvmM ()
ghcInternalFunctions = do
dflags <- getDynFlags
mk "memcpy" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags]
mk "memmove" i8Ptr [i8Ptr, i8Ptr, llvmWord dflags]
mk "memset" i8Ptr [i8Ptr, llvmWord dflags, llvmWord dflags]
mk "newSpark" (llvmWord dflags) [i8Ptr, i8Ptr]
where
mk n ret args = do
let n' = fsLit n
decl = LlvmFunctionDecl n' ExternallyVisible CC_Ccc ret
FixedArgs (tysToParams args) Nothing
renderLlvm $ ppLlvmFunctionDecl decl
funInsert n' (LMFunction decl)
strCLabel_llvm :: CLabel -> LlvmM LMString
strCLabel_llvm lbl = do
platform <- getLlvmPlatform
dflags <- getDynFlags
let sdoc = pprCLabel platform lbl
str = Outp.renderWithStyle dflags sdoc (Outp.mkCodeStyle Outp.CStyle)
return (fsLit str)
strDisplayName_llvm :: CLabel -> LlvmM LMString
strDisplayName_llvm lbl = do
platform <- getLlvmPlatform
dflags <- getDynFlags
let sdoc = pprCLabel platform lbl
depth = Outp.PartWay 1
style = Outp.mkUserStyle (const Outp.NameNotInScope2, const True) depth
str = Outp.renderWithStyle dflags sdoc style
return (fsLit (dropInfoSuffix str))
dropInfoSuffix :: String -> String
dropInfoSuffix = go
where go "_info" = []
go "_static_info" = []
go "_con_info" = []
go (x:xs) = x:go xs
go [] = []
strProcedureName_llvm :: CLabel -> LlvmM LMString
strProcedureName_llvm lbl = do
platform <- getLlvmPlatform
dflags <- getDynFlags
let sdoc = pprCLabel platform lbl
depth = Outp.PartWay 1
style = Outp.mkUserStyle (const Outp.NameUnqual, const False) depth
str = Outp.renderWithStyle dflags sdoc style
return (fsLit str)
getGlobalPtr :: LMString -> LlvmM LlvmVar
getGlobalPtr llvmLbl = do
m_ty <- funLookup llvmLbl
let mkGlbVar lbl ty = LMGlobalVar lbl (LMPointer ty) Private Nothing Nothing
case m_ty of
Just ty -> return $ mkGlbVar llvmLbl ty Global
Nothing -> do
saveAlias llvmLbl
return $ mkGlbVar (llvmLbl `appendFS` fsLit "$alias") i8 Alias
generateAliases :: LlvmM ([LMGlobal], [LlvmType])
generateAliases = do
delayed <- fmap uniqSetToList $ getEnv envAliases
defss <- flip mapM delayed $ \lbl -> do
let var ty = LMGlobalVar lbl (LMPointer ty) External Nothing Nothing Global
aliasLbl = lbl `appendFS` fsLit "$alias"
aliasVar = LMGlobalVar aliasLbl i8Ptr Private Nothing Nothing Alias
m_ty <- funLookup lbl
case m_ty of
Just ty -> return [LMGlobal aliasVar $ Just $ LMBitc (LMStaticPointer (var ty)) i8Ptr]
Nothing -> return [LMGlobal (var i8) Nothing,
LMGlobal aliasVar $ Just $ LMStaticPointer (var i8) ]
modifyEnv $ \env -> env { envAliases = emptyUniqSet }
return (concat defss, [])
panic :: String -> a
panic s = Outp.panic $ "LlvmCodeGen.Base." ++ s