{-# LANGUAGE TemplateHaskell, MultiParamTypeClasses, RecordWildCards, UndecidableInstances, OverloadedStrings #-} module LLVM.Internal.Target where import LLVM.Prelude import Control.Monad.AnyCont import Control.Monad.Catch import Control.Monad.IO.Class import Control.Monad.Trans.Except import Data.Attoparsec.ByteString import Data.Attoparsec.ByteString.Char8 import qualified Data.ByteString as ByteString import Data.Char import Data.Map (Map) import qualified Data.Map as Map import Foreign.C.String import Foreign.Ptr import LLVM.Internal.Coding import LLVM.Internal.String () import LLVM.Internal.LibraryFunction import LLVM.DataLayout import LLVM.Exception import LLVM.AST.DataLayout import qualified LLVM.Internal.FFI.LLVMCTypes as FFI import qualified LLVM.Internal.FFI.Target as FFI import qualified LLVM.Relocation as Reloc import qualified LLVM.Target.Options as TO import qualified LLVM.CodeModel as CodeModel import qualified LLVM.CodeGenOpt as CodeGenOpt genCodingInstance [t| Reloc.Model |] ''FFI.RelocModel [ (FFI.relocModelDefault, Reloc.Default), (FFI.relocModelStatic, Reloc.Static), (FFI.relocModelPIC, Reloc.PIC), (FFI.relocModelDynamicNoPic, Reloc.DynamicNoPIC) ] genCodingInstance [t| CodeModel.Model |] ''FFI.CodeModel [ (FFI.codeModelDefault,CodeModel.Default), (FFI.codeModelJITDefault, CodeModel.JITDefault), (FFI.codeModelSmall, CodeModel.Small), (FFI.codeModelKernel, CodeModel.Kernel), (FFI.codeModelMedium, CodeModel.Medium), (FFI.codeModelLarge, CodeModel.Large) ] genCodingInstance [t| CodeGenOpt.Level |] ''FFI.CodeGenOptLevel [ (FFI.codeGenOptLevelNone, CodeGenOpt.None), (FFI.codeGenOptLevelLess, CodeGenOpt.Less), (FFI.codeGenOptLevelDefault, CodeGenOpt.Default), (FFI.codeGenOptLevelAggressive, CodeGenOpt.Aggressive) ] genCodingInstance [t| TO.FloatABI |] ''FFI.FloatABIType [ (FFI.floatABIDefault, TO.FloatABIDefault), (FFI.floatABISoft, TO.FloatABISoft), (FFI.floatABIHard, TO.FloatABIHard) ] genCodingInstance [t| TO.FloatingPointOperationFusionMode |] ''FFI.FPOpFusionMode [ (FFI.fpOpFusionModeFast, TO.FloatingPointOperationFusionFast), (FFI.fpOpFusionModeStandard, TO.FloatingPointOperationFusionStandard), (FFI.fpOpFusionModeStrict, TO.FloatingPointOperationFusionStrict) ] genCodingInstance[t| TO.DebugCompressionType |] ''FFI.DebugCompressionType [ (FFI.debugCompressionTypeNone, TO.CompressNone), (FFI.debugCompressionTypeGNU, TO.CompressGNU), (FFI.debugCompressionTypeZ, TO.CompressZ) ] genCodingInstance[t| TO.ThreadModel |] ''FFI.ThreadModel [ (FFI.threadModelPOSIX, TO.ThreadModelPOSIX), (FFI.threadModelSingle, TO.ThreadModelSingle) ] genCodingInstance[t| TO.EABIVersion |] ''FFI.EABI [ (FFI.eabiVersionUnknown, TO.EABIVersionUnknown), (FFI.eabiVersionDefault, TO.EABIVersionDefault), (FFI.eabiVersionEABI4, TO.EABIVersion4), (FFI.eabiVersionEABI5, TO.EABIVersion5), (FFI.eabiVersionGNU, TO.EABIVersionGNU) ] genCodingInstance[t| TO.DebuggerKind |] ''FFI.DebuggerKind [ (FFI.debuggerKindDefault, TO.DebuggerDefault), (FFI.debuggerKindGDB, TO.DebuggerGDB), (FFI.debuggerKindLLDB, TO.DebuggerLLDB), (FFI.debuggerKindSCE, TO.DebuggerSCE) ] genCodingInstance[t| TO.FloatingPointDenormalMode |] ''FFI.FPDenormalMode [ (FFI.fpDenormalModeIEEE, TO.FloatingPointDenormalIEEE), (FFI.fpDenormalModePreserveSign, TO.FloatingPointDenormalPreserveSign), (FFI.fpDenormalModePositiveZero, TO.FloatingPointDenormalPositiveZero) ] genCodingInstance[t| TO.ExceptionHandling |] ''FFI.ExceptionHandling [ (FFI.exceptionHandlingNone, TO.ExceptionHandlingNone), (FFI.exceptionHandlingDwarfCFI, TO.ExceptionHandlingDwarfCFI), (FFI.exceptionHandlingSjLj, TO.ExceptionHandlingSjLj), (FFI.exceptionHandlingARM, TO.ExceptionHandlingARM), (FFI.exceptionHandlingWinEH, TO.ExceptionHandlingWinEH) ] -- | newtype Target = Target (Ptr FFI.Target) -- | e.g. an instruction set extension newtype CPUFeature = CPUFeature ByteString deriving (Eq, Ord, Read, Show) instance EncodeM e ByteString es => EncodeM e (Map CPUFeature Bool) es where encodeM = encodeM . ByteString.intercalate "," . map (\(CPUFeature f, enabled) -> (if enabled then "+" else "-") <> f) . Map.toList instance (Monad d, DecodeM d ByteString es) => DecodeM d (Map CPUFeature Bool) es where decodeM es = do s <- decodeM es let flag = do en <- choice [char8 '-' >> return False, char8 '+' >> return True] s <- ByteString.pack <$> many1 (notWord8 (fromIntegral (ord ','))) return (CPUFeature s, en) features = liftM Map.fromList (flag `sepBy` (char8 ',')) case parseOnly (features <* endOfInput) s of Right features -> return features Left err -> fail ("failure to parse CPUFeature string: " <> err) -- | Find a 'Target' given an architecture and/or a \"triple\". -- | -- | Be sure to run either 'initializeAllTargets' or -- 'initializeNativeTarget' before expecting this to succeed, -- depending on what target(s) you want to use. May throw -- 'LookupTargetException' if no target is found. lookupTarget :: Maybe ShortByteString -- ^ arch -> ShortByteString -- ^ \"triple\" - e.g. x86_64-unknown-linux-gnu -> IO (Target, ShortByteString) lookupTarget arch triple = flip runAnyContT return $ do cErrorP <- alloca cNewTripleP <- alloca arch <- encodeM (maybe "" id arch) triple <- encodeM triple target <- liftIO $ FFI.lookupTarget arch triple cNewTripleP cErrorP when (target == nullPtr) $ throwM . LookupTargetException =<< decodeM cErrorP liftM (Target target, ) $ decodeM cNewTripleP -- | newtype TargetOptions = TargetOptions (Ptr FFI.TargetOptions) newtype MCTargetOptions = MCTargetOptions (Ptr FFI.MCTargetOptions) -- | bracket creation and destruction of a 'TargetOptions' object withTargetOptions :: (TargetOptions -> IO a) -> IO a withTargetOptions = bracket FFI.createTargetOptions FFI.disposeTargetOptions . (. TargetOptions) -- | set all target options pokeTargetOptions :: TO.Options -> TargetOptions -> IO () pokeTargetOptions hOpts opts@(TargetOptions cOpts) = do mapM_ (\(c, ha) -> FFI.setTargetOptionFlag cOpts c =<< encodeM (ha hOpts)) [ (FFI.targetOptionFlagPrintMachineCode, TO.printMachineCode), (FFI.targetOptionFlagUnsafeFPMath, TO.unsafeFloatingPointMath), (FFI.targetOptionFlagNoInfsFPMath, TO.noInfinitiesFloatingPointMath), (FFI.targetOptionFlagNoNaNsFPMath, TO.noNaNsFloatingPointMath), (FFI.targetOptionFlagNoTrappingFPMath, TO.noTrappingFloatingPointMath), (FFI.targetOptionFlagNoSignedZerosFPMath, TO.noSignedZeroesFloatingPointMath), (FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption, TO.honorSignDependentRoundingFloatingPointMathOption), (FFI.targetOptionFlagNoZerosInBSS, TO.noZerosInBSS), (FFI.targetOptionFlagGuaranteedTailCallOpt, TO.guaranteedTailCallOptimization), (FFI.targetOptionFlagStackSymbolOrdering, TO.stackSymbolOrdering), (FFI.targetOptionFlagEnableFastISel, TO.enableFastInstructionSelection), (FFI.targetOptionFlagUseInitArray, TO.useInitArray), (FFI.targetOptionFlagDisableIntegratedAS, TO.disableIntegratedAssembler), (FFI.targetOptionFlagRelaxELFRelocations, TO.relaxELFRelocations), (FFI.targetOptionFlagFunctionSections, TO.functionSections), (FFI.targetOptionFlagDataSections, TO.dataSections), (FFI.targetOptionFlagUniqueSectionNames, TO.uniqueSectionNames), (FFI.targetOptionFlagTrapUnreachable, TO.trapUnreachable), (FFI.targetOptionFlagEmulatedTLS, TO.emulatedThreadLocalStorage), (FFI.targetOptionFlagEnableIPRA, TO.enableInterProceduralRegisterAllocation) ] FFI.setStackAlignmentOverride cOpts =<< encodeM (TO.stackAlignmentOverride hOpts) FFI.setFloatABIType cOpts =<< encodeM (TO.floatABIType hOpts) FFI.setAllowFPOpFusion cOpts =<< encodeM (TO.allowFloatingPointOperationFusion hOpts) FFI.setCompressDebugSections cOpts =<< encodeM (TO.compressDebugSections hOpts) FFI.setThreadModel cOpts =<< encodeM (TO.threadModel hOpts) FFI.setEABIVersion cOpts =<< encodeM (TO.eabiVersion hOpts) FFI.setDebuggerTuning cOpts =<< encodeM (TO.debuggerTuning hOpts) FFI.setFPDenormalMode cOpts =<< encodeM (TO.floatingPointDenormalMode hOpts) FFI.setExceptionModel cOpts =<< encodeM (TO.exceptionModel hOpts) pokeMachineCodeOptions (TO.machineCodeOptions hOpts) =<< machineCodeOptions opts pokeMachineCodeOptions :: TO.MachineCodeOptions -> MCTargetOptions -> IO () pokeMachineCodeOptions hOpts (MCTargetOptions cOpts) = mapM_ (\(c, ha) -> FFI.setMCTargetOptionFlag cOpts c =<< encodeM (ha hOpts)) [ (FFI.mcTargetOptionFlagSanitizeAddress, TO.sanitizeAddresses), (FFI.mcTargetOptionFlagMCRelaxAll, TO.relaxAll), (FFI.mcTargetOptionFlagMCNoExecStack, TO.noExecutableStack), (FFI.mcTargetOptionFlagMCFatalWarnings, TO.fatalWarnings), (FFI.mcTargetOptionFlagMCNoWarn, TO.noWarnings), (FFI.mcTargetOptionFlagMCNoDeprecatedWarn, TO.noDeprecatedWarning), (FFI.mcTargetOptionFlagMCSaveTempLabels, TO.saveTemporaryLabels), (FFI.mcTargetOptionFlagMCUseDwarfDirectory, TO.useDwarfDirectory), (FFI.mcTargetOptionFlagMCIncrementalLinkerCompatible, TO.incrementalLinkerCompatible), (FFI.mcTargetOptionFlagMCPIECopyRelocations, TO.pieCopyRelocations), (FFI.mcTargetOptionFlagShowMCEncoding, TO.showMachineCodeEncoding), (FFI.mcTargetOptionFlagShowMCInst, TO.showMachineCodeInstructions), (FFI.mcTargetOptionFlagAsmVerbose, TO.verboseAssembly), (FFI.mcTargetOptionFlagPreserveAsmComments, TO.preserveComentsInAssembly) ] -- | get all target options peekTargetOptions :: TargetOptions -> IO TO.Options peekTargetOptions opts@(TargetOptions tOpts) = do let gof = decodeM <=< FFI.getTargetOptionsFlag tOpts printMachineCode <- gof FFI.targetOptionFlagPrintMachineCode unsafeFloatingPointMath <- gof FFI.targetOptionFlagUnsafeFPMath noInfinitiesFloatingPointMath <- gof FFI.targetOptionFlagNoInfsFPMath noNaNsFloatingPointMath <- gof FFI.targetOptionFlagNoNaNsFPMath noTrappingFloatingPointMath <- gof FFI.targetOptionFlagNoTrappingFPMath noSignedZeroesFloatingPointMath <- gof FFI.targetOptionFlagNoSignedZerosFPMath honorSignDependentRoundingFloatingPointMathOption <- gof FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption noZerosInBSS <- gof FFI.targetOptionFlagNoZerosInBSS guaranteedTailCallOptimization <- gof FFI.targetOptionFlagGuaranteedTailCallOpt stackSymbolOrdering <- gof FFI.targetOptionFlagStackSymbolOrdering enableFastInstructionSelection <- gof FFI.targetOptionFlagEnableFastISel useInitArray <- gof FFI.targetOptionFlagUseInitArray disableIntegratedAssembler <- gof FFI.targetOptionFlagDisableIntegratedAS compressDebugSections <- decodeM =<< FFI.getCompressDebugSections tOpts relaxELFRelocations <- gof FFI.targetOptionFlagRelaxELFRelocations functionSections <- gof FFI.targetOptionFlagFunctionSections dataSections <- gof FFI.targetOptionFlagDataSections uniqueSectionNames <- gof FFI.targetOptionFlagUniqueSectionNames trapUnreachable <- gof FFI.targetOptionFlagTrapUnreachable emulatedThreadLocalStorage <- gof FFI.targetOptionFlagEmulatedTLS enableInterProceduralRegisterAllocation <- gof FFI.targetOptionFlagEnableIPRA stackAlignmentOverride <- decodeM =<< FFI.getStackAlignmentOverride tOpts floatABIType <- decodeM =<< FFI.getFloatABIType tOpts allowFloatingPointOperationFusion <- decodeM =<< FFI.getAllowFPOpFusion tOpts threadModel <- decodeM =<< FFI.getThreadModel tOpts eabiVersion <- decodeM =<< FFI.getEABIVersion tOpts debuggerTuning <- decodeM =<< FFI.getDebuggerTuning tOpts floatingPointDenormalMode <- decodeM =<< FFI.getFPDenormalMode tOpts exceptionModel <- decodeM =<< FFI.getExceptionModel tOpts machineCodeOptions <- peekMachineCodeOptions =<< machineCodeOptions opts return TO.Options { .. } -- | get all machine code options peekMachineCodeOptions :: MCTargetOptions -> IO TO.MachineCodeOptions peekMachineCodeOptions (MCTargetOptions tOpts) = do let gof = decodeM <=< FFI.getMCTargetOptionsFlag tOpts sanitizeAddresses <- gof FFI.mcTargetOptionFlagSanitizeAddress relaxAll <- gof FFI.mcTargetOptionFlagMCRelaxAll noExecutableStack <- gof FFI.mcTargetOptionFlagMCNoExecStack fatalWarnings <- gof FFI.mcTargetOptionFlagMCFatalWarnings noWarnings <- gof FFI.mcTargetOptionFlagMCNoWarn noDeprecatedWarning <- gof FFI.mcTargetOptionFlagMCNoDeprecatedWarn saveTemporaryLabels <- gof FFI.mcTargetOptionFlagMCSaveTempLabels useDwarfDirectory <- gof FFI.mcTargetOptionFlagMCUseDwarfDirectory incrementalLinkerCompatible <- gof FFI.mcTargetOptionFlagMCIncrementalLinkerCompatible pieCopyRelocations <- gof FFI.mcTargetOptionFlagMCPIECopyRelocations showMachineCodeEncoding <- gof FFI.mcTargetOptionFlagShowMCEncoding showMachineCodeInstructions <- gof FFI.mcTargetOptionFlagShowMCInst verboseAssembly <- gof FFI.mcTargetOptionFlagAsmVerbose preserveComentsInAssembly <- gof FFI.mcTargetOptionFlagPreserveAsmComments return TO.MachineCodeOptions { .. } -- | newtype TargetMachine = TargetMachine (Ptr FFI.TargetMachine) -- | bracket creation and destruction of a 'TargetMachine' withTargetMachine :: Target -> ShortByteString -- ^ triple -> ByteString -- ^ cpu -> Map CPUFeature Bool -- ^ features -> TargetOptions -> Reloc.Model -> CodeModel.Model -> CodeGenOpt.Level -> (TargetMachine -> IO a) -> IO a withTargetMachine (Target target) triple cpu features (TargetOptions targetOptions) relocModel codeModel codeGenOptLevel = runAnyContT $ do triple <- encodeM triple cpu <- encodeM cpu features <- encodeM features relocModel <- encodeM relocModel codeModel <- encodeM codeModel codeGenOptLevel <- encodeM codeGenOptLevel anyContToM $ bracket ( FFI.createTargetMachine target triple cpu features targetOptions relocModel codeModel codeGenOptLevel ) FFI.disposeTargetMachine . (. TargetMachine) targetMachineOptions :: TargetMachine -> IO TargetOptions targetMachineOptions (TargetMachine tm) = TargetOptions <$> FFI.targetMachineOptions tm machineCodeOptions :: TargetOptions -> IO MCTargetOptions machineCodeOptions (TargetOptions to) = MCTargetOptions <$> FFI.machineCodeOptions to -- | newtype TargetLowering = TargetLowering (Ptr FFI.TargetLowering) -- | get the 'TargetLowering' of a 'TargetMachine' getTargetLowering :: TargetMachine -> IO TargetLowering getTargetLowering (TargetMachine _) = TargetLowering <$> error "FIXME: getTargetLowering" -- FFI.getTargetLowering tm -- | Initialize the native target. This function is called automatically in these Haskell bindings -- when creating an 'LLVM.ExecutionEngine.ExecutionEngine' which will require it, and so it should -- not be necessary to call it separately. initializeNativeTarget :: IO () initializeNativeTarget = do failure <- decodeM =<< liftIO FFI.initializeNativeTarget when failure $ fail "native target initialization failed" -- | the target triple corresponding to the target machine getTargetMachineTriple :: TargetMachine -> IO ShortByteString getTargetMachineTriple (TargetMachine m) = decodeM =<< FFI.getTargetMachineTriple m -- | the default target triple that LLVM has been configured to produce code for getDefaultTargetTriple :: IO ShortByteString getDefaultTargetTriple = decodeM =<< FFI.getDefaultTargetTriple -- | a target triple suitable for loading code into the current process getProcessTargetTriple :: IO ShortByteString getProcessTargetTriple = decodeM =<< FFI.getProcessTargetTriple -- | the LLVM name for the host CPU getHostCPUName :: IO ByteString getHostCPUName = decodeM FFI.getHostCPUName -- | a space-separated list of LLVM feature names supported by the host CPU getHostCPUFeatures :: IO (Map CPUFeature Bool) getHostCPUFeatures = decodeM =<< FFI.getHostCPUFeatures -- | 'DataLayout' to use for the given 'TargetMachine' getTargetMachineDataLayout :: TargetMachine -> IO DataLayout getTargetMachineDataLayout (TargetMachine m) = do dlString <- decodeM =<< FFI.getTargetMachineDataLayout m let Right (Just dl) = runExcept . parseDataLayout BigEndian $ dlString return dl -- | Initialize all targets so they can be found by 'lookupTarget' initializeAllTargets :: IO () initializeAllTargets = FFI.initializeAllTargets -- | Bracket creation and destruction of a 'TargetMachine' configured for the host withHostTargetMachine :: (TargetMachine -> IO a) -> IO a withHostTargetMachine f = do initializeAllTargets triple <- getProcessTargetTriple cpu <- getHostCPUName features <- getHostCPUFeatures (target, _) <- lookupTarget Nothing triple withTargetOptions $ \options -> withTargetMachine target triple cpu features options Reloc.Default CodeModel.Default CodeGenOpt.Default f -- | newtype TargetLibraryInfo = TargetLibraryInfo (Ptr FFI.TargetLibraryInfo) -- | Look up a 'LibraryFunction' by its standard name getLibraryFunction :: TargetLibraryInfo -> ShortByteString -> IO (Maybe LibraryFunction) getLibraryFunction (TargetLibraryInfo f) name = flip runAnyContT return $ do libFuncP <- alloca :: AnyContT IO (Ptr FFI.LibFunc) name <- (encodeM name :: AnyContT IO CString) r <- decodeM =<< (liftIO $ FFI.getLibFunc f name libFuncP) forM (if r then Just libFuncP else Nothing) $ decodeM <=< peek -- | Get a the current name to be emitted for a 'LibraryFunction' getLibraryFunctionName :: TargetLibraryInfo -> LibraryFunction -> IO ShortByteString getLibraryFunctionName (TargetLibraryInfo f) l = flip runAnyContT return $ do l <- encodeM l decodeM $ FFI.libFuncGetName f l -- | Set the name of the function on the target platform that corresponds to funcName setLibraryFunctionAvailableWithName :: TargetLibraryInfo -> LibraryFunction -> ShortByteString -- ^ The function name to be emitted -> IO () setLibraryFunctionAvailableWithName (TargetLibraryInfo f) libraryFunction name = flip runAnyContT return $ do name <- encodeM name libraryFunction <- encodeM libraryFunction liftIO $ FFI.libFuncSetAvailableWithName f libraryFunction name -- | look up information about the library functions available on a given platform withTargetLibraryInfo :: ShortByteString -- ^ triple -> (TargetLibraryInfo -> IO a) -> IO a withTargetLibraryInfo triple f = flip runAnyContT return $ do triple <- encodeM triple liftIO $ bracket (FFI.createTargetLibraryInfo triple) FFI.disposeTargetLibraryInfo (f . TargetLibraryInfo)