/compiler/llvmGen/LlvmCodeGen/Base.hs
Haskell | 479 lines | 286 code | 81 blank | 112 comment | 3 complexity | 3cf95480d36a62dcca8a670da9c0b585 MD5 | raw file
- -- ----------------------------------------------------------------------------
- -- | Base LLVM Code Generation module
- --
- -- Contains functions useful through out the code generator.
- --
- 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
- -- ----------------------------------------------------------------------------
- -- * Some Data Types
- --
- type LlvmCmmDecl = GenCmmDecl [LlvmData] (Maybe CmmStatics) (ListGraph LlvmStatement)
- type LlvmBasicBlock = GenBasicBlock LlvmStatement
- -- | Global registers live on proc entry
- type LiveGlobalRegs = [GlobalReg]
- -- | Unresolved code.
- -- Of the form: (data label, data type, unresolved data)
- type LlvmUnresData = (CLabel, Section, LlvmType, [UnresStatic])
- -- | Top level LLVM Data (globals and type aliases)
- type LlvmData = ([LMGlobal], [LlvmType])
- -- | An unresolved Label.
- --
- -- Labels are unresolved when we haven't yet determined if they are defined in
- -- the module we are currently compiling, or an external one.
- type UnresLabel = CmmLit
- type UnresStatic = Either UnresLabel LlvmStatic
- -- ----------------------------------------------------------------------------
- -- * Type translations
- --
- -- | Translate a basic CmmType to an LlvmType.
- cmmToLlvmType :: CmmType -> LlvmType
- cmmToLlvmType ty | isVecType ty = LMVector (vecLength ty) (cmmToLlvmType (vecElemType ty))
- | isFloatType ty = widthToLlvmFloat $ typeWidth ty
- | otherwise = widthToLlvmInt $ typeWidth ty
- -- | Translate a Cmm Float Width to a LlvmType.
- widthToLlvmFloat :: Width -> LlvmType
- widthToLlvmFloat W32 = LMFloat
- widthToLlvmFloat W64 = LMDouble
- widthToLlvmFloat W80 = LMFloat80
- widthToLlvmFloat W128 = LMFloat128
- widthToLlvmFloat w = panic $ "widthToLlvmFloat: Bad float size: " ++ show w
- -- | Translate a Cmm Bit Width to a LlvmType.
- widthToLlvmInt :: Width -> LlvmType
- widthToLlvmInt w = LMInt $ widthInBits w
- -- | GHC Call Convention for LLVM
- llvmGhcCC :: DynFlags -> LlvmCallConvention
- llvmGhcCC dflags
- | platformUnregisterised (targetPlatform dflags) = CC_Ccc
- | otherwise = CC_Ncc 10
- -- | Llvm Function type for Cmm function
- llvmFunTy :: LiveGlobalRegs -> LlvmM LlvmType
- llvmFunTy live = return . LMFunction =<< llvmFunSig' live (fsLit "a") ExternallyVisible
- -- | Llvm Function signature
- 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)
- -- | Create a Haskell function in LLVM.
- 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
- -- | Alignment to use for functions
- llvmFunAlign :: DynFlags -> LMAlign
- llvmFunAlign dflags = Just (wORD_SIZE dflags)
- -- | Alignment to use for into tables
- llvmInfAlign :: DynFlags -> LMAlign
- llvmInfAlign dflags = Just (wORD_SIZE dflags)
- -- | A Function's arguments
- 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
- -- | Llvm standard fun attributes
- llvmStdFunAttrs :: [LlvmFuncAttr]
- llvmStdFunAttrs = [NoUnwind]
- -- | Convert a list of types to a list of function parameters
- -- (each with no parameter attributes)
- tysToParams :: [LlvmType] -> [LlvmParameter]
- tysToParams = map (\ty -> (ty, []))
- -- | Pointer width
- llvmPtrBits :: DynFlags -> Int
- llvmPtrBits dflags = widthInBits $ typeWidth $ gcWord dflags
- -- ----------------------------------------------------------------------------
- -- * Llvm Version
- --
- -- | LLVM Version Number
- type LlvmVersion = Int
- -- | The LLVM Version we assume if we don't know
- defaultLlvmVersion :: LlvmVersion
- defaultLlvmVersion = 30
- minSupportLlvmVersion :: LlvmVersion
- minSupportLlvmVersion = 28
- maxSupportLlvmVersion :: LlvmVersion
- maxSupportLlvmVersion = 33
- -- ----------------------------------------------------------------------------
- -- * Environment Handling
- --
- data LlvmEnv = LlvmEnv
- { envVersion :: LlvmVersion -- ^ LLVM version
- , envDynFlags :: DynFlags -- ^ Dynamic flags
- , envOutput :: BufHandle -- ^ Output buffer
- , envUniq :: UniqSupply -- ^ Supply of unique values
- , envNextSection :: Int -- ^ Supply of fresh section IDs
- , envFreshMeta :: Int -- ^ Supply of fresh metadata IDs
- , envUniqMeta :: UniqFM Int -- ^ Global metadata nodes
- , envFunMap :: LlvmEnvMap -- ^ Global functions so far, with type
- , envAliases :: UniqSet LMString -- ^ Globals that we had to alias, see [Llvm Forward References]
- , envUsedVars :: [LlvmVar] -- ^ Pointers to be added to llvm.used (see @cmmUsedLlvmGens@)
- -- the following get cleared for every function (see @withClearVars@)
- , envVarMap :: LlvmEnvMap -- ^ Local variables so far, with type
- , envStackRegs :: [GlobalReg] -- ^ Non-constant registers (alloca'd in the function prelude)
- }
- type LlvmEnvMap = UniqFM LlvmType
- -- | The Llvm monad. Wraps @LlvmEnv@ state as well as the @IO@ monad
- 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)
- -- | Lifting of IO actions. Not exported, as we want to encapsulate IO.
- liftIO :: IO a -> LlvmM a
- liftIO m = LlvmM $ \env -> do x <- m
- return (x, env)
- -- | Get initial Llvm environment.
- 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
- }
- -- | Get environment (internal)
- getEnv :: (LlvmEnv -> a) -> LlvmM a
- getEnv f = LlvmM (\env -> return (f env, env))
- -- | Modify environment (internal)
- modifyEnv :: (LlvmEnv -> LlvmEnv) -> LlvmM ()
- modifyEnv f = LlvmM (\env -> return ((), f env))
- -- | Lift a stream into the LlvmM monad
- 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))
- -- | Clear variables from the environment for a subcomputation
- withClearVars :: LlvmM a -> LlvmM a
- withClearVars m = LlvmM $ \env -> do
- (x, env') <- runLlvmM m env { envVarMap = emptyUFM, envStackRegs = [] }
- return (x, env' { envVarMap = emptyUFM, envStackRegs = [] })
- -- | Insert variables or functions into the environment.
- 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 }
- -- | Lookup variables or functions in the environment.
- varLookup, funLookup :: Uniquable key => key -> LlvmM (Maybe LlvmType)
- varLookup s = getEnv (flip lookupUFM s . envVarMap)
- funLookup s = getEnv (flip lookupUFM s . envFunMap)
- -- | Set a register as allocated on the stack
- markStackReg :: GlobalReg -> LlvmM ()
- markStackReg r = modifyEnv $ \env -> env { envStackRegs = r : envStackRegs env }
- -- | Check whether a register is allocated on the stack
- checkStackReg :: GlobalReg -> LlvmM Bool
- checkStackReg r = getEnv ((elem r) . envStackRegs)
- -- | Allocate a new global unnamed metadata identifier
- getMetaUniqueId :: LlvmM Int
- getMetaUniqueId = LlvmM $ \env -> return (envFreshMeta env, env { envFreshMeta = envFreshMeta env + 1})
- -- | Get the LLVM version we are generating code for
- getLlvmVer :: LlvmM LlvmVersion
- getLlvmVer = getEnv envVersion
- -- | Get the platform we are generating code for
- getDynFlag :: (DynFlags -> a) -> LlvmM a
- getDynFlag f = getEnv (f . envDynFlags)
- -- | Get the platform we are generating code for
- getLlvmPlatform :: LlvmM Platform
- getLlvmPlatform = getDynFlag targetPlatform
- -- | Dumps the document if the corresponding flag has been set by the user
- dumpIfSetLlvm :: DumpFlag -> String -> Outp.SDoc -> LlvmM ()
- dumpIfSetLlvm flag hdr doc = do
- dflags <- getDynFlags
- liftIO $ dumpIfSet_dyn dflags flag hdr doc
- -- | Prints the given contents to the output handle
- renderLlvm :: Outp.SDoc -> LlvmM ()
- renderLlvm sdoc = do
- -- Write to output
- dflags <- getDynFlags
- out <- getEnv envOutput
- let doc = Outp.withPprStyleDoc dflags (Outp.mkCodeStyle Outp.CStyle) sdoc
- liftIO $ Prt.bufLeftRender out doc
- -- Dump, if requested
- dumpIfSetLlvm Opt_D_dump_llvm "LLVM Code" sdoc
- return ()
- -- | Run a @UniqSM@ action with our unique supply
- runUs :: UniqSM a -> LlvmM a
- runUs m = LlvmM $ \env -> do
- let (x, us') = initUs (envUniq env) m
- return (x, env { envUniq = us' })
- -- | Marks a variable as "used"
- markUsedVar :: LlvmVar -> LlvmM ()
- markUsedVar v = modifyEnv $ \env -> env { envUsedVars = v : envUsedVars env }
- -- | Return all variables marked as "used" so far
- getUsedVars :: LlvmM [LlvmVar]
- getUsedVars = getEnv envUsedVars
- -- | Saves that at some point we didn't know the type of the label and
- -- generated a reference to a type variable instead
- saveAlias :: LMString -> LlvmM ()
- saveAlias lbl = modifyEnv $ \env -> env { envAliases = addOneToUniqSet (envAliases env) lbl }
- -- | Sets metadata node for a given unique
- setUniqMeta :: Unique -> Int -> LlvmM ()
- setUniqMeta f m = modifyEnv $ \env -> env { envUniqMeta = addToUFM (envUniqMeta env) f m }
- -- | Gets metadata node for given unique
- getUniqMeta :: Unique -> LlvmM (Maybe Int)
- getUniqMeta s = getEnv (flip lookupUFM s . envUniqMeta)
- -- | Returns a fresh section ID
- freshSectionId :: LlvmM Int
- freshSectionId = LlvmM $ \env -> return (envNextSection env, env { envNextSection = envNextSection env + 1})
- -- ----------------------------------------------------------------------------
- -- * Internal functions
- --
- -- | Here we pre-initialise some functions that are used internally by GHC
- -- so as to make sure they have the most general type in the case that
- -- user code also uses these functions but with a different type than GHC
- -- internally. (Main offender is treating return type as 'void' instead of
- -- 'void *'). Fixes trac #5486.
- 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)
- -- ----------------------------------------------------------------------------
- -- * Label handling
- --
- -- | Pretty print a 'CLabel'.
- 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)
- -- ----------------------------------------------------------------------------
- -- * Global variables / forward references
- --
- -- | Create/get a pointer to a global value. Might return an alias if
- -- the value in question hasn't been defined yet. We especially make
- -- no guarantees on the type of the returned pointer.
- 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
- -- Directly reference if we have seen it already
- Just ty -> return $ mkGlbVar llvmLbl ty Global
- -- Otherwise use a forward alias of it
- Nothing -> do
- saveAlias llvmLbl
- return $ mkGlbVar (llvmLbl `appendFS` fsLit "$alias") i8 Alias
- -- | Generate definitions for aliases forward-referenced by @getGlobalPtr@.
- --
- -- Must be called at a point where we are sure that no new global definitions
- -- will be generated anymore!
- 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
- -- If we have a definition, set the alias value using a
- -- cost. Otherwise, declare it as an undefined external symbol.
- 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) ]
- -- Reset forward list
- modifyEnv $ \env -> env { envAliases = emptyUniqSet }
- return (concat defss, [])
- -- Note [Llvm Forward References]
- --
- -- The issue here is that LLVM insists on being strongly typed at
- -- every corner, so the first time we mention something, we have to
- -- settle what type we assign to it. That makes things awkward, as Cmm
- -- will often reference things before their definition, and we have no
- -- idea what (LLVM) type it is going to be before that point.
- --
- -- Our work-around is to define "aliases" of a standard type (i8 *) in
- -- these kind of situations, which we later tell LLVM to be either
- -- references to their actual local definitions (involving a cast) or
- -- an external reference. This obviously only works for pointers.
- -- ----------------------------------------------------------------------------
- -- * Misc
- --
- -- | Error function
- panic :: String -> a
- panic s = Outp.panic $ "LlvmCodeGen.Base." ++ s