llvm-ffi-17.0: example/JIT.hs
{-# LANGUAGE ForeignFunctionInterface #-}
{- |
This program has two purposes:
First a minimalistic demonstration of
how to generate and run and optimize code with LLVM.
Second a test program that forces to run the linker.
It let us check whether Haskell bindings match C functions.
-}
module Main where
import Common (withArrayLen, createExecutionEngine)
import qualified LLVM.FFI.Transforms.PassBuilder as PB
import qualified LLVM.FFI.TargetMachine as TM
import qualified LLVM.FFI.Error as Error
import qualified LLVM.FFI.ExecutionEngine as EE
import qualified LLVM.FFI.BitWriter as BW
import qualified LLVM.FFI.Core as Core
import qualified LLVM.Target.Native as Native
import qualified System.IO as IO
import System.Exit (exitFailure)
import qualified Foreign.Marshal.Array as Array
import qualified Foreign.Marshal.Alloc as Alloc
import qualified Foreign.C.String as CStr
import Foreign.C.String (withCString)
import Foreign.C.Types (CFloat)
import Foreign.Storable (peek, sizeOf)
import Foreign.Ptr (Ptr, FunPtr, nullPtr)
import Control.Exception (bracket)
import Control.Monad (when, void)
import Data.Tuple.HT (mapSnd)
vectorSize :: Int
roundName :: String
(vectorSize, roundName) =
if False
then (4, "llvm.x86.sse41.round.ps")
else (8, "llvm.x86.avx.round.ps.256")
type Importer f = FunPtr f -> f
foreign import ccall safe "dynamic" derefFuncPtr :: Importer (Ptr a -> IO ())
die :: String -> IO a
die msg = do
IO.hPutStrLn IO.stderr msg
exitFailure
exitFromError :: Ptr CStr.CString -> IO a
exitFromError errorRef =
bracket (peek errorRef) Alloc.free $ \errorMsg ->
CStr.peekCString errorMsg >>= die
main :: IO ()
main = do
Native.initializeNativeTarget
modul <- withCString "_module" Core.moduleCreateWithName
withCString Core.hostTriple $ Core.setTarget modul
vectorType <-
if True
then Core.floatType >>= flip Core.vectorType (fromIntegral vectorSize)
else Core.floatType
ptrType <- Core.pointerType vectorType 0
voidType <- Core.voidType
let params = [ptrType]
roundType <-
withArrayLen params $ \len ps ->
Core.functionType voidType ps len Core.false
func <- withCString "round" $ \name -> Core.addFunction modul name roundType
Core.setLinkage func $ Core.fromLinkage Core.ExternalLinkage
builder <- Core.createBuilder
block <- withCString "_L1" $ Core.appendBasicBlock func
Core.positionAtEnd builder block
param <- Core.getParam func 0
loaded <- withCString "" $ Core.buildLoad2 builder vectorType param
int32Type <- Core.int32Type
let funcParams = [vectorType, int32Type]
funcType <-
withArrayLen funcParams $ \len ps ->
Core.functionType vectorType ps len Core.false
roundFunc <-
withCString roundName $ \name -> Core.addFunction modul name funcType
Core.setLinkage roundFunc $ Core.fromLinkage Core.ExternalLinkage
callRound <-
if True
then do
const1 <- Core.constInt int32Type 1 Core.false
let callParams = [loaded, const1]
call <-
withArrayLen callParams $ \len ps ->
withCString "" $
Core.buildCall2 builder funcType roundFunc ps len
Core.setInstructionCallConv call $
Core.fromCallingConvention Core.C
context <- Core.getGlobalContext
attrKind <-
CStr.withCStringLen "nofree" $
uncurry Core.getEnumAttributeKindForName . mapSnd fromIntegral
attr <- Core.createEnumAttribute context attrKind 0
Core.addCallSiteAttribute call Core.attributeFunctionIndex attr
return call
else do
void $ withCString "" $ Core.buildFAdd builder loaded loaded
zero <- Core.constNull vectorType
add <- withCString "" $ Core.buildFAdd builder loaded zero
Core.setHasNoSignedZeros add Core.true
return add
void $ Core.buildStore builder callRound param
void $ Core.buildRetVoid builder
void $ withCString "round-avx.bc" $ BW.writeBitcodeToFile modul
when True $
withCString Core.hostTriple $ \triple ->
(\cont -> do
target <-
Alloc.alloca $ \targetRef ->
Alloc.alloca $ \errorRef -> do
failure <- TM.getTargetFromTriple triple targetRef errorRef
if Core.deconsBool failure
then exitFromError errorRef
else peek targetRef
cpu <- TM.getHostCPUName
cont target cpu) $ \target cpu ->
withCString "" $ \features ->
bracket
(TM.createTargetMachine target triple cpu features
TM.codeGenLevelDefault TM.relocDefault TM.codeModelDefault)
TM.disposeTargetMachine $
\tm ->
bracket PB.createPassBuilderOptions PB.disposePassBuilderOptions $
\pbOpt ->
withCString "default<O2>" $ \passName -> do
PB.passBuilderOptionsSetVerifyEach pbOpt Core.true
do
errorRef <- PB.runPasses modul passName tm pbOpt
when (errorRef /= nullPtr) $
bracket
(Error.getErrorMessage errorRef)
Error.disposeErrorMessage
$ \errorMsg ->
CStr.peekCString errorMsg >>= die
withCString "round-avx-opt.s" $ \path ->
Alloc.alloca $ \errorRef -> do
failure <-
TM.targetMachineEmitToFile
tm modul path TM.assemblyFile errorRef
when (Core.deconsBool failure) $
exitFromError errorRef
void $ withCString "round-avx-opt.bc" $ BW.writeBitcodeToFile modul
bracket (createExecutionEngine modul) EE.disposeExecutionEngine $
\execEngine -> do
let vector = take vectorSize $ iterate (1+) (-1.3 :: CFloat)
funcPtr <- EE.getPointerToFunction execEngine func
let size = sum $ map sizeOf vector
Alloc.allocaBytesAligned size size $ \ptr -> do
Array.pokeArray ptr vector
derefFuncPtr funcPtr ptr
print =<< Array.peekArray vectorSize ptr