ASTs and Modules

We’ll refer to a Module as holding the internal representation of the LLVM IR. Modules can be generated from the Haskell LLVM AST or from strings containing bitcode.

Both data types have the same name ( Module ), so as convention we will qualify the imports of the libraries to distinguish between the two.

  • AST.Module : Haskell AST Module
  • Module : Internal LLVM Module

llvm-hs provides two important functions for converting between them. withModuleFromAST has type ExceptT since it may fail if given a malformed expression, it is important to handle both cases of the resulting Either value.

  1. withModuleFromAST :: Context -> AST.Module -> (Module -> IO a) -> ExceptT String IO a
  2. moduleAST :: Module -> IO AST.Module

We can also generate the assembly code for our given module by passing a specification of the CPU and platform information we wish to target, called the TargetMachine.

  1. moduleTargetAssembly :: TargetMachine -> Module -> ExceptT String IO String

Recall the so called “Bracket” pattern in Haskell for managing IO resources. llvm-hs makes heavy use this pattern to manage the life-cycle of certain LLVM resources. It is very important to remember not to pass or attempt to use resources outside of the bracket as this will lead to undefined behavior and/or segfaults.

  1. bracket :: IO a -- computation to run first ("acquire resource")
  2. -> (a -> IO b) -- computation to run last ("release resource")
  3. -> (a -> IO c) -- computation to run in-between
  4. -> IO c

In addition to this we’ll often be dealing with operations which can fail in an EitherT monad if given bad code. We’ll often want to lift this error up the monad transformer stack with the pattern:

  1. liftExcept :: ExceptT String IO a -> IO a
  2. liftExcept = runExceptT >=> either fail return

To start we’ll create a runJIT function which will start with a stack of brackets. We’ll then simply generate the IR and print it out to the screen.

  1. runJIT :: AST.Module -> IO (Either String ())
  2. runJIT mod = do
  3. withContext $ \context ->
  4. runErrorT $ withModuleFromAST context mod $ \m ->
  5. s <- moduleLLVMAssembly m
  6. putStrLn s